Back to index

php5  5.3.10
fpm_status.c
Go to the documentation of this file.
00001 
00002        /* $Id: fpm_status.c 317901 2011-10-08 14:04:09Z fat $ */
00003        /* (c) 2009 Jerome Loyet */
00004 
00005 #include "php.h"
00006 #include "SAPI.h"
00007 #include <stdio.h>
00008 
00009 #include "fpm_config.h"
00010 #include "fpm_scoreboard.h"
00011 #include "fpm_status.h"
00012 #include "fpm_clock.h"
00013 #include "fpm_scoreboard.h"
00014 #include "zlog.h"
00015 #include "fpm_atomic.h"
00016 #include "fpm_conf.h"
00017 #include <ext/standard/html.h>
00018 
00019 static char *fpm_status_uri = NULL;
00020 static char *fpm_status_ping_uri = NULL;
00021 static char *fpm_status_ping_response = NULL;
00022 
00023 
00024 int fpm_status_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
00025 {
00026        if (!wp || !wp->config) {
00027               zlog(ZLOG_ERROR, "unable to init fpm_status because conf structure is NULL");
00028               return -1;
00029        }
00030 
00031        if (wp->config->pm_status_path) {
00032               fpm_status_uri = strdup(wp->config->pm_status_path);
00033        }
00034 
00035        if (wp->config->ping_path) {
00036               if (!wp->config->ping_response) {
00037                      zlog(ZLOG_ERROR, "[pool %s] ping is set (%s) but ping.response is not set.", wp->config->name, wp->config->ping_path);
00038                      return -1;
00039               }
00040               fpm_status_ping_uri = strdup(wp->config->ping_path);
00041               fpm_status_ping_response = strdup(wp->config->ping_response);
00042        }
00043 
00044        return 0;
00045 }
00046 /* }}} */
00047 
00048 int fpm_status_handle_request(TSRMLS_D) /* {{{ */
00049 {
00050        struct fpm_scoreboard_s scoreboard, *scoreboard_p;
00051        struct fpm_scoreboard_proc_s proc;
00052        char *buffer, *time_format, time_buffer[64];
00053        time_t now_epoch;
00054        int full, encode;
00055        char *short_syntax, *short_post;
00056        char *full_pre, *full_syntax, *full_post, *full_separator;
00057 
00058        if (!SG(request_info).request_uri) {
00059               return 0;
00060        }
00061 
00062        /* PING */
00063        if (fpm_status_ping_uri && fpm_status_ping_response && !strcmp(fpm_status_ping_uri, SG(request_info).request_uri)) {
00064               fpm_request_executing();
00065               sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
00066               sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
00067               sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
00068               SG(sapi_headers).http_response_code = 200;
00069 
00070               /* handle HEAD */
00071               if (SG(request_info).headers_only) {
00072                      return 1;
00073               }
00074 
00075               PUTS(fpm_status_ping_response);
00076               return 1;
00077        }
00078 
00079        /* STATUS */
00080        if (fpm_status_uri && !strcmp(fpm_status_uri, SG(request_info).request_uri)) {
00081               fpm_request_executing();
00082 
00083               scoreboard_p = fpm_scoreboard_get();
00084               if (!scoreboard_p) {
00085                      zlog(ZLOG_ERROR, "status: unable to find or access status shared memory");
00086                      SG(sapi_headers).http_response_code = 500;
00087                      sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
00088                      sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
00089                      sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
00090                      PUTS("Internal error. Please review log file for errors.");
00091                      return 1;
00092               }
00093 
00094               if (!fpm_spinlock(&scoreboard_p->lock, 1)) {
00095                      zlog(ZLOG_NOTICE, "[pool %s] status: scoreboard already in used.", scoreboard_p->pool);
00096                      SG(sapi_headers).http_response_code = 503;
00097                      sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
00098                      sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
00099                      sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
00100                      PUTS("Server busy. Please try again later.");
00101                      return 1;
00102               }
00103               /* copy the scoreboard not to bother other processes */
00104               scoreboard = *scoreboard_p;
00105               fpm_unlock(scoreboard_p->lock);
00106 
00107               if (scoreboard.idle < 0 || scoreboard.active < 0) {
00108                      zlog(ZLOG_ERROR, "[pool %s] invalid status values", scoreboard.pool);
00109                      SG(sapi_headers).http_response_code = 500;
00110                      sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
00111                      sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
00112                      sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
00113                      PUTS("Internal error. Please review log file for errors.");
00114                      return 1;
00115               }
00116 
00117               /* send common headers */
00118               sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC);
00119               sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC);
00120               SG(sapi_headers).http_response_code = 200;
00121 
00122               /* handle HEAD */
00123               if (SG(request_info).headers_only) {
00124                      return 1;
00125               }
00126 
00127               /* full status ? */
00128               full = SG(request_info).request_uri && strstr(SG(request_info).query_string, "full");
00129               short_syntax = short_post = NULL;
00130               full_separator = full_pre = full_syntax = full_post = NULL;
00131               encode = 0;
00132 
00133               /* HTML */
00134               if (SG(request_info).query_string && strstr(SG(request_info).query_string, "html")) {
00135                      sapi_add_header_ex(ZEND_STRL("Content-Type: text/html"), 1, 1 TSRMLS_CC);
00136                      time_format = "%d/%b/%Y:%H:%M:%S %z";
00137                      encode = 1;
00138 
00139                      short_syntax =
00140                             "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
00141                             "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"
00142                             "<head><title>PHP-FPM Status Page</title></head>\n"
00143                             "<body>\n"
00144                             "<table>\n"
00145                                    "<tr><th>pool</th><td>%s</td></tr>\n"
00146                                    "<tr><th>process manager</th><td>%s</td></tr>\n"
00147                                    "<tr><th>start time</th><td>%s</td></tr>\n"
00148                                    "<tr><th>start since</th><td>%lu</td></tr>\n"
00149                                    "<tr><th>accepted conn</th><td>%lu</td></tr>\n"
00150 #if HAVE_FPM_LQ
00151                                    "<tr><th>listen queue</th><td>%u</td></tr>\n"
00152                                    "<tr><th>max listen queue</th><td>%u</td></tr>\n"
00153                                    "<tr><th>listen queue len</th><td>%d</td></tr>\n"
00154 #endif
00155                                    "<tr><th>idle processes</th><td>%d</td></tr>\n"
00156                                    "<tr><th>active processes</th><td>%d</td></tr>\n"
00157                                    "<tr><th>total processes</th><td>%d</td></tr>\n"
00158                                    "<tr><th>max active processes</th><td>%d</td></tr>\n"
00159                                    "<tr><th>max children reached</th><td>%u</td></tr>\n"
00160                             "</table>\n";
00161 
00162                      if (!full) {
00163                             short_post = "</body></html>";
00164                      } else {
00165                             full_pre =
00166                                    "<table border=\"1\">\n"
00167                                    "<tr>"
00168                                           "<th>pid</th>"
00169                                           "<th>state</th>"
00170                                           "<th>start time</th>"
00171                                           "<th>start since</th>"
00172                                           "<th>requests</th>"
00173                                           "<th>request duration</th>"
00174                                           "<th>request method</th>"
00175                                           "<th>request uri</th>"
00176                                           "<th>content length</th>"
00177                                           "<th>user</th>"
00178                                           "<th>script</th>"
00179 #if HAVE_FPM_LQ
00180                                           "<th>last request cpu</th>"
00181 #endif
00182                                           "<th>last request memory</th>"
00183                                    "</tr>\n";
00184 
00185                             full_syntax =
00186                                    "<tr>"
00187                                           "<td>%d</td>"
00188                                           "<td>%s</td>"
00189                                           "<td>%s</td>"
00190                                           "<td>%lu</td>"
00191                                           "<td>%lu</td>"
00192                                           "<td>%lu</td>"
00193                                           "<td>%s</td>"
00194                                           "<td>%s%s%s</td>"
00195                                           "<td>%zu</td>"
00196                                           "<td>%s</td>"
00197                                           "<td>%s</td>"
00198 #if HAVE_FPM_LQ
00199                                           "<td>%.2f</td>"
00200 #endif
00201                                           "<td>%zu</td>"
00202                                    "</tr>\n";
00203 
00204                             full_post = "</table></body></html>";
00205                      }
00206 
00207               /* XML */
00208               } else if (SG(request_info).request_uri && strstr(SG(request_info).query_string, "xml")) {
00209                      sapi_add_header_ex(ZEND_STRL("Content-Type: text/xml"), 1, 1 TSRMLS_CC);
00210                      time_format = "%s";
00211                      encode = 1;
00212 
00213                      short_syntax =
00214                             "<?xml version=\"1.0\" ?>\n"
00215                             "<status>\n"
00216                             "<pool>%s</pool>\n"
00217                             "<process-manager>%s</process-manager>\n"
00218                             "<start-time>%s</start-time>\n"
00219                             "<start-since>%lu</start-since>\n"
00220                             "<accepted-conn>%lu</accepted-conn>\n"
00221 #if HAVE_FPM_LQ
00222                             "<listen-queue>%u</listen-queue>\n"
00223                             "<max-listen-queue>%u</max-listen-queue>\n"
00224                             "<listen-queue-len>%d</listen-queue-len>\n"
00225 #endif
00226                             "<idle-processes>%d</idle-processes>\n"
00227                             "<active-processes>%d</active-processes>\n"
00228                             "<total-processes>%d</total-processes>\n"
00229                             "<max-active-processes>%d</max-active-processes>\n"
00230                             "<max-children-reached>%u</max-children-reached>\n";
00231 
00232                             if (!full) {
00233                                    short_post = "</status>";
00234                             } else {
00235                                    full_pre = "<processes>\n";
00236                                    full_syntax = 
00237                                           "<process>"
00238                                                  "<pid>%d</pid>"
00239                                                  "<state>%s</state>"
00240                                                  "<start-time>%s</start-time>"
00241                                                  "<start-since>%lu</start-since>"
00242                                                  "<requests>%lu</requests>"
00243                                                  "<request-duration>%lu</request-duration>"
00244                                                  "<request-method>%s</request-method>"
00245                                                  "<request-uri>%s%s%s</request-uri>"
00246                                                  "<content-length>%zu</content-length>"
00247                                                  "<user>%s</user>"
00248                                                  "<script>%s</script>"
00249 #if HAVE_FPM_LQ
00250                                                  "<last-request-cpu>%.2f</last-request-cpu>"
00251 #endif
00252                                                  "<last-request-memory>%zu</last-request-memory>"
00253                                           "</process>\n"
00254                                    ;
00255                                    full_post = "</processes>\n</status>";
00256                             }
00257 
00258                      /* JSON */
00259               } else if (SG(request_info).request_uri && strstr(SG(request_info).query_string, "json")) {
00260                      sapi_add_header_ex(ZEND_STRL("Content-Type: application/json"), 1, 1 TSRMLS_CC);
00261                      time_format = "%s";
00262 
00263                      short_syntax =
00264                             "{"
00265                             "\"pool\":\"%s\","
00266                             "\"process manager\":\"%s\","
00267                             "\"start time\":%s,"
00268                             "\"start since\":%lu,"
00269                             "\"accepted conn\":%lu,"
00270 #if HAVE_FPM_LQ
00271                             "\"listen queue\":%u,"
00272                             "\"max listen queue\":%u,"
00273                             "\"listen queue len\":%d,"
00274 #endif
00275                             "\"idle processes\":%d,"
00276                             "\"active processes\":%d,"
00277                             "\"total processes\":%d,"
00278                             "\"max active processes\":%d,"
00279                             "\"max children reached\":%u";
00280 
00281                      if (!full) {
00282                             short_post = "}";
00283                      } else {
00284                             full_separator = ",";
00285                             full_pre = ", \"processes\":[";
00286 
00287                             full_syntax = "{"
00288                                    "\"pid\":%d,"
00289                                    "\"state\":\"%s\","
00290                                    "\"start time\":%s,"
00291                                    "\"start since\":%lu,"
00292                                    "\"requests\":%lu,"
00293                                    "\"request duration\":%lu,"
00294                                    "\"request method\":\"%s\","
00295                                    "\"request uri\":\"%s%s%s\","
00296                                    "\"content length\":%zu,"
00297                                    "\"user\":\"%s\","
00298                                    "\"script\":\"%s\","
00299 #if HAVE_FPM_LQ
00300                                    "\"last request cpu\":%.2f,"
00301 #endif
00302                                    "\"last request memory\":%zu"
00303                                    "}";
00304 
00305                             full_post = "]}";
00306                      }
00307 
00308               /* TEXT */
00309               } else {
00310                      sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC);
00311                      time_format = "%d/%b/%Y:%H:%M:%S %z";
00312 
00313                      short_syntax =
00314                             "pool:                 %s\n"
00315                             "process manager:      %s\n"
00316                             "start time:           %s\n"
00317                             "start since:          %lu\n"
00318                             "accepted conn:        %lu\n"
00319 #if HAVE_FPM_LQ
00320                             "listen queue:         %u\n"
00321                             "max listen queue:     %u\n"
00322                             "listen queue len:     %d\n"
00323 #endif
00324                             "idle processes:       %d\n"
00325                             "active processes:     %d\n"
00326                             "total processes:      %d\n"
00327                             "max active processes: %d\n"
00328                             "max children reached: %u\n";
00329 
00330                             if (full) {
00331                                    full_syntax =
00332                                           "\n"
00333                                           "************************\n"
00334                                           "pid:                  %d\n"
00335                                           "state:                %s\n"
00336                                           "start time:           %s\n"
00337                                           "start since:          %lu\n"
00338                                           "requests:             %lu\n"
00339                                           "request duration:     %lu\n"
00340                                           "request method:       %s\n"
00341                                           "request URI:          %s%s%s\n"
00342                                           "content length:       %zu\n"
00343                                           "user:                 %s\n"
00344                                           "script:               %s\n"
00345 #ifdef HAVE_FPM_LQ
00346                                           "last request cpu:     %.2f\n"
00347 #endif
00348                                           "last request memory:  %zu\n";
00349                             }
00350               }
00351 
00352               strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&scoreboard.start_epoch));
00353               now_epoch = time(NULL);
00354               spprintf(&buffer, 0, short_syntax,
00355                             scoreboard.pool,
00356                             PM2STR(scoreboard.pm),
00357                             time_buffer,
00358                             now_epoch - scoreboard.start_epoch,
00359                             scoreboard.requests,
00360 #if HAVE_FPM_LQ
00361                             scoreboard.lq,
00362                             scoreboard.lq_max,
00363                             scoreboard.lq_len,
00364 #endif
00365                             scoreboard.idle,
00366                             scoreboard.active,
00367                             scoreboard.idle + scoreboard.active,
00368                             scoreboard.active_max,
00369                             scoreboard.max_children_reached);
00370 
00371               PUTS(buffer);
00372               efree(buffer);
00373 
00374               if (short_post) {
00375                      PUTS(short_post);
00376               }
00377 
00378               /* no need to test the var 'full' */
00379               if (full_syntax) {
00380                      int i, len, first;
00381                      char *query_string;
00382                      struct timeval duration, now;
00383 #ifdef HAVE_FPM_LQ
00384                      float cpu;
00385 #endif
00386 
00387                      fpm_clock_get(&now);
00388 
00389                      if (full_pre) {
00390                             PUTS(full_pre);
00391                      }
00392 
00393                      first = 1;
00394                      for (i=0; i<scoreboard_p->nprocs; i++) {
00395                             if (!scoreboard_p->procs[i] || !scoreboard_p->procs[i]->used) {
00396                                    continue;
00397                             }
00398                             proc = *scoreboard_p->procs[i];
00399 
00400                             if (first) {
00401                                    first = 0;
00402                             } else {
00403                                    if (full_separator) {
00404                                           PUTS(full_separator);
00405                                    }
00406                             }
00407 
00408                             query_string = NULL;
00409                             len = 0;
00410                             if (proc.query_string[0] != '\0') {
00411                                    if (!encode) {
00412                                           query_string = proc.query_string;
00413                                    } else {
00414                                           query_string = php_escape_html_entities_ex((unsigned char *)proc.query_string, strlen(proc.query_string), &len, 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, NULL, 1 TSRMLS_CC);
00415                                    }
00416                             }
00417 
00418 #ifdef HAVE_FPM_LQ
00419                             /* prevent NaN */
00420                             if (proc.cpu_duration.tv_sec == 0 && proc.cpu_duration.tv_usec == 0) {
00421                                    cpu = 0.;
00422                             } else {
00423                                    cpu = (proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cutime + proc.last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.;
00424                             }
00425 #endif
00426 
00427                             if (proc.request_stage == FPM_REQUEST_ACCEPTING) {
00428                                    duration = proc.duration;
00429                             } else {
00430                                    timersub(&now, &proc.accepted, &duration);
00431                             }
00432                             strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&proc.start_epoch));
00433                             spprintf(&buffer, 0, full_syntax,
00434                                    proc.pid,
00435                                    fpm_request_get_stage_name(proc.request_stage),
00436                                    time_buffer,
00437                                    now_epoch - proc.start_epoch,
00438                                    proc.requests,
00439                                    duration.tv_sec * 1000000UL + duration.tv_usec,
00440                                    proc.request_method[0] != '\0' ? proc.request_method : "-",
00441                                    proc.request_uri[0] != '\0' ? proc.request_uri : "-",
00442                                    query_string ? "?" : "",
00443                                    query_string ? query_string : "",
00444                                    proc.content_length,
00445                                    proc.auth_user[0] != '\0' ? proc.auth_user : "-",
00446                                    proc.script_filename[0] != '\0' ? proc.script_filename : "-",
00447 #ifdef HAVE_FPM_LQ
00448                                    proc.request_stage == FPM_REQUEST_ACCEPTING ? cpu : 0.,
00449 #endif
00450                                    proc.request_stage == FPM_REQUEST_ACCEPTING ? proc.memory : 0);
00451                             PUTS(buffer);
00452                             efree(buffer);
00453 
00454                             if (len > 0 && query_string) {
00455                                    efree(query_string);
00456                             }
00457                      }
00458 
00459                      if (full_post) {
00460                             PUTS(full_post);
00461                      }
00462               }
00463 
00464               return 1;
00465        }
00466 
00467        return 0;
00468 }
00469 /* }}} */
00470