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