Example #1
0
ApacheRequest::ApacheRequest( request_rec *r, PHIResponseRec *resp )
    : PHISRequest()
{
    //qDebug( "ApacheRequest::ApacheRequest()" );
    _resp=resp;
    _url=QUrl::fromEncoded( QByteArray::fromRawData( r->unparsed_uri,
        qstrlen( r->unparsed_uri ) ), QUrl::StrictMode );
    _documentRoot=QString::fromUtf8( ap_document_root( r ) );

    apr_table_do( addHeaderItem, &_headers, r->headers_in, NULL );

    // Content
    _keywords.insert( KContentType, _headers.value( "Content-Type" ) );
    _contentLength=_headers.value( "Content-Length", 0 ).toLongLong();

    _keywords.insert( KMethod, QByteArray::fromRawData( r->method, qstrlen( r->method ) ) );
    _url.setHost( QString::fromUtf8( QByteArray::fromRawData( r->hostname, qstrlen( r->hostname ) ) ) );
    if ( _url.userName().isEmpty() )
        _url.setUserName( QString::fromUtf8( QByteArray::fromRawData( r->user, qstrlen( r->user ) ) ) );
    if ( _url.password().isEmpty() )
        _url.setPassword( QString() );

    QByteArray arr=QByteArray::fromRawData( r->protocol, qstrlen( r->protocol ) );
    _httpMajor=arr.mid( 5, 1 ).toInt();
    _httpMinor=arr.mid( 7, 1 ).toInt();
    _canonicalFilename=QString::fromUtf8( QByteArray::fromRawData( r->filename, qstrlen( r->filename ) ) );
    qDebug( "------------------->%s", qPrintable(_canonicalFilename) );

    server_rec *s=r->server;
    Q_ASSERT( s );
    _keywords.insert( KDefName, QByteArray::fromRawData( s->defn_name, qstrlen( s->defn_name ) ) );
    _keywords.insert( KServerDesc, QByteArray( ap_get_server_description(), qstrlen( ap_get_server_description() ) ) );
    _keywords.insert( KAdmin, QByteArray::fromRawData( s->server_admin, qstrlen( s->server_admin ) ) );
    _keywords.insert( KServerHostname, QByteArray::fromRawData( s->server_hostname, qstrlen( s->server_hostname ) ) );
    if ( s->port>0 ) _url.setPort( s->port );

    /** @todo implement SSL stuff properly */
    if ( s->port==443 ) _url.setScheme( QStringLiteral( "https" ) );
    else _url.setScheme( QStringLiteral( "http" ) );

    _keepAlive=static_cast<qint32>( s->keep_alive_timeout/1000 );
    _tmpDir=PHIParent::instance()->tempDir( _url.host() );
    _imgDir=_tmpDir+QDir::separator()+QLatin1String( "img" );
    //qDebug( "_tmpDir=%s", qPrintable( _tmpDir ) );

    conn_rec *c=r->connection;
    Q_ASSERT( c );
    _localIP=QHostAddress( QString::fromLatin1( c->local_ip ) );
    _remoteIP=QHostAddress( QString::fromLatin1( c->remote_ip ) );
}
Example #2
0
static void show_server_data()
{
    ap_listen_rec *lr;
    module **m;

    printf("%s\n", ap_get_server_description());
    if (ap_my_addrspace && (ap_my_addrspace[0] != 'O') && (ap_my_addrspace[1] != 'S'))
        printf("   Running in address space %s\n", ap_my_addrspace);


    /* Display listening ports */
    printf("   Listening on port(s):");
    lr = ap_listeners;
    do {
       printf(" %d", lr->bind_addr->port);
       lr = lr->next;
    } while(lr && lr != ap_listeners);

    /* Display dynamic modules loaded */
    printf("\n");
    for (m = ap_loaded_modules; *m != NULL; m++) {
        if (((module*)*m)->dynamic_load_handle) {
            printf("   Loaded dynamic module %s\n", ((module*)*m)->name);
        }
    }
}
Example #3
0
/* A bit like mod_status: add a location:

     <Location /svn-status>
       SetHandler svn-status
     </Location>

  and then point a browser at http://server/svn-status.
*/
int dav_svn__status(request_rec *r)
{
  svn_cache__info_t *info;
  svn_string_t *text_stats;
  apr_array_header_t *lines;
  int i;

  if (r->method_number != M_GET || strcmp(r->handler, "svn-status"))
    return DECLINED;

  info = svn_cache__membuffer_get_global_info(r->pool);
  text_stats = svn_cache__format_info(info, FALSE, r->pool);
  lines = svn_cstring_split(text_stats->data, "\n", FALSE, r->pool);

  ap_set_content_type(r, "text/html; charset=ISO-8859-1");

  ap_rvputs(r,
            DOCTYPE_HTML_3_2
            "<html><head>\n"
            "<title>Apache SVN Status</title>\n"
            "</head><body>\n"
            "<h1>Apache SVN Cache Status for ",
            ap_escape_html(r->pool, ap_get_server_name(r)),
            " (via ",
            r->connection->local_ip,
            ")</h1>\n<dl>\n<dt>Server Version: ",
            ap_get_server_description(),
            "</dt>\n<dt>Current Time: ",
            ap_ht_time(r->pool, apr_time_now(), DEFAULT_TIME_FORMAT, 0),
            "</dt>\n", SVN_VA_NULL);

#if defined(WIN32) || (defined(HAVE_UNISTD_H) && defined(HAVE_GETPID))
  /* On Unix the server is generally multiple processes and this
     request only shows the status of the single process that handles
     the request. Ideally we would iterate over all processes but that
     would need some MPM support, so we settle for simply showing the
     process ID. */
  ap_rprintf(r, "<dt>Server process id: %d</dt>\n", (int)getpid());
#endif

  for (i = 0; i < lines->nelts; ++i)
    {
      const char *line = APR_ARRAY_IDX(lines, i, const char *);
      ap_rvputs(r, "<dt>", line, "</dt>\n", SVN_VA_NULL);
    }

  ap_rvputs(r, "</dl></body></html>\n", SVN_VA_NULL);

  return 0;
}
Example #4
0
static int netware_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
    apr_status_t status=0;

    pconf = _pconf;
    ap_server_conf = s;

    if (setup_listeners(s)) {
        ap_log_error(APLOG_MARK, APLOG_ALERT, status, s, APLOGNO(00223)
            "no listening sockets available, shutting down");
        return !OK;
    }

    restart_pending = shutdown_pending = 0;
    worker_thread_count = 0;

    if (!is_graceful) {
        if (ap_run_pre_mpm(s->process->pool, SB_NOT_SHARED) != OK) {
            return !OK;
        }
    }

    /* Only set slot 0 since that is all NetWare will ever have. */
    ap_scoreboard_image->parent[0].pid = getpid();
    ap_run_child_status(ap_server_conf,
                        ap_scoreboard_image->parent[0].pid,
                        ap_my_generation,
                        0,
                        MPM_CHILD_STARTED);

    set_signals();

    apr_pool_create(&pmain, pconf);
    ap_run_child_init(pmain, ap_server_conf);

    if (ap_threads_max_free < ap_threads_min_free + 1)  /* Don't thrash... */
        ap_threads_max_free = ap_threads_min_free + 1;
    request_count = 0;

    startup_workers(ap_threads_to_start);

     /* Allow the Apache screen to be closed normally on exit() only if it
        has not been explicitly forced to close on exit(). (ie. the -E flag
        was specified at startup) */
    if (hold_screen_on_exit > 0) {
        hold_screen_on_exit = 0;
    }

    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00224)
            "%s configured -- resuming normal operations",
            ap_get_server_description());
    ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00225)
            "Server built: %s", ap_get_server_built());
    ap_log_command_line(plog, s);
    ap_log_common(s);
    show_server_data();

    mpm_state = AP_MPMQ_RUNNING;
    while (!restart_pending && !shutdown_pending) {
        perform_idle_server_maintenance(pconf);
        if (show_settings)
            display_settings();
        apr_thread_yield();
        apr_sleep(SCOREBOARD_MAINTENANCE_INTERVAL);
    }
    mpm_state = AP_MPMQ_STOPPING;

    ap_run_child_status(ap_server_conf,
                        ap_scoreboard_image->parent[0].pid,
                        ap_my_generation,
                        0,
                        MPM_CHILD_EXITED);

    /* Shutdown the listen sockets so that we don't get stuck in a blocking call.
    shutdown_listeners();*/

    if (shutdown_pending) { /* Got an unload from the console */
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00226)
            "caught SIGTERM, shutting down");

        while (worker_thread_count > 0) {
            printf ("\rShutdown pending. Waiting for %lu thread(s) to terminate...",
                    worker_thread_count);
            apr_thread_yield();
        }

        mpm_main_cleanup();
        return DONE;
    }
    else {  /* the only other way out is a restart */
        /* advance to the next generation */
        /* XXX: we really need to make sure this new generation number isn't in
         * use by any of the children.
         */
        ++ap_my_generation;
        ap_scoreboard_image->global->running_generation = ap_my_generation;

        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00227)
                "Graceful restart requested, doing restart");

        /* Wait for all of the threads to terminate before initiating the restart */
        while (worker_thread_count > 0) {
            printf ("\rRestart pending. Waiting for %lu thread(s) to terminate...",
                    worker_thread_count);
            apr_thread_yield();
        }
        printf ("\nRestarting...\n");
    }

    mpm_main_cleanup();
    return OK;
}
Example #5
0
static void display_settings()
{
    int status_array[SERVER_NUM_STATUS];
    int i, status, total=0;
    int reqs = request_count;
#ifdef DBINFO_ON
    int wblock = would_block;

    would_block = 0;
#endif

    request_count = 0;

    ClearScreen (getscreenhandle());
    printf("%s \n", ap_get_server_description());

    for (i=0;i<SERVER_NUM_STATUS;i++) {
        status_array[i] = 0;
    }

    for (i = 0; i < ap_threads_limit; ++i) {
        status = (ap_scoreboard_image->servers[0][i]).status;
        status_array[status]++;
    }

    for (i=0;i<SERVER_NUM_STATUS;i++) {
        switch(i)
        {
        case SERVER_DEAD:
            printf ("Available:\t%d\n", status_array[i]);
            break;
        case SERVER_STARTING:
            printf ("Starting:\t%d\n", status_array[i]);
            break;
        case SERVER_READY:
            printf ("Ready:\t\t%d\n", status_array[i]);
            break;
        case SERVER_BUSY_READ:
            printf ("Busy:\t\t%d\n", status_array[i]);
            break;
        case SERVER_BUSY_WRITE:
            printf ("Busy Write:\t%d\n", status_array[i]);
            break;
        case SERVER_BUSY_KEEPALIVE:
            printf ("Busy Keepalive:\t%d\n", status_array[i]);
            break;
        case SERVER_BUSY_LOG:
            printf ("Busy Log:\t%d\n", status_array[i]);
            break;
        case SERVER_BUSY_DNS:
            printf ("Busy DNS:\t%d\n", status_array[i]);
            break;
        case SERVER_CLOSING:
            printf ("Closing:\t%d\n", status_array[i]);
            break;
        case SERVER_GRACEFUL:
            printf ("Restart:\t%d\n", status_array[i]);
            break;
        case SERVER_IDLE_KILL:
            printf ("Idle Kill:\t%d\n", status_array[i]);
            break;
        default:
            printf ("Unknown Status:\t%d\n", status_array[i]);
            break;
        }
        if (i != SERVER_DEAD)
            total+=status_array[i];
    }
    printf ("Total Running:\t%d\tout of: \t%d\n", total, ap_threads_limit);
    printf ("Requests per interval:\t%d\n", reqs);

#ifdef DBINFO_ON
    printf ("Would blocks:\t%d\n", wblock);
    printf ("Successful retries:\t%d\n", retry_success);
    printf ("Failed retries:\t%d\n", retry_fail);
    printf ("Avg retries:\t%d\n", retry_success == 0 ? 0 : avg_retries / retry_success);
#endif
}
Example #6
0
static int CommandLineInterpreter(scr_t screenID, const char *commandLine)
{
    char *szCommand = "APACHE2 ";
    int iCommandLen = 8;
    char szcommandLine[256];
    char *pID;
    screenID = screenID;


    if (commandLine == NULL)
        return NOTMYCOMMAND;
    if (strlen(commandLine) <= strlen(szCommand))
        return NOTMYCOMMAND;

    apr_cpystrn(szcommandLine, commandLine, sizeof(szcommandLine));

    /*  All added commands begin with "APACHE2 " */

    if (!strnicmp(szCommand, szcommandLine, iCommandLen)) {
        ActivateScreen (getscreenhandle());

        /* If an instance id was not given but the nlm is loaded in
            protected space, then the the command belongs to the
            OS address space instance to pass it on. */
        pID = strstr (szcommandLine, "-p");
        if ((pID == NULL) && nlmisloadedprotected())
            return NOTMYCOMMAND;

        /* If we got an instance id but it doesn't match this
            instance of the nlm, pass it on. */
        if (pID) {
            pID = &pID[2];
            while (*pID && (*pID == ' '))
                pID++;
        }
        if (pID && ap_my_addrspace && strnicmp(pID, ap_my_addrspace, strlen(ap_my_addrspace)))
            return NOTMYCOMMAND;

        /* If we have determined that this command belongs to this
            instance of the nlm, then handle it. */
        if (!strnicmp("RESTART",&szcommandLine[iCommandLen],3)) {
            printf("Restart Requested...\n");
            restart();
        }
        else if (!strnicmp("VERSION",&szcommandLine[iCommandLen],3)) {
            printf("Server version: %s\n", ap_get_server_description());
            printf("Server built:   %s\n", ap_get_server_built());
        }
        else if (!strnicmp("MODULES",&szcommandLine[iCommandLen],3)) {
            ap_show_modules();
        }
        else if (!strnicmp("DIRECTIVES",&szcommandLine[iCommandLen],3)) {
                ap_show_directives();
        }
        else if (!strnicmp("SHUTDOWN",&szcommandLine[iCommandLen],3)) {
            printf("Shutdown Requested...\n");
            shutdown_pending = 1;
        }
        else if (!strnicmp("SETTINGS",&szcommandLine[iCommandLen],3)) {
            if (show_settings) {
                show_settings = 0;
                ClearScreen (getscreenhandle());
                show_server_data();
            }
            else {
                show_settings = 1;
                display_settings();
            }
        }
        else {
            show_settings = 0;
            if (strnicmp("HELP",&szcommandLine[iCommandLen],3))
                printf("Unknown APACHE2 command %s\n", &szcommandLine[iCommandLen]);
            printf("Usage: APACHE2 [command] [-p <instance ID>]\n");
            printf("Commands:\n");
            printf("\tDIRECTIVES - Show directives\n");
            printf("\tHELP       - Display this help information\n");
            printf("\tMODULES    - Show a list of the loaded modules\n");
            printf("\tRESTART    - Reread the configuration file and restart Apache\n");
            printf("\tSETTINGS   - Show current thread status\n");
            printf("\tSHUTDOWN   - Shutdown Apache\n");
            printf("\tVERSION    - Display the server version information\n");
        }

        /*  Tell NetWare we handled the command */
        return HANDLEDCOMMAND;
    }

    /*  Tell NetWare that the command isn't mine */
    return NOTMYCOMMAND;
}
Example #7
0
/* This function connects to the server and sends enough data to
 * ensure the child wakes up and processes a new connection.  This
 * permits the MPM to skip the poll when there is only one listening
 * socket, because it provides a alternate way to unblock an accept()
 * when the pod is used.  */
