worker.c revision e4d36aa1eb0631a1b696c7a70d696f9c869bddcc
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* ====================================================================
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * The Apache Software License, Version 1.1
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * reserved.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Redistribution and use in source and binary forms, with or without
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * modification, are permitted provided that the following conditions
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * are met:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 1. Redistributions of source code must retain the above copyright
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * notice, this list of conditions and the following disclaimer.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 2. Redistributions in binary form must reproduce the above copyright
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * notice, this list of conditions and the following disclaimer in
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * the documentation and/or other materials provided with the
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * distribution.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 3. The end-user documentation included with the redistribution,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * if any, must include the following acknowledgment:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * "This product includes software developed by the
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Apache Software Foundation (http://www.apache.org/)."
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Alternately, this acknowledgment may appear in the software itself,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * if and wherever such third-party acknowledgments normally appear.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 4. The names "Apache" and "Apache Software Foundation" must
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * not be used to endorse or promote products derived from this
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * software without prior written permission. For written
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * permission, please contact apache@apache.org.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * 5. Products derived from this software may not be called "Apache",
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * nor may "Apache" appear in their name, without prior written
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * permission of the Apache Software Foundation.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * SUCH DAMAGE.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * ====================================================================
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * This software consists of voluntary contributions made by many
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * individuals on behalf of the Apache Software Foundation. For more
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * information on the Apache Software Foundation, please see
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * <http://www.apache.org/>.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Portions of this software are based upon public domain software
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * originally written at the National Center for Supercomputing Applications,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * University of Illinois, Urbana-Champaign.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* The purpose of this MPM is to fix the design flaws in the threaded
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * model. Because of the way that pthreads and mutex locks interact,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * it is basically impossible to cleanly gracefully shutdown a child
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * process if multiple threads are all blocked in accept. This model
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * fixes those problems.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr_portable.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr_strings.h"
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#include "apr_file_io.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr_thread_proc.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr_signal.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr_thread_mutex.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr_proc_mutex.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr_poll.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define APR_WANT_STRFUNC
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "apr_want.h"
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#if APR_HAVE_UNISTD_H
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include <unistd.h>
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#if APR_HAVE_SYS_SOCKET_H
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include <sys/socket.h>
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#if APR_HAVE_SYS_WAIT_H
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#include <sys/wait.h>
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef HAVE_SYS_PROCESSOR_H
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include <sys/processor.h> /* for bindprocessor() */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#if !APR_HAS_THREADS
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#error The Worker MPM requires APR threads, but they are unavailable.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define CORE_PRIVATE
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "ap_config.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "httpd.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "http_main.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "http_log.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "http_config.h" /* for read_config */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "http_core.h" /* for get_remote_host */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "http_connection.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "ap_mpm.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "pod.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "mpm_common.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "ap_listen.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "scoreboard.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "fdqueue.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include "mpm_default.h"
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include <signal.h>
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#include <limits.h> /* for INT_MAX */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* Limit on the total --- clients will be locked out if more servers than
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * this are needed. It is intended solely to keep the server from crashing
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * when things get out of hand.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * We keep a hard maximum number of servers, for two reasons --- first off,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * in case something goes seriously wrong, we want to stop the fork bomb
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * short of actually crashing the machine we're running on by filling some
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * kernel table. Secondly, it keeps the size of the scoreboard file small
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * enough that we can read the whole thing without worrying too much about
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * the overhead.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifndef DEFAULT_SERVER_LIMIT
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#define DEFAULT_SERVER_LIMIT 16
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* Admin can't tune ServerLimit beyond MAX_SERVER_LIMIT. We want
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * some sort of compile-time limit to help catch typos.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifndef MAX_SERVER_LIMIT
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define MAX_SERVER_LIMIT 20000
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* Limit on the threads per process. Clients will be locked out if more than
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * this * server_limit are needed.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * We keep this for one reason it keeps the size of the scoreboard file small
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * enough that we can read the whole thing without worrying too much about
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * the overhead.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifndef DEFAULT_THREAD_LIMIT
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define DEFAULT_THREAD_LIMIT 64
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* Admin can't tune ThreadLimit beyond MAX_THREAD_LIMIT. We want
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * some sort of compile-time limit to help catch typos.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifndef MAX_THREAD_LIMIT
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define MAX_THREAD_LIMIT 20000
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/*
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Actual definitions of config globals
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversint ap_threads_per_child = 0; /* Worker threads per child */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int ap_daemons_to_start = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int min_spare_threads = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int max_spare_threads = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int ap_daemons_limit = 0;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int server_limit = DEFAULT_SERVER_LIMIT;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int first_server_limit;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int thread_limit = DEFAULT_THREAD_LIMIT;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int first_thread_limit;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int changed_limit_at_restart;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int dying = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int workers_may_exit = 0;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int start_thread_may_exit = 0;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int listener_may_exit = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int requests_this_child;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int num_listensocks = 0;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int resource_shortage = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic fd_queue_t *worker_queue;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic fd_queue_info_t *worker_queue_info;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* The structure used to pass unique initialization info to each thread */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieverstypedef struct {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int pid;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers int tid;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int sd;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers} proc_info;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* Structure used to pass information to the thread responsible for
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * creating the rest of the threads.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieverstypedef struct {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_thread_t **threads;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_thread_t *listener;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int child_num_arg;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_threadattr_t *threadattr;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers} thread_starter;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define ID_FROM_CHILD_THREAD(c, t) ((c * thread_limit) + t)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/*
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * The max child slot ever assigned, preserved across restarts. Necessary
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * to deal with MaxClients changes across AP_SIG_GRACEFUL restarts. We
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * use this value to optimize routines that have to scan the entire
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * scoreboard.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversint ap_max_daemons_limit = -1;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic ap_pod_t *pod;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* *Non*-shared http_main globals... */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversserver_rec *ap_server_conf;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* The worker MPM respects a couple of runtime flags that can aid
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * in debugging. Setting the -DNO_DETACH flag will prevent the root process
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * from detaching from its controlling terminal. Additionally, setting
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * the -DONE_PROCESS flag (which implies -DNO_DETACH) will get you the
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * child_main loop running in the process which originally started up.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * This gives you a pretty nice debugging environment. (You'll get a SIGHUP
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * early in standalone_main; just continue through. This is the server
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * trying to kill off any child processes which it might have lying
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * around --- Apache doesn't keep track of their pids, it just sends
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * SIGHUP to the process group, ignoring it in the root process.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Continue through and you'll be fine.).
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int one_process = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef DEBUG_SIGSTOP
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversint raise_sigstop_flags;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic apr_pool_t *pconf; /* Pool for config stuff */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic apr_pool_t *pchild; /* Pool for httpd child stuff */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic pid_t ap_my_pid; /* Linux getpid() doesn't work except in main
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers thread. Use this instead */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic pid_t parent_pid;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic apr_os_thread_t *listener_os_thread;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* Locks for accept serialization */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic apr_proc_mutex_t *accept_mutex;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef SINGLE_LISTEN_UNSERIALIZED_ACCEPT
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define SAFE_ACCEPT(stmt) (ap_listeners->next ? (stmt) : APR_SUCCESS)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#else
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define SAFE_ACCEPT(stmt) (stmt)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* The LISTENER_SIGNAL signal will be sent from the main thread to the
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * listener thread to wake it up for graceful termination (what a child
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * process from an old generation does when the admin does "apachectl
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * graceful"). This signal will be blocked in all threads of a child
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * process except for the listener thread.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define LISTENER_SIGNAL SIGHUP
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* An array of socket descriptors in use by each thread used to
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * perform a non-graceful (forced) shutdown of the server. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic apr_socket_t **worker_sockets;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void close_worker_sockets(void)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int i;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers for (i = 0; i < ap_threads_per_child; i++) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (worker_sockets[i]) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_socket_close(worker_sockets[i]);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers worker_sockets[i] = NULL;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void wakeup_listener(void)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers listener_may_exit = 1;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (!listener_os_thread) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* XXX there is an obscure path that this doesn't handle perfectly:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * right after listener thread is created but before
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * listener_os_thread is set, the first worker thread hits an
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * error and starts graceful termination
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /*
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * we should just be able to "kill(ap_my_pid, LISTENER_SIGNAL)" on all
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * platforms and wake up the listener thread since it is the only thread
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * with SIGHUP unblocked, but that doesn't work on Linux
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef HAVE_PTHREAD_KILL
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers pthread_kill(*listener_os_thread, LISTENER_SIGNAL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#else
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers kill(ap_my_pid, LISTENER_SIGNAL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define ST_INIT 0
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define ST_GRACEFUL 1
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#define ST_UNGRACEFUL 2
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int terminate_mode = ST_INIT;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void signal_threads(int mode)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (terminate_mode == mode) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers terminate_mode = mode;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* in case we weren't called from the listener thread, wake up the
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * listener thread
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers wakeup_listener();
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* for ungraceful termination, let the workers exit now;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * for graceful termination, the listener thread will notify the
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * workers to exit once it has stopped accepting new connections
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (mode == ST_UNGRACEFUL) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers workers_may_exit = 1;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_queue_interrupt_all(worker_queue);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_queue_info_term(worker_queue_info);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers close_worker_sockets(); /* forcefully kill all current connections */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay SieversAP_DECLARE(apr_status_t) ap_mpm_query(int query_code, int *result)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers switch(query_code){
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers case AP_MPMQ_MAX_DAEMON_USED:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *result = ap_max_daemons_limit;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return APR_SUCCESS;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers case AP_MPMQ_IS_THREADED:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *result = AP_MPMQ_STATIC;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return APR_SUCCESS;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers case AP_MPMQ_IS_FORKED:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *result = AP_MPMQ_DYNAMIC;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return APR_SUCCESS;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers case AP_MPMQ_HARD_LIMIT_DAEMONS:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *result = server_limit;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return APR_SUCCESS;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers case AP_MPMQ_HARD_LIMIT_THREADS:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *result = thread_limit;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return APR_SUCCESS;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers case AP_MPMQ_MAX_THREADS:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *result = ap_threads_per_child;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return APR_SUCCESS;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers case AP_MPMQ_MIN_SPARE_DAEMONS:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *result = 0;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return APR_SUCCESS;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers case AP_MPMQ_MIN_SPARE_THREADS:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *result = min_spare_threads;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return APR_SUCCESS;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers case AP_MPMQ_MAX_SPARE_DAEMONS:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *result = 0;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return APR_SUCCESS;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers case AP_MPMQ_MAX_SPARE_THREADS:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *result = max_spare_threads;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return APR_SUCCESS;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers case AP_MPMQ_MAX_REQUESTS_DAEMON:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *result = ap_max_requests_per_child;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return APR_SUCCESS;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers case AP_MPMQ_MAX_DAEMONS:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *result = ap_daemons_limit;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return APR_SUCCESS;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return APR_ENOTIMPL;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers}
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* a clean exit from a child with proper cleanup */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic void clean_child_exit(int code) __attribute__ ((noreturn));
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic void clean_child_exit(int code)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers{
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (pchild) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_pool_destroy(pchild);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers exit(code);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers}
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic void just_die(int sig)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers{
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers clean_child_exit(0);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers}
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/*****************************************************************
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Connection structures and accounting...
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* volatile just in case */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int volatile shutdown_pending;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int volatile restart_pending;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic int volatile is_graceful;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic volatile int child_fatal;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversap_generation_t volatile ap_my_generation;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/*
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * ap_start_shutdown() and ap_start_restart(), below, are a first stab at
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * functions to initiate shutdown or restart without relying on signals.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * Previously this was initiated in sig_term() and restart() signal handlers,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * but we want to be able to start a shutdown/restart from other sources --
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * e.g. on Win32, from the service manager. Now the service manager can
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * call ap_start_shutdown() or ap_start_restart() as appropiate. Note that
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * these functions can also be called by the child processes, since global
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * variables are no longer used to pass on the required action to the parent.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers *
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * These should only be called from the parent process itself, since the
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * parent process will use the shutdown_pending and restart_pending variables
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * to determine whether to shutdown or restart. The child process should
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * call signal_parent() directly to tell the parent to die -- this will
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * cause neither of those variable to be set, which the parent will
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * assume means something serious is wrong (which it will be, for the
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * child to force an exit) and so do an exit anyway.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic void ap_start_shutdown(void)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers{
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (shutdown_pending == 1) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* Um, is this _probably_ not an error, if the user has
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * tried to do a shutdown twice quickly, so we won't
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * worry about reporting it.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers shutdown_pending = 1;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers}
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers/* do a graceful restart if graceful == 1 */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sieversstatic void ap_start_restart(int graceful)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers{
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (restart_pending == 1) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* Probably not an error - don't bother reporting it */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers restart_pending = 1;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers is_graceful = graceful;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void sig_term(int sig)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_start_shutdown();
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void restart(int sig)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_start_restart(sig == AP_SIG_GRACEFUL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void set_signals(void)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifndef NO_USE_SIGACTION
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers struct sigaction sa;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
ddc77f62244bb41d5c8261517e2e1ff1b763fc94Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (!one_process) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_fatal_signal_setup(ap_server_conf, pconf);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifndef NO_USE_SIGACTION
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers sigemptyset(&sa.sa_mask);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers sa.sa_flags = 0;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers sa.sa_handler = sig_term;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (sigaction(SIGTERM, &sa, NULL) < 0)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "sigaction(SIGTERM)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef SIGINT
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (sigaction(SIGINT, &sa, NULL) < 0)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGINT)");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#endif
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#ifdef SIGXCPU
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers sa.sa_handler = SIG_DFL;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (sigaction(SIGXCPU, &sa, NULL) < 0)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGXCPU)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef SIGXFSZ
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers sa.sa_handler = SIG_DFL;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (sigaction(SIGXFSZ, &sa, NULL) < 0)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGXFSZ)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef SIGPIPE
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers sa.sa_handler = SIG_IGN;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (sigaction(SIGPIPE, &sa, NULL) < 0)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGPIPE)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* we want to ignore HUPs and AP_SIG_GRACEFUL while we're busy
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * processing one */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers sigaddset(&sa.sa_mask, SIGHUP);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers sigaddset(&sa.sa_mask, AP_SIG_GRACEFUL);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers sa.sa_handler = restart;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (sigaction(SIGHUP, &sa, NULL) < 0)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(SIGHUP)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (sigaction(AP_SIG_GRACEFUL, &sa, NULL) < 0)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "sigaction(" AP_SIG_GRACEFUL_STRING ")");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#else
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (!one_process) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef SIGXCPU
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_signal(SIGXCPU, SIG_DFL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif /* SIGXCPU */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef SIGXFSZ
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_signal(SIGXFSZ, SIG_DFL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif /* SIGXFSZ */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_signal(SIGTERM, sig_term);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef SIGHUP
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_signal(SIGHUP, restart);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#endif /* SIGHUP */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef AP_SIG_GRACEFUL
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_signal(AP_SIG_GRACEFUL, restart);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif /* AP_SIG_GRACEFUL */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#ifdef SIGPIPE
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_signal(SIGPIPE, SIG_IGN);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers#endif /* SIGPIPE */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers#endif
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/*****************************************************************
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Here follows a long bunch of generic server bookkeeping stuff...
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversint ap_graceful_stop_signalled(void)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* XXX this is really a bad confusing obsolete name
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * maybe it should be ap_mpm_process_exiting?
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* note: for a graceful termination, listener_may_exit will be set before
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * workers_may_exit, so check listener_may_exit
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return listener_may_exit;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/*****************************************************************
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Child process main loop.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void process_socket(apr_pool_t *p, apr_socket_t *sock, int my_child_num,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int my_thread_num, apr_bucket_alloc_t *bucket_alloc)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers conn_rec *current_conn;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers long conn_id = ID_FROM_CHILD_THREAD(my_child_num, my_thread_num);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int csd;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_sb_handle_t *sbh;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_create_sb_handle(&sbh, p, my_child_num, my_thread_num);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_os_sock_get(&csd, sock);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (csd >= FD_SETSIZE) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "new file descriptor %d is too large; you probably need "
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "to rebuild Apache with a larger FD_SETSIZE "
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "(currently %d)",
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers csd, FD_SETSIZE);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_socket_close(sock);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers return;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers current_conn = ap_run_create_connection(p, ap_server_conf, sock,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers conn_id, sbh, bucket_alloc);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (current_conn) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_process_connection(current_conn, sock);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_lingering_close(current_conn);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers}
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt/* requests_this_child has gone to zero or below. See if the admin coded
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers "MaxRequestsPerChild 0", and keep going in that case. Doing it this way
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers simplifies the hot path in worker_thread */
ce39bb6909578017aa10031638e724e038f0b859Kay Sieversstatic void check_infinite_requests(void)
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers{
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers if (ap_max_requests_per_child) {
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers signal_threads(ST_GRACEFUL);
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt }
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt else {
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt /* wow! if you're executing this code, you may have set a record.
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * either this child process has served over 2 billion requests, or
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * you're running a threaded 2.0 on a 16 bit machine.
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt *
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * I'll buy pizza and beers at Apachecon for the first person to do
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * the former without cheating (dorking with INT_MAX, or running with
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers * uncommitted performance patches, for example).
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt *
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt * for the latter case, you probably deserve a beer too. Greg Ames
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers */
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt requests_this_child = INT_MAX; /* keep going */
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt }
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt}
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt
ce39bb6909578017aa10031638e724e038f0b859Kay Sieversstatic void unblock_signal(int sig)
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers{
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers sigset_t sig_mask;
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers sigemptyset(&sig_mask);
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers sigaddset(&sig_mask, sig);
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt#if defined(SIGPROCMASK_SETS_THREAD_MASK)
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt sigprocmask(SIG_UNBLOCK, &sig_mask, NULL);
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt#else
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt pthread_sigmask(SIG_UNBLOCK, &sig_mask, NULL);
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt#endif
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt}
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pittstatic void dummy_signal_handler(int sig)
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt{
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt /* XXX If specifying SIG_IGN is guaranteed to unblock a syscall,
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt * then we don't need this goofy function.
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers */
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers}
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers
ce39bb6909578017aa10031638e724e038f0b859Kay Sieversstatic void *listener_thread(apr_thread_t *thd, void * dummy)
ce39bb6909578017aa10031638e724e038f0b859Kay Sievers{
bf89b99c5a39115112c2eda4c2103e2db54988d2Martin Pitt proc_info * ti = dummy;
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt int process_slot = ti->pid;
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt apr_pool_t *tpool = apr_thread_pool_get(thd);
1b6bce89b3383904d0dab619dd38bff673f7286eMartin Pitt void *csd = NULL;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_pool_t *ptrans; /* Pool for per-transaction stuff */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_pool_t *recycled_pool = NULL;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int n;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_pollfd_t *pollset;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_status_t rv;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_listen_rec *lr, *last_lr = ap_listeners;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int have_idle_worker = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers free(ti);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_poll_setup(&pollset, num_listensocks, tpool);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers for(lr = ap_listeners ; lr != NULL ; lr = lr->next)
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_poll_socket_add(pollset, lr->sd, APR_POLLIN);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* Unblock the signal used to wake this thread up, and set a handler for
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * it.
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers unblock_signal(LISTENER_SIGNAL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_signal(LISTENER_SIGNAL, dummy_signal_handler);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* TODO: Switch to a system where threads reuse the results from earlier
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers poll calls - manoj */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers while (1) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* TODO: requests_this_child should be synchronized - aaron */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (requests_this_child <= 0) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers check_infinite_requests();
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (listener_may_exit) break;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (!have_idle_worker) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = ap_queue_info_wait_for_idler(worker_queue_info,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers &recycled_pool);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (APR_STATUS_IS_EOF(rv)) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers break; /* we've been signaled to die now */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers else if (rv != APR_SUCCESS) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "apr_queue_info_wait failed. Attempting to "
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers " shutdown process gracefully.");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers signal_threads(ST_GRACEFUL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers break;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers have_idle_worker = 1;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We've already decremented the idle worker count inside
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * ap_queue_info_wait_for_idler. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if ((rv = SAFE_ACCEPT(apr_proc_mutex_lock(accept_mutex)))
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers != APR_SUCCESS) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int level = APLOG_EMERG;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (listener_may_exit) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers break;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (ap_scoreboard_image->parent[process_slot].generation !=
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_scoreboard_image->global->running_generation) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers level = APLOG_DEBUG; /* common to get these at restart time */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "apr_proc_mutex_lock failed. Attempting to shutdown "
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "process gracefully.");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers signal_threads(ST_GRACEFUL);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers break; /* skip the lock release */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (!ap_listeners->next) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* Only one listener, so skip the poll */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers lr = ap_listeners;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers else {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers while (!listener_may_exit) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_status_t ret;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_int16_t event;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ret = apr_poll(pollset, num_listensocks, &n, -1);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (ret != APR_SUCCESS) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (APR_STATUS_IS_EINTR(ret)) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers continue;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* apr_poll() will only return errors in catastrophic
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * circumstances. Let's try exiting gracefully, for now. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ERR, ret, (const server_rec *)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_server_conf, "apr_poll: (listen)");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers signal_threads(ST_GRACEFUL);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (listener_may_exit) break;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* find a listener */
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers lr = last_lr;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers do {
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers lr = lr->next;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers if (lr == NULL) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers lr = ap_listeners;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* XXX: Should we check for POLLERR? */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_poll_revents_get(&event, lr->sd, pollset);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers if (event & APR_POLLIN) {
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers last_lr = lr;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers goto got_fd;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers }
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers } while (lr != last_lr);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers got_fd:
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (!listener_may_exit) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* create a new transaction pool for each accepted socket */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (recycled_pool == NULL) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_allocator_t *allocator;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_allocator_create(&allocator);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_allocator_max_free_set(allocator, ap_max_mem_free);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_pool_create_ex(&ptrans, NULL, NULL, allocator);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_allocator_owner_set(allocator, ptrans);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers else {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ptrans = recycled_pool;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_pool_tag(ptrans, "transaction");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers rv = lr->accept_func(&csd, lr, ptrans);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* later we trash rv and rely on csd to indicate success/failure */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers AP_DEBUG_ASSERT(rv == APR_SUCCESS || !csd);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (rv == APR_EGENERAL) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* E[NM]FILE, ENOMEM, etc */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers resource_shortage = 1;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers signal_threads(ST_GRACEFUL);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex)))
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers != APR_SUCCESS) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers int level = APLOG_EMERG;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (listener_may_exit) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers break;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (ap_scoreboard_image->parent[process_slot].generation !=
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_scoreboard_image->global->running_generation) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers level = APLOG_DEBUG; /* common to get these at restart time */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, level, rv, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "apr_proc_mutex_unlock failed. Attempting to "
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "shutdown process gracefully.");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers signal_threads(ST_GRACEFUL);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (csd != NULL) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers rv = ap_queue_push(worker_queue, csd, ptrans);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers if (rv) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* trash the connection; we couldn't queue the connected
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * socket to a worker
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_socket_close(csd);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "ap_queue_push failed");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers else {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers have_idle_worker = 0;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers else {
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(accept_mutex)))
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers != APR_SUCCESS) {
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "apr_proc_mutex_unlock failed. Attempting to "
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers "shutdown process gracefully.");
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers signal_threads(ST_GRACEFUL);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers break;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_queue_term(worker_queue);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers dying = 1;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers ap_scoreboard_image->parent[process_slot].quiescing = 1;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* wake up the main thread */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers kill(ap_my_pid, SIGTERM);
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_thread_exit(thd, APR_SUCCESS);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return NULL;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* XXX For ungraceful termination/restart, we definitely don't want to
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * wait for active connections to finish but we may want to wait
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * for idle workers to get out of the queue code and release mutexes,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * since those mutexes are cleaned up pretty soon and some systems
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * may not react favorably (i.e., segfault) if operations are attempted
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * on cleaned-up mutexes.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void * APR_THREAD_FUNC worker_thread(apr_thread_t *thd, void * dummy)
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers{
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers proc_info * ti = dummy;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers int process_slot = ti->pid;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers int thread_slot = ti->tid;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_socket_t *csd = NULL;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_bucket_alloc_t *bucket_alloc;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_pool_t *last_ptrans = NULL;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers apr_pool_t *ptrans; /* Pool for per-transaction stuff */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_status_t rv;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers int is_idle = 0;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers free(ti);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_STARTING, NULL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers while (!workers_may_exit) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (!is_idle) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = ap_queue_info_set_idle(worker_queue_info, last_ptrans);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers last_ptrans = NULL;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (rv != APR_SUCCESS) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "ap_queue_info_set_idle failed. Attempting to "
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "shutdown process gracefully.");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers signal_threads(ST_GRACEFUL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers break;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers is_idle = 1;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_update_child_status_from_indexes(process_slot, thread_slot, SERVER_READY, NULL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversworker_pop:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (workers_may_exit) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers break;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = ap_queue_pop(worker_queue, &csd, &ptrans);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (rv != APR_SUCCESS) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We get APR_EOF during a graceful shutdown once all the connections
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * accepted by this server process have been handled.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (APR_STATUS_IS_EOF(rv)) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers break;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We get APR_EINTR whenever ap_queue_pop() has been interrupted
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * from an explicit call to ap_queue_interrupt_all(). This allows
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * us to unblock threads stuck in ap_queue_pop() when a shutdown
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * is pending.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers *
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * If workers_may_exit is set and this is ungraceful termination/
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * restart, we are bound to get an error on some systems (e.g.,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * AIX, which sanity-checks mutex operations) since the queue
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * may have already been cleaned up. Don't log the "error" if
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * workers_may_exit is set.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers else if (APR_STATUS_IS_EINTR(rv)) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers goto worker_pop;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We got some other error. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers else if (!workers_may_exit) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "ap_queue_pop failed");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers continue;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers is_idle = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers worker_sockets[thread_slot] = csd;
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers bucket_alloc = apr_bucket_alloc_create(ptrans);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers process_socket(ptrans, csd, process_slot, thread_slot, bucket_alloc);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers worker_sockets[thread_slot] = NULL;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers requests_this_child--; /* FIXME: should be synchronized - aaron */
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers apr_pool_clear(ptrans);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers last_ptrans = ptrans;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_update_child_status_from_indexes(process_slot, thread_slot,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers (dying) ? SERVER_DEAD : SERVER_GRACEFUL, (request_rec *) NULL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_thread_exit(thd, APR_SUCCESS);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return NULL;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic int check_signal(int signum)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers switch (signum) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers case SIGTERM:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers case SIGINT:
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return 1;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers return 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void create_listener_thread(thread_starter *ts)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int my_child_num = ts->child_num_arg;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_threadattr_t *thread_attr = ts->threadattr;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers proc_info *my_info;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_status_t rv;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info = (proc_info *)malloc(sizeof(proc_info));
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info->pid = my_child_num;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info->tid = -1; /* listener thread doesn't have a thread slot */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info->sd = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info, pchild);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (rv != APR_SUCCESS) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "apr_thread_create: unable to create listener thread");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* In case system resources are maxxed out, we don't want
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * Apache running away with the CPU trying to fork over and
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * over and over again if we exit.
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * XXX Jeff doesn't see how Apache is going to try to fork again since
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers * the exit code is APEXIT_CHILDFATAL
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_sleep(apr_time_from_sec(10));
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers clean_child_exit(APEXIT_CHILDFATAL);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_os_thread_get(&listener_os_thread, ts->listener);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers}
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers/* XXX under some circumstances not understood, children can get stuck
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * in start_threads forever trying to take over slots which will
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * never be cleaned up; for now there is an APLOG_DEBUG message issued
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * every so often when this condition occurs
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sieversstatic void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers{
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers thread_starter *ts = dummy;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_thread_t **threads = ts->threads;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_threadattr_t *thread_attr = ts->threadattr;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int child_num_arg = ts->child_num_arg;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int my_child_num = child_num_arg;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers proc_info *my_info;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers apr_status_t rv;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int i;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int threads_created = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int listener_started = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int loops;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int prev_threads_created;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* We must create the fd queues before we start up the listener
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * and worker threads. */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers worker_queue = apr_pcalloc(pchild, sizeof(*worker_queue));
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers rv = ap_queue_init(worker_queue, ap_threads_per_child, pchild);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers if (rv != APR_SUCCESS) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers "ap_queue_init() failed");
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers clean_child_exit(APEXIT_CHILDFATAL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers rv = ap_queue_info_create(&worker_queue_info, pchild,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_threads_per_child);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (rv != APR_SUCCESS) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "ap_queue_info_create() failed");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers clean_child_exit(APEXIT_CHILDFATAL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers worker_sockets = apr_pcalloc(pchild, ap_threads_per_child
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers * sizeof(apr_socket_t *));
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers loops = prev_threads_created = 0;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers while (1) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers /* ap_threads_per_child does not include the listener thread */
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers for (i = 0; i < ap_threads_per_child; i++) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers int status = ap_scoreboard_image->servers[child_num_arg][i].status;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (status != SERVER_GRACEFUL && status != SERVER_DEAD) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers continue;
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers my_info = (proc_info *)malloc(sizeof(proc_info));
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers if (my_info == NULL) {
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers "malloc: out of memory");
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers clean_child_exit(APEXIT_CHILDFATAL);
aedc2eddd16e48d468e6ad0aea2caf00c7d37365Kay Sievers }
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers my_info->pid = my_child_num;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers my_info->tid = i;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers my_info->sd = 0;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers
0c959b39175b126fdb70ae00de37ca6d9c8ca3a1Kay Sievers /* We are creating threads right now */
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers ap_update_child_status_from_indexes(my_child_num, i,
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers SERVER_STARTING, NULL);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers /* We let each thread update its own scoreboard entry. This is
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers * done because it lets us deal with tid better.
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers */
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers rv = apr_thread_create(&threads[i], thread_attr,
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers worker_thread, my_info, pchild);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers if (rv != APR_SUCCESS) {
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers "apr_thread_create: unable to create worker thread");
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers /* In case system resources are maxxed out, we don't want
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers Apache running away with the CPU trying to fork over and
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers over and over again if we exit. */
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers apr_sleep(apr_time_from_sec(10));
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers clean_child_exit(APEXIT_CHILDFATAL);
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers }
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers threads_created++;
e8193554925a22b63bef0e77b8397b56d63a91ffKay Sievers }
/* Start the listener only when there are workers available */
if (!listener_started && threads_created) {
create_listener_thread(ts);
listener_started = 1;
}
if (start_thread_may_exit || threads_created == ap_threads_per_child) {
break;
}
/* wait for previous generation to clean up an entry */
apr_sleep(apr_time_from_sec(1));
++loops;
if (loops % 120 == 0) { /* every couple of minutes */
if (prev_threads_created == threads_created) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
"child %" APR_PID_T_FMT " isn't taking over "
"slots very quickly (%d of %d)",
ap_my_pid, threads_created, ap_threads_per_child);
}
prev_threads_created = threads_created;
}
}
/* What state should this child_main process be listed as in the
* scoreboard...?
* ap_update_child_status_from_indexes(my_child_num, i, SERVER_STARTING,
* (request_rec *) NULL);
*
* This state should be listed separately in the scoreboard, in some kind
* of process_status, not mixed in with the worker threads' status.
* "life_status" is almost right, but it's in the worker's structure, and
* the name could be clearer. gla
*/
apr_thread_exit(thd, APR_SUCCESS);
return NULL;
}
static void join_workers(apr_thread_t *listener, apr_thread_t **threads)
{
int i;
apr_status_t rv, thread_rv;
if (listener) {
int iter;
/* deal with a rare timing window which affects waking up the
* listener thread... if the signal sent to the listener thread
* is delivered between the time it verifies that the
* listener_may_exit flag is clear and the time it enters a
* blocking syscall, the signal didn't do any good... work around
* that by sleeping briefly and sending it again
*/
iter = 0;
while (iter < 10 &&
#ifdef HAVE_PTHREAD_KILL
pthread_kill(*listener_os_thread, 0)
#else
kill(ap_my_pid, 0)
#endif
== 0) {
/* listener not dead yet */
apr_sleep(apr_time_make(0, 500000));
wakeup_listener();
++iter;
}
if (iter >= 10) {
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, ap_server_conf,
"the listener thread didn't exit");
}
else {
rv = apr_thread_join(&thread_rv, listener);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
"apr_thread_join: unable to join listener thread");
}
}
}
for (i = 0; i < ap_threads_per_child; i++) {
if (threads[i]) { /* if we ever created this thread */
rv = apr_thread_join(&thread_rv, threads[i]);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
"apr_thread_join: unable to join worker "
"thread %d",
i);
}
}
}
}
static void join_start_thread(apr_thread_t *start_thread_id)
{
apr_status_t rv, thread_rv;
start_thread_may_exit = 1; /* tell it to give up in case it is still
* trying to take over slots from a
* previous generation
*/
rv = apr_thread_join(&thread_rv, start_thread_id);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, ap_server_conf,
"apr_thread_join: unable to join the start "
"thread");
}
}
static void child_main(int child_num_arg)
{
apr_thread_t **threads;
apr_status_t rv;
thread_starter *ts;
apr_threadattr_t *thread_attr;
apr_thread_t *start_thread_id;
ap_my_pid = getpid();
ap_fatal_signal_child_setup(ap_server_conf);
apr_pool_create(&pchild, pconf);
/*stuff to do before we switch id's, so we have permissions.*/
ap_reopen_scoreboard(pchild, NULL, 0);
rv = SAFE_ACCEPT(apr_proc_mutex_child_init(&accept_mutex, ap_lock_fname,
pchild));
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
"Couldn't initialize cross-process lock in child");
clean_child_exit(APEXIT_CHILDFATAL);
}
if (unixd_setup_child()) {
clean_child_exit(APEXIT_CHILDFATAL);
}
ap_run_child_init(pchild, ap_server_conf);
/* done with init critical section */
/* Just use the standard apr_setup_signal_thread to block all signals
* from being received. The child processes no longer use signals for
* any communication with the parent process.
*/
rv = apr_setup_signal_thread();
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf,
"Couldn't initialize signal thread");
clean_child_exit(APEXIT_CHILDFATAL);
}
if (ap_max_requests_per_child) {
requests_this_child = ap_max_requests_per_child;
}
else {
/* coding a value of zero means infinity */
requests_this_child = INT_MAX;
}
/* Setup worker threads */
/* clear the storage; we may not create all our threads immediately,
* and we want a 0 entry to indicate a thread which was not created
*/
threads = (apr_thread_t **)calloc(1,
sizeof(apr_thread_t *) * ap_threads_per_child);
if (threads == NULL) {
ap_log_error(APLOG_MARK, APLOG_ALERT, errno, ap_server_conf,
"malloc: out of memory");
clean_child_exit(APEXIT_CHILDFATAL);
}
ts = (thread_starter *)apr_palloc(pchild, sizeof(*ts));
apr_threadattr_create(&thread_attr, pchild);
/* 0 means PTHREAD_CREATE_JOINABLE */
apr_threadattr_detach_set(thread_attr, 0);
ts->threads = threads;
ts->listener = NULL;
ts->child_num_arg = child_num_arg;
ts->threadattr = thread_attr;
rv = apr_thread_create(&start_thread_id, thread_attr, start_threads,
ts, pchild);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
"apr_thread_create: unable to create worker thread");
/* In case system resources are maxxed out, we don't want
Apache running away with the CPU trying to fork over and
over and over again if we exit. */
apr_sleep(apr_time_from_sec(10));
clean_child_exit(APEXIT_CHILDFATAL);
}
/* If we are only running in one_process mode, we will want to
* still handle signals. */
if (one_process) {
/* Block until we get a terminating signal. */
apr_signal_thread(check_signal);
/* make sure the start thread has finished; signal_threads()
* and join_workers() depend on that
*/
/* XXX join_start_thread() won't be awakened if one of our
* threads encounters a critical error and attempts to
* shutdown this child
*/
join_start_thread(start_thread_id);
signal_threads(ST_UNGRACEFUL); /* helps us terminate a little more
* quickly than the dispatch of the signal thread
* beats the Pipe of Death and the browsers
*/
/* A terminating signal was received. Now join each of the
* workers to clean them up.
* If the worker already exited, then the join frees
* their resources and returns.
* If the worker hasn't exited, then this blocks until
* they have (then cleans up).
*/
join_workers(ts->listener, threads);
}
else { /* !one_process */
/* remove SIGTERM from the set of blocked signals... if one of
* the other threads in the process needs to take us down
* (e.g., for MaxRequestsPerChild) it will send us SIGTERM
*/
unblock_signal(SIGTERM);
apr_signal(SIGTERM, dummy_signal_handler);
/* Watch for any messages from the parent over the POD */
while (1) {
rv = ap_mpm_pod_check(pod);
if (rv == AP_NORESTART) {
/* see if termination was triggered while we slept */
switch(terminate_mode) {
case ST_GRACEFUL:
rv = AP_GRACEFUL;
break;
case ST_UNGRACEFUL:
rv = AP_RESTART;
break;
}
}
if (rv == AP_GRACEFUL || rv == AP_RESTART) {
/* make sure the start thread has finished;
* signal_threads() and join_workers depend on that
*/
join_start_thread(start_thread_id);
signal_threads(rv == AP_GRACEFUL ? ST_GRACEFUL : ST_UNGRACEFUL);
break;
}
}
/* A terminating signal was received. Now join each of the
* workers to clean them up.
* If the worker already exited, then the join frees
* their resources and returns.
* If the worker hasn't exited, then this blocks until
* they have (then cleans up).
*/
join_workers(ts->listener, threads);
}
free(threads);
clean_child_exit(resource_shortage ? APEXIT_CHILDSICK : 0);
}
static int make_child(server_rec *s, int slot)
{
int pid;
if (slot + 1 > ap_max_daemons_limit) {
ap_max_daemons_limit = slot + 1;
}
if (one_process) {
set_signals();
ap_scoreboard_image->parent[slot].pid = getpid();
child_main(slot);
}
if ((pid = fork()) == -1) {
ap_log_error(APLOG_MARK, APLOG_ERR, errno, s,
"fork: Unable to fork new process");
/* fork didn't succeed. Fix the scoreboard or else
* it will say SERVER_STARTING forever and ever
*/
ap_update_child_status_from_indexes(slot, 0, SERVER_DEAD, NULL);
/* In case system resources are maxxed out, we don't want
Apache running away with the CPU trying to fork over and
over and over again. */
apr_sleep(apr_time_from_sec(10));
return -1;
}
if (!pid) {
#ifdef HAVE_BINDPROCESSOR
/* By default, AIX binds to a single processor. This bit unbinds
* children which will then bind to another CPU.
*/
int status = bindprocessor(BINDPROCESS, (int)getpid(),
PROCESSOR_CLASS_ANY);
if (status != OK)
ap_log_error(APLOG_MARK, APLOG_WARNING, errno,
ap_server_conf,
"processor unbind failed %d", status);
#endif
RAISE_SIGSTOP(MAKE_CHILD);
apr_signal(SIGTERM, just_die);
child_main(slot);
clean_child_exit(0);
}
/* else */
ap_scoreboard_image->parent[slot].quiescing = 0;
ap_scoreboard_image->parent[slot].pid = pid;
return 0;
}
/* start up a bunch of children */
static void startup_children(int number_to_start)
{
int i;
for (i = 0; number_to_start && i < ap_daemons_limit; ++i) {
if (ap_scoreboard_image->parent[i].pid != 0) {
continue;
}
if (make_child(ap_server_conf, i) < 0) {
break;
}
--number_to_start;
}
}
/*
* idle_spawn_rate is the number of children that will be spawned on the
* next maintenance cycle if there aren't enough idle servers. It is
* doubled up to MAX_SPAWN_RATE, and reset only when a cycle goes by
* without the need to spawn.
*/
static int idle_spawn_rate = 1;
#ifndef MAX_SPAWN_RATE
#define MAX_SPAWN_RATE (32)
#endif
static int hold_off_on_exponential_spawning;
static void perform_idle_server_maintenance(void)
{
int i, j;
int idle_thread_count;
worker_score *ws;
process_score *ps;
int free_length;
int totally_free_length = 0;
int free_slots[MAX_SPAWN_RATE];
int last_non_dead;
int total_non_dead;
/* initialize the free_list */
free_length = 0;
idle_thread_count = 0;
last_non_dead = -1;
total_non_dead = 0;
for (i = 0; i < ap_daemons_limit; ++i) {
/* Initialization to satisfy the compiler. It doesn't know
* that ap_threads_per_child is always > 0 */
int status = SERVER_DEAD;
int any_dying_threads = 0;
int any_dead_threads = 0;
int all_dead_threads = 1;
if (i >= ap_max_daemons_limit && totally_free_length == idle_spawn_rate)
break;
ps = &ap_scoreboard_image->parent[i];
for (j = 0; j < ap_threads_per_child; j++) {
ws = &ap_scoreboard_image->servers[i][j];
status = ws->status;
/* XXX any_dying_threads is probably no longer needed GLA */
any_dying_threads = any_dying_threads ||
(status == SERVER_GRACEFUL);
any_dead_threads = any_dead_threads || (status == SERVER_DEAD);
all_dead_threads = all_dead_threads &&
(status == SERVER_DEAD ||
status == SERVER_GRACEFUL);
/* We consider a starting server as idle because we started it
* at least a cycle ago, and if it still hasn't finished starting
* then we're just going to swamp things worse by forking more.
* So we hopefully won't need to fork more if we count it.
* This depends on the ordering of SERVER_READY and SERVER_STARTING.
*/
if (status <= SERVER_READY && status != SERVER_DEAD &&
!ps->quiescing &&
ps->generation == ap_my_generation &&
/* XXX the following shouldn't be necessary if we clean up
* properly after seg faults, but we're not yet GLA
*/
ps->pid != 0) {
++idle_thread_count;
}
}
if (any_dead_threads && totally_free_length < idle_spawn_rate
&& (!ps->pid /* no process in the slot */
|| ps->quiescing)) { /* or at least one is going away */
if (all_dead_threads) {
/* great! we prefer these, because the new process can
* start more threads sooner. So prioritize this slot
* by putting it ahead of any slots with active threads.
*
* first, make room by moving a slot that's potentially still
* in use to the end of the array
*/
free_slots[free_length] = free_slots[totally_free_length];
free_slots[totally_free_length++] = i;
}
else {
/* slot is still in use - back of the bus
*/
free_slots[free_length] = i;
}
++free_length;
}
/* XXX if (!ps->quiescing) is probably more reliable GLA */
if (!any_dying_threads) {
last_non_dead = i;
++total_non_dead;
}
}
ap_max_daemons_limit = last_non_dead + 1;
if (idle_thread_count > max_spare_threads) {
/* Kill off one child */
ap_mpm_pod_signal(pod, TRUE);
idle_spawn_rate = 1;
}
else if (idle_thread_count < min_spare_threads) {
/* terminate the free list */
if (free_length == 0) {
/* only report this condition once */
static int reported = 0;
if (!reported) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0,
ap_server_conf,
"server reached MaxClients setting, consider"
" raising the MaxClients setting");
reported = 1;
}
idle_spawn_rate = 1;
}
else {
if (free_length > idle_spawn_rate) {
free_length = idle_spawn_rate;
}
if (idle_spawn_rate >= 8) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0,
ap_server_conf,
"server seems busy, (you may need "
"to increase StartServers, ThreadsPerChild "
"or Min/MaxSpareThreads), "
"spawning %d children, there are around %d idle "
"threads, and %d total children", free_length,
idle_thread_count, total_non_dead);
}
for (i = 0; i < free_length; ++i) {
make_child(ap_server_conf, free_slots[i]);
}
/* the next time around we want to spawn twice as many if this
* wasn't good enough, but not if we've just done a graceful
*/
if (hold_off_on_exponential_spawning) {
--hold_off_on_exponential_spawning;
}
else if (idle_spawn_rate < MAX_SPAWN_RATE) {
idle_spawn_rate *= 2;
}
}
}
else {
idle_spawn_rate = 1;
}
}
static void server_main_loop(int remaining_children_to_start)
{
int child_slot;
apr_exit_why_e exitwhy;
int status, processed_status;
apr_proc_t pid;
int i;
while (!restart_pending && !shutdown_pending) {
ap_wait_or_timeout(&exitwhy, &status, &pid, pconf);
if (pid.pid != -1) {
processed_status = ap_process_child_status(&pid, exitwhy, status);
if (processed_status == APEXIT_CHILDFATAL) {
shutdown_pending = 1;
child_fatal = 1;
return;
}
/* non-fatal death... note that it's gone in the scoreboard. */
child_slot = find_child_by_pid(&pid);
if (child_slot >= 0) {
for (i = 0; i < ap_threads_per_child; i++)
ap_update_child_status_from_indexes(child_slot, i, SERVER_DEAD,
(request_rec *) NULL);
ap_scoreboard_image->parent[child_slot].pid = 0;
ap_scoreboard_image->parent[child_slot].quiescing = 0;
if (processed_status == APEXIT_CHILDSICK) {
/* resource shortage, minimize the fork rate */
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_read(&pid, status) == 0) {
/* 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();
}
}
int ap_mpm_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
{
int remaining_children_to_start;
apr_status_t rv;
ap_log_pid(pconf, ap_pid_fname);
first_server_limit = server_limit;
first_thread_limit = thread_limit;
if (changed_limit_at_restart) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s,
"WARNING: Attempt to change ServerLimit or ThreadLimit "
"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");
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");
return 1;
}
}
if (!is_graceful) {
if (ap_run_pre_mpm(s->process->pool, SB_SHARED) != OK) {
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();
/* Don't thrash... */
if (max_spare_threads < min_spare_threads + ap_threads_per_child)
max_spare_threads = min_spare_threads + ap_threads_per_child;
/* 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_version());
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;
server_main_loop(remaining_children_to_start);
if (shutdown_pending) {
/* Time to gracefully shut down:
* Kill child processes, tell them to call child_exit, etc...
* (By "gracefully" we don't mean graceful in the same sense as
* "apachectl graceful" where we allow old connections to finish.)
*/
ap_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
ap_reclaim_child_processes(1); /* Start with SIGTERM */
if (!child_fatal) {
/* 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;
}
/* we've been told to restart */
apr_signal(SIGHUP, 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,
AP_SIG_GRACEFUL_STRING " received. Doing graceful restart");
/* wake up the children...time to die. But we'll have more soon */
ap_mpm_pod_killpg(pod, ap_daemons_limit, TRUE);
/* This is mostly for debugging... so that we know what is still
* gracefully dealing with existing request.
*/
}
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_mpm_pod_killpg(pod, ap_daemons_limit, FALSE);
ap_reclaim_child_processes(1); /* Start with SIGTERM */
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 worker_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 (!one_process) {
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;
}
static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp)
{
static int restart_num = 0;
int no_detach, debug, foreground;
ap_directive_t *pdir;
ap_directive_t *max_clients = NULL;
apr_status_t rv;
/* make sure that "ThreadsPerChild" gets set before "MaxClients" */
for (pdir = ap_conftree; pdir != NULL; pdir = pdir->next) {
if (strncasecmp(pdir->directive, "ThreadsPerChild", 15) == 0) {
if (!max_clients) {
break; /* we're in the clear, got ThreadsPerChild first */
}
else {
/* now to swap the data */
ap_directive_t temp;
temp.directive = pdir->directive;
temp.args = pdir->args;
/* Make sure you don't change 'next', or you may get loops! */
/* XXX: first_child, parent, and data can never be set
* for these directives, right? -aaron */
temp.filename = pdir->filename;
temp.line_num = pdir->line_num;
pdir->directive = max_clients->directive;
pdir->args = max_clients->args;
pdir->filename = max_clients->filename;
pdir->line_num = max_clients->line_num;
max_clients->directive = temp.directive;
max_clients->args = temp.args;
max_clients->filename = temp.filename;
max_clients->line_num = temp.line_num;
break;
}
}
else if (!max_clients
&& strncasecmp(pdir->directive, "MaxClients", 10) == 0) {
max_clients = pdir;
}
}
debug = ap_exists_config_define("DEBUG");
if (debug) {
foreground = one_process = 1;
no_detach = 0;
}
else {
one_process = ap_exists_config_define("ONE_PROCESS");
no_detach = ap_exists_config_define("NO_DETACH");
foreground = ap_exists_config_define("FOREGROUND");
}
/* sigh, want this only the second time around */
if (restart_num++ == 1) {
is_graceful = 0;
if (!one_process && !foreground) {
rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND
: APR_PROC_DETACH_DAEMONIZE);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL,
"apr_proc_detach failed");
return HTTP_INTERNAL_SERVER_ERROR;
}
}
parent_pid = ap_my_pid = getpid();
}
unixd_pre_config(ptemp);
ap_listen_pre_config();
ap_daemons_to_start = DEFAULT_START_DAEMON;
min_spare_threads = DEFAULT_MIN_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
max_spare_threads = DEFAULT_MAX_FREE_DAEMON * DEFAULT_THREADS_PER_CHILD;
ap_daemons_limit = server_limit;
ap_threads_per_child = DEFAULT_THREADS_PER_CHILD;
ap_pid_fname = DEFAULT_PIDLOG;
ap_lock_fname = DEFAULT_LOCKFILE;
ap_max_requests_per_child = DEFAULT_MAX_REQUESTS_PER_CHILD;
ap_extended_status = 0;
#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
ap_max_mem_free = APR_ALLOCATOR_MAX_FREE_UNLIMITED;
#endif
apr_cpystrn(ap_coredump_dir, ap_server_root, sizeof(ap_coredump_dir));
return OK;
}
static void worker_hooks(apr_pool_t *p)
{
/* The worker open_logs phase must run before the core's, or stderr
* will be redirected to a file, and the messages won't print to the
* console.
*/
static const char *const aszSucc[] = {"core.c", NULL};
one_process = 0;
ap_hook_open_logs(worker_open_logs, NULL, aszSucc, APR_HOOK_MIDDLE);
ap_hook_pre_config(worker_pre_config, NULL, NULL, APR_HOOK_MIDDLE);
}
static const char *set_daemons_to_start(cmd_parms *cmd, void *dummy,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_daemons_to_start = atoi(arg);
return NULL;
}
static const char *set_min_spare_threads(cmd_parms *cmd, void *dummy,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
min_spare_threads = atoi(arg);
if (min_spare_threads <= 0) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: detected MinSpareThreads set to non-positive.");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"Resetting to 1 to avoid almost certain Apache failure.");
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"Please read the documentation.");
min_spare_threads = 1;
}
return NULL;
}
static const char *set_max_spare_threads(cmd_parms *cmd, void *dummy,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
max_spare_threads = atoi(arg);
return NULL;
}
static const char *set_max_clients (cmd_parms *cmd, void *dummy,
const char *arg)
{
int max_clients;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
/* It is ok to use ap_threads_per_child here because we are
* sure that it gets set before MaxClients in the pre_config stage. */
max_clients = atoi(arg);
if (max_clients < ap_threads_per_child) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: MaxClients (%d) must be at least as large",
max_clients);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" as ThreadsPerChild (%d). Automatically",
ap_threads_per_child);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" increasing MaxClients to %d.",
ap_threads_per_child);
max_clients = ap_threads_per_child;
}
ap_daemons_limit = max_clients / ap_threads_per_child;
if ((max_clients > 0) && (max_clients % ap_threads_per_child)) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: MaxClients (%d) is not an integer multiple",
max_clients);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" of ThreadsPerChild (%d), lowering MaxClients to %d",
ap_threads_per_child,
ap_daemons_limit * ap_threads_per_child);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" for a maximum of %d child processes,",
ap_daemons_limit);
max_clients = ap_daemons_limit * ap_threads_per_child;
}
if (ap_daemons_limit > server_limit) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: MaxClients of %d would require %d servers,",
max_clients, ap_daemons_limit);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" and would exceed the ServerLimit value of %d.",
server_limit);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" Automatically lowering MaxClients to %d. To increase,",
server_limit);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" please see the ServerLimit directive.");
ap_daemons_limit = server_limit;
}
else if (ap_daemons_limit < 1) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: Require MaxClients > 0, setting to 1");
ap_daemons_limit = 1;
}
return NULL;
}
static const char *set_threads_per_child (cmd_parms *cmd, void *dummy,
const char *arg)
{
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
ap_threads_per_child = atoi(arg);
if (ap_threads_per_child > thread_limit) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: ThreadsPerChild of %d exceeds ThreadLimit "
"value of %d", ap_threads_per_child,
thread_limit);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"threads, lowering ThreadsPerChild to %d. To increase, please"
" see the", thread_limit);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" ThreadLimit directive.");
ap_threads_per_child = thread_limit;
}
else if (ap_threads_per_child < 1) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: Require ThreadsPerChild > 0, setting to 1");
ap_threads_per_child = 1;
}
return NULL;
}
static const char *set_server_limit (cmd_parms *cmd, void *dummy, const char *arg)
{
int tmp_server_limit;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
tmp_server_limit = atoi(arg);
/* you cannot change ServerLimit across a restart; ignore
* any such attempts
*/
if (first_server_limit &&
tmp_server_limit != server_limit) {
/* how do we log a message? the error log is a bit bucket at this
* point; we'll just have to set a flag so that ap_mpm_run()
* logs a warning later
*/
changed_limit_at_restart = 1;
return NULL;
}
server_limit = tmp_server_limit;
if (server_limit > MAX_SERVER_LIMIT) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: ServerLimit of %d exceeds compile time limit "
"of %d servers,", server_limit, MAX_SERVER_LIMIT);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" lowering ServerLimit to %d.", MAX_SERVER_LIMIT);
server_limit = MAX_SERVER_LIMIT;
}
else if (server_limit < 1) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: Require ServerLimit > 0, setting to 1");
server_limit = 1;
}
return NULL;
}
static const char *set_thread_limit (cmd_parms *cmd, void *dummy, const char *arg)
{
int tmp_thread_limit;
const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
if (err != NULL) {
return err;
}
tmp_thread_limit = atoi(arg);
/* you cannot change ThreadLimit across a restart; ignore
* any such attempts
*/
if (first_thread_limit &&
tmp_thread_limit != thread_limit) {
/* how do we log a message? the error log is a bit bucket at this
* point; we'll just have to set a flag so that ap_mpm_run()
* logs a warning later
*/
changed_limit_at_restart = 1;
return NULL;
}
thread_limit = tmp_thread_limit;
if (thread_limit > MAX_THREAD_LIMIT) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: ThreadLimit of %d exceeds compile time limit "
"of %d servers,", thread_limit, MAX_THREAD_LIMIT);
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
" lowering ThreadLimit to %d.", MAX_THREAD_LIMIT);
thread_limit = MAX_THREAD_LIMIT;
}
else if (thread_limit < 1) {
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"WARNING: Require ThreadLimit > 0, setting to 1");
thread_limit = 1;
}
return NULL;
}
static const command_rec worker_cmds[] = {
UNIX_DAEMON_COMMANDS,
LISTEN_COMMANDS,
AP_INIT_TAKE1("StartServers", set_daemons_to_start, NULL, RSRC_CONF,
"Number of child processes launched at server startup"),
AP_INIT_TAKE1("MinSpareThreads", set_min_spare_threads, NULL, RSRC_CONF,
"Minimum number of idle threads, to handle request spikes"),
AP_INIT_TAKE1("MaxSpareThreads", set_max_spare_threads, NULL, RSRC_CONF,
"Maximum number of idle threads"),
AP_INIT_TAKE1("MaxClients", set_max_clients, NULL, RSRC_CONF,
"Maximum number of threads alive at the same time"),
AP_INIT_TAKE1("ThreadsPerChild", set_threads_per_child, NULL, RSRC_CONF,
"Number of threads each child creates"),
AP_INIT_TAKE1("ServerLimit", set_server_limit, NULL, RSRC_CONF,
"Maximum number of child processes for this run of Apache"),
AP_INIT_TAKE1("ThreadLimit", set_thread_limit, NULL, RSRC_CONF,
"Maximum number of worker threads per child process for this run of Apache - Upper limit for ThreadsPerChild"),
{ NULL }
};
module AP_MODULE_DECLARE_DATA mpm_worker_module = {
MPM20_MODULE_STUFF,
ap_mpm_rewrite_args, /* hook to run before apache parses args */
NULL, /* create per-directory config structure */
NULL, /* merge per-directory config structures */
NULL, /* create per-server config structure */
NULL, /* merge per-server config structures */
worker_cmds, /* command apr_table_t */
worker_hooks /* register_hooks */
};