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