mpm_unix.c revision 185aa71728867671e105178b4c66fbc22b65ae26
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* The purpose of this file is to store the code that MOST mpm's will need
* this does not mean a function only goes into this file if every MPM needs
* it. It means that if a function is needed by more than one MPM, and
* future maintenance would be served by making the code common, then the
* function belongs here.
*
* specific to multi-process servers, but NOT to Unix. Which is why it
*/
#ifndef WIN32
#include "apr.h"
#include "apr_thread_proc.h"
#include "apr_signal.h"
#include "apr_strings.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "apr_getopt.h"
#include "apr_optional.h"
#include "apr_allocator.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "mpm_common.h"
#include "ap_mpm.h"
#include "ap_listen.h"
#include "scoreboard.h"
#include "util_mutex.h"
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif
#ifdef HAVE_GRP_H
#include <grp.h>
#endif
#include <unistd.h>
#endif
/* we know core's module_index is 0 */
typedef struct extra_process_t {
struct extra_process_t *next;
static extra_process_t *extras;
{
extras = p;
}
{
}
if (cur) {
if (prev) {
}
else {
}
return 1; /* found */
}
else {
/* we don't know about any such process */
return 0;
}
}
{
int status;
/* Ensure pid sanity. */
if (pid < 1) {
return 1;
}
if (waitret != APR_CHILD_NOTDONE) {
if (waitret == APR_CHILD_DONE)
return 1;
}
switch(action) {
case DO_NOTHING:
break;
case SEND_SIGTERM:
/* ok, now it's being annoying */
"child process %" APR_PID_T_FMT
" still did not exit, "
"sending a SIGTERM",
pid);
break;
case SEND_SIGKILL:
"child process %" APR_PID_T_FMT
" still did not exit, "
"sending a SIGKILL",
pid);
break;
case GIVEUP:
/* gave it our best shot, but alas... If this really
* is a child we are trying to kill and it really hasn't
* exited, we will likely fail to bind to the port
* after the restart.
*/
"could not make child process %" APR_PID_T_FMT
" exit, "
"attempting to continue anyway",
pid);
break;
}
return 0;
}
void ap_reclaim_child_processes(int terminate,
{
int i;
int not_dead_yet;
int max_daemons;
/* this table of actions and elapsed times tells what action is taken
* at which elapsed time from starting the reclaim
*/
struct {
} action_table[] = {
{DO_NOTHING, 0}, /* dummy entry for iterations where we reap
* children but take no action against
* stragglers
*/
};
int cur_action; /* index of action we decided to take this
* iteration
*/
do {
/* don't let waittime get longer than 1 second; otherwise, we don't
* react quickly to the last child exiting, and taking action can
* be delayed
*/
}
/* see what action to take, if any */
++next_action;
}
else {
cur_action = 0; /* nothing to do */
}
/* now see who is done */
not_dead_yet = 0;
for (i = 0; i < max_daemons; ++i) {
if (pid == 0) {
continue; /* not every scoreboard entry is in use */
}
mpm_callback(i, 0, 0);
}
else {
++not_dead_yet;
}
}
while (cur_extra) {
}
else {
AP_DEBUG_ASSERT(1 == 0);
}
}
else {
++not_dead_yet;
}
}
#endif
} while (not_dead_yet > 0 &&
}
{
int i;
int max_daemons;
/* now see who is done */
for (i = 0; i < max_daemons; ++i) {
if (pid == 0) {
continue; /* not every scoreboard entry is in use */
}
mpm_callback(i, 0, 0);
}
}
while (cur_extra) {
}
else {
AP_DEBUG_ASSERT(1 == 0);
}
}
}
}
/* Before sending the signal to the pid this function verifies that
* the pid is a member of the current process group; either using
* apr_proc_wait(), where waitpid() guarantees to fail for non-child
* processes; or by using getpgid() directly, if available. */
{
#ifndef HAVE_GETPGID
int status;
/* Ensure pid sanity */
if (pid < 1) {
return APR_EINVAL;
}
if (rv == APR_CHILD_DONE) {
/* The child already died - log the termination status if
* necessary: */
return APR_EINVAL;
}
else if (rv != APR_CHILD_NOTDONE) {
/* The child is already dead and reaped, or was a bogus pid -
* log this either way. */
"cannot send signal %d to pid %ld (non-child or "
return APR_EINVAL;
}
#else
/* Ensure pid sanity. */
if (pid < 1) {
return APR_EINVAL;
}
if (pg == -1) {
/* Process already dead... */
return errno;
}
"refusing to send signal %d to pid %ld outside "
return APR_EINVAL;
}
#endif
}
{
const char *sigdesc;
/* Child died... if it died due to a fatal error,
* we should simply bail out. The caller needs to
* check for bad rc from us and exit, running any
* appropriate cleanups.
*
* If the child died due to a resource shortage,
* the parent should limit the rate of forking
*/
if (APR_PROC_CHECK_EXIT(why)) {
if (status == APEXIT_CHILDSICK) {
return status;
}
if (status == APEXIT_CHILDFATAL) {
"Child %" APR_PID_T_FMT
" returned a Fatal error... Apache is exiting!",
return APEXIT_CHILDFATAL;
}
return 0;
}
if (APR_PROC_CHECK_SIGNALED(why)) {
switch (signum) {
case SIGTERM:
case SIGHUP:
case AP_SIG_GRACEFUL:
case SIGKILL:
break;
default:
if (APR_PROC_CHECK_CORE_DUMP(why)) {
"child pid %ld exit signal %s (%d), "
"possible coredump in %s",
}
else {
"child pid %ld exit signal %s (%d)",
}
}
}
return 0;
}
{
APR_WRITE_BLOCK, p);
if (rv != APR_SUCCESS) {
return rv;
}
(*pod)->p = p;
/* close these before exec. */
return APR_SUCCESS;
}
{
char c;
return APR_SUCCESS;
}
if (rv != APR_SUCCESS) {
return rv;
}
return AP_NORESTART;
}
{
if (rv != APR_SUCCESS) {
return rv;
}
if (rv != APR_SUCCESS) {
return rv;
}
return APR_SUCCESS;
}
{
char char_of_death = '!';
if (rv != APR_SUCCESS) {
"write pipe_of_death");
}
return rv;
}
/* This function connects to the server, then immediately closes the connection.
* This permits the MPM to skip the poll when there is only one listening
* socket, because it provides a alternate way to unblock an accept() when
* the pod is used.
*/
{
char *srequest;
apr_pool_t *p;
/* create a temporary pool for the socket. pconf stays around too long */
if (rv != APR_SUCCESS) {
return rv;
}
/* If possible, find a listener which is configured for
* plain-HTTP, not SSL; using an SSL port would either be
* expensive to do correctly (performing a complete SSL handshake)
* or cause log spam by doing incorrectly (simply sending EOF). */
lp = ap_listeners;
}
if (!lp) {
lp = ap_listeners;
}
if (rv != APR_SUCCESS) {
"get socket to connect to listener");
apr_pool_destroy(p);
return rv;
}
/* on some platforms (e.g., FreeBSD), the kernel won't accept many
* queued connections before it starts blocking local connects...
* we need to keep from blocking too long and instead return an error,
* because the MPM won't want to hold up a graceful restart for a
* long time
*/
if (rv != APR_SUCCESS) {
"set timeout on socket to connect to listener");
apr_pool_destroy(p);
return rv;
}
if (rv != APR_SUCCESS) {
int log_level = APLOG_WARNING;
if (APR_STATUS_IS_TIMEUP(rv)) {
/* probably some server processes bailed out already and there
* is nobody around to call accept and clear out the kernel
* connection queue; usually this is not worth logging
*/
}
}
/* Create the request string. We include a User-Agent so that
* adminstrators can track down the cause of the odd-looking
* requests in their logs.
*/
" (internal dummy connection)\r\n\r\n", NULL);
/* Since some operating systems support buffering of data or entire
* requests in the kernel, we send a simple request, to make sure
* the server pops out of a blocking accept().
*/
/* XXX: This is HTTP specific. We should look at the Protocol for each
* listener, and send the correct type of request to trigger any Accept
* Filters.
*/
apr_pool_destroy(p);
return rv;
}
{
if (rv != APR_SUCCESS) {
return rv;
}
return dummy_connection(pod);
}
{
int i;
/* we don't write anything to the pod here... we assume
* that the would-be reader of the pod has another way to
* see that it is time to die once we wake it up
*
* writing lots of things to the pod at once is very
* problematic... we can fill the kernel pipe buffer and
* be blocked until somebody consumes some bytes or
* we hit a timeout... if we hit a timeout we can't just
* keep trying because maybe we'll never successfully
* write again... but then maybe we'll leave would-be
* readers stranded (a number of them could be tied up for
* a while serving time-consuming requests)
*/
}
}
static const char *dash_k_arg = NULL;
static const char *dash_k_arg_noarg = "noarg";
{
"sending signal to server");
return 1;
}
return 0;
}
{
int running = 0;
const char *status;
*exit_status = 0;
if (rv != APR_SUCCESS) {
if (!APR_STATUS_IS_ENOENT(rv)) {
"Error retrieving pid file %s", ap_pid_fname);
"Remove it before continuing if it is corrupted.");
*exit_status = 1;
return 1;
}
status = "httpd (no pid file) not running";
}
else {
running = 1;
"running", otherpid);
}
else {
otherpid);
}
}
if (running) {
return 1;
}
}
if (!running) {
}
else {
}
return 1;
}
if (!running) {
printf("httpd not running, trying to start\n");
}
else {
return 1;
}
}
if (!running) {
printf("httpd not running, trying to start\n");
}
else {
return 1;
}
}
if (!running) {
}
else {
}
return 1;
}
return 0;
}
{
char optbuf[3];
const char *optarg;
sizeof(const char **));
optbuf[0] = '-';
/* option char returned by apr_getopt() will be stored in optbuf[1] */
switch(optbuf[1]) {
case 'k':
if (!dash_k_arg) {
dash_k_arg = optarg;
break;
}
}
default:
*(const char **)apr_array_push(mpm_new_argv) =
if (optarg) {
}
}
}
/* back up to capture the bad argument */
}
*(const char **)apr_array_push(mpm_new_argv) =
}
if (NULL == dash_k_arg) {
}
}
static apr_pool_t *pconf;
static int exception_hook_enabled;
const char *arg)
{
return err;
}
return "EnableExceptionHook directive not allowed in <VirtualHost>";
}
}
}
else {
return "parameter must be 'on' or 'off'";
}
return NULL;
}
static void run_fatal_exception_hook(int sig)
{
ap_exception_info_t ei = {0};
if (exception_hook_enabled &&
geteuid() != 0 &&
my_pid != parent_pid) {
}
}
#endif /* AP_ENABLE_EXCEPTION_HOOK */
/* handle all varieties of core dumping signals */
static void sig_coredump(int sig)
{
#endif
/* linuxthreads issue calling getpid() here:
* This comparison won't match if the crashing thread is
* some module's thread that runs in the parent process.
* The fallout, which is limited to linuxthreads:
* The special log message won't be written when such a
* thread in the parent causes the parent to crash.
*/
if (getpid() == parent_pid) {
"seg fault or similar nasty error detected "
"in the parent process");
/* XXX we can probably add some rudimentary cleanup code here,
* like getting rid of the pid file. If any additional bad stuff
* happens, we are protected from recursive errors taking down the
* system since this function is no longer the signal handler GLA
*/
}
/* At this point we've got sig blocked, because we're still inside
* the signal handler. When we leave the signal handler it will
* be unblocked, and we'll take the signal... and coredump or whatever
* is appropriate for this particular Unix. In addition the parent
* will see the real signal we received -- whereas if we called
* abort() here, the parent would only see SIGABRT.
*/
}
{
return APR_SUCCESS;
}
{
#ifndef NO_USE_SIGACTION
#if defined(SA_ONESHOT)
#elif defined(SA_RESETHAND)
#else
#endif
#ifdef SIGBUS
#endif
#ifdef SIGABORT
#endif
#ifdef SIGABRT
#endif
#ifdef SIGILL
#endif
#ifdef SIGFPE
#endif
#else /* NO_USE_SIGACTION */
#ifdef SIGBUS
#endif /* SIGBUS */
#ifdef SIGABORT
#endif /* SIGABORT */
#ifdef SIGABRT
#endif /* SIGABRT */
#ifdef SIGILL
#endif /* SIGILL */
#ifdef SIGFPE
#endif /* SIGFPE */
#endif /* NO_USE_SIGACTION */
return APR_SUCCESS;
}
#endif /* WIN32 */