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