xref: /PHP-7.2/sapi/fpm/fpm/fpm_stdio.c (revision 60a69dae)
1 	/* $Id: fpm_stdio.c,v 1.22.2.2 2008/12/13 03:32:24 anight Exp $ */
2 	/* (c) 2007,2008 Andrei Nigmatulin */
3 
4 #include "fpm_config.h"
5 
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <string.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <errno.h>
12 
13 #include "php_syslog.h"
14 
15 #include "fpm.h"
16 #include "fpm_children.h"
17 #include "fpm_events.h"
18 #include "fpm_sockets.h"
19 #include "fpm_stdio.h"
20 #include "zlog.h"
21 
22 static int fd_stdout[2];
23 static int fd_stderr[2];
24 
fpm_stdio_init_main()25 int fpm_stdio_init_main() /* {{{ */
26 {
27 	int fd = open("/dev/null", O_RDWR);
28 
29 	if (0 > fd) {
30 		zlog(ZLOG_SYSERROR, "failed to init stdio: open(\"/dev/null\")");
31 		return -1;
32 	}
33 
34 	if (0 > dup2(fd, STDIN_FILENO) || 0 > dup2(fd, STDOUT_FILENO)) {
35 		zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()");
36 		close(fd);
37 		return -1;
38 	}
39 	close(fd);
40 	return 0;
41 }
42 /* }}} */
43 
fpm_use_error_log()44 static inline int fpm_use_error_log() {  /* {{{ */
45 	/*
46 	 * the error_log is NOT used when running in foreground
47 	 * and from a tty (user looking at output).
48 	 * So, error_log is used by
49 	 * - SysV init launch php-fpm as a daemon
50 	 * - Systemd launch php-fpm in foreground
51 	 */
52 #if HAVE_UNISTD_H
53 	if (fpm_global_config.daemonize || (!isatty(STDERR_FILENO) && !fpm_globals.force_stderr)) {
54 #else
55 	if (fpm_global_config.daemonize) {
56 #endif
57 		return 1;
58 	}
59 	return 0;
60 }
61 
62 /* }}} */
63 int fpm_stdio_init_final() /* {{{ */
64 {
65 	if (fpm_use_error_log()) {
66 		/* prevent duping if logging to syslog */
67 		if (fpm_globals.error_log_fd > 0 && fpm_globals.error_log_fd != STDERR_FILENO) {
68 
69 			/* there might be messages to stderr from other parts of the code, we need to log them all */
70 			if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) {
71 				zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()");
72 				return -1;
73 			}
74 		}
75 #ifdef HAVE_SYSLOG_H
76 		else if (fpm_globals.error_log_fd == ZLOG_SYSLOG) {
77 			/* dup to /dev/null when using syslog */
78 			dup2(STDOUT_FILENO, STDERR_FILENO);
79 		}
80 #endif
81 	}
82 	zlog_set_launched();
83 	return 0;
84 }
85 /* }}} */
86 
87 int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
88 {
89 #ifdef HAVE_SYSLOG_H
90 	if (fpm_globals.error_log_fd == ZLOG_SYSLOG) {
91 		closelog(); /* ensure to close syslog not to interrupt with PHP syslog code */
92 	} else
93 #endif
94 
95 	/* Notice: child cannot use master error_log
96 	 * because not aware when being reopen
97 	 * else, should use if (!fpm_use_error_log())
98 	 */
99 	if (fpm_globals.error_log_fd > 0) {
100 		close(fpm_globals.error_log_fd);
101 	}
102 	fpm_globals.error_log_fd = -1;
103 	zlog_set_fd(-1);
104 
105 	return 0;
106 }
107 /* }}} */
108 
109 static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
110 {
111 	static const int max_buf_size = 1024;
112 	int fd = ev->fd;
113 	char buf[max_buf_size];
114 	struct fpm_child_s *child;
115 	int is_stdout;
116 	struct fpm_event_s *event;
117 	int fifo_in = 1, fifo_out = 1;
118 	int is_last_message = 0;
119 	int in_buf = 0;
120 	int res;
121 
122 	if (!arg) {
123 		return;
124 	}
125 	child = (struct fpm_child_s *)arg;
126 	is_stdout = (fd == child->fd_stdout);
127 	if (is_stdout) {
128 		event = &child->ev_stdout;
129 	} else {
130 		event = &child->ev_stderr;
131 	}
132 
133 	while (fifo_in || fifo_out) {
134 		if (fifo_in) {
135 			res = read(fd, buf + in_buf, max_buf_size - 1 - in_buf);
136 			if (res <= 0) { /* no data */
137 				fifo_in = 0;
138 				if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
139 					/* just no more data ready */
140 				} else { /* error or pipe is closed */
141 
142 					if (res < 0) { /* error */
143 						zlog(ZLOG_SYSERROR, "unable to read what child say");
144 					}
145 
146 					fpm_event_del(event);
147 					is_last_message = 1;
148 
149 					if (is_stdout) {
150 						close(child->fd_stdout);
151 						child->fd_stdout = -1;
152 					} else {
153 						close(child->fd_stderr);
154 						child->fd_stderr = -1;
155 					}
156 				}
157 			} else {
158 				in_buf += res;
159 			}
160 		}
161 
162 		if (fifo_out) {
163 			if (in_buf == 0) {
164 				fifo_out = 0;
165 			} else {
166 				char *nl;
167 				int should_print = 0;
168 				buf[in_buf] = '\0';
169 
170 				/* FIXME: there might be binary data */
171 
172 				/* we should print if no more space in the buffer */
173 				if (in_buf == max_buf_size - 1) {
174 					should_print = 1;
175 				}
176 
177 				/* we should print if no more data to come */
178 				if (!fifo_in) {
179 					should_print = 1;
180 				}
181 
182 				nl = strchr(buf, '\n');
183 				if (nl || should_print) {
184 
185 					if (nl) {
186 						*nl = '\0';
187 					}
188 
189 					zlog(ZLOG_WARNING, "[pool %s] child %d said into %s: \"%s\"%s", child->wp->config->name,
190 					  (int) child->pid, is_stdout ? "stdout" : "stderr", buf, is_last_message ? ", pipe is closed" : "");
191 
192 					if (nl) {
193 						int out_buf = 1 + nl - buf;
194 						memmove(buf, buf + out_buf, in_buf - out_buf);
195 						in_buf -= out_buf;
196 					} else {
197 						in_buf = 0;
198 					}
199 				}
200 			}
201 		}
202 	}
203 }
204 /* }}} */
205 
206 int fpm_stdio_prepare_pipes(struct fpm_child_s *child) /* {{{ */
207 {
208 	if (0 == child->wp->config->catch_workers_output) { /* not required */
209 		return 0;
210 	}
211 
212 	if (0 > pipe(fd_stdout)) {
213 		zlog(ZLOG_SYSERROR, "failed to prepare the stdout pipe");
214 		return -1;
215 	}
216 
217 	if (0 > pipe(fd_stderr)) {
218 		zlog(ZLOG_SYSERROR, "failed to prepare the stderr pipe");
219 		close(fd_stdout[0]);
220 		close(fd_stdout[1]);
221 		return -1;
222 	}
223 
224 	if (0 > fd_set_blocked(fd_stdout[0], 0) || 0 > fd_set_blocked(fd_stderr[0], 0)) {
225 		zlog(ZLOG_SYSERROR, "failed to unblock pipes");
226 		close(fd_stdout[0]);
227 		close(fd_stdout[1]);
228 		close(fd_stderr[0]);
229 		close(fd_stderr[1]);
230 		return -1;
231 	}
232 	return 0;
233 }
234 /* }}} */
235 
236 int fpm_stdio_parent_use_pipes(struct fpm_child_s *child) /* {{{ */
237 {
238 	if (0 == child->wp->config->catch_workers_output) { /* not required */
239 		return 0;
240 	}
241 
242 	close(fd_stdout[1]);
243 	close(fd_stderr[1]);
244 
245 	child->fd_stdout = fd_stdout[0];
246 	child->fd_stderr = fd_stderr[0];
247 
248 	fpm_event_set(&child->ev_stdout, child->fd_stdout, FPM_EV_READ, fpm_stdio_child_said, child);
249 	fpm_event_add(&child->ev_stdout, 0);
250 
251 	fpm_event_set(&child->ev_stderr, child->fd_stderr, FPM_EV_READ, fpm_stdio_child_said, child);
252 	fpm_event_add(&child->ev_stderr, 0);
253 	return 0;
254 }
255 /* }}} */
256 
257 int fpm_stdio_discard_pipes(struct fpm_child_s *child) /* {{{ */
258 {
259 	if (0 == child->wp->config->catch_workers_output) { /* not required */
260 		return 0;
261 	}
262 
263 	close(fd_stdout[1]);
264 	close(fd_stderr[1]);
265 
266 	close(fd_stdout[0]);
267 	close(fd_stderr[0]);
268 	return 0;
269 }
270 /* }}} */
271 
272 void fpm_stdio_child_use_pipes(struct fpm_child_s *child) /* {{{ */
273 {
274 	if (child->wp->config->catch_workers_output) {
275 		dup2(fd_stdout[1], STDOUT_FILENO);
276 		dup2(fd_stderr[1], STDERR_FILENO);
277 		close(fd_stdout[0]); close(fd_stdout[1]);
278 		close(fd_stderr[0]); close(fd_stderr[1]);
279 	} else {
280 		/* stdout of parent is always /dev/null */
281 		dup2(STDOUT_FILENO, STDERR_FILENO);
282 	}
283 }
284 /* }}} */
285 
286 int fpm_stdio_open_error_log(int reopen) /* {{{ */
287 {
288 	int fd;
289 
290 #ifdef HAVE_SYSLOG_H
291 	if (!strcasecmp(fpm_global_config.error_log, "syslog")) {
292 		openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility);
293 		fpm_globals.error_log_fd = ZLOG_SYSLOG;
294 		if (fpm_use_error_log()) {
295 			zlog_set_fd(fpm_globals.error_log_fd);
296 		}
297 		return 0;
298 	}
299 #endif
300 
301 	fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
302 	if (0 > fd) {
303 		zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log);
304 		return -1;
305 	}
306 
307 	if (reopen) {
308 		if (fpm_use_error_log()) {
309 			dup2(fd, STDERR_FILENO);
310 		}
311 
312 		dup2(fd, fpm_globals.error_log_fd);
313 		close(fd);
314 		fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */
315 	} else {
316 		fpm_globals.error_log_fd = fd;
317 		if (fpm_use_error_log()) {
318 			zlog_set_fd(fpm_globals.error_log_fd);
319 		}
320 	}
321 	if (0 > fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)) {
322 		zlog(ZLOG_WARNING, "failed to change attribute of error_log");
323 	}
324 	return 0;
325 }
326 /* }}} */
327