static apr_status_t dummy_connection(ap_pod_t *pod)
{
    const char *data;
    apr_status_t rv;
    apr_socket_t *sock;
    apr_pool_t *p;
    apr_size_t len;
    ap_listen_rec *lp;

    /* create a temporary pool for the socket.  pconf stays around too long */
    rv = apr_pool_create(&p, pod->p);
    if (rv != APR_SUCCESS) {
        return rv;
    }

    /* If possible, find a listener which is configured for
     * plain-HTTP, not SSL; using an SSL port would either be
     * expensive to do correctly (performing a complete SSL handshake)
     * or cause log spam by doing incorrectly (simply sending EOF). */
    lp = ap_listeners;
    while (lp && lp->protocol && strcasecmp(lp->protocol, "http") != 0) {
        lp = lp->next;
    }
    if (!lp) {
        lp = ap_listeners;
    }

    rv = apr_socket_create(&sock, lp->bind_addr->family, SOCK_STREAM, 0, p);
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00054)
                     "get socket to connect to listener");
        apr_pool_destroy(p);
        return rv;
    }

    /* on some platforms (e.g., FreeBSD), the kernel won't accept many
     * queued connections before it starts blocking local connects...
     * we need to keep from blocking too long and instead return an error,
     * because the MPM won't want to hold up a graceful restart for a
     * long time
     */
    rv = apr_socket_timeout_set(sock, apr_time_from_sec(3));
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_WARNING, rv, ap_server_conf, APLOGNO(00055)
                     "set timeout on socket to connect to listener");
        apr_socket_close(sock);
        apr_pool_destroy(p);
        return rv;
    }

    rv = apr_socket_connect(sock, lp->bind_addr);
    if (rv != APR_SUCCESS) {
        int log_level = APLOG_WARNING;

        if (APR_STATUS_IS_TIMEUP(rv)) {
            /* probably some server processes bailed out already and there
             * is nobody around to call accept and clear out the kernel
             * connection queue; usually this is not worth logging
             */
            log_level = APLOG_DEBUG;
        }

        ap_log_error(APLOG_MARK, log_level, rv, ap_server_conf, APLOGNO(00056)
                     "connect to listener on %pI", lp->bind_addr);
        apr_pool_destroy(p);
        return rv;
    }

    if (lp->protocol && strcasecmp(lp->protocol, "https") == 0) {
        /* Send a TLS 1.0 close_notify alert.  This is perhaps the
         * "least wrong" way to open and cleanly terminate an SSL
         * connection.  It should "work" without noisy error logs if
         * the server actually expects SSLv3/TLSv1.  With
         * SSLv23_server_method() OpenSSL's SSL_accept() fails
         * ungracefully on receipt of this message, since it requires
         * an 11-byte ClientHello message and this is too short. */
        static const unsigned char tls10_close_notify[7] = {
            '\x15',         /* TLSPlainText.type = Alert (21) */
            '\x03', '\x01', /* TLSPlainText.version = {3, 1} */
            '\x00', '\x02', /* TLSPlainText.length = 2 */
            '\x01',         /* Alert.level = warning (1) */
            '\x00'          /* Alert.description = close_notify (0) */
        };
        data = (const char *)tls10_close_notify;
        len = sizeof(tls10_close_notify);
    }
    else /* ... XXX other request types here? */ {
        /* Create an HTTP request string.  We include a User-Agent so
         * that adminstrators can track down the cause of the
         * odd-looking requests in their logs.  A complete request is
         * used since kernel-level filtering may require that much
         * data before returning from accept(). */
        data = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ",
                           ap_get_server_description(),
                           " (internal dummy connection)\r\n\r\n", NULL);
        len = strlen(data);
    }

    apr_socket_send(sock, data, &len);
    apr_socket_close(sock);
    apr_pool_destroy(p);

    return rv;
}
Example #8
0
/* This is called to not only setup and run for the initial time, but also
 * when we've asked for a restart. This means it must be able to handle both
 * situations. It also means that when we exit here we should have tidied
 * up after ourselves fully.
 */
