/* 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. */ #include "httpd.h" #include "http_config.h" #include "simple_types.h" #include "simple_event.h" #include "simple_run.h" #include "http_log.h" #include "simple_children.h" #include "apr_hash.h" #include /* For fork() */ #define SPAWN_CHILDREN_INTERVAL (apr_time_from_sec(5)) static void simple_kill_random_child(simple_core_t * sc) { /* See comment in simple_spawn_child for why we check here. */ if (!sc->run_single_process) { apr_hash_index_t *hi; simple_child_t *child = NULL; apr_thread_mutex_lock(sc->mtx); hi = apr_hash_first(sc->pool, sc->children); if (hi != NULL) { apr_hash_this(hi, NULL, NULL, (void **)&child); apr_hash_set(sc->children, &child->pid, sizeof(child->pid), NULL); } apr_thread_mutex_unlock(sc->mtx); if (child != NULL) { kill(child->pid, 9); /* TODO: recycle child object */ } } } static void clean_child_exit(int code) __attribute__ ((noreturn)); static void clean_child_exit(int code) { /* TODO: Pool cleanups.... sigh. */ exit(code); } static int simple_spawn_child(simple_core_t * sc) { pid_t pid = 0; int rv = 0; /* Although we could cut this off 'earlier', and not even invoke this * function, I would like to keep the functions invoked when in debug mode * to be as close as possible to those when not in debug... So, we just skip * the actual spawn itself, but go through all of the motions... */ if (!sc->run_single_process) { if (sc->spawn_via == SIMPLE_SPAWN_FORK) { pid = fork(); if (pid == -1) { rv = errno; ap_log_error(APLOG_MARK, APLOG_CRIT, rv, NULL, "simple_spawn_child: Unable to fork new process"); return rv; } if (pid == 0) { /* this is the child process */ rv = simple_child_loop(sc); if (rv) { clean_child_exit(APEXIT_CHILDFATAL); } else { clean_child_exit(0); } } } else { /* TODO: SIMPLE_SPAWN_EXEC */ abort(); } } if (pid != 0) { simple_child_t *child; apr_thread_mutex_lock(sc->mtx); child = apr_palloc(sc->pool, sizeof(simple_child_t)); child->pid = pid; apr_hash_set(sc->children, &child->pid, sizeof(child->pid), child); apr_thread_mutex_unlock(sc->mtx); } return 0; } void simple_check_children_size(simple_core_t * sc, void *baton) { unsigned int count; int wanted; int i; simple_register_timer(sc, simple_check_children_size, NULL, SPAWN_CHILDREN_INTERVAL, sc->pool); if (sc->run_single_process && sc->restart_num == 2) { static int run = 0; /* This is kinda of hack, but rather than spawning a child process, * we register the normal IO handlers in the main event loop.... */ if (run == 0) { simple_single_process_hack(sc); run++; } } { apr_thread_mutex_lock(sc->mtx); count = apr_hash_count(sc->children); wanted = sc->procmgr.proc_count; apr_thread_mutex_unlock(sc->mtx); } if (count > wanted) { /* kill some kids */ int to_kill = count - wanted; for (i = 0; i < to_kill; i++) { simple_kill_random_child(sc); } } else if (count < wanted) { int rv = 0; /* spawn some kids */ int to_spawn = wanted - count; for (i = 0; rv == 0 && i < to_spawn; i++) { rv = simple_spawn_child(sc); } } else { /* juuuuust right. */ } }