/src/unit/src/nxt_signal.c
Line | Count | Source |
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 * |
30 | | nxt_event_engine_signals(const nxt_sig_event_t *sigev) |
31 | 0 | { |
32 | 0 | nxt_event_signals_t *signals; |
33 | |
|
34 | 0 | signals = nxt_zalloc(sizeof(nxt_event_signals_t)); |
35 | 0 | if (signals == NULL) { |
36 | 0 | return NULL; |
37 | 0 | } |
38 | | |
39 | 0 | signals->sigev = sigev; |
40 | |
|
41 | 0 | if (nxt_signal_action(SIGSYS, SIG_IGN) != NXT_OK) { |
42 | 0 | goto fail; |
43 | 0 | } |
44 | | |
45 | 0 | if (nxt_signal_action(SIGPIPE, SIG_IGN) != NXT_OK) { |
46 | 0 | goto fail; |
47 | 0 | } |
48 | | |
49 | 0 | sigemptyset(&signals->sigmask); |
50 | |
|
51 | 0 | while (sigev->signo != 0) { |
52 | 0 | sigaddset(&signals->sigmask, sigev->signo); |
53 | 0 | sigev++; |
54 | 0 | } |
55 | |
|
56 | 0 | if (sigprocmask(SIG_BLOCK, &signals->sigmask, NULL) != 0) { |
57 | 0 | nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno); |
58 | 0 | goto fail; |
59 | 0 | } |
60 | | |
61 | 0 | return signals; |
62 | | |
63 | 0 | fail: |
64 | |
|
65 | 0 | nxt_free(signals); |
66 | |
|
67 | 0 | return NULL; |
68 | 0 | } |
69 | | |
70 | | |
71 | | static nxt_int_t |
72 | | nxt_signal_action(int signo, void (*handler)(int)) |
73 | 0 | { |
74 | 0 | struct sigaction sa; |
75 | |
|
76 | 0 | nxt_memzero(&sa, sizeof(struct sigaction)); |
77 | 0 | sigemptyset(&sa.sa_mask); |
78 | 0 | sa.sa_handler = handler; |
79 | |
|
80 | 0 | if (sigaction(signo, &sa, NULL) == 0) { |
81 | 0 | return NXT_OK; |
82 | 0 | } |
83 | | |
84 | 0 | nxt_main_log_alert("sigaction(%d) failed %E", signo, nxt_errno); |
85 | |
|
86 | 0 | return NXT_ERROR; |
87 | 0 | } |
88 | | |
89 | | |
90 | | static void |
91 | | nxt_signal_handler(int signo) |
92 | 0 | { |
93 | 0 | nxt_thread_t *thr; |
94 | |
|
95 | 0 | thr = nxt_thread(); |
96 | | |
97 | | /* Thread is running in a single context now. */ |
98 | 0 | thr->time.signal++; |
99 | |
|
100 | 0 | nxt_thread_time_update(thr); |
101 | |
|
102 | 0 | nxt_main_log_error(NXT_LOG_INFO, "signal handler: %d", signo); |
103 | |
|
104 | 0 | nxt_event_engine_signal(thr->engine, signo); |
105 | |
|
106 | 0 | thr->time.signal--; |
107 | 0 | } |
108 | | |
109 | | |
110 | | nxt_int_t |
111 | | nxt_signal_thread_start(nxt_event_engine_t *engine) |
112 | 0 | { |
113 | 0 | nxt_thread_link_t *link; |
114 | 0 | const nxt_sig_event_t *sigev; |
115 | |
|
116 | 0 | if (engine->signals->process == nxt_pid) { |
117 | 0 | return NXT_OK; |
118 | 0 | } |
119 | | |
120 | 0 | if (sigprocmask(SIG_BLOCK, &engine->signals->sigmask, NULL) != 0) { |
121 | 0 | nxt_main_log_alert("sigprocmask(SIG_BLOCK) failed %E", nxt_errno); |
122 | 0 | return NXT_ERROR; |
123 | 0 | } |
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 | 0 | for (sigev = engine->signals->sigev; sigev->signo != 0; sigev++) { |
131 | 0 | if (nxt_signal_action(sigev->signo, nxt_signal_handler) != NXT_OK) { |
132 | 0 | return NXT_ERROR; |
133 | 0 | } |
134 | 0 | } |
135 | | |
136 | 0 | link = nxt_zalloc(sizeof(nxt_thread_link_t)); |
137 | |
|
138 | 0 | if (nxt_fast_path(link != NULL)) { |
139 | 0 | link->start = nxt_signal_thread; |
140 | 0 | link->work.data = engine; |
141 | |
|
142 | 0 | if (nxt_thread_create(&engine->signals->thread, link) == NXT_OK) { |
143 | 0 | engine->signals->process = nxt_pid; |
144 | 0 | return NXT_OK; |
145 | 0 | } |
146 | 0 | } |
147 | | |
148 | 0 | return NXT_ERROR; |
149 | 0 | } |
150 | | |
151 | | |
152 | | static void |
153 | | nxt_signal_thread(void *data) |
154 | 0 | { |
155 | 0 | int signo; |
156 | 0 | nxt_err_t err; |
157 | 0 | nxt_thread_t *thr; |
158 | 0 | nxt_event_engine_t *engine; |
159 | |
|
160 | 0 | engine = data; |
161 | |
|
162 | 0 | thr = nxt_thread(); |
163 | |
|
164 | 0 | nxt_main_log_debug("signal thread"); |
165 | |
|
166 | 0 | for ( ;; ) { |
167 | 0 | err = sigwait(&engine->signals->sigmask, &signo); |
168 | |
|
169 | 0 | nxt_thread_time_update(thr); |
170 | |
|
171 | 0 | if (nxt_fast_path(err == 0)) { |
172 | 0 | nxt_main_log_error(NXT_LOG_INFO, "signo: %d", signo); |
173 | |
|
174 | 0 | nxt_event_engine_signal(engine, signo); |
175 | |
|
176 | 0 | } else { |
177 | 0 | nxt_main_log_alert("sigwait() failed %E", err); |
178 | 0 | } |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | |
183 | | void |
184 | | nxt_signal_thread_stop(nxt_event_engine_t *engine) |
185 | 0 | { |
186 | 0 | nxt_thread_handle_t thread; |
187 | |
|
188 | 0 | thread = engine->signals->thread; |
189 | |
|
190 | 0 | nxt_thread_cancel(thread); |
191 | 0 | nxt_thread_wait(thread); |
192 | 0 | } |