xref: /PHP-5.5/sapi/fpm/fpm/fpm_stdio.c (revision f8acf826)
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_stdio_init_final()45 int fpm_stdio_init_final() /* {{{ */
46 {
47 	if (fpm_global_config.daemonize) {
48 		/* prevent duping if logging to syslog */
49 		if (fpm_globals.error_log_fd > 0 && fpm_globals.error_log_fd != STDERR_FILENO) {
50 
51 			/* there might be messages to stderr from other parts of the code, we need to log them all */
52 			if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) {
53 				zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()");
54 				return -1;
55 			}
56 		}
57 	}
58 	zlog_set_launched();
59 	return 0;
60 }
61 /* }}} */
62 
fpm_stdio_init_child(struct fpm_worker_pool_s * wp)63 int fpm_stdio_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
64 {
65 #ifdef HAVE_SYSLOG_H
66 	if (fpm_globals.error_log_fd == ZLOG_SYSLOG) {
67 		closelog(); /* ensure to close syslog not to interrupt with PHP syslog code */
68 	} else
69 #endif
70 	if (fpm_globals.error_log_fd > 0) {
71 		close(fpm_globals.error_log_fd);
72 	}
73 	fpm_globals.error_log_fd = -1;
74 	zlog_set_fd(-1);
75 
76 	if (wp->listening_socket != STDIN_FILENO) {
77 		if (0 > dup2(wp->listening_socket, STDIN_FILENO)) {
78 			zlog(ZLOG_SYSERROR, "failed to init child stdio: dup2()");
79 			return -1;
80 		}
81 	}
82 	return 0;
83 }
84 /* }}} */
85 
fpm_stdio_child_said(struct fpm_event_s * ev,short which,void * arg)86 static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
87 {
88 	static const int max_buf_size = 1024;
89 	int fd = ev->fd;
90 	char buf[max_buf_size];
91 	struct fpm_child_s *child;
92 	int is_stdout;
93 	struct fpm_event_s *event;
94 	int fifo_in = 1, fifo_out = 1;
95 	int is_last_message = 0;
96 	int in_buf = 0;
97 	int res;
98 
99 	if (!arg) {
100 		return;
101 	}
102 	child = (struct fpm_child_s *)arg;
103 	is_stdout = (fd == child->fd_stdout);
104 	if (is_stdout) {
105 		event = &child->ev_stdout;
106 	} else {
107 		event = &child->ev_stderr;
108 	}
109 
110 	while (fifo_in || fifo_out) {
111 		if (fifo_in) {
112 			res = read(fd, buf + in_buf, max_buf_size - 1 - in_buf);
113 			if (res <= 0) { /* no data */
114 				fifo_in = 0;
115 				if (res < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
116 					/* just no more data ready */
117 				} else { /* error or pipe is closed */
118 
119 					if (res < 0) { /* error */
120 						zlog(ZLOG_SYSERROR, "unable to read what child say");
121 					}
122 
123 					fpm_event_del(event);
124 					is_last_message = 1;
125 
126 					if (is_stdout) {
127 						close(child->fd_stdout);
128 						child->fd_stdout = -1;
129 					} else {
130 						close(child->fd_stderr);
131 						child->fd_stderr = -1;
132 					}
133 				}
134 			} else {
135 				in_buf += res;
136 			}
137 		}
138 
139 		if (fifo_out) {
140 			if (in_buf == 0) {
141 				fifo_out = 0;
142 			} else {
143 				char *nl;
144 				int should_print = 0;
145 				buf[in_buf] = '\0';
146 
147 				/* FIXME: there might be binary data */
148 
149 				/* we should print if no more space in the buffer */
150 				if (in_buf == max_buf_size - 1) {
151 					should_print = 1;
152 				}
153 
154 				/* we should print if no more data to come */
155 				if (!fifo_in) {
156 					should_print = 1;
157 				}
158 
159 				nl = strchr(buf, '\n');
160 				if (nl || should_print) {
161 
162 					if (nl) {
163 						*nl = '\0';
164 					}
165 
166 					zlog(ZLOG_WARNING, "[pool %s] child %d said into %s: \"%s\"%s", child->wp->config->name,
167 					  (int) child->pid, is_stdout ? "stdout" : "stderr", buf, is_last_message ? ", pipe is closed" : "");
168 
169 					if (nl) {
170 						int out_buf = 1 + nl - buf;
171 						memmove(buf, buf + out_buf, in_buf - out_buf);
172 						in_buf -= out_buf;
173 					} else {
174 						in_buf = 0;
175 					}
176 				}
177 			}
178 		}
179 	}
180 }
181 /* }}} */
182 
fpm_stdio_prepare_pipes(struct fpm_child_s * child)183 int fpm_stdio_prepare_pipes(struct fpm_child_s *child) /* {{{ */
184 {
185 	if (0 == child->wp->config->catch_workers_output) { /* not required */
186 		return 0;
187 	}
188 
189 	if (0 > pipe(fd_stdout)) {
190 		zlog(ZLOG_SYSERROR, "failed to prepare the stdout pipe");
191 		return -1;
192 	}
193 
194 	if (0 > pipe(fd_stderr)) {
195 		zlog(ZLOG_SYSERROR, "failed to prepare the stderr pipe");
196 		close(fd_stdout[0]);
197 		close(fd_stdout[1]);
198 		return -1;
199 	}
200 
201 	if (0 > fd_set_blocked(fd_stdout[0], 0) || 0 > fd_set_blocked(fd_stderr[0], 0)) {
202 		zlog(ZLOG_SYSERROR, "failed to unblock pipes");
203 		close(fd_stdout[0]);
204 		close(fd_stdout[1]);
205 		close(fd_stderr[0]);
206 		close(fd_stderr[1]);
207 		return -1;
208 	}
209 	return 0;
210 }
211 /* }}} */
212 
fpm_stdio_parent_use_pipes(struct fpm_child_s * child)213 int fpm_stdio_parent_use_pipes(struct fpm_child_s *child) /* {{{ */
214 {
215 	if (0 == child->wp->config->catch_workers_output) { /* not required */
216 		return 0;
217 	}
218 
219 	close(fd_stdout[1]);
220 	close(fd_stderr[1]);
221 
222 	child->fd_stdout = fd_stdout[0];
223 	child->fd_stderr = fd_stderr[0];
224 
225 	fpm_event_set(&child->ev_stdout, child->fd_stdout, FPM_EV_READ, fpm_stdio_child_said, child);
226 	fpm_event_add(&child->ev_stdout, 0);
227 
228 	fpm_event_set(&child->ev_stderr, child->fd_stderr, FPM_EV_READ, fpm_stdio_child_said, child);
229 	fpm_event_add(&child->ev_stderr, 0);
230 	return 0;
231 }
232 /* }}} */
233 
fpm_stdio_discard_pipes(struct fpm_child_s * child)234 int fpm_stdio_discard_pipes(struct fpm_child_s *child) /* {{{ */
235 {
236 	if (0 == child->wp->config->catch_workers_output) { /* not required */
237 		return 0;
238 	}
239 
240 	close(fd_stdout[1]);
241 	close(fd_stderr[1]);
242 
243 	close(fd_stdout[0]);
244 	close(fd_stderr[0]);
245 	return 0;
246 }
247 /* }}} */
248 
fpm_stdio_child_use_pipes(struct fpm_child_s * child)249 void fpm_stdio_child_use_pipes(struct fpm_child_s *child) /* {{{ */
250 {
251 	if (child->wp->config->catch_workers_output) {
252 		dup2(fd_stdout[1], STDOUT_FILENO);
253 		dup2(fd_stderr[1], STDERR_FILENO);
254 		close(fd_stdout[0]); close(fd_stdout[1]);
255 		close(fd_stderr[0]); close(fd_stderr[1]);
256 	} else {
257 		/* stdout of parent is always /dev/null */
258 		dup2(STDOUT_FILENO, STDERR_FILENO);
259 	}
260 }
261 /* }}} */
262 
fpm_stdio_open_error_log(int reopen)263 int fpm_stdio_open_error_log(int reopen) /* {{{ */
264 {
265 	int fd;
266 
267 #ifdef HAVE_SYSLOG_H
268 	if (!strcasecmp(fpm_global_config.error_log, "syslog")) {
269 		openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility);
270 		fpm_globals.error_log_fd = ZLOG_SYSLOG;
271 #if HAVE_UNISTD_H
272 		if (fpm_global_config.daemonize || !isatty(STDERR_FILENO)) {
273 #else
274 		if (fpm_global_config.daemonize) {
275 #endif
276 			zlog_set_fd(fpm_globals.error_log_fd);
277 		}
278 		return 0;
279 	}
280 #endif
281 
282 	fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
283 	if (0 > fd) {
284 		zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log);
285 		return -1;
286 	}
287 
288 	if (reopen) {
289 		if (fpm_global_config.daemonize) {
290 			dup2(fd, STDERR_FILENO);
291 		}
292 
293 		dup2(fd, fpm_globals.error_log_fd);
294 		close(fd);
295 		fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */
296 	} else {
297 		fpm_globals.error_log_fd = fd;
298 #if HAVE_UNISTD_H
299 		if (fpm_global_config.daemonize || !isatty(STDERR_FILENO)) {
300 #else
301 		if (fpm_global_config.daemonize) {
302 #endif
303 			zlog_set_fd(fpm_globals.error_log_fd);
304 		}
305 	}
306 	if (0 > fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)) {
307 		zlog(ZLOG_WARNING, "failed to change attribute of error_log");
308 	}
309 	return 0;
310 }
311 /* }}} */
312 
313