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