int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
    int remaining_threads_to_start, i,j;
    apr_status_t rv;
    ap_listen_rec *lr;
    pconf = _pconf;
    ap_server_conf = s;

    /* Increase the available pool of fd's.  This code from
     * Joe Kloss <*****@*****.**>
     */
    if( FD_SETSIZE > 128 && (i = _kset_fd_limit_( 128 )) < 0 ){
        ap_log_error(APLOG_MARK, APLOG_ERR, i, s,
            "could not set FD_SETSIZE (_kset_fd_limit_ failed)");
    }

    /* BeOS R5 doesn't support pipes on select() calls, so we use a
     * UDP socket as these are supported in both R5 and BONE.  If we only cared
     * about BONE we'd use a pipe, but there it is.
     * As we have UDP support in APR, now use the APR functions and check all the
     * return values...
     */
    if (apr_sockaddr_info_get(&udp_sa, "127.0.0.1", APR_UNSPEC, 7772, 0, _pconf)
        != APR_SUCCESS){
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
            "couldn't create control socket information, shutting down");
        return 1;
    }
    if (apr_socket_create(&udp_sock, udp_sa->family, SOCK_DGRAM, 0,
                      _pconf) != APR_SUCCESS){
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
            "couldn't create control socket, shutting down");
        return 1;
    }
    if (apr_socket_bind(udp_sock, udp_sa) != APR_SUCCESS){
        ap_log_error(APLOG_MARK, APLOG_ALERT, errno, s,
            "couldn't bind UDP socket!");
        return 1;
    }

    if ((num_listening_sockets = ap_setup_listeners(ap_server_conf)) < 1) {
        ap_log_error(APLOG_MARK, APLOG_ALERT, 0, s,
            "no listening sockets available, shutting down");
        return 1;
    }

    ap_log_pid(pconf, ap_pid_fname);

    /*
     * Create our locks...
     */

    /* accept_mutex
     * used to lock around select so we only have one thread
     * in select at a time
     */
    rv = apr_thread_mutex_create(&accept_mutex, 0, pconf);
    if (rv != APR_SUCCESS) {
        /* tsch tsch, can't have more than one thread in the accept loop
           at a time so we need to fall on our sword... */
        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
                     "Couldn't create accept lock");
        return 1;
    }

    /*
     * Startup/shutdown...
     */

    if (!is_graceful) {
        /* setup the scoreboard shared memory */
        if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
            return 1;
        }

        for (i = 0; i < HARD_SERVER_LIMIT; i++) {
            ap_scoreboard_image->parent[i].pid = 0;
            for (j = 0;j < HARD_THREAD_LIMIT; j++)
                ap_scoreboard_image->servers[i][j].tid = 0;
        }
    }

    if (HARD_SERVER_LIMIT == 1)
        ap_scoreboard_image->parent[0].pid = getpid();

    set_signals();

    /* Sanity checks to avoid thrashing... */
    if (max_spare_threads < min_spare_threads )
        max_spare_threads = min_spare_threads;

    /* If we're doing a graceful_restart then we're going to see a lot
     * of threads exiting immediately when we get into the main loop
     * below (because we just sent them AP_SIG_GRACEFUL).  This happens
     * pretty rapidly... and for each one that exits we'll start a new one
     * until we reach at least threads_min_free.  But we may be permitted to
     * start more than that, so we'll just keep track of how many we're
     * supposed to start up without the 1 second penalty between each fork.
     */
    remaining_threads_to_start = ap_threads_to_start;
    /* sanity check on the number to start... */
    if (remaining_threads_to_start > ap_thread_limit) {
            remaining_threads_to_start = ap_thread_limit;
    }

    /* If we're doing the single process thing or we're in a graceful_restart
     * then we don't start threads here.
     * if we're in one_process mode we don't want to start threads
     * do we??
     */
    if (!is_graceful && !one_process) {
            startup_threads(remaining_threads_to_start);
            remaining_threads_to_start = 0;
    } else {
            /* give the system some time to recover before kicking into
             * exponential mode */
        hold_off_on_exponential_spawning = 10;
    }

    /*
     * record that we've entered the world !
     */
    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                "%s configured -- resuming normal operations",
                ap_get_server_description());

    ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
                "Server built: %s", ap_get_server_built());

    restart_pending = shutdown_pending = 0;

    mpm_state = AP_MPMQ_RUNNING;

    /* We sit in the server_main_loop() until we somehow manage to exit. When
     * we do, we need to kill the workers we have, so we start by using the
     * tell_workers_to_exit() function, but as it sometimes takes a short while
     * to accomplish this we have a pause builtin to allow them the chance to
     * gracefully exit.
     */
    if (!one_process) {
        server_main_loop(remaining_threads_to_start);
        tell_workers_to_exit();
        snooze(1000000);
    } else {
        worker_thread((void*)0);
    }
    mpm_state = AP_MPMQ_STOPPING;

    /* close the UDP socket we've been using... */
    apr_socket_close(udp_sock);

    if ((one_process || shutdown_pending) && !child_fatal) {
        const char *pidfile = NULL;
        pidfile = ap_server_root_relative (pconf, ap_pid_fname);
        if ( pidfile != NULL && unlink(pidfile) == 0)
            ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
                         "removed PID file %s (pid=%ld)", pidfile,
                         (long)getpid());
    }

    if (one_process) {
        return 1;
    }

    /*
     * If we get here we're shutting down...
     */
    if (shutdown_pending) {
        /* Time to gracefully shut down:
         * Kill child processes, tell them to call child_exit, etc...
         */
        if (beosd_killpg(getpgrp(), SIGTERM) < 0)
            ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
             "killpg SIGTERM");

        /* use ap_reclaim_child_processes starting with SIGTERM */
        ap_reclaim_child_processes(1);

        if (!child_fatal) {         /* already recorded */
            /* record the shutdown in the log */
            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                         "caught SIGTERM, shutting down");
        }

        return 1;
    }

    /* we've been told to restart */
    signal(SIGHUP, SIG_IGN);

    if (is_graceful) {
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                    AP_SIG_GRACEFUL_STRING " received.  Doing graceful restart");
    } else {
        /* Kill 'em all.  Since the child acts the same on the parents SIGTERM
         * and a SIGHUP, we may as well use the same signal, because some user
         * pthreads are stealing signals from us left and right.
         */

        ap_reclaim_child_processes(1);   /* Start with SIGTERM */
            ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                    "SIGHUP received.  Attempting to restart");
    }

    /* just before we go, tidy up the lock we created to prevent a
     * potential leak of semaphores...
     */
    apr_thread_mutex_destroy(accept_mutex);

    return 0;
}
Example #9
0
int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
    int index;
    int remaining_children_to_start;
    apr_status_t rv;

    ap_log_pid(pconf, ap_pid_fname);

    first_server_limit = server_limit;
    if (changed_limit_at_restart) {
        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
                     "WARNING: Attempt to change ServerLimit "
                     "ignored during restart");
        changed_limit_at_restart = 0;
    }

    /* Initialize cross-process accept lock */
    ap_lock_fname = apr_psprintf(_pconf, "%s.%" APR_PID_T_FMT,
                                 ap_server_root_relative(_pconf, ap_lock_fname),
                                 ap_my_pid);

    rv = apr_proc_mutex_create(&accept_mutex, ap_lock_fname,
                               ap_accept_lock_mech, _pconf);
    if (rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
                     "Couldn't create accept lock (%s) (%d)",
                     ap_lock_fname, ap_accept_lock_mech);
        mpm_state = AP_MPMQ_STOPPING;
        return 1;
    }

