xref: /PHP-8.2/sapi/fpm/fpm/fpm_signals.c (revision e2a5428c)
1 	/* (c) 2007,2008 Andrei Nigmatulin */
2 
3 #include "fpm_config.h"
4 
5 #include <signal.h>
6 #include <stdio.h>
7 #include <sys/types.h>
8 #include <sys/socket.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include <errno.h>
14 
15 #include "fpm.h"
16 #include "fpm_signals.h"
17 #include "fpm_sockets.h"
18 #include "fpm_php.h"
19 #include "zlog.h"
20 
21 static int sp[2];
22 static sigset_t block_sigset;
23 static sigset_t child_block_sigset;
24 
25 const char *fpm_signal_names[NSIG + 1] = {
26 #ifdef SIGHUP
27 	[SIGHUP] 		= "SIGHUP",
28 #endif
29 #ifdef SIGINT
30 	[SIGINT] 		= "SIGINT",
31 #endif
32 #ifdef SIGQUIT
33 	[SIGQUIT] 		= "SIGQUIT",
34 #endif
35 #ifdef SIGILL
36 	[SIGILL] 		= "SIGILL",
37 #endif
38 #ifdef SIGTRAP
39 	[SIGTRAP] 		= "SIGTRAP",
40 #endif
41 #ifdef SIGABRT
42 	[SIGABRT] 		= "SIGABRT",
43 #endif
44 #ifdef SIGEMT
45 	[SIGEMT] 		= "SIGEMT",
46 #endif
47 #ifdef SIGBUS
48 	[SIGBUS] 		= "SIGBUS",
49 #endif
50 #ifdef SIGFPE
51 	[SIGFPE] 		= "SIGFPE",
52 #endif
53 #ifdef SIGKILL
54 	[SIGKILL] 		= "SIGKILL",
55 #endif
56 #ifdef SIGUSR1
57 	[SIGUSR1] 		= "SIGUSR1",
58 #endif
59 #ifdef SIGSEGV
60 	[SIGSEGV] 		= "SIGSEGV",
61 #endif
62 #ifdef SIGUSR2
63 	[SIGUSR2] 		= "SIGUSR2",
64 #endif
65 #ifdef SIGPIPE
66 	[SIGPIPE] 		= "SIGPIPE",
67 #endif
68 #ifdef SIGALRM
69 	[SIGALRM] 		= "SIGALRM",
70 #endif
71 #ifdef SIGTERM
72 	[SIGTERM] 		= "SIGTERM",
73 #endif
74 #ifdef SIGCHLD
75 	[SIGCHLD] 		= "SIGCHLD",
76 #endif
77 #ifdef SIGCONT
78 	[SIGCONT] 		= "SIGCONT",
79 #endif
80 #ifdef SIGSTOP
81 	[SIGSTOP] 		= "SIGSTOP",
82 #endif
83 #ifdef SIGTSTP
84 	[SIGTSTP] 		= "SIGTSTP",
85 #endif
86 #ifdef SIGTTIN
87 	[SIGTTIN] 		= "SIGTTIN",
88 #endif
89 #ifdef SIGTTOU
90 	[SIGTTOU] 		= "SIGTTOU",
91 #endif
92 #ifdef SIGURG
93 	[SIGURG] 		= "SIGURG",
94 #endif
95 #ifdef SIGXCPU
96 	[SIGXCPU] 		= "SIGXCPU",
97 #endif
98 #ifdef SIGXFSZ
99 	[SIGXFSZ] 		= "SIGXFSZ",
100 #endif
101 #ifdef SIGVTALRM
102 	[SIGVTALRM] 	= "SIGVTALRM",
103 #endif
104 #ifdef SIGPROF
105 	[SIGPROF] 		= "SIGPROF",
106 #endif
107 #ifdef SIGWINCH
108 	[SIGWINCH] 		= "SIGWINCH",
109 #endif
110 #ifdef SIGINFO
111 	[SIGINFO] 		= "SIGINFO",
112 #endif
113 #ifdef SIGIO
114 	[SIGIO] 		= "SIGIO",
115 #endif
116 #ifdef SIGPWR
117 	[SIGPWR] 		= "SIGPWR",
118 #endif
119 #ifdef SIGSYS
120 	[SIGSYS] 		= "SIGSYS",
121 #endif
122 #ifdef SIGWAITING
123 	[SIGWAITING] 	= "SIGWAITING",
124 #endif
125 #ifdef SIGLWP
126 	[SIGLWP] 		= "SIGLWP",
127 #endif
128 #ifdef SIGFREEZE
129 	[SIGFREEZE] 	= "SIGFREEZE",
130 #endif
131 #ifdef SIGTHAW
132 	[SIGTHAW] 		= "SIGTHAW",
133 #endif
134 #ifdef SIGCANCEL
135 	[SIGCANCEL] 	= "SIGCANCEL",
136 #endif
137 #ifdef SIGLOST
138 	[SIGLOST] 		= "SIGLOST",
139 #endif
140 };
141 
sig_soft_quit(int signo)142 static void sig_soft_quit(int signo) /* {{{ */
143 {
144 	int saved_errno = errno;
145 
146 	/* closing fastcgi listening socket will force fcgi_accept() exit immediately */
147 	close(fpm_globals.listening_socket);
148 	if (0 > socket(AF_UNIX, SOCK_STREAM, 0)) {
149 		zlog(ZLOG_WARNING, "failed to create a new socket");
150 	}
151 	fpm_php_soft_quit();
152 	errno = saved_errno;
153 }
154 /* }}} */
155 
sig_handler(int signo)156 static void sig_handler(int signo) /* {{{ */
157 {
158 	static const char sig_chars[NSIG + 1] = {
159 		[SIGTERM] = 'T',
160 		[SIGINT]  = 'I',
161 		[SIGUSR1] = '1',
162 		[SIGUSR2] = '2',
163 		[SIGQUIT] = 'Q',
164 		[SIGCHLD] = 'C'
165 	};
166 	char s;
167 	int saved_errno;
168 
169 	if (fpm_globals.parent_pid != getpid()) {
170 		/* Avoid using of signal handlers from the master process in a worker
171 			before the child sets up its own signal handlers.
172 			Normally it is prevented by the sigprocmask() calls
173 			around fork(). This execution branch is a last resort trap
174 			that has no protection against #76601. */
175 		return;
176 	}
177 
178 	saved_errno = errno;
179 	s = sig_chars[signo];
180 	zend_quiet_write(sp[1], &s, sizeof(s));
181 	errno = saved_errno;
182 }
183 /* }}} */
184 
fpm_signals_init_main(void)185 int fpm_signals_init_main(void)
186 {
187 	struct sigaction act;
188 
189 	if (0 > socketpair(AF_UNIX, SOCK_STREAM, 0, sp)) {
190 		zlog(ZLOG_SYSERROR, "failed to init signals: socketpair()");
191 		return -1;
192 	}
193 
194 	if (0 > fd_set_blocked(sp[0], 0) || 0 > fd_set_blocked(sp[1], 0)) {
195 		zlog(ZLOG_SYSERROR, "failed to init signals: fd_set_blocked()");
196 		return -1;
197 	}
198 
199 	if (0 > fcntl(sp[0], F_SETFD, FD_CLOEXEC) || 0 > fcntl(sp[1], F_SETFD, FD_CLOEXEC)) {
200 		zlog(ZLOG_SYSERROR, "failed to init signals: fcntl(F_SETFD, FD_CLOEXEC)");
201 		return -1;
202 	}
203 
204 	memset(&act, 0, sizeof(act));
205 	act.sa_handler = sig_handler;
206 	sigfillset(&act.sa_mask);
207 
208 	if (0 > sigaction(SIGTERM,  &act, 0) ||
209 	    0 > sigaction(SIGINT,   &act, 0) ||
210 	    0 > sigaction(SIGUSR1,  &act, 0) ||
211 	    0 > sigaction(SIGUSR2,  &act, 0) ||
212 	    0 > sigaction(SIGCHLD,  &act, 0) ||
213 	    0 > sigaction(SIGQUIT,  &act, 0)) {
214 
215 		zlog(ZLOG_SYSERROR, "failed to init signals: sigaction()");
216 		return -1;
217 	}
218 
219 	zlog(ZLOG_DEBUG, "Unblocking all signals");
220 	if (0 > fpm_signals_unblock()) {
221 		return -1;
222 	}
223 	return 0;
224 }
225 
fpm_signals_init_child(void)226 int fpm_signals_init_child(void)
227 {
228 	struct sigaction act, act_dfl;
229 
230 	memset(&act, 0, sizeof(act));
231 	memset(&act_dfl, 0, sizeof(act_dfl));
232 
233 	act.sa_handler = &sig_soft_quit;
234 	act.sa_flags |= SA_RESTART;
235 
236 	act_dfl.sa_handler = SIG_DFL;
237 
238 	close(sp[0]);
239 	close(sp[1]);
240 
241 	if (0 > sigaction(SIGTERM,  &act_dfl,  0) ||
242 	    0 > sigaction(SIGINT,   &act_dfl,  0) ||
243 	    0 > sigaction(SIGUSR1,  &act_dfl,  0) ||
244 	    0 > sigaction(SIGUSR2,  &act_dfl,  0) ||
245 	    0 > sigaction(SIGCHLD,  &act_dfl,  0) ||
246 	    0 > sigaction(SIGQUIT,  &act,      0)) {
247 
248 		zlog(ZLOG_SYSERROR, "failed to init child signals: sigaction()");
249 		return -1;
250 	}
251 
252 	zend_signal_init();
253 
254 	if (0 > fpm_signals_unblock()) {
255 		return -1;
256 	}
257 	return 0;
258 }
259 
fpm_signals_get_fd(void)260 int fpm_signals_get_fd(void)
261 {
262 	return sp[0];
263 }
264 
fpm_signals_init_mask(void)265 int fpm_signals_init_mask(void)
266 {
267 	/* Subset of signals from fpm_signals_init_main() and fpm_got_signal()
268 		blocked to avoid unexpected death during early init
269 		or during reload just after execvp() or fork */
270 	int init_signal_array[] = { SIGUSR1, SIGUSR2, SIGCHLD };
271 	size_t size = sizeof(init_signal_array)/sizeof(init_signal_array[0]);
272 	size_t i = 0;
273 	if (0 > sigemptyset(&block_sigset) ||
274 	    0 > sigemptyset(&child_block_sigset)) {
275 		zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigemptyset()");
276 		return -1;
277 	}
278 	for (i = 0; i < size; ++i) {
279 		int sig_i = init_signal_array[i];
280 		if (0 > sigaddset(&block_sigset, sig_i) ||
281 		    0 > sigaddset(&child_block_sigset, sig_i)) {
282 			if (sig_i <= NSIG && fpm_signal_names[sig_i] != NULL) {
283 				zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigaddset(%s)",
284 						fpm_signal_names[sig_i]);
285 			} else {
286 				zlog(ZLOG_SYSERROR, "failed to prepare signal block mask: sigaddset(%d)", sig_i);
287 			}
288 			return -1;
289 		}
290 	}
291 	if (0 > sigaddset(&child_block_sigset, SIGTERM) ||
292 	    0 > sigaddset(&child_block_sigset, SIGQUIT)) {
293 		zlog(ZLOG_SYSERROR, "failed to prepare child signal block mask: sigaddset()");
294 		return -1;
295 	}
296 	return 0;
297 }
298 
fpm_signals_block(void)299 int fpm_signals_block(void)
300 {
301 	if (0 > sigprocmask(SIG_BLOCK, &block_sigset, NULL)) {
302 		zlog(ZLOG_SYSERROR, "failed to block signals");
303 		return -1;
304 	}
305 	return 0;
306 }
307 
fpm_signals_child_block(void)308 int fpm_signals_child_block(void)
309 {
310 	if (0 > sigprocmask(SIG_BLOCK, &child_block_sigset, NULL)) {
311 		zlog(ZLOG_SYSERROR, "failed to block child signals");
312 		return -1;
313 	}
314 	return 0;
315 }
316 
fpm_signals_unblock(void)317 int fpm_signals_unblock(void)
318 {
319 	/* Ensure that during reload after upgrade all signals are unblocked.
320 		block_sigset could have different value before execve() */
321 	sigset_t all_signals;
322 	sigfillset(&all_signals);
323 	if (0 > sigprocmask(SIG_UNBLOCK, &all_signals, NULL)) {
324 		zlog(ZLOG_SYSERROR, "failed to unblock signals");
325 		return -1;
326 	}
327 	return 0;
328 }
329