1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 /*
11  * Signals are handled only via a main thread event engine work queue.
12  * There are three ways to route signals to the work queue:
13  *
14  * 1) Using signal event notifications if an event facility supports it:
15  *    kqueue and epoll/signalfd.  This method is used regardless of thread mode.
16  *
17  * 2) Multi-threaded mode: a dedicated signal thread which waits in sigwait()
18  *    and post a signal number to the main thread event engine.
19  *
20  * 3) Single-threaded mode: a signal handler which posts a signal number
21  *    to the event engine.
22  */
23 
24 
25 static nxt_int_t nxt_signal_action(int signo, void (*handler)(int));
26 static void nxt_signal_thread(void *data);
27 
28 
29 nxt_event_signals_t *
nxt_event_engine_signals(const nxt_sig_event_t * sigev)30 nxt_event_engine_signals(const nxt_sig_event_t *sigev)
31 {
32     nxt_event_signals_t  *signals;
33 
34     signals = nxt_zalloc(sizeof(nxt_event_signals_t));
35     if (signals == NULL) {
36         return NULL;
37     }
38 
39     signals->sigev = sigev;
40 
41     if (nxt_signal_action(SIGSYS, SIG_IGN) != NXT_OK) {
42         goto fail;
43     }
44 
45     if (nxt_signal_action(SIGPIPE, SIG_IGN) != NXT_OK) {
46         goto fail;
47     }
48 
49     sigemptyset(&signals->sigmask);
50 
51     while (sigev->signo != 0) {
52         sigaddset(&signals->sigmask, sigev->signo);
53         sigev++;
54     }
55 
56     if (sigprocmask(SIG_BLOCK, &signals->sigmask, NULL) != 0) {
57         nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
58         goto fail;
59     }
60 
61     return signals;
62 
63 fail:
64 
65     nxt_free(signals);
66 
67     return NULL;
68 }
69 
70 
71 static nxt_int_t
nxt_signal_action(int signo,void (* handler)(int))72 nxt_signal_action(int signo, void (*handler)(int))
73 {
74     struct sigaction  sa;
75 
76     nxt_memzero(&sa, sizeof(struct sigaction));
77     sigemptyset(&sa.sa_mask);
78     sa.sa_handler = handler;
79 
80     if (sigaction(signo, &sa, NULL) == 0) {
81         return NXT_OK;
82     }
83 
84     nxt_main_log_alert("sigaction(%d) failed %E", signo, nxt_errno);
85 
86     return NXT_ERROR;
87 }
88 
89 
90 static void
nxt_signal_handler(int signo)91 nxt_signal_handler(int signo)
92 {
93     nxt_thread_t  *thr;
94 
95     thr = nxt_thread();
96 
97     /* Thread is running in a single context now. */
98     thr->time.signal++;
99 
100     nxt_thread_time_update(thr);
101 
102     nxt_main_log_error(NXT_LOG_INFO, "signal handler: %d", signo);
103 
104     nxt_event_engine_signal(thr->engine, signo);
105 
106     thr->time.signal--;
107 }
108 
109 
110 nxt_int_t
nxt_signal_thread_start(nxt_event_engine_t * engine)111 nxt_signal_thread_start(nxt_event_engine_t *engine)
112 {
113     nxt_thread_link_t      *link;
114     const nxt_sig_event_t  *sigev;
115 
116     if (engine->signals->process == nxt_pid) {
117         return NXT_OK;
118     }
119 
120     if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) {
121         nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
122         return NXT_ERROR;
123     }
124 
125     /*
126      * kqueue sets signal handlers to SIG_IGN and sigwait() ignores
127      * them after the switch of event facility from "kqueue" to "select".
128      */
129 
130     for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) {
131         if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) {
132             return NXT_ERROR;
133         }
134     }
135 
136     link = nxt_zalloc(sizeof(nxt_thread_link_t));
137 
138     if (nxt_fast_path(link != NULL)) {
139         link->start = nxt_signal_thread;
140         link->work.data = engine;
141 
142         if (nxt_thread_create(&engine->signals->thread, link) == NXT_OK) {
143             engine->signals->process = nxt_pid;
144             return NXT_OK;
145         }
146     }
147 
148     return NXT_ERROR;
149 }
150 
151 
152 static void
nxt_signal_thread(void * data)153 nxt_signal_thread(void *data)
154 {
155     int                 signo;
156     nxt_err_t           err;
157     nxt_thread_t        *thr;
158     nxt_event_engine_t  *engine;
159 
160     engine = data;
161 
162     thr = nxt_thread();
163 
164     nxt_main_log_debug("signal thread");
165 
166     for ( ;; ) {
167         err = sigwait(&engine->signals->sigmask, &signo);
168 
169         nxt_thread_time_update(thr);
170 
171         if (nxt_fast_path(err == 0)) {
172             nxt_main_log_error(NXT_LOG_INFO, "signo: %d", signo);
173 
174             nxt_event_engine_signal(engine, signo);
175 
176         } else {
177             nxt_main_log_alert("sigwait() failed %E", err);
178         }
179     }
180 }
181 
182 
183 void
nxt_signal_thread_stop(nxt_event_engine_t * engine)184 nxt_signal_thread_stop(nxt_event_engine_t *engine)
185 {
186     nxt_thread_handle_t  thread;
187 
188     thread = engine->signals->thread;
189 
190     nxt_thread_cancel(thread);
191     nxt_thread_wait(thread);
192 }
193