/* * Copyright (C) Igor Sysoev * Copyright (C) NGINX, Inc. */ #include /* * Signals are handled only via a main thread event engine work queue. * There are three ways to route signals to the work queue: * * 1) Using signal event notifications if an event facility supports it: * kqueue and epoll/signalfd. This method is used regardless of thread mode. * * 2) Multi-threaded mode: a dedicated signal thread which waits in sigwait() * and post a signal number to the main thread event engine. * * 3) Single-threaded mode: a signal handler which posts a signal number * to the event engine. */ static nxt_int_t nxt_signal_action(int signo, void (*handler)(int)); static void nxt_signal_thread(void *data); nxt_event_signals_t * nxt_event_engine_signals(const nxt_sig_event_t *sigev) { nxt_event_signals_t *signals; signals = nxt_zalloc(sizeof(nxt_event_signals_t)); if (signals == NULL) { return NULL; } signals->sigev = sigev; if (nxt_signal_action(SIGSYS, SIG_IGN) != NXT_OK) { goto fail; } if (nxt_signal_action(SIGPIPE, SIG_IGN) != NXT_OK) { goto fail; } sigemptyset(&signals->sigmask); while (sigev->signo != 0) { sigaddset(&signals->sigmask, sigev->signo); sigev++; } if (sigprocmask(SIG_BLOCK, &signals->sigmask, NULL) != 0) { nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno); goto fail; } return signals; fail: nxt_free(signals); return NULL; } static nxt_int_t nxt_signal_action(int signo, void (*handler)(int)) { struct sigaction sa; nxt_memzero(&sa, sizeof(struct sigaction)); sigemptyset(&sa.sa_mask); sa.sa_handler = handler; if (sigaction(signo, &sa, NULL) == 0) { return NXT_OK; } nxt_main_log_alert("sigaction(%d) failed %E", signo, nxt_errno); return NXT_ERROR; } static void nxt_signal_handler(int signo) { nxt_thread_t *thr; thr = nxt_thread(); /* Thread is running in a single context now. */ thr->time.signal++; nxt_thread_time_update(thr); nxt_main_log_error(NXT_LOG_INFO, "signal handler: %d", signo); nxt_event_engine_signal(thr->engine, signo); thr->time.signal--; } nxt_int_t nxt_signal_thread_start(nxt_event_engine_t *engine) { nxt_thread_link_t *link; const nxt_sig_event_t *sigev; if (engine->signals->process == nxt_pid) { return NXT_OK; } if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) { nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno); return NXT_ERROR; } /* * kqueue sets signal handlers to SIG_IGN and sigwait() ignores * them after the switch of event facility from "kqueue" to "select". */ for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) { if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) { return NXT_ERROR; } } link = nxt_zalloc(sizeof(nxt_thread_link_t)); if (nxt_fast_path(link != NULL)) { link->start = nxt_signal_thread; link->work.data = engine; if (nxt_thread_create(&engine->signals->thread, link) == NXT_OK) { engine->signals->process = nxt_pid; return NXT_OK; } } return NXT_ERROR; } static void nxt_signal_thread(void *data) { int signo; nxt_err_t err; nxt_thread_t *thr; nxt_event_engine_t *engine; engine = data; thr = nxt_thread(); nxt_main_log_debug("signal thread"); for ( ;; ) { err = sigwait(&engine->signals->sigmask, &signo); nxt_thread_time_update(thr); if (nxt_fast_path(err == 0)) { nxt_main_log_error(NXT_LOG_INFO, "signo: %d", signo); nxt_event_engine_signal(engine, signo); } else { nxt_main_log_alert("sigwait() failed %E", err); } } } void nxt_signal_thread_stop(nxt_event_engine_t *engine) { nxt_thread_handle_t thread; thread = engine->signals->thread; nxt_thread_cancel(thread); nxt_thread_wait(thread); }