#if APR_USE_SYSVSEM_SERIALIZE
    if (ap_accept_lock_mech == APR_LOCK_DEFAULT ||
        ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
#else
    if (ap_accept_lock_mech == APR_LOCK_SYSVSEM) {
#endif
        rv = unixd_set_proc_mutex_perms(accept_mutex);
        if (rv != APR_SUCCESS) {
            ap_log_error(APLOG_MARK, APLOG_EMERG, rv, s,
                         "Couldn't set permissions on cross-process lock; "
                         "check User and Group directives");
            mpm_state = AP_MPMQ_STOPPING;
            return 1;
        }
    }

    if (!is_graceful) {
        if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
            mpm_state = AP_MPMQ_STOPPING;
            return 1;
        }
        /* fix the generation number in the global score; we just got a new,
         * cleared scoreboard
         */
        ap_scoreboard_image->global->running_generation = ap_my_generation;
    }

    set_signals();

    if (one_process) {
        AP_MONCONTROL(1);
        make_child(ap_server_conf, 0);
    }
    else {
    if (ap_daemons_max_free < ap_daemons_min_free + 1)  /* Don't thrash... */
        ap_daemons_max_free = ap_daemons_min_free + 1;

    /* If we're doing a graceful_restart then we're going to see a lot
     * of children exiting immediately when we get into the main loop
     * below (because we just sent them AP_SIG_GRACEFUL).  This happens pretty
     * rapidly... and for each one that exits we'll start a new one until
     * we reach at least daemons_min_free.  But we may be permitted to
     * start more than that, so we'll just keep track of how many we're
     * supposed to start up without the 1 second penalty between each fork.
     */
    remaining_children_to_start = ap_daemons_to_start;
    if (remaining_children_to_start > ap_daemons_limit) {
        remaining_children_to_start = ap_daemons_limit;
    }
    if (!is_graceful) {
        startup_children(remaining_children_to_start);
        remaining_children_to_start = 0;
    }
    else {
        /* give the system some time to recover before kicking into
         * exponential mode
         */
        hold_off_on_exponential_spawning = 10;
    }

    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                "%s configured -- resuming normal operations",
                ap_get_server_description());
    ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf,
                "Server built: %s", ap_get_server_built());
