xref: /unit/src/nxt_signal.c (revision 12)
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));
260Sigor@sysoev.ru 
270Sigor@sysoev.ru 
280Sigor@sysoev.ru nxt_event_signals_t *
29*12Sigor@sysoev.ru nxt_event_engine_signals(const nxt_sig_event_t *sigev)
300Sigor@sysoev.ru {
310Sigor@sysoev.ru     nxt_event_signals_t  *signals;
320Sigor@sysoev.ru 
330Sigor@sysoev.ru     signals = nxt_zalloc(sizeof(nxt_event_signals_t));
340Sigor@sysoev.ru     if (signals == NULL) {
350Sigor@sysoev.ru         return NULL;
360Sigor@sysoev.ru     }
370Sigor@sysoev.ru 
380Sigor@sysoev.ru     signals->sigev = sigev;
390Sigor@sysoev.ru 
400Sigor@sysoev.ru     if (nxt_signal_action(SIGSYS, SIG_IGN) != NXT_OK) {
410Sigor@sysoev.ru         goto fail;
420Sigor@sysoev.ru     }
430Sigor@sysoev.ru 
440Sigor@sysoev.ru     if (nxt_signal_action(SIGPIPE, SIG_IGN) != NXT_OK) {
450Sigor@sysoev.ru         goto fail;
460Sigor@sysoev.ru     }
470Sigor@sysoev.ru 
480Sigor@sysoev.ru     sigemptyset(&signals->sigmask);
490Sigor@sysoev.ru 
500Sigor@sysoev.ru     while (sigev->signo != 0) {
510Sigor@sysoev.ru         sigaddset(&signals->sigmask, sigev->signo);
520Sigor@sysoev.ru         sigev++;
530Sigor@sysoev.ru     }
540Sigor@sysoev.ru 
550Sigor@sysoev.ru     if (sigprocmask(SIG_BLOCK, &signals->sigmask, NULL) != 0) {
560Sigor@sysoev.ru         nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
570Sigor@sysoev.ru         goto fail;
580Sigor@sysoev.ru     }
590Sigor@sysoev.ru 
600Sigor@sysoev.ru     return signals;
610Sigor@sysoev.ru 
620Sigor@sysoev.ru fail:
630Sigor@sysoev.ru 
640Sigor@sysoev.ru     nxt_free(signals);
650Sigor@sysoev.ru 
660Sigor@sysoev.ru     return NULL;
670Sigor@sysoev.ru }
680Sigor@sysoev.ru 
690Sigor@sysoev.ru 
700Sigor@sysoev.ru static nxt_int_t
710Sigor@sysoev.ru nxt_signal_action(int signo, void (*handler)(int))
720Sigor@sysoev.ru {
730Sigor@sysoev.ru     struct sigaction  sa;
740Sigor@sysoev.ru 
750Sigor@sysoev.ru     nxt_memzero(&sa, sizeof(struct sigaction));
760Sigor@sysoev.ru     sigemptyset(&sa.sa_mask);
770Sigor@sysoev.ru     sa.sa_handler = handler;
780Sigor@sysoev.ru 
790Sigor@sysoev.ru     if (sigaction(signo, &sa, NULL) == 0) {
800Sigor@sysoev.ru         return NXT_OK;
810Sigor@sysoev.ru     }
820Sigor@sysoev.ru 
830Sigor@sysoev.ru     nxt_main_log_alert("sigaction(%d) failed %E", signo, nxt_errno);
840Sigor@sysoev.ru 
850Sigor@sysoev.ru     return NXT_ERROR;
860Sigor@sysoev.ru }
870Sigor@sysoev.ru 
880Sigor@sysoev.ru 
890Sigor@sysoev.ru static void
900Sigor@sysoev.ru nxt_signal_handler(int signo)
910Sigor@sysoev.ru {
920Sigor@sysoev.ru     nxt_thread_t  *thr;
930Sigor@sysoev.ru 
940Sigor@sysoev.ru     thr = nxt_thread();
950Sigor@sysoev.ru 
960Sigor@sysoev.ru     /* Thread is running in a single context now. */
970Sigor@sysoev.ru     thr->time.signal++;
980Sigor@sysoev.ru 
990Sigor@sysoev.ru     nxt_thread_time_update(thr);
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru     nxt_main_log_error(NXT_LOG_INFO, "signal handler: %d", signo);
1020Sigor@sysoev.ru 
1030Sigor@sysoev.ru     nxt_event_engine_signal(thr->engine, signo);
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru     thr->time.signal--;
1060Sigor@sysoev.ru }
1070Sigor@sysoev.ru 
1080Sigor@sysoev.ru 
1090Sigor@sysoev.ru #if (NXT_THREADS)
1100Sigor@sysoev.ru 
1110Sigor@sysoev.ru static void nxt_signal_thread(void *data);
1120Sigor@sysoev.ru 
1130Sigor@sysoev.ru 
1140Sigor@sysoev.ru nxt_int_t
1150Sigor@sysoev.ru nxt_signal_thread_start(nxt_event_engine_t *engine)
1160Sigor@sysoev.ru {
1170Sigor@sysoev.ru     nxt_thread_link_t      *link;
118*12Sigor@sysoev.ru     const nxt_sig_event_t  *sigev;
1190Sigor@sysoev.ru 
1200Sigor@sysoev.ru     if (engine->signals->process == nxt_pid) {
1210Sigor@sysoev.ru         return NXT_OK;
1220Sigor@sysoev.ru     }
1230Sigor@sysoev.ru 
1240Sigor@sysoev.ru     if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) {
1250Sigor@sysoev.ru         nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
1260Sigor@sysoev.ru         return NXT_ERROR;
1270Sigor@sysoev.ru     }
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru     /*
1300Sigor@sysoev.ru      * kqueue sets signal handlers to SIG_IGN and sigwait() ignores
1310Sigor@sysoev.ru      * them after the switch of event facility from "kqueue" to "select".
1320Sigor@sysoev.ru      */
1330Sigor@sysoev.ru 
1340Sigor@sysoev.ru     for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) {
1350Sigor@sysoev.ru         if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) {
1360Sigor@sysoev.ru             return NXT_ERROR;
1370Sigor@sysoev.ru         }
1380Sigor@sysoev.ru     }
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru     link = nxt_zalloc(sizeof(nxt_thread_link_t));
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru     if (nxt_fast_path(link != NULL)) {
1430Sigor@sysoev.ru         link->start = nxt_signal_thread;
1440Sigor@sysoev.ru         link->data = engine;
1450Sigor@sysoev.ru 
1460Sigor@sysoev.ru         if (nxt_thread_create(&engine->signals->thread, link) == NXT_OK) {
1470Sigor@sysoev.ru             engine->signals->process = nxt_pid;
1480Sigor@sysoev.ru             return NXT_OK;
1490Sigor@sysoev.ru         }
1500Sigor@sysoev.ru     }
1510Sigor@sysoev.ru 
1520Sigor@sysoev.ru     return NXT_ERROR;
1530Sigor@sysoev.ru }
1540Sigor@sysoev.ru 
1550Sigor@sysoev.ru 
1560Sigor@sysoev.ru static void
1570Sigor@sysoev.ru nxt_signal_thread(void *data)
1580Sigor@sysoev.ru {
1590Sigor@sysoev.ru     int                 signo;
1600Sigor@sysoev.ru     nxt_err_t           err;
1610Sigor@sysoev.ru     nxt_thread_t        *thr;
1620Sigor@sysoev.ru     nxt_event_engine_t  *engine;
1630Sigor@sysoev.ru 
1640Sigor@sysoev.ru     engine = data;
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru     thr = nxt_thread();
1670Sigor@sysoev.ru 
1680Sigor@sysoev.ru     nxt_main_log_debug("signal thread");
1690Sigor@sysoev.ru 
1700Sigor@sysoev.ru     for ( ;; ) {
1710Sigor@sysoev.ru         err = sigwait(&engine->signals->sigmask, &signo);
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru         nxt_thread_time_update(thr);
1740Sigor@sysoev.ru 
1750Sigor@sysoev.ru         if (nxt_fast_path(err == 0)) {
1760Sigor@sysoev.ru             nxt_main_log_error(NXT_LOG_INFO, "signo: %d", signo);
1770Sigor@sysoev.ru 
1780Sigor@sysoev.ru             nxt_event_engine_signal(engine, signo);
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru         } else {
1810Sigor@sysoev.ru             nxt_main_log_alert("sigwait() failed %E", err);
1820Sigor@sysoev.ru         }
1830Sigor@sysoev.ru     }
1840Sigor@sysoev.ru }
1850Sigor@sysoev.ru 
1860Sigor@sysoev.ru 
1870Sigor@sysoev.ru void
1880Sigor@sysoev.ru nxt_signal_thread_stop(nxt_event_engine_t *engine)
1890Sigor@sysoev.ru {
1900Sigor@sysoev.ru     nxt_thread_handle_t  thread;
1910Sigor@sysoev.ru 
1920Sigor@sysoev.ru     thread = engine->signals->thread;
1930Sigor@sysoev.ru 
1940Sigor@sysoev.ru     nxt_thread_cancel(thread);
1950Sigor@sysoev.ru     nxt_thread_wait(thread);
1960Sigor@sysoev.ru }
1970Sigor@sysoev.ru 
1980Sigor@sysoev.ru 
1990Sigor@sysoev.ru #else /* !(NXT_THREADS) */
2000Sigor@sysoev.ru 
2010Sigor@sysoev.ru 
2020Sigor@sysoev.ru nxt_int_t
2030Sigor@sysoev.ru nxt_signal_handlers_start(nxt_event_engine_t *engine)
2040Sigor@sysoev.ru {
205*12Sigor@sysoev.ru     const nxt_sig_event_t  *sigev;
2060Sigor@sysoev.ru 
2070Sigor@sysoev.ru     for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) {
2080Sigor@sysoev.ru         if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) {
2090Sigor@sysoev.ru             return NXT_ERROR;
2100Sigor@sysoev.ru         }
2110Sigor@sysoev.ru     }
2120Sigor@sysoev.ru 
2130Sigor@sysoev.ru     if (sigprocmask(SIG_UNBLOCK, &engine->signals->sigmask, NULL) != 0) {
2140Sigor@sysoev.ru         nxt_main_log_alert("sigprocmask(SIG_UNBLOCK) failed %E", nxt_errno);
2150Sigor@sysoev.ru         return NXT_ERROR;
2160Sigor@sysoev.ru     }
2170Sigor@sysoev.ru 
2180Sigor@sysoev.ru     return NXT_OK;
2190Sigor@sysoev.ru }
2200Sigor@sysoev.ru 
2210Sigor@sysoev.ru 
2220Sigor@sysoev.ru void
2230Sigor@sysoev.ru nxt_signal_handlers_stop(nxt_event_engine_t *engine)
2240Sigor@sysoev.ru {
2250Sigor@sysoev.ru     if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) {
2260Sigor@sysoev.ru         nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
2270Sigor@sysoev.ru     }
2280Sigor@sysoev.ru }
2290Sigor@sysoev.ru 
2300Sigor@sysoev.ru #endif
231