root/sapi/fpm/fpm/fpm_stdio.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fpm_stdio_init_main
  2. fpm_use_error_log
  3. fpm_stdio_init_final
  4. fpm_stdio_init_child
  5. fpm_stdio_child_said
  6. fpm_stdio_prepare_pipes
  7. fpm_stdio_parent_use_pipes
  8. fpm_stdio_discard_pipes
  9. fpm_stdio_child_use_pipes
  10. fpm_stdio_open_error_log

   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 
  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 
  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 

/* [<][>][^][v][top][bottom][index][help] */