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 (fpm_global_config.daemonize) {
272 zlog_set_fd(fpm_globals.error_log_fd);
273 }
274 return 0;
275 }
276 #endif
277
278 fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
279 if (0 > fd) {
280 zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log);
281 return -1;
282 }
283
284 if (reopen) {
285 if (fpm_global_config.daemonize) {
286 dup2(fd, STDERR_FILENO);
287 }
288
289 dup2(fd, fpm_globals.error_log_fd);
290 close(fd);
291 fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */
292 } else {
293 fpm_globals.error_log_fd = fd;
294 if (fpm_global_config.daemonize) {
295 zlog_set_fd(fpm_globals.error_log_fd);
296 }
297 }
298 if (0 > fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)) {
299 zlog(ZLOG_WARNING, "failed to change attribute of error_log");
300 }
301 return 0;
302 }
303 /* }}} */
304
305