xref: /unit/src/nxt_signal.c (revision 12:477899a6661b)
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 
27 
28 nxt_event_signals_t *
29 nxt_event_engine_signals(const nxt_sig_event_t *sigev)
30 {
31     nxt_event_signals_t  *signals;
32 
33     signals = nxt_zalloc(sizeof(nxt_event_signals_t));
34     if (signals == NULL) {
35         return NULL;
36     }
37 
38     signals->sigev = sigev;
39 
40     if (nxt_signal_action(SIGSYS, SIG_IGN) != NXT_OK) {
41         goto fail;
42     }
43 
44     if (nxt_signal_action(SIGPIPE, SIG_IGN) != NXT_OK) {
45         goto fail;
46     }
47 
48     sigemptyset(&signals->sigmask);
49 
50     while (sigev->signo != 0) {
51         sigaddset(&signals->sigmask, sigev->signo);
52         sigev++;
53     }
54 
55     if (sigprocmask(SIG_BLOCK, &signals->sigmask, NULL) != 0) {
56         nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
57         goto fail;
58     }
59 
60     return signals;
61 
62 fail:
63 
64     nxt_free(signals);
65 
66     return NULL;
67 }
68 
69 
70 static nxt_int_t
71 nxt_signal_action(int signo, void (*handler)(int))
72 {
73     struct sigaction  sa;
74 
75     nxt_memzero(&sa, sizeof(struct sigaction));
76     sigemptyset(&sa.sa_mask);
77     sa.sa_handler = handler;
78 
79     if (sigaction(signo, &sa, NULL) == 0) {
80         return NXT_OK;
81     }
82 
83     nxt_main_log_alert("sigaction(%d) failed %E", signo, nxt_errno);
84 
85     return NXT_ERROR;
86 }
87 
88 
89 static void
90 nxt_signal_handler(int signo)
91 {
92     nxt_thread_t  *thr;
93 
94     thr = nxt_thread();
95 
96     /* Thread is running in a single context now. */
97     thr->time.signal++;
98 
99     nxt_thread_time_update(thr);
100 
101     nxt_main_log_error(NXT_LOG_INFO, "signal handler: %d", signo);
102 
103     nxt_event_engine_signal(thr->engine, signo);
104 
105     thr->time.signal--;
106 }
107 
108 
109 #if (NXT_THREADS)
110 
111 static void nxt_signal_thread(void *data);
112 
113 
114 nxt_int_t
115 nxt_signal_thread_start(nxt_event_engine_t *engine)
116 {
117     nxt_thread_link_t      *link;
118     const nxt_sig_event_t  *sigev;
119 
120     if (engine->signals->process == nxt_pid) {
121         return NXT_OK;
122     }
123 
124     if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) {
125         nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
126         return NXT_ERROR;
127     }
128 
129     /*
130      * kqueue sets signal handlers to SIG_IGN and sigwait() ignores
131      * them after the switch of event facility from "kqueue" to "select".
132      */
133 
134     for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) {
135         if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) {
136             return NXT_ERROR;
137         }
138     }
139 
140     link = nxt_zalloc(sizeof(nxt_thread_link_t));
141 
142     if (nxt_fast_path(link != NULL)) {
143         link->start = nxt_signal_thread;
144         link->data = engine;
145 
146         if (nxt_thread_create(&engine->signals->thread, link) == NXT_OK) {
147             engine->signals->process = nxt_pid;
148             return NXT_OK;
149         }
150     }
151 
152     return NXT_ERROR;
153 }
154 
155 
156 static void
157 nxt_signal_thread(void *data)
158 {
159     int                 signo;
160     nxt_err_t           err;
161     nxt_thread_t        *thr;
162     nxt_event_engine_t  *engine;
163 
164     engine = data;
165 
166     thr = nxt_thread();
167 
168     nxt_main_log_debug("signal thread");
169 
170     for ( ;; ) {
171         err = sigwait(&engine->signals->sigmask, &signo);
172 
173         nxt_thread_time_update(thr);
174 
175         if (nxt_fast_path(err == 0)) {
176             nxt_main_log_error(NXT_LOG_INFO, "signo: %d", signo);
177 
178             nxt_event_engine_signal(engine, signo);
179 
180         } else {
181             nxt_main_log_alert("sigwait() failed %E", err);
182         }
183     }
184 }
185 
186 
187 void
188 nxt_signal_thread_stop(nxt_event_engine_t *engine)
189 {
190     nxt_thread_handle_t  thread;
191 
192     thread = engine->signals->thread;
193 
194     nxt_thread_cancel(thread);
195     nxt_thread_wait(thread);
196 }
197 
198 
199 #else /* !(NXT_THREADS) */
200 
201 
202 nxt_int_t
203 nxt_signal_handlers_start(nxt_event_engine_t *engine)
204 {
205     const nxt_sig_event_t  *sigev;
206 
207     for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) {
208         if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) {
209             return NXT_ERROR;
210         }
211     }
212 
213     if (sigprocmask(SIG_UNBLOCK, &engine->signals->sigmask, NULL) != 0) {
214         nxt_main_log_alert("sigprocmask(SIG_UNBLOCK) failed %E", nxt_errno);
215         return NXT_ERROR;
216     }
217 
218     return NXT_OK;
219 }
220 
221 
222 void
223 nxt_signal_handlers_stop(nxt_event_engine_t *engine)
224 {
225     if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) {
226         nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno);
227     }
228 }
229 
230 #endif
231