#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
                "AcceptMutex: %s (default: %s)",
                apr_proc_mutex_name(accept_mutex),
                apr_proc_mutex_defname());
#endif
    restart_pending = shutdown_pending = 0;

    mpm_state = AP_MPMQ_RUNNING;

    while (!restart_pending && !shutdown_pending) {
        int child_slot;
        apr_exit_why_e exitwhy;
        int status, processed_status;
        /* this is a memory leak, but I'll fix it later. */
        apr_proc_t pid;

        ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);

        /* XXX: if it takes longer than 1 second for all our children
         * to start up and get into IDLE state then we may spawn an
         * extra child
         */
        if (pid.pid != -1) {
            processed_status = ap_process_child_status(&pid, exitwhy, status);
            if (processed_status == APEXIT_CHILDFATAL) {
                mpm_state = AP_MPMQ_STOPPING;
                return 1;
            }

            /* non-fatal death... note that it's gone in the scoreboard. */
            child_slot = find_child_by_pid(&pid);
            if (child_slot >= 0) {
                (void) ap_update_child_status_from_indexes(child_slot, 0, SERVER_DEAD,
                                                           (request_rec *) NULL);
                if (processed_status == APEXIT_CHILDSICK) {
                    /* child detected a resource shortage (E[NM]FILE, ENOBUFS, etc)
                     * cut the fork rate to the minimum
                     */
                    idle_spawn_rate = 1;
                }
                else if (remaining_children_to_start
                    && child_slot < ap_daemons_limit) {
                    /* we're still doing a 1-for-1 replacement of dead
                     * children with new children
                     */
                    make_child(ap_server_conf, child_slot);
                    --remaining_children_to_start;
                }
#if APR_HAS_OTHER_CHILD
            }
            else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH, status) == APR_SUCCESS) {
                /* handled */
#endif
            }
            else if (is_graceful) {
                /* Great, we've probably just lost a slot in the
                 * scoreboard.  Somehow we don't know about this
                 * child.
                 */
                ap_log_error(APLOG_MARK, APLOG_WARNING,
                            0, ap_server_conf,
                            "long lost child came home! (pid %ld)", (long)pid.pid);
            }
            /* Don't perform idle maintenance when a child dies,
             * only do it when there's a timeout.  Remember only a
             * finite number of children can die, and it's pretty
             * pathological for a lot to die suddenly.
             */
            continue;
        }
        else if (remaining_children_to_start) {
            /* we hit a 1 second timeout in which none of the previous
             * generation of children needed to be reaped... so assume
             * they're all done, and pick up the slack if any is left.
             */
            startup_children(remaining_children_to_start);
            remaining_children_to_start = 0;
            /* In any event we really shouldn't do the code below because
             * few of the servers we just started are in the IDLE state
             * yet, so we'd mistakenly create an extra server.
             */
            continue;
        }

        perform_idle_server_maintenance(pconf);
