1 /* (c) 2007,2008 Andrei Nigmatulin */
2
3 #include "fpm_config.h"
4
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <string.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <errno.h>
11
12 #include "php_syslog.h"
13
14 #include "fpm.h"
15 #include "fpm_children.h"
16 #include "fpm_cleanup.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 int fpm_stdio_flush_child() /* {{{ */
110 {
111 return write(STDERR_FILENO, "\0", 1);
112 }
113 /* }}} */
114
115 static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
116 {
117 static const int max_buf_size = 1024;
118 int fd = ev->fd;
119 char buf[max_buf_size];
120 struct fpm_child_s *child;
121 int is_stdout;
122 struct fpm_event_s *event;
123 int fifo_in = 1, fifo_out = 1;
124 int in_buf = 0;
125 int read_fail = 0, finish_log_stream = 0, create_log_stream;
126 int res;
127 struct zlog_stream *log_stream;
128
129 if (!arg) {
130 return;
131 }
132 child = (struct fpm_child_s *)arg;
133
134 is_stdout = (fd == child->fd_stdout);
135 if (is_stdout) {
136 event = &child->ev_stdout;
137 } else {
138 event = &child->ev_stderr;
139 }
140
141 create_log_stream = !child->log_stream;
142 if (create_log_stream) {
143 log_stream = child->log_stream = malloc(sizeof(struct zlog_stream));
144 zlog_stream_init_ex(log_stream, ZLOG_WARNING, STDERR_FILENO);
145 zlog_stream_set_decorating(log_stream, child->wp->config->decorate_workers_output);
146 zlog_stream_set_wrapping(log_stream, ZLOG_TRUE);
147 zlog_stream_set_msg_prefix(log_stream, STREAM_SET_MSG_PREFIX_FMT,
148 child->wp->config->name, (int) child->pid, is_stdout ? "stdout" : "stderr");
149 zlog_stream_set_msg_quoting(log_stream, ZLOG_TRUE);
150 zlog_stream_set_is_stdout(log_stream, is_stdout);
151 zlog_stream_set_child_pid(log_stream, (int)child->pid);
152 } else {
153 log_stream = child->log_stream;
154 // if fd type (stdout/stderr) or child's pid is changed,
155 // then the stream will be finished and msg's prefix will be reinitialized
156 if (log_stream->is_stdout != (unsigned int)is_stdout || log_stream->child_pid != (int)child->pid) {
157 zlog_stream_finish(log_stream);
158 zlog_stream_set_msg_prefix(log_stream, STREAM_SET_MSG_PREFIX_FMT,
159 child->wp->config->name, (int) child->pid, is_stdout ? "stdout" : "stderr");
160 zlog_stream_set_is_stdout(log_stream, is_stdout);
161 zlog_stream_set_child_pid(log_stream, (int)child->pid);
162 }
163 }
164
165 while (fifo_in || fifo_out) {
166 if (fifo_in) {
167 res = read(fd, buf + in_buf, max_buf_size - 1 - in_buf);
168 if (res <= 0) { /* no data */
169 fifo_in = 0;
170 if (res == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) {
171 /* pipe is closed or error */
172 read_fail = (res < 0) ? res : 1;
173 }
174 } else {
175 in_buf += res;
176 /* if buffer ends with \0, then the stream will be finished */
177 if (!buf[in_buf - 1]) {
178 finish_log_stream = 1;
179 in_buf--;
180 }
181 }
182 }
183
184 if (fifo_out) {
185 if (in_buf == 0) {
186 fifo_out = 0;
187 } else {
188 char *nl;
189
190 nl = memchr(buf, '\n', in_buf);
191 if (nl) {
192 /* we should print each new line int the new message */
193 int out_len = nl - buf;
194 zlog_stream_str(log_stream, buf, out_len);
195 zlog_stream_finish(log_stream);
196 /* skip new line */
197 out_len++;
198 /* move data in the buffer */
199 memmove(buf, buf + out_len, in_buf - out_len);
200 in_buf -= out_len;
201 } else if (in_buf == max_buf_size - 1 || !fifo_in) {
202 /* we should print if no more space in the buffer or no more data to come */
203 zlog_stream_str(log_stream, buf, in_buf);
204 in_buf = 0;
205 }
206 }
207 }
208 }
209
210 if (read_fail) {
211 if (create_log_stream) {
212 zlog_stream_set_msg_suffix(log_stream, NULL, ", pipe is closed");
213 zlog_stream_finish(log_stream);
214 }
215 if (read_fail < 0) {
216 zlog(ZLOG_SYSERROR, "unable to read what child say");
217 }
218
219 fpm_event_del(event);
220
221 if (is_stdout) {
222 close(child->fd_stdout);
223 child->fd_stdout = -1;
224 } else {
225 close(child->fd_stderr);
226 child->fd_stderr = -1;
227 }
228 } else if (finish_log_stream) {
229 zlog_stream_finish(log_stream);
230 }
231 }
232 /* }}} */
233
234 int fpm_stdio_prepare_pipes(struct fpm_child_s *child) /* {{{ */
235 {
236 if (0 == child->wp->config->catch_workers_output) { /* not required */
237 return 0;
238 }
239
240 if (0 > pipe(fd_stdout)) {
241 zlog(ZLOG_SYSERROR, "failed to prepare the stdout pipe");
242 return -1;
243 }
244
245 if (0 > pipe(fd_stderr)) {
246 zlog(ZLOG_SYSERROR, "failed to prepare the stderr pipe");
247 close(fd_stdout[0]);
248 close(fd_stdout[1]);
249 return -1;
250 }
251
252 if (0 > fd_set_blocked(fd_stdout[0], 0) || 0 > fd_set_blocked(fd_stderr[0], 0)) {
253 zlog(ZLOG_SYSERROR, "failed to unblock pipes");
254 close(fd_stdout[0]);
255 close(fd_stdout[1]);
256 close(fd_stderr[0]);
257 close(fd_stderr[1]);
258 return -1;
259 }
260 return 0;
261 }
262 /* }}} */
263
264 int fpm_stdio_parent_use_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 child->fd_stdout = fd_stdout[0];
274 child->fd_stderr = fd_stderr[0];
275
276 fpm_event_set(&child->ev_stdout, child->fd_stdout, FPM_EV_READ, fpm_stdio_child_said, child);
277 fpm_event_add(&child->ev_stdout, 0);
278
279 fpm_event_set(&child->ev_stderr, child->fd_stderr, FPM_EV_READ, fpm_stdio_child_said, child);
280 fpm_event_add(&child->ev_stderr, 0);
281 return 0;
282 }
283 /* }}} */
284
285 int fpm_stdio_discard_pipes(struct fpm_child_s *child) /* {{{ */
286 {
287 if (0 == child->wp->config->catch_workers_output) { /* not required */
288 return 0;
289 }
290
291 close(fd_stdout[1]);
292 close(fd_stderr[1]);
293
294 close(fd_stdout[0]);
295 close(fd_stderr[0]);
296 return 0;
297 }
298 /* }}} */
299
300 void fpm_stdio_child_use_pipes(struct fpm_child_s *child) /* {{{ */
301 {
302 if (child->wp->config->catch_workers_output) {
303 dup2(fd_stdout[1], STDOUT_FILENO);
304 dup2(fd_stderr[1], STDERR_FILENO);
305 close(fd_stdout[0]); close(fd_stdout[1]);
306 close(fd_stderr[0]); close(fd_stderr[1]);
307 } else {
308 /* stdout of parent is always /dev/null */
309 dup2(STDOUT_FILENO, STDERR_FILENO);
310 }
311 }
312 /* }}} */
313
314 int fpm_stdio_open_error_log(int reopen) /* {{{ */
315 {
316 int fd;
317
318 #ifdef HAVE_SYSLOG_H
319 if (!strcasecmp(fpm_global_config.error_log, "syslog")) {
320 php_openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility);
321 fpm_globals.error_log_fd = ZLOG_SYSLOG;
322 if (fpm_use_error_log()) {
323 zlog_set_fd(fpm_globals.error_log_fd);
324 }
325 return 0;
326 }
327 #endif
328
329 fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
330 if (0 > fd) {
331 zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log);
332 return -1;
333 }
334
335 if (reopen) {
336 if (fpm_use_error_log()) {
337 dup2(fd, STDERR_FILENO);
338 }
339
340 dup2(fd, fpm_globals.error_log_fd);
341 close(fd);
342 fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */
343 } else {
344 fpm_globals.error_log_fd = fd;
345 if (fpm_use_error_log()) {
346 zlog_set_fd(fpm_globals.error_log_fd);
347 }
348 }
349 if (0 > fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)) {
350 zlog(ZLOG_WARNING, "failed to change attribute of error_log");
351 }
352 return 0;
353 }
354 /* }}} */
355