php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
fpm_stdio.c
Go to the documentation of this file.
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#include "php_network.h"
14
15#include "fpm.h"
16#include "fpm_children.h"
17#include "fpm_cleanup.h"
18#include "fpm_events.h"
19#include "fpm_sockets.h"
20#include "fpm_stdio.h"
21#include "zlog.h"
22
23static int fd_stderr_original = -1;
24static int fd_stdout[2];
25static int fd_stderr[2];
26
28{
29 int fd = open("/dev/null", O_RDWR);
30
31 if (0 > fd) {
32 zlog(ZLOG_SYSERROR, "failed to init stdio: open(\"/dev/null\")");
33 return -1;
34 }
35
36 if (0 > dup2(fd, STDIN_FILENO) || 0 > dup2(fd, STDOUT_FILENO)) {
37 zlog(ZLOG_SYSERROR, "failed to init stdio: dup2()");
38 close(fd);
39 return -1;
40 }
41 close(fd);
42 return 0;
43}
44
45static inline int fpm_use_error_log(void) {
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#ifdef 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
64{
67
68 return -1;
69 }
70
72 return 0;
73}
74/* }}} */
75
77{
78 /* STDERR fd gets lost after calling fpm_stdio_init_final() (check GH-8555) so it can be saved.
79 * It should be used only when PHP-FPM is not daemonized otherwise it might break some
80 * applications (e.g. GH-9754). */
81 zlog(ZLOG_DEBUG, "saving original STDERR fd: dup()");
82 fd_stderr_original = dup(STDERR_FILENO);
83 if (0 > fd_stderr_original) {
84 zlog(ZLOG_SYSERROR, "failed to save original STDERR fd, access.log records may appear in error_log: dup()");
85 return -1;
86 }
87
88 return 0;
89}
90
91int fpm_stdio_restore_original_stderr(int close_after_restore)
92{
93 /* Restore original STDERR fd if it was previously saved. */
94 if (-1 != fd_stderr_original) {
95 zlog(ZLOG_DEBUG, "restoring original STDERR fd: dup2()");
96 if (0 > dup2(fd_stderr_original, STDERR_FILENO)) {
97 zlog(ZLOG_SYSERROR, "failed to restore original STDERR fd, access.log records may appear in error_log: dup2()");
98 return -1;
99 } else {
100 if (close_after_restore) {
101 close(fd_stderr_original);
102 }
103 }
104 }
105
106 return 0;
107}
108
110{
111 if (fpm_use_error_log()) {
112 /* prevent duping if logging to syslog */
113 if (fpm_globals.error_log_fd > 0 && fpm_globals.error_log_fd != STDERR_FILENO) {
114
115 /* there might be messages to stderr from other parts of the code, we need to log them all */
116 if (0 > dup2(fpm_globals.error_log_fd, STDERR_FILENO)) {
117 zlog(ZLOG_SYSERROR, "failed to tie stderr fd with error_log fd: dup2()");
118 return -1;
119 }
120 }
121 }
122
123 return 0;
124}
125
127{
128 if (fpm_use_error_log()) {
129#ifdef HAVE_SYSLOG_H
130 if (fpm_globals.error_log_fd == ZLOG_SYSLOG) {
131 /* dup to /dev/null when using syslog */
133 }
134#endif
135 }
136
137 return 0;
138}
139
141{
142#ifdef HAVE_SYSLOG_H
143 if (fpm_globals.error_log_fd == ZLOG_SYSLOG) {
144 php_closelog(); /* ensure to close syslog not to interrupt with PHP syslog code */
145 } else
146#endif
147
148 /* Notice: child cannot use master error_log
149 * because not aware when being reopen
150 * else, should use if (!fpm_use_error_log())
151 */
152 if (fpm_globals.error_log_fd > 0) {
153 close(fpm_globals.error_log_fd);
154 }
155 fpm_globals.error_log_fd = -1;
156 zlog_set_fd(-1, 0);
157
158 return 0;
159}
160/* }}} */
161
162#define FPM_STDIO_CMD_FLUSH "\0fscf"
163
165{
167}
168
169static void fpm_stdio_child_said(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
170{
171 int fd = ev->fd;
172 char buf[1024];
173 struct fpm_child_s *child;
174 int is_stdout;
175 struct fpm_event_s *event;
176 int in_buf = 0, cmd_pos = 0, pos, start;
177 int read_fail = 0, create_log_stream;
178 struct zlog_stream *log_stream;
179
180 if (!arg) {
181 return;
182 }
183 child = (struct fpm_child_s *) arg;
184
185 is_stdout = (fd == child->fd_stdout);
186 if (is_stdout) {
187 event = &child->ev_stdout;
188 } else {
189 event = &child->ev_stderr;
190 }
191
192 create_log_stream = !child->log_stream;
193 if (create_log_stream) {
194 log_stream = child->log_stream = malloc(sizeof(struct zlog_stream));
199 child->wp->config->name, (int) child->pid, is_stdout ? "stdout" : "stderr");
203 } else {
204 log_stream = child->log_stream;
205 // if fd type (stdout/stderr) or child's pid is changed,
206 // then the stream will be finished and msg's prefix will be reinitialized
207 if (log_stream->is_stdout != (unsigned int)is_stdout || log_stream->child_pid != (int)child->pid) {
210 child->wp->config->name, (int) child->pid, is_stdout ? "stdout" : "stderr");
213 }
214 }
215
216 while (1) {
217stdio_read:
218 in_buf = read(fd, buf, sizeof(buf) - 1);
219 if (in_buf <= 0) { /* no data */
220 if (in_buf == 0 || !PHP_IS_TRANSIENT_ERROR(errno)) {
221 /* pipe is closed or error */
222 read_fail = (in_buf < 0) ? in_buf : 1;
223 }
224 break;
225 }
226 start = 0;
227 if (cmd_pos > 0) {
228 if ((sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos) <= in_buf &&
229 !memcmp(buf, &FPM_STDIO_CMD_FLUSH[cmd_pos], sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos)) {
231 start = sizeof(FPM_STDIO_CMD_FLUSH) - cmd_pos;
232 } else {
234 }
235 cmd_pos = 0;
236 }
237 for (pos = start; pos < in_buf; pos++) {
238 switch (buf[pos]) {
239 case '\n':
242 start = pos + 1;
243 break;
244 case '\0':
245 if (pos + sizeof(FPM_STDIO_CMD_FLUSH) <= in_buf) {
246 if (!memcmp(buf + pos, FPM_STDIO_CMD_FLUSH, sizeof(FPM_STDIO_CMD_FLUSH))) {
249 start = pos + sizeof(FPM_STDIO_CMD_FLUSH);
250 pos = start - 1;
251 }
252 } else if (!memcmp(buf + pos, FPM_STDIO_CMD_FLUSH, in_buf - pos)) {
253 cmd_pos = in_buf - pos;
255 goto stdio_read;
256 }
257 break;
258 }
259 }
260 if (start < pos) {
262 }
263 }
264
265 if (read_fail) {
266 if (create_log_stream) {
267 zlog_stream_set_msg_suffix(log_stream, NULL, ", pipe is closed");
269 }
270 if (read_fail < 0) {
271 zlog(ZLOG_SYSERROR, "unable to read what child %d said into %s", (int) child->pid, is_stdout ? "stdout" : "stderr");
272 }
273
274 fpm_event_del(event);
275
276 child->postponed_free = true;
277 if (is_stdout) {
278 close(child->fd_stdout);
279 child->fd_stdout = -1;
280 } else {
281 close(child->fd_stderr);
282 child->fd_stderr = -1;
283 }
284 }
285}
286/* }}} */
287
288int fpm_stdio_prepare_pipes(struct fpm_child_s *child) /* {{{ */
289{
290 if (0 == child->wp->config->catch_workers_output) { /* not required */
291 return 0;
292 }
293
294 if (0 > pipe(fd_stdout)) {
295 zlog(ZLOG_SYSERROR, "failed to prepare the stdout pipe");
296 return -1;
297 }
298
299 if (0 > pipe(fd_stderr)) {
300 zlog(ZLOG_SYSERROR, "failed to prepare the stderr pipe");
301 close(fd_stdout[0]);
302 close(fd_stdout[1]);
303 return -1;
304 }
305
306 if (0 > fd_set_blocked(fd_stdout[0], 0) || 0 > fd_set_blocked(fd_stderr[0], 0)) {
307 zlog(ZLOG_SYSERROR, "failed to unblock pipes");
308 close(fd_stdout[0]);
309 close(fd_stdout[1]);
310 close(fd_stderr[0]);
311 close(fd_stderr[1]);
312 return -1;
313 }
314 return 0;
315}
316/* }}} */
317
318int fpm_stdio_parent_use_pipes(struct fpm_child_s *child) /* {{{ */
319{
320 if (0 == child->wp->config->catch_workers_output) { /* not required */
321 return 0;
322 }
323
324 close(fd_stdout[1]);
325 close(fd_stderr[1]);
326
327 child->fd_stdout = fd_stdout[0];
328 child->fd_stderr = fd_stderr[0];
329
330 fpm_event_set(&child->ev_stdout, child->fd_stdout, FPM_EV_READ, fpm_stdio_child_said, child);
331 fpm_event_add(&child->ev_stdout, 0);
332
333 fpm_event_set(&child->ev_stderr, child->fd_stderr, FPM_EV_READ, fpm_stdio_child_said, child);
334 fpm_event_add(&child->ev_stderr, 0);
335 return 0;
336}
337/* }}} */
338
339int fpm_stdio_discard_pipes(struct fpm_child_s *child) /* {{{ */
340{
341 if (0 == child->wp->config->catch_workers_output) { /* not required */
342 return 0;
343 }
344
345 close(fd_stdout[1]);
346 close(fd_stderr[1]);
347
348 close(fd_stdout[0]);
349 close(fd_stderr[0]);
350 return 0;
351}
352/* }}} */
353
354void fpm_stdio_child_use_pipes(struct fpm_child_s *child) /* {{{ */
355{
356 if (child->wp->config->catch_workers_output) {
357 dup2(fd_stdout[1], STDOUT_FILENO);
358 dup2(fd_stderr[1], STDERR_FILENO);
359 close(fd_stdout[0]); close(fd_stdout[1]);
360 close(fd_stderr[0]); close(fd_stderr[1]);
361 } else {
362 /* stdout of parent is always /dev/null */
364 }
365}
366/* }}} */
367
368int fpm_stdio_open_error_log(int reopen) /* {{{ */
369{
370 int fd;
371
372#ifdef HAVE_SYSLOG_H
373 if (!strcasecmp(fpm_global_config.error_log, "syslog")) {
374 php_openlog(fpm_global_config.syslog_ident, LOG_PID | LOG_CONS, fpm_global_config.syslog_facility);
375 fpm_globals.error_log_fd = ZLOG_SYSLOG;
376 if (fpm_use_error_log()) {
377 zlog_set_fd(fpm_globals.error_log_fd, 0);
378 }
379 return 0;
380 }
381#endif
382
383 fd = open(fpm_global_config.error_log, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR);
384
385 if (0 > fd) {
386 zlog(ZLOG_SYSERROR, "failed to open error_log (%s)", fpm_global_config.error_log);
387 return -1;
388 }
389
390 if (reopen) {
391 dup2(fd, fpm_globals.error_log_fd);
392 close(fd);
393 fd = fpm_globals.error_log_fd; /* for FD_CLOSEXEC to work */
394 } else {
395 fpm_globals.error_log_fd = fd;
396 if (fpm_use_error_log()) {
397 bool is_stderr = (
398 strcmp(fpm_global_config.error_log, "/dev/stderr") == 0 ||
399 strcmp(fpm_global_config.error_log, "/proc/self/fd/2") == 0
400 );
401 zlog_set_fd(fpm_globals.error_log_fd, is_stderr);
402 }
403 }
404 if (0 > fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC)) {
405 zlog(ZLOG_WARNING, "failed to change attribute of error_log");
406 }
407 return 0;
408}
409/* }}} */
#define STDOUT_FILENO
Definition cgi_main.c:291
zval * arg
Definition ffi.c:3975
buf start
Definition ffi.c:4687
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
#define FD_CLOEXEC
Definition file.h:148
struct fpm_globals_s fpm_globals
Definition fpm.c:24
struct fpm_global_config_s fpm_global_config
Definition fpm_conf.c:64
int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void(*callback)(struct fpm_event_s *, short, void *), void *arg)
Definition fpm_events.c:482
int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency)
Definition fpm_events.c:496
int fpm_event_del(struct fpm_event_s *ev)
Definition fpm_events.c:538
#define FPM_EV_READ
Definition fpm_events.h:7
int fpm_stdio_init_child(struct fpm_worker_pool_s *wp)
Definition fpm_stdio.c:140
int fpm_stdio_flush_child(void)
Definition fpm_stdio.c:164
int fpm_stdio_discard_pipes(struct fpm_child_s *child)
Definition fpm_stdio.c:339
#define FPM_STDIO_CMD_FLUSH
Definition fpm_stdio.c:162
int fpm_stdio_init_final(void)
Definition fpm_stdio.c:63
int fpm_stdio_redirect_stderr_to_error_log(void)
Definition fpm_stdio.c:109
void fpm_stdio_child_use_pipes(struct fpm_child_s *child)
Definition fpm_stdio.c:354
int fpm_stdio_prepare_pipes(struct fpm_child_s *child)
Definition fpm_stdio.c:288
int fpm_stdio_redirect_stderr_to_dev_null_for_syslog(void)
Definition fpm_stdio.c:126
int fpm_stdio_open_error_log(int reopen)
Definition fpm_stdio.c:368
int fpm_stdio_save_original_stderr(void)
Definition fpm_stdio.c:76
int fpm_stdio_parent_use_pipes(struct fpm_child_s *child)
Definition fpm_stdio.c:318
int fpm_stdio_init_main(void)
Definition fpm_stdio.c:27
int fpm_stdio_restore_original_stderr(int close_after_restore)
Definition fpm_stdio.c:91
#define STREAM_SET_MSG_PREFIX_FMT
Definition fpm_stdio.h:8
#define NULL
Definition gdcache.h:45
#define STDIN_FILENO
Definition magic.c:78
#define STDERR_FILENO
Definition php_cli.c:221
unsigned const char * pos
Definition php_ffi.h:52
#define PHP_IS_TRANSIENT_ERROR(err)
Definition php_network.h:54
void php_openlog(const char *ident, int option, int facility)
Definition php_syslog.c:74
void php_closelog(void)
Definition php_syslog.c:80
int fd
Definition phpdbg.h:282
bool postponed_free
struct fpm_worker_pool_s * wp
struct zlog_stream * log_stream
struct fpm_worker_pool_config_s * config
int child_pid
Definition zlog.h:79
unsigned int is_stdout
Definition zlog.h:76
#define LOG_PID
Definition syslog.h:61
#define LOG_CONS
Definition syslog.h:62
#define close(a)
#define errno
strcmp(string $string1, string $string2)
#define strcasecmp(s1, s2)
ZEND_DLIMPORT int isatty(int fd)
zlog_bool zlog_stream_finish(struct zlog_stream *stream)
Definition zlog.c:838
void zlog_set_launched(void)
Definition zlog.c:71
ssize_t zlog_stream_str(struct zlog_stream *stream, const char *str, size_t str_len)
Definition zlog.c:748
void zlog_stream_init_ex(struct zlog_stream *stream, int flags, int fd)
Definition zlog.c:543
zlog_bool zlog_stream_set_msg_prefix(struct zlog_stream *stream, const char *fmt,...)
Definition zlog.c:586
void zlog_stream_set_child_pid(struct zlog_stream *stream, int child_pid)
Definition zlog.c:574
void zlog_stream_set_msg_quoting(struct zlog_stream *stream, zlog_bool quote)
Definition zlog.c:580
zlog_bool zlog_stream_set_msg_suffix(struct zlog_stream *stream, const char *suffix, const char *final_suffix)
Definition zlog.c:614
void zlog_stream_set_decorating(struct zlog_stream *stream, zlog_bool decorate)
Definition zlog.c:550
void zlog_stream_set_is_stdout(struct zlog_stream *stream, zlog_bool is_stdout)
Definition zlog.c:568
void zlog_stream_set_wrapping(struct zlog_stream *stream, zlog_bool wrap)
Definition zlog.c:562
int zlog_set_fd(int new_fd, zlog_bool is_stderr)
Definition zlog.c:92
#define ZLOG_SYSLOG
Definition zlog.h:55
#define ZLOG_TRUE
Definition zlog.h:16
#define ZLOG_SYSERROR
Definition zlog.h:53
@ ZLOG_DEBUG
Definition zlog.h:42
@ ZLOG_WARNING
Definition zlog.h:44
#define zlog(flags,...)
Definition zlog.h:9