#ifdef TPF
        shutdown_pending = os_check_server(tpf_server_name);
        ap_check_signals();
        sleep(1);
#endif /*TPF */
    }
    } /* one_process */

    mpm_state = AP_MPMQ_STOPPING;

    if (shutdown_pending && !is_graceful) {
        /* Time to shut down:
         * Kill child processes, tell them to call child_exit, etc...
         */
        if (unixd_killpg(getpgrp(), SIGTERM) < 0) {
            ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGTERM");
        }
        ap_reclaim_child_processes(1);          /* Start with SIGTERM */

        /* cleanup pid file on normal shutdown */
        {
            const char *pidfile = NULL;
            pidfile = ap_server_root_relative (pconf, ap_pid_fname);
            if ( pidfile != NULL && unlink(pidfile) == 0)
                ap_log_error(APLOG_MARK, APLOG_INFO,
                                0, ap_server_conf,
                                "removed PID file %s (pid=%ld)",
                                pidfile, (long)getpid());
        }

        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                    "caught SIGTERM, shutting down");

        return 1;
    } else if (shutdown_pending) {
        /* Time to perform a graceful shut down:
         * Reap the inactive children, and ask the active ones
         * to close their listeners, then wait until they are
         * all done to exit.
         */
        int active_children;
        apr_time_t cutoff = 0;

        /* Stop listening */
        ap_close_listeners();

        /* kill off the idle ones */
        ap_mpm_pod_killpg(pod, ap_max_daemons_limit);

        /* Send SIGUSR1 to the active children */
        active_children = 0;
        for (index = 0; index < ap_daemons_limit; ++index) {
            if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) {
                /* Ask each child to close its listeners. */
                ap_mpm_safe_kill(MPM_CHILD_PID(index), AP_SIG_GRACEFUL);
                active_children++;
            }
        }

        /* Allow each child which actually finished to exit */
        ap_relieve_child_processes();

        /* cleanup pid file */
        {
            const char *pidfile = NULL;
            pidfile = ap_server_root_relative (pconf, ap_pid_fname);
            if ( pidfile != NULL && unlink(pidfile) == 0)
                ap_log_error(APLOG_MARK, APLOG_INFO,
                                0, ap_server_conf,
                                "removed PID file %s (pid=%ld)",
                                pidfile, (long)getpid());
        }

        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
           "caught " AP_SIG_GRACEFUL_STOP_STRING ", shutting down gracefully");

        if (ap_graceful_shutdown_timeout) {
            cutoff = apr_time_now() +
                     apr_time_from_sec(ap_graceful_shutdown_timeout);
        }

        /* Don't really exit until each child has finished */
        shutdown_pending = 0;
        do {
            /* Pause for a second */
            sleep(1);

            /* Relieve any children which have now exited */
            ap_relieve_child_processes();

            active_children = 0;
            for (index = 0; index < ap_daemons_limit; ++index) {
                if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) {
                    active_children = 1;
                    /* Having just one child is enough to stay around */
                    break;
                }
            }
        } while (!shutdown_pending && active_children &&
                 (!ap_graceful_shutdown_timeout || apr_time_now() < cutoff));

        /* We might be here because we received SIGTERM, either
         * way, try and make sure that all of our processes are
         * really dead.
         */
        unixd_killpg(getpgrp(), SIGTERM);

        return 1;
    }

    /* we've been told to restart */
    apr_signal(SIGHUP, SIG_IGN);
    apr_signal(AP_SIG_GRACEFUL, SIG_IGN);
    if (one_process) {
        /* not worth thinking about */
        return 1;
    }

    /* advance to the next generation */
    /* XXX: we really need to make sure this new generation number isn't in
     * use by any of the children.
     */
    ++ap_my_generation;
    ap_scoreboard_image->global->running_generation = ap_my_generation;

    if (is_graceful) {
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                    "Graceful restart requested, doing restart");

        /* kill off the idle ones */
        ap_mpm_pod_killpg(pod, ap_max_daemons_limit);

        /* This is mostly for debugging... so that we know what is still
         * gracefully dealing with existing request.  This will break
         * in a very nasty way if we ever have the scoreboard totally
         * file-based (no shared memory)
         */
        for (index = 0; index < ap_daemons_limit; ++index) {
            if (ap_scoreboard_image->servers[index][0].status != SERVER_DEAD) {
                ap_scoreboard_image->servers[index][0].status = SERVER_GRACEFUL;
                /* Ask each child to close its listeners.
                 *
                 * NOTE: we use the scoreboard, because if we send SIGUSR1
                 * to every process in the group, this may include CGI's,
                 * piped loggers, etc. They almost certainly won't handle
                 * it gracefully.
                 */
                ap_mpm_safe_kill(ap_scoreboard_image->parent[index].pid, AP_SIG_GRACEFUL);
            }
        }
    }
    else {
        /* Kill 'em off */
        if (unixd_killpg(getpgrp(), SIGHUP) < 0) {
            ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf, "killpg SIGHUP");
        }
        ap_reclaim_child_processes(0);          /* Not when just starting up */
        ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf,
                    "SIGHUP received.  Attempting to restart");
    }

    return 0;
}

/* This really should be a post_config hook, but the error log is already
 * redirected by that point, so we need to do this in the open_logs phase.
 */
static int prefork_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
    apr_status_t rv;

    pconf = p;
    ap_server_conf = s;

    if ((num_listensocks = ap_setup_listeners(ap_server_conf)) < 1) {
        ap_log_error(APLOG_MARK, APLOG_ALERT|APLOG_STARTUP, 0,
                     NULL, "no listening sockets available, shutting down");
        return DONE;
    }

    if ((rv = ap_mpm_pod_open(pconf, &pod))) {
        ap_log_error(APLOG_MARK, APLOG_CRIT|APLOG_STARTUP, rv, NULL,
                "Could not open pipe-of-death.");
        return DONE;
    }
    return OK;
}
Example #10
0
/* Manages the loadfactors and member status
 */
static int balancer_handler(request_rec *r)
{
    void *sconf = r->server->module_config;
    proxy_server_conf *conf = (proxy_server_conf *)
        ap_get_module_config(sconf, &proxy_module);
    proxy_balancer *balancer, *bsel = NULL;
    proxy_worker *worker, *wsel = NULL;
    apr_table_t *params = apr_table_make(r->pool, 10);
    int access_status;
    int i, n;
    const char *name;

    /* is this for us? */
    if (strcmp(r->handler, "balancer-manager"))
        return DECLINED;
    r->allowed = (AP_METHOD_BIT << M_GET);
    if (r->method_number != M_GET)
        return DECLINED;

    if (r->args) {
        char *args = apr_pstrdup(r->pool, r->args);
        char *tok, *val;
        while (args && *args) {
            if ((val = ap_strchr(args, '='))) {
                *val++ = '\0';
                if ((tok = ap_strchr(val, '&')))
                    *tok++ = '\0';
                /*
                 * Special case: workers are allowed path information
                 */
                if ((access_status = ap_unescape_url(val)) != OK)
                    if (strcmp(args, "w") || (access_status !=  HTTP_NOT_FOUND))
                        return access_status;
                apr_table_setn(params, args, val);
                args = tok;
            }
            else
                return HTTP_BAD_REQUEST;
        }
    }
    
    /* Check that the supplied nonce matches this server's nonce;
     * otherwise ignore all parameters, to prevent a CSRF attack. */
    if ((name = apr_table_get(params, "nonce")) == NULL 
        || strcmp(balancer_nonce, name) != 0) {
        apr_table_clear(params);
    }

    if ((name = apr_table_get(params, "b")))
        bsel = ap_proxy_get_balancer(r->pool, conf,
            apr_pstrcat(r->pool, "balancer://", name, NULL));
    if ((name = apr_table_get(params, "w"))) {
        proxy_worker *ws;

        ws = ap_proxy_get_worker(r->pool, conf, name);
        if (bsel && ws) {
            worker = (proxy_worker *)bsel->workers->elts;
            for (n = 0; n < bsel->workers->nelts; n++) {
                if (strcasecmp(worker->name, ws->name) == 0) {
                    wsel = worker;
                    break;
                }
                ++worker;
            }
        }
    }
    /* First set the params */
    /*
     * Note that it is not possible set the proxy_balancer because it is not
     * in shared memory.
     */
    if (wsel) {
        const char *val;
        if ((val = apr_table_get(params, "lf"))) {
            int ival = atoi(val);
            if (ival >= 1 && ival <= 100) {
                wsel->s->lbfactor = ival;
                if (bsel)
                    recalc_factors(bsel);
            }
        }
        if ((val = apr_table_get(params, "wr"))) {
            if (strlen(val) && strlen(val) < PROXY_WORKER_MAX_ROUTE_SIZ)
                strcpy(wsel->s->route, val);
            else
                *wsel->s->route = '\0';
        }
        if ((val = apr_table_get(params, "rr"))) {
            if (strlen(val) && strlen(val) < PROXY_WORKER_MAX_ROUTE_SIZ)
                strcpy(wsel->s->redirect, val);
            else
                *wsel->s->redirect = '\0';
        }
        if ((val = apr_table_get(params, "dw"))) {
            if (!strcasecmp(val, "Disable"))
                wsel->s->status |= PROXY_WORKER_DISABLED;
            else if (!strcasecmp(val, "Enable"))
                wsel->s->status &= ~PROXY_WORKER_DISABLED;
        }
        if ((val = apr_table_get(params, "ls"))) {
            int ival = atoi(val);
            if (ival >= 0 && ival <= 99) {
                wsel->s->lbset = ival;
             }
        }

    }
    if (apr_table_get(params, "xml")) {
        ap_set_content_type(r, "text/xml");
        ap_rputs("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n", r);
        ap_rputs("<httpd:manager xmlns:httpd=\"http://httpd.apache.org\">\n", r);
        ap_rputs("  <httpd:balancers>\n", r);
        balancer = (proxy_balancer *)conf->balancers->elts;
        for (i = 0; i < conf->balancers->nelts; i++) {
            ap_rputs("    <httpd:balancer>\n", r);
            ap_rvputs(r, "      <httpd:name>", balancer->name, "</httpd:name>\n", NULL);
            ap_rputs("      <httpd:workers>\n", r);
            worker = (proxy_worker *)balancer->workers->elts;
            for (n = 0; n < balancer->workers->nelts; n++) {
                ap_rputs("        <httpd:worker>\n", r);
                ap_rvputs(r, "          <httpd:scheme>", worker->scheme,
                          "</httpd:scheme>\n", NULL);
                ap_rvputs(r, "          <httpd:hostname>", worker->hostname,
                          "</httpd:hostname>\n", NULL);
               ap_rprintf(r, "          <httpd:loadfactor>%d</httpd:loadfactor>\n",
                          worker->s->lbfactor);
                ap_rputs("        </httpd:worker>\n", r);
                ++worker;
            }
            ap_rputs("      </httpd:workers>\n", r);
            ap_rputs("    </httpd:balancer>\n", r);
            ++balancer;
        }
        ap_rputs("  </httpd:balancers>\n", r);
        ap_rputs("</httpd:manager>", r);
    }
    else {
        ap_set_content_type(r, "text/html; charset=ISO-8859-1");
        ap_rputs(DOCTYPE_HTML_3_2
                 "<html><head><title>Balancer Manager</title></head>\n", r);
        ap_rputs("<body><h1>Load Balancer Manager for ", r);
        ap_rvputs(r, ap_get_server_name(r), "</h1>\n\n", NULL);
        ap_rvputs(r, "<dl><dt>Server Version: ",
                  ap_get_server_description(), "</dt>\n", NULL);
        ap_rvputs(r, "<dt>Server Built: ",
                  ap_get_server_built(), "\n</dt></dl>\n", NULL);
        balancer = (proxy_balancer *)conf->balancers->elts;
        for (i = 0; i < conf->balancers->nelts; i++) {

            ap_rputs("<hr />\n<h3>LoadBalancer Status for ", r);
            ap_rvputs(r, balancer->name, "</h3>\n\n", NULL);
            ap_rputs("\n\n<table border=\"0\" style=\"text-align: left;\"><tr>"
                "<th>StickySession</th><th>Timeout</th><th>FailoverAttempts</th><th>Method</th>"
                "</tr>\n<tr>", r);
            if (balancer->sticky) {
                ap_rvputs(r, "<td>", balancer->sticky, NULL);
            }
            else {
                ap_rputs("<td> - ", r);
            }
            ap_rprintf(r, "</td><td>%" APR_TIME_T_FMT "</td>",
                apr_time_sec(balancer->timeout));
            ap_rprintf(r, "<td>%d</td>\n", balancer->max_attempts);
            ap_rprintf(r, "<td>%s</td>\n",
                       balancer->lbmethod->name);
            ap_rputs("</table>\n<br />", r);
            ap_rputs("\n\n<table border=\"0\" style=\"text-align: left;\"><tr>"
                "<th>Worker URL</th>"
                "<th>Route</th><th>RouteRedir</th>"
                "<th>Factor</th><th>Set</th><th>Status</th>"
                "<th>Elected</th><th>To</th><th>From</th>"
                "</tr>\n", r);

            worker = (proxy_worker *)balancer->workers->elts;
            for (n = 0; n < balancer->workers->nelts; n++) {
                char fbuf[50];
                ap_rvputs(r, "<tr>\n<td><a href=\"", r->uri, "?b=",
                          balancer->name + sizeof("balancer://") - 1, "&w=",
                          ap_escape_uri(r->pool, worker->name),
                          "&nonce=", balancer_nonce, 
                          "\">", NULL);
                ap_rvputs(r, worker->name, "</a></td>", NULL);
                ap_rvputs(r, "<td>", ap_escape_html(r->pool, worker->s->route),
                          NULL);
                ap_rvputs(r, "</td><td>",
                          ap_escape_html(r->pool, worker->s->redirect), NULL);
                ap_rprintf(r, "</td><td>%d</td>", worker->s->lbfactor);
                ap_rprintf(r, "<td>%d</td><td>", worker->s->lbset);
                if (worker->s->status & PROXY_WORKER_DISABLED)
                   ap_rputs("Dis ", r);
                if (worker->s->status & PROXY_WORKER_IN_ERROR)
                   ap_rputs("Err ", r);
                if (worker->s->status & PROXY_WORKER_STOPPED)
                   ap_rputs("Stop ", r);
                if (worker->s->status & PROXY_WORKER_HOT_STANDBY)
                   ap_rputs("Stby ", r);
                if (PROXY_WORKER_IS_USABLE(worker))
                    ap_rputs("Ok", r);
                if (!PROXY_WORKER_IS_INITIALIZED(worker))
                    ap_rputs("-", r);
                ap_rputs("</td>", r);
                ap_rprintf(r, "<td>%" APR_SIZE_T_FMT "</td><td>", worker->s->elected);
                ap_rputs(apr_strfsize(worker->s->transferred, fbuf), r);
                ap_rputs("</td><td>", r);
                ap_rputs(apr_strfsize(worker->s->read, fbuf), r);
                ap_rputs("</td></tr>\n", r);

                ++worker;
            }
            ap_rputs("</table>\n", r);
            ++balancer;
        }
        ap_rputs("<hr />\n", r);
        if (wsel && bsel) {
            ap_rputs("<h3>Edit worker settings for ", r);
            ap_rvputs(r, wsel->name, "</h3>\n", NULL);
            ap_rvputs(r, "<form method=\"GET\" action=\"", NULL);
            ap_rvputs(r, r->uri, "\">\n<dl>", NULL);
            ap_rputs("<table><tr><td>Load factor:</td><td><input name=\"lf\" type=text ", r);
            ap_rprintf(r, "value=\"%d\"></td></tr>\n", wsel->s->lbfactor);
            ap_rputs("<tr><td>LB Set:</td><td><input name=\"ls\" type=text ", r);
            ap_rprintf(r, "value=\"%d\"></td></tr>\n", wsel->s->lbset);
            ap_rputs("<tr><td>Route:</td><td><input name=\"wr\" type=text ", r);
            ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->route),
                      NULL);
            ap_rputs("\"></td></tr>\n", r);
            ap_rputs("<tr><td>Route Redirect:</td><td><input name=\"rr\" type=text ", r);
            ap_rvputs(r, "value=\"", ap_escape_html(r->pool, wsel->s->redirect),
                      NULL);
            ap_rputs("\"></td></tr>\n", r);
            ap_rputs("<tr><td>Status:</td><td>Disabled: <input name=\"dw\" value=\"Disable\" type=radio", r);
            if (wsel->s->status & PROXY_WORKER_DISABLED)
                ap_rputs(" checked", r);
            ap_rputs("> | Enabled: <input name=\"dw\" value=\"Enable\" type=radio", r);
            if (!(wsel->s->status & PROXY_WORKER_DISABLED))
                ap_rputs(" checked", r);
            ap_rputs("></td></tr>\n", r);
            ap_rputs("<tr><td colspan=2><input type=submit value=\"Submit\"></td></tr>\n", r);
            ap_rvputs(r, "</table>\n<input type=hidden name=\"w\" ",  NULL);
            ap_rvputs(r, "value=\"", ap_escape_uri(r->pool, wsel->name), "\">\n", NULL);
            ap_rvputs(r, "<input type=hidden name=\"b\" ", NULL);
            ap_rvputs(r, "value=\"", bsel->name + sizeof("balancer://") - 1,
                      "\">\n", NULL);
            ap_rvputs(r, "<input type=hidden name=\"nonce\" value=\"", 
                      balancer_nonce, "\">\n", NULL);
            ap_rvputs(r, "</form>\n", NULL);
            ap_rputs("<hr />\n", r);
        }
        ap_rputs(ap_psignature("",r), r);
        ap_rputs("</body></html>\n", r);
    }
    return OK;
}