10Sigor@sysoev.ru
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru */
60Sigor@sysoev.ru
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru
90Sigor@sysoev.ru
100Sigor@sysoev.ru /*
110Sigor@sysoev.ru * Signals are handled only via a main thread event engine work queue.
120Sigor@sysoev.ru * There are three ways to route signals to the work queue:
130Sigor@sysoev.ru *
140Sigor@sysoev.ru * 1) Using signal event notifications if an event facility supports it:
150Sigor@sysoev.ru * kqueue and epoll/signalfd. This method is used regardless of thread mode.
160Sigor@sysoev.ru *
170Sigor@sysoev.ru * 2) Multi-threaded mode: a dedicated signal thread which waits in sigwait()
180Sigor@sysoev.ru * and post a signal number to the main thread event engine.
190Sigor@sysoev.ru *
200Sigor@sysoev.ru * 3) Single-threaded mode: a signal handler which posts a signal number
210Sigor@sysoev.ru * to the event engine.
220Sigor@sysoev.ru */
230Sigor@sysoev.ru
240Sigor@sysoev.ru
250Sigor@sysoev.ru static nxt_int_t nxt_signal_action(int signo, void (*handler)(int));
26*223Sigor@sysoev.ru static void nxt_signal_thread(void *data);
270Sigor@sysoev.ru
280Sigor@sysoev.ru
290Sigor@sysoev.ru nxt_event_signals_t *
nxt_event_engine_signals(const nxt_sig_event_t * sigev)3012Sigor@sysoev.ru nxt_event_engine_signals(const nxt_sig_event_t *sigev)
310Sigor@sysoev.ru {
320Sigor@sysoev.ru nxt_event_signals_t *signals;
330Sigor@sysoev.ru
340Sigor@sysoev.ru signals = nxt_zalloc(sizeof(nxt_event_signals_t));
350Sigor@sysoev.ru if (signals == NULL) {
360Sigor@sysoev.ru return NULL;
370Sigor@sysoev.ru }
380Sigor@sysoev.ru
390Sigor@sysoev.ru signals->sigev = sigev;
400Sigor@sysoev.ru
410Sigor@sysoev.ru if (nxt_signal_action(SIGSYS, SIG_IGN) != NXT_OK) {
420Sigor@sysoev.ru goto fail;
430Sigor@sysoev.ru }
440Sigor@sysoev.ru
450Sigor@sysoev.ru if (nxt_signal_action(SIGPIPE, SIG_IGN) != NXT_OK) {
460Sigor@sysoev.ru goto fail;
470Sigor@sysoev.ru }
480Sigor@sysoev.ru
490Sigor@sysoev.ru sigemptyset(&signals->sigmask);
500Sigor@sysoev.ru
510Sigor@sysoev.ru while (sigev->signo != 0) {
520Sigor@sysoev.ru sigaddset(&signals->sigmask, sigev->signo);
530Sigor@sysoev.ru sigev++;
540Sigor@sysoev.ru }
550Sigor@sysoev.ru
560Sigor@sysoev.ru if (sigprocmask(SIG_BLOCK, &signals->sigmask, NULL) != 0) {
570Sigor@sysoev.ru nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
580Sigor@sysoev.ru goto fail;
590Sigor@sysoev.ru }
600Sigor@sysoev.ru
610Sigor@sysoev.ru return signals;
620Sigor@sysoev.ru
630Sigor@sysoev.ru fail:
640Sigor@sysoev.ru
650Sigor@sysoev.ru nxt_free(signals);
660Sigor@sysoev.ru
670Sigor@sysoev.ru return NULL;
680Sigor@sysoev.ru }
690Sigor@sysoev.ru
700Sigor@sysoev.ru
710Sigor@sysoev.ru static nxt_int_t
nxt_signal_action(int signo,void (* handler)(int))720Sigor@sysoev.ru nxt_signal_action(int signo, void (*handler)(int))
730Sigor@sysoev.ru {
740Sigor@sysoev.ru struct sigaction sa;
750Sigor@sysoev.ru
760Sigor@sysoev.ru nxt_memzero(&sa, sizeof(struct sigaction));
770Sigor@sysoev.ru sigemptyset(&sa.sa_mask);
780Sigor@sysoev.ru sa.sa_handler = handler;
790Sigor@sysoev.ru
800Sigor@sysoev.ru if (sigaction(signo, &sa, NULL) == 0) {
810Sigor@sysoev.ru return NXT_OK;
820Sigor@sysoev.ru }
830Sigor@sysoev.ru
840Sigor@sysoev.ru nxt_main_log_alert("sigaction(%d) failed %E", signo, nxt_errno);
850Sigor@sysoev.ru
860Sigor@sysoev.ru return NXT_ERROR;
870Sigor@sysoev.ru }
880Sigor@sysoev.ru
890Sigor@sysoev.ru
900Sigor@sysoev.ru static void
nxt_signal_handler(int signo)910Sigor@sysoev.ru nxt_signal_handler(int signo)
920Sigor@sysoev.ru {
930Sigor@sysoev.ru nxt_thread_t *thr;
940Sigor@sysoev.ru
950Sigor@sysoev.ru thr = nxt_thread();
960Sigor@sysoev.ru
970Sigor@sysoev.ru /* Thread is running in a single context now. */
980Sigor@sysoev.ru thr->time.signal++;
990Sigor@sysoev.ru
1000Sigor@sysoev.ru nxt_thread_time_update(thr);
1010Sigor@sysoev.ru
1020Sigor@sysoev.ru nxt_main_log_error(NXT_LOG_INFO, "signal handler: %d", signo);
1030Sigor@sysoev.ru
1040Sigor@sysoev.ru nxt_event_engine_signal(thr->engine, signo);
1050Sigor@sysoev.ru
1060Sigor@sysoev.ru thr->time.signal--;
1070Sigor@sysoev.ru }
1080Sigor@sysoev.ru
1090Sigor@sysoev.ru
1100Sigor@sysoev.ru nxt_int_t
nxt_signal_thread_start(nxt_event_engine_t * engine)1110Sigor@sysoev.ru nxt_signal_thread_start(nxt_event_engine_t *engine)
1120Sigor@sysoev.ru {
1130Sigor@sysoev.ru nxt_thread_link_t *link;
11412Sigor@sysoev.ru const nxt_sig_event_t *sigev;
1150Sigor@sysoev.ru
1160Sigor@sysoev.ru if (engine->signals->process == nxt_pid) {
1170Sigor@sysoev.ru return NXT_OK;
1180Sigor@sysoev.ru }
1190Sigor@sysoev.ru
1200Sigor@sysoev.ru if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) {
1210Sigor@sysoev.ru nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
1220Sigor@sysoev.ru return NXT_ERROR;
1230Sigor@sysoev.ru }
1240Sigor@sysoev.ru
1250Sigor@sysoev.ru /*
1260Sigor@sysoev.ru * kqueue sets signal handlers to SIG_IGN and sigwait() ignores
1270Sigor@sysoev.ru * them after the switch of event facility from "kqueue" to "select".
1280Sigor@sysoev.ru */
1290Sigor@sysoev.ru
1300Sigor@sysoev.ru for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) {
1310Sigor@sysoev.ru if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) {
1320Sigor@sysoev.ru return NXT_ERROR;
1330Sigor@sysoev.ru }
1340Sigor@sysoev.ru }
1350Sigor@sysoev.ru
1360Sigor@sysoev.ru link = nxt_zalloc(sizeof(nxt_thread_link_t));
1370Sigor@sysoev.ru
1380Sigor@sysoev.ru if (nxt_fast_path(link != NULL)) {
1390Sigor@sysoev.ru link->start = nxt_signal_thread;
14053Sigor@sysoev.ru link->work.data = engine;
1410Sigor@sysoev.ru
1420Sigor@sysoev.ru if (nxt_thread_create(&engine->signals->thread, link) == NXT_OK) {
1430Sigor@sysoev.ru engine->signals->process = nxt_pid;
1440Sigor@sysoev.ru return NXT_OK;
1450Sigor@sysoev.ru }
1460Sigor@sysoev.ru }
1470Sigor@sysoev.ru
1480Sigor@sysoev.ru return NXT_ERROR;
1490Sigor@sysoev.ru }
1500Sigor@sysoev.ru
1510Sigor@sysoev.ru
1520Sigor@sysoev.ru static void
nxt_signal_thread(void * data)1530Sigor@sysoev.ru nxt_signal_thread(void *data)
1540Sigor@sysoev.ru {
1550Sigor@sysoev.ru int signo;
1560Sigor@sysoev.ru nxt_err_t err;
1570Sigor@sysoev.ru nxt_thread_t *thr;
1580Sigor@sysoev.ru nxt_event_engine_t *engine;
1590Sigor@sysoev.ru
1600Sigor@sysoev.ru engine = data;
1610Sigor@sysoev.ru
1620Sigor@sysoev.ru thr = nxt_thread();
1630Sigor@sysoev.ru
1640Sigor@sysoev.ru nxt_main_log_debug("signal thread");
1650Sigor@sysoev.ru
1660Sigor@sysoev.ru for ( ;; ) {
1670Sigor@sysoev.ru err = sigwait(&engine->signals->sigmask, &signo);
1680Sigor@sysoev.ru
1690Sigor@sysoev.ru nxt_thread_time_update(thr);
1700Sigor@sysoev.ru
1710Sigor@sysoev.ru if (nxt_fast_path(err == 0)) {
1720Sigor@sysoev.ru nxt_main_log_error(NXT_LOG_INFO, "signo: %d", signo);
1730Sigor@sysoev.ru
1740Sigor@sysoev.ru nxt_event_engine_signal(engine, signo);
1750Sigor@sysoev.ru
1760Sigor@sysoev.ru } else {
1770Sigor@sysoev.ru nxt_main_log_alert("sigwait() failed %E", err);
1780Sigor@sysoev.ru }
1790Sigor@sysoev.ru }
1800Sigor@sysoev.ru }
1810Sigor@sysoev.ru
1820Sigor@sysoev.ru
1830Sigor@sysoev.ru void
nxt_signal_thread_stop(nxt_event_engine_t * engine)1840Sigor@sysoev.ru nxt_signal_thread_stop(nxt_event_engine_t *engine)
1850Sigor@sysoev.ru {
1860Sigor@sysoev.ru nxt_thread_handle_t thread;
1870Sigor@sysoev.ru
1880Sigor@sysoev.ru thread = engine->signals->thread;
1890Sigor@sysoev.ru
1900Sigor@sysoev.ru nxt_thread_cancel(thread);
1910Sigor@sysoev.ru nxt_thread_wait(thread);
1920Sigor@sysoev.ru }
193