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->work.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