|
Packit |
d68d13 |
/*
|
|
Packit |
d68d13 |
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
d68d13 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
d68d13 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
d68d13 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
d68d13 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
d68d13 |
* the License. You may obtain a copy of the License at
|
|
Packit |
d68d13 |
*
|
|
Packit |
d68d13 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
d68d13 |
*
|
|
Packit |
d68d13 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
d68d13 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
d68d13 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
d68d13 |
* See the License for the specific language governing permissions and
|
|
Packit |
d68d13 |
* limitations under the License.
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* For DEFAULT_PATH */
|
|
Packit |
d68d13 |
#define CORE_PRIVATE
|
|
Packit |
d68d13 |
#include "httpd.h"
|
|
Packit |
d68d13 |
#include "http_config.h"
|
|
Packit |
d68d13 |
#include "apr_strings.h"
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
#include "fcgid_pm.h"
|
|
Packit |
d68d13 |
#include "fcgid_pm_main.h"
|
|
Packit |
d68d13 |
#include "fcgid_conf.h"
|
|
Packit |
d68d13 |
#include "fcgid_proctbl.h"
|
|
Packit |
d68d13 |
#include "fcgid_proc.h"
|
|
Packit |
d68d13 |
#include "fcgid_spawn_ctl.h"
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
#define HAS_GRACEFUL_KILL "Gracefulkill"
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
static void
|
|
Packit |
d68d13 |
link_node_to_list(server_rec * main_server,
|
|
Packit |
d68d13 |
fcgid_procnode * header,
|
|
Packit |
d68d13 |
fcgid_procnode * node, fcgid_procnode * table_array)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
proctable_pm_lock(main_server);
|
|
Packit |
d68d13 |
node->next_index = header->next_index;
|
|
Packit |
d68d13 |
header->next_index = node - table_array;
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
static apr_time_t lastidlescan = 0;
|
|
Packit |
d68d13 |
static void scan_idlelist(server_rec * main_server)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
/*
|
|
Packit |
d68d13 |
Scan the idle list
|
|
Packit |
d68d13 |
1. move all processes idle timeout to error list
|
|
Packit |
d68d13 |
2. move all processes lifetime expired to error list
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
fcgid_procnode *previous_node, *current_node, *next_node;
|
|
Packit |
d68d13 |
fcgid_procnode *error_list_header;
|
|
Packit |
d68d13 |
fcgid_procnode *proc_table;
|
|
Packit |
d68d13 |
apr_time_t last_active_time, start_time;
|
|
Packit |
d68d13 |
apr_time_t now = apr_time_now();
|
|
Packit |
d68d13 |
int idle_timeout, proc_lifetime;
|
|
Packit |
d68d13 |
fcgid_server_conf *sconf =
|
|
Packit |
d68d13 |
ap_get_module_config(main_server->module_config,
|
|
Packit |
d68d13 |
&fcgid_module);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Should I check the idle list now? */
|
|
Packit |
d68d13 |
if (procmgr_must_exit()
|
|
Packit |
d68d13 |
|| apr_time_sec(now) - apr_time_sec(lastidlescan) <=
|
|
Packit |
d68d13 |
sconf->idle_scan_interval)
|
|
Packit |
d68d13 |
return;
|
|
Packit |
d68d13 |
lastidlescan = now;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Check the list */
|
|
Packit |
d68d13 |
proc_table = proctable_get_table_array();
|
|
Packit |
d68d13 |
previous_node = proctable_get_idle_list();
|
|
Packit |
d68d13 |
error_list_header = proctable_get_error_list();
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
proctable_pm_lock(main_server);
|
|
Packit |
d68d13 |
current_node = &proc_table[previous_node->next_index];
|
|
Packit |
d68d13 |
while (current_node != proc_table) {
|
|
Packit |
d68d13 |
next_node = &proc_table[current_node->next_index];
|
|
Packit |
d68d13 |
last_active_time = current_node->last_active_time;
|
|
Packit |
d68d13 |
start_time = current_node->start_time;
|
|
Packit |
d68d13 |
idle_timeout = current_node->cmdopts.idle_timeout;
|
|
Packit |
d68d13 |
proc_lifetime = current_node->cmdopts.proc_lifetime;
|
|
Packit |
d68d13 |
if (((idle_timeout &&
|
|
Packit |
d68d13 |
(apr_time_sec(now) - apr_time_sec(last_active_time) >
|
|
Packit |
d68d13 |
idle_timeout))
|
|
Packit |
d68d13 |
|| (proc_lifetime
|
|
Packit |
d68d13 |
&& (apr_time_sec(now) - apr_time_sec(start_time) >
|
|
Packit |
d68d13 |
proc_lifetime)))
|
|
Packit |
d68d13 |
&& is_kill_allowed(main_server, current_node)) {
|
|
Packit |
d68d13 |
/* Set die reason for log */
|
|
Packit |
d68d13 |
if (idle_timeout &&
|
|
Packit |
d68d13 |
(apr_time_sec(now) - apr_time_sec(last_active_time) >
|
|
Packit |
d68d13 |
idle_timeout))
|
|
Packit |
d68d13 |
current_node->diewhy = FCGID_DIE_IDLE_TIMEOUT;
|
|
Packit |
d68d13 |
else if (proc_lifetime &&
|
|
Packit |
d68d13 |
(apr_time_sec(now) - apr_time_sec(start_time) >
|
|
Packit |
d68d13 |
proc_lifetime))
|
|
Packit |
d68d13 |
current_node->diewhy = FCGID_DIE_LIFETIME_EXPIRED;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Unlink from idle list */
|
|
Packit |
d68d13 |
previous_node->next_index = current_node->next_index;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Link to error list */
|
|
Packit |
d68d13 |
current_node->next_index = error_list_header->next_index;
|
|
Packit |
d68d13 |
error_list_header->next_index = current_node - proc_table;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
else
|
|
Packit |
d68d13 |
previous_node = current_node;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
current_node = next_node;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
static apr_time_t lastbusyscan = 0;
|
|
Packit |
d68d13 |
static void scan_busylist(server_rec * main_server)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
fcgid_procnode *current_node;
|
|
Packit |
d68d13 |
fcgid_procnode *proc_table;
|
|
Packit |
d68d13 |
apr_time_t last_active_time;
|
|
Packit |
d68d13 |
apr_time_t now = apr_time_now();
|
|
Packit |
d68d13 |
fcgid_server_conf *sconf =
|
|
Packit |
d68d13 |
ap_get_module_config(main_server->module_config,
|
|
Packit |
d68d13 |
&fcgid_module);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Should I check the busy list? */
|
|
Packit |
d68d13 |
if (procmgr_must_exit()
|
|
Packit |
d68d13 |
|| apr_time_sec(now) - apr_time_sec(lastbusyscan) <=
|
|
Packit |
d68d13 |
sconf->busy_scan_interval)
|
|
Packit |
d68d13 |
return;
|
|
Packit |
d68d13 |
lastbusyscan = now;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Check busy list */
|
|
Packit |
d68d13 |
proc_table = proctable_get_table_array();
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
proctable_pm_lock(main_server);
|
|
Packit |
d68d13 |
current_node = &proc_table[proctable_get_busy_list()->next_index];
|
|
Packit |
d68d13 |
while (current_node != proc_table) {
|
|
Packit |
d68d13 |
last_active_time = current_node->last_active_time;
|
|
Packit |
d68d13 |
if (apr_time_sec(now) - apr_time_sec(last_active_time) >
|
|
Packit |
d68d13 |
(current_node->cmdopts.busy_timeout)) {
|
|
Packit |
d68d13 |
/* Protocol:
|
|
Packit |
d68d13 |
1. diewhy init with FCGID_DIE_KILLSELF
|
|
Packit |
d68d13 |
2. Process manager set diewhy to FCGID_DIE_BUSY_TIMEOUT and gracefully kill process while busy timeout
|
|
Packit |
d68d13 |
3. Process manager forced kill process while busy timeout and diewhy is FCGID_DIE_BUSY_TIMEOUT
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
if (current_node->diewhy == FCGID_DIE_BUSY_TIMEOUT)
|
|
Packit |
d68d13 |
proc_kill_force(current_node, main_server);
|
|
Packit |
d68d13 |
else {
|
|
Packit |
d68d13 |
current_node->diewhy = FCGID_DIE_BUSY_TIMEOUT;
|
|
Packit |
d68d13 |
proc_kill_gracefully(current_node, main_server);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
current_node = &proc_table[current_node->next_index];
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
static apr_time_t lastzombiescan = 0;
|
|
Packit |
d68d13 |
static void scan_idlelist_zombie(server_rec * main_server)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
/*
|
|
Packit |
d68d13 |
Scan the idle list
|
|
Packit |
d68d13 |
1. pick up the node for scan(now-last_activ>g_zombie_scan_interval)
|
|
Packit |
d68d13 |
2. check if it's zombie process
|
|
Packit |
d68d13 |
3. if it's zombie process, wait() and return to free list
|
|
Packit |
d68d13 |
4. return to idle list if it's not zombie process
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
pid_t thepid;
|
|
Packit |
d68d13 |
fcgid_procnode *previous_node, *current_node, *next_node;
|
|
Packit |
d68d13 |
fcgid_procnode *check_list_header;
|
|
Packit |
d68d13 |
fcgid_procnode *proc_table;
|
|
Packit |
d68d13 |
apr_time_t last_active_time;
|
|
Packit |
d68d13 |
apr_time_t now = apr_time_now();
|
|
Packit |
d68d13 |
fcgid_procnode temp_header;
|
|
Packit |
d68d13 |
fcgid_server_conf *sconf =
|
|
Packit |
d68d13 |
ap_get_module_config(main_server->module_config,
|
|
Packit |
d68d13 |
&fcgid_module);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
memset(&temp_header, 0, sizeof(temp_header));
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Should I check zombie processes in idle list now? */
|
|
Packit |
d68d13 |
if (procmgr_must_exit()
|
|
Packit |
d68d13 |
|| apr_time_sec(now) - apr_time_sec(lastzombiescan) <=
|
|
Packit |
d68d13 |
sconf->zombie_scan_interval)
|
|
Packit |
d68d13 |
return;
|
|
Packit |
d68d13 |
lastzombiescan = now;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/*
|
|
Packit |
d68d13 |
Check the list
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
proc_table = proctable_get_table_array();
|
|
Packit |
d68d13 |
previous_node = proctable_get_idle_list();
|
|
Packit |
d68d13 |
check_list_header = &temp_header;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
proctable_pm_lock(main_server);
|
|
Packit |
d68d13 |
current_node = &proc_table[previous_node->next_index];
|
|
Packit |
d68d13 |
while (current_node != proc_table) {
|
|
Packit |
d68d13 |
next_node = &proc_table[current_node->next_index];
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Is it time for zombie check? */
|
|
Packit |
d68d13 |
last_active_time = current_node->last_active_time;
|
|
Packit |
d68d13 |
if (apr_time_sec(now) - apr_time_sec(last_active_time) >
|
|
Packit |
d68d13 |
sconf->zombie_scan_interval) {
|
|
Packit |
d68d13 |
/* Unlink from idle list */
|
|
Packit |
d68d13 |
previous_node->next_index = current_node->next_index;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Link to check list */
|
|
Packit |
d68d13 |
current_node->next_index = check_list_header->next_index;
|
|
Packit |
d68d13 |
check_list_header->next_index = current_node - proc_table;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
else
|
|
Packit |
d68d13 |
previous_node = current_node;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
current_node = next_node;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/*
|
|
Packit |
d68d13 |
Now check every node in check list
|
|
Packit |
d68d13 |
1) If it's zombie process, wait() and return to free list
|
|
Packit |
d68d13 |
2) If it's not zombie process, link it to the tail of idle list
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
previous_node = check_list_header;
|
|
Packit |
d68d13 |
current_node = &proc_table[previous_node->next_index];
|
|
Packit |
d68d13 |
while (current_node != proc_table) {
|
|
Packit |
d68d13 |
next_node = &proc_table[current_node->next_index];
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Is it zombie process? */
|
|
Packit |
d68d13 |
thepid = current_node->proc_id.pid;
|
|
Packit |
d68d13 |
if (proc_wait_process(main_server, current_node) == APR_CHILD_DONE) {
|
|
Packit |
d68d13 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server,
|
|
Packit |
d68d13 |
"mod_fcgid: cleanup zombie process %"
|
|
Packit |
d68d13 |
APR_PID_T_FMT, thepid);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Unlink from check list */
|
|
Packit |
d68d13 |
previous_node->next_index = current_node->next_index;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Link to free list */
|
|
Packit |
d68d13 |
link_node_to_list(main_server, proctable_get_free_list(),
|
|
Packit |
d68d13 |
current_node, proc_table);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
else
|
|
Packit |
d68d13 |
previous_node = current_node;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
current_node = next_node;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/*
|
|
Packit |
d68d13 |
Now link the check list back to the tail of idle list
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
if (check_list_header->next_index) {
|
|
Packit |
d68d13 |
proctable_pm_lock(main_server);
|
|
Packit |
d68d13 |
previous_node = proctable_get_idle_list();
|
|
Packit |
d68d13 |
current_node = &proc_table[previous_node->next_index];
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Find the tail of idle list */
|
|
Packit |
d68d13 |
while (current_node != proc_table) {
|
|
Packit |
d68d13 |
previous_node = current_node;
|
|
Packit |
d68d13 |
current_node = &proc_table[current_node->next_index];
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Link check list to the tail of idle list */
|
|
Packit |
d68d13 |
previous_node->next_index = check_list_header->next_index;
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
static apr_time_t lasterrorscan = 0;
|
|
Packit |
d68d13 |
static void scan_errorlist(server_rec * main_server)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
/*
|
|
Packit |
d68d13 |
kill() and wait() every node in error list
|
|
Packit |
d68d13 |
put them back to free list after that
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
void *dummy;
|
|
Packit |
d68d13 |
fcgid_procnode *previous_node, *current_node, *next_node;
|
|
Packit |
d68d13 |
apr_time_t now = apr_time_now();
|
|
Packit |
d68d13 |
fcgid_procnode *error_list_header = proctable_get_error_list();
|
|
Packit |
d68d13 |
fcgid_procnode *free_list_header = proctable_get_free_list();
|
|
Packit |
d68d13 |
fcgid_procnode *proc_table = proctable_get_table_array();
|
|
Packit |
d68d13 |
fcgid_procnode temp_error_header;
|
|
Packit |
d68d13 |
fcgid_server_conf *sconf =
|
|
Packit |
d68d13 |
ap_get_module_config(main_server->module_config,
|
|
Packit |
d68d13 |
&fcgid_module);
|
|
Packit |
d68d13 |
int graceful_terminations = 0;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Should I check the busy list? */
|
|
Packit |
d68d13 |
if (procmgr_must_exit()
|
|
Packit |
d68d13 |
|| apr_time_sec(now) - apr_time_sec(lasterrorscan) <=
|
|
Packit |
d68d13 |
sconf->error_scan_interval)
|
|
Packit |
d68d13 |
return;
|
|
Packit |
d68d13 |
lasterrorscan = now;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Try wait dead processes, restore to free list */
|
|
Packit |
d68d13 |
/* Note: I can't keep the lock during the scan */
|
|
Packit |
d68d13 |
proctable_pm_lock(main_server);
|
|
Packit |
d68d13 |
temp_error_header.next_index = error_list_header->next_index;
|
|
Packit |
d68d13 |
error_list_header->next_index = 0;
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
previous_node = &temp_error_header;
|
|
Packit |
d68d13 |
current_node = &proc_table[previous_node->next_index];
|
|
Packit |
d68d13 |
while (current_node != proc_table) {
|
|
Packit |
d68d13 |
next_node = &proc_table[current_node->next_index];
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
if (proc_wait_process(main_server, current_node) != APR_CHILD_NOTDONE) {
|
|
Packit |
d68d13 |
/* Unlink from error list */
|
|
Packit |
d68d13 |
previous_node->next_index = current_node->next_index;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Link to free list */
|
|
Packit |
d68d13 |
current_node->next_index = free_list_header->next_index;
|
|
Packit |
d68d13 |
free_list_header->next_index = current_node - proc_table;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
else
|
|
Packit |
d68d13 |
previous_node = current_node;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
current_node = next_node;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Kill the left processes, wait() them in the next round */
|
|
Packit |
d68d13 |
for (current_node = &proc_table[temp_error_header.next_index];
|
|
Packit |
d68d13 |
current_node != proc_table;
|
|
Packit |
d68d13 |
current_node = &proc_table[current_node->next_index]) {
|
|
Packit |
d68d13 |
/* Try gracefully first */
|
|
Packit |
d68d13 |
dummy = NULL;
|
|
Packit |
d68d13 |
apr_pool_userdata_get(&dummy, HAS_GRACEFUL_KILL,
|
|
Packit |
d68d13 |
current_node->proc_pool);
|
|
Packit |
d68d13 |
if (!dummy) {
|
|
Packit |
d68d13 |
proc_kill_gracefully(current_node, main_server);
|
|
Packit |
d68d13 |
++graceful_terminations;
|
|
Packit |
d68d13 |
apr_pool_userdata_set("set", HAS_GRACEFUL_KILL,
|
|
Packit |
d68d13 |
apr_pool_cleanup_null,
|
|
Packit |
d68d13 |
current_node->proc_pool);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
else {
|
|
Packit |
d68d13 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server,
|
|
Packit |
d68d13 |
"mod_fcgid: process %" APR_PID_T_FMT
|
|
Packit |
d68d13 |
" graceful kill fail, sending SIGKILL",
|
|
Packit |
d68d13 |
current_node->proc_id.pid);
|
|
Packit |
d68d13 |
proc_kill_force(current_node, main_server);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Link the temp error list back */
|
|
Packit |
d68d13 |
proctable_pm_lock(main_server);
|
|
Packit |
d68d13 |
/* Find the tail of error list */
|
|
Packit |
d68d13 |
previous_node = error_list_header;
|
|
Packit |
d68d13 |
current_node = &proc_table[previous_node->next_index];
|
|
Packit |
d68d13 |
while (current_node != proc_table) {
|
|
Packit |
d68d13 |
previous_node = current_node;
|
|
Packit |
d68d13 |
current_node = &proc_table[current_node->next_index];
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
previous_node->next_index = temp_error_header.next_index;
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
if (graceful_terminations) {
|
|
Packit |
d68d13 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server,
|
|
Packit |
d68d13 |
"mod_fcgid: gracefully terminated %d processes",
|
|
Packit |
d68d13 |
graceful_terminations);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
typedef enum action_t {DO_NOTHING, KILL_GRACEFULLY, KILL_FORCEFULLY,
|
|
Packit |
d68d13 |
HARD_WAIT} action_t;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
static int reclaim_one_pid(server_rec *main_server, fcgid_procnode *proc,
|
|
Packit |
d68d13 |
action_t action)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
int exitcode;
|
|
Packit |
d68d13 |
apr_exit_why_e exitwhy;
|
|
Packit |
d68d13 |
apr_wait_how_e wait_how = action == HARD_WAIT ? APR_WAIT : APR_NOWAIT;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
if (apr_proc_wait(&proc->proc_id, &exitcode, &exitwhy,
|
|
Packit |
d68d13 |
wait_how) != APR_CHILD_NOTDONE) {
|
|
Packit |
d68d13 |
proc->diewhy = FCGID_DIE_SHUTDOWN;
|
|
Packit |
d68d13 |
proc_print_exit_info(proc, exitcode, exitwhy,
|
|
Packit |
d68d13 |
main_server);
|
|
Packit |
d68d13 |
proc->proc_pool = NULL;
|
|
Packit |
d68d13 |
return 1;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
switch(action) {
|
|
Packit |
d68d13 |
case DO_NOTHING:
|
|
Packit |
d68d13 |
case HARD_WAIT:
|
|
Packit |
d68d13 |
break;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
case KILL_GRACEFULLY:
|
|
Packit |
d68d13 |
proc_kill_gracefully(proc, main_server);
|
|
Packit |
d68d13 |
break;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
case KILL_FORCEFULLY:
|
|
Packit |
d68d13 |
ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
|
|
Packit |
d68d13 |
"FastCGI process %" APR_PID_T_FMT
|
|
Packit |
d68d13 |
" still did not exit, "
|
|
Packit |
d68d13 |
"terminating forcefully",
|
|
Packit |
d68d13 |
proc->proc_id.pid);
|
|
Packit |
d68d13 |
proc_kill_force(proc, main_server);
|
|
Packit |
d68d13 |
break;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
return 0;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
static void kill_all_subprocess(server_rec *main_server)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
apr_time_t waittime = 1024 * 16;
|
|
Packit |
d68d13 |
size_t i, table_size = proctable_get_table_size();
|
|
Packit |
d68d13 |
int not_dead_yet;
|
|
Packit |
d68d13 |
int cur_action, next_action;
|
|
Packit |
d68d13 |
apr_time_t starttime = apr_time_now();
|
|
Packit |
d68d13 |
struct {
|
|
Packit |
d68d13 |
action_t action;
|
|
Packit |
d68d13 |
apr_time_t action_time;
|
|
Packit |
d68d13 |
} action_table[] = {
|
|
Packit |
d68d13 |
{DO_NOTHING, 0}, /* dummy entry for iterations where
|
|
Packit |
d68d13 |
* we reap children but take no action
|
|
Packit |
d68d13 |
* against stragglers
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
{KILL_GRACEFULLY, apr_time_from_sec(0)},
|
|
Packit |
d68d13 |
{KILL_GRACEFULLY, apr_time_from_sec(1)},
|
|
Packit |
d68d13 |
{KILL_FORCEFULLY, apr_time_from_sec(8)},
|
|
Packit |
d68d13 |
{HARD_WAIT, apr_time_from_sec(8)}
|
|
Packit |
d68d13 |
};
|
|
Packit |
d68d13 |
fcgid_procnode *proc_table = proctable_get_table_array();
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
next_action = 1;
|
|
Packit |
d68d13 |
do {
|
|
Packit |
d68d13 |
apr_sleep(waittime);
|
|
Packit |
d68d13 |
/* don't let waittime get longer than 1 second; otherwise, we don't
|
|
Packit |
d68d13 |
* react quickly to the last child exiting, and taking action can
|
|
Packit |
d68d13 |
* be delayed
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
waittime = waittime * 4;
|
|
Packit |
d68d13 |
if (waittime > apr_time_from_sec(1)) {
|
|
Packit |
d68d13 |
waittime = apr_time_from_sec(1);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* see what action to take, if any */
|
|
Packit |
d68d13 |
if (action_table[next_action].action_time <= apr_time_now() - starttime) {
|
|
Packit |
d68d13 |
cur_action = next_action;
|
|
Packit |
d68d13 |
++next_action;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
else {
|
|
Packit |
d68d13 |
cur_action = 0; /* index of DO_NOTHING entry */
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* now see who is done */
|
|
Packit |
d68d13 |
not_dead_yet = 0;
|
|
Packit |
d68d13 |
for (i = 0; i < table_size; i++) {
|
|
Packit |
d68d13 |
if (!proc_table[i].proc_pool) {
|
|
Packit |
d68d13 |
continue; /* unused */
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
if (!reclaim_one_pid(main_server, &proc_table[i],
|
|
Packit |
d68d13 |
action_table[cur_action].action)) {
|
|
Packit |
d68d13 |
++not_dead_yet;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
} while (not_dead_yet &&
|
|
Packit |
d68d13 |
action_table[cur_action].action != HARD_WAIT);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* This should be proposed as a stand-alone improvement to the httpd module,
|
|
Packit |
d68d13 |
* either in the arch/ platform-specific modules or util_script.c from whence
|
|
Packit |
d68d13 |
* it came.
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
static void default_proc_env(apr_table_t * e)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
const char *env_temp;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
if (!(env_temp = getenv("PATH"))) {
|
|
Packit |
d68d13 |
env_temp = DEFAULT_PATH;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
apr_table_addn(e, "PATH", env_temp);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
#ifdef WIN32
|
|
Packit |
d68d13 |
if ((env_temp = getenv("SYSTEMROOT"))) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "SYSTEMROOT", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
if ((env_temp = getenv("COMSPEC"))) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "COMSPEC", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
if ((env_temp = getenv("PATHEXT"))) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "PATHEXT", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
if ((env_temp = getenv("WINDIR"))) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "WINDIR", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
#elif defined(OS2)
|
|
Packit |
d68d13 |
if ((env_temp = getenv("COMSPEC")) != NULL) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "COMSPEC", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
if ((env_temp = getenv("ETC")) != NULL) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "ETC", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
if ((env_temp = getenv("DPATH")) != NULL) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "DPATH", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
if ((env_temp = getenv("PERLLIB_PREFIX")) != NULL) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "PERLLIB_PREFIX", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
#elif defined(BEOS)
|
|
Packit |
d68d13 |
if ((env_temp = getenv("LIBRARY_PATH")) != NULL) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "LIBRARY_PATH", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
#elif defined (AIX)
|
|
Packit |
d68d13 |
if ((env_temp = getenv("LIBPATH"))) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "LIBPATH", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
#else
|
|
Packit |
d68d13 |
/* DARWIN, HPUX vary depending on circumstance */
|
|
Packit |
d68d13 |
#if defined (DARWIN)
|
|
Packit |
d68d13 |
if ((env_temp = getenv("DYLD_LIBRARY_PATH"))) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "DYLD_LIBRARY_PATH", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
#elif defined (HPUX11) || defined (HPUX10) || defined (HPUX)
|
|
Packit |
d68d13 |
if ((env_temp = getenv("SHLIB_PATH"))) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "SHLIB_PATH", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
#endif
|
|
Packit |
d68d13 |
if ((env_temp = getenv("LD_LIBRARY_PATH"))) {
|
|
Packit |
d68d13 |
apr_table_addn(e, "LD_LIBRARY_PATH", env_temp);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
#endif
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* End of common to util_script.c */
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
static void
|
|
Packit |
d68d13 |
fastcgi_spawn(fcgid_command * command, server_rec * main_server,
|
|
Packit |
d68d13 |
apr_pool_t * configpool)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
fcgid_procnode *free_list_header, *proctable_array,
|
|
Packit |
d68d13 |
*procnode, *idle_list_header;
|
|
Packit |
d68d13 |
fcgid_proc_info procinfo;
|
|
Packit |
d68d13 |
apr_status_t rv;
|
|
Packit |
d68d13 |
int i;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
free_list_header = proctable_get_free_list();
|
|
Packit |
d68d13 |
idle_list_header = proctable_get_idle_list();
|
|
Packit |
d68d13 |
proctable_array = proctable_get_table_array();
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Apply a slot from free list */
|
|
Packit |
d68d13 |
proctable_pm_lock(main_server);
|
|
Packit |
d68d13 |
if (free_list_header->next_index == 0) {
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, main_server,
|
|
Packit |
d68d13 |
"mod_fcgid: too much processes, please increase FCGID_MAX_APPLICATION");
|
|
Packit |
d68d13 |
return;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
procnode = &proctable_array[free_list_header->next_index];
|
|
Packit |
d68d13 |
free_list_header->next_index = procnode->next_index;
|
|
Packit |
d68d13 |
procnode->next_index = 0;
|
|
Packit |
d68d13 |
proctable_pm_unlock(main_server);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Prepare to spawn */
|
|
Packit |
d68d13 |
procnode->deviceid = command->deviceid;
|
|
Packit |
d68d13 |
procnode->inode = command->inode;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* no truncation should ever occur */
|
|
Packit |
d68d13 |
AP_DEBUG_ASSERT(sizeof procnode->cmdline > strlen(command->cmdline));
|
|
Packit |
d68d13 |
apr_cpystrn(procnode->cmdline, command->cmdline, sizeof procnode->cmdline);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
procnode->vhost_id = command->vhost_id;
|
|
Packit |
d68d13 |
procnode->uid = command->uid;
|
|
Packit |
d68d13 |
procnode->gid = command->gid;
|
|
Packit |
d68d13 |
procnode->start_time = procnode->last_active_time = apr_time_now();
|
|
Packit |
d68d13 |
procnode->requests_handled = 0;
|
|
Packit |
d68d13 |
procnode->diewhy = FCGID_DIE_KILLSELF;
|
|
Packit |
d68d13 |
procnode->proc_pool = NULL;
|
|
Packit |
d68d13 |
procnode->cmdopts = command->cmdopts;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
procinfo.cgipath = command->cgipath;
|
|
Packit |
d68d13 |
procinfo.configpool = configpool;
|
|
Packit |
d68d13 |
procinfo.main_server = main_server;
|
|
Packit |
d68d13 |
procinfo.uid = command->uid;
|
|
Packit |
d68d13 |
procinfo.gid = command->gid;
|
|
Packit |
d68d13 |
procinfo.userdir = command->userdir;
|
|
Packit |
d68d13 |
if ((rv =
|
|
Packit |
d68d13 |
apr_pool_create(&procnode->proc_pool, configpool)) != APR_SUCCESS
|
|
Packit |
d68d13 |
|| (procinfo.proc_environ =
|
|
Packit |
d68d13 |
apr_table_make(procnode->proc_pool, INITENV_CNT)) == NULL) {
|
|
Packit |
d68d13 |
/* Link the node back to free list in this case */
|
|
Packit |
d68d13 |
if (procnode->proc_pool)
|
|
Packit |
d68d13 |
apr_pool_destroy(procnode->proc_pool);
|
|
Packit |
d68d13 |
link_node_to_list(main_server, free_list_header, procnode,
|
|
Packit |
d68d13 |
proctable_array);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, main_server,
|
|
Packit |
d68d13 |
"mod_fcgid: can't create pool for process");
|
|
Packit |
d68d13 |
return;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
/* Set up longer, system defaults before falling into parsing fixed-limit
|
|
Packit |
d68d13 |
* request-by-request variables, so if any are overriden, they preempt
|
|
Packit |
d68d13 |
* any system default assumptions
|
|
Packit |
d68d13 |
*/
|
|
Packit |
d68d13 |
default_proc_env(procinfo.proc_environ);
|
|
Packit |
d68d13 |
for (i = 0; i < INITENV_CNT; i++) {
|
|
Packit |
d68d13 |
if (command->cmdenv.initenv_key[i][0] == '\0')
|
|
Packit |
d68d13 |
break;
|
|
Packit |
d68d13 |
apr_table_set(procinfo.proc_environ, command->cmdenv.initenv_key[i],
|
|
Packit |
d68d13 |
command->cmdenv.initenv_val[i]);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Spawn the process now */
|
|
Packit |
d68d13 |
/* XXX Spawn uses wrapper_cmdline, but log uses cgipath ? */
|
|
Packit |
d68d13 |
if ((rv =
|
|
Packit |
d68d13 |
proc_spawn_process(command->cmdline, &procinfo,
|
|
Packit |
d68d13 |
procnode)) != APR_SUCCESS) {
|
|
Packit |
d68d13 |
ap_log_error(APLOG_MARK, APLOG_WARNING, rv, main_server,
|
|
Packit |
d68d13 |
"mod_fcgid: spawn process %s error", command->cgipath);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
apr_pool_destroy(procnode->proc_pool);
|
|
Packit |
d68d13 |
link_node_to_list(main_server, free_list_header,
|
|
Packit |
d68d13 |
procnode, proctable_array);
|
|
Packit |
d68d13 |
return;
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
else {
|
|
Packit |
d68d13 |
/* The job done */
|
|
Packit |
d68d13 |
link_node_to_list(main_server, idle_list_header,
|
|
Packit |
d68d13 |
procnode, proctable_array);
|
|
Packit |
d68d13 |
ap_log_error(APLOG_MARK, APLOG_INFO, 0, main_server,
|
|
Packit |
d68d13 |
"mod_fcgid: server %s:%s(%" APR_PID_T_FMT ") started",
|
|
Packit |
d68d13 |
command->server_hostname[0] ?
|
|
Packit |
d68d13 |
command->server_hostname : "(unknown)",
|
|
Packit |
d68d13 |
command->cgipath,
|
|
Packit |
d68d13 |
procnode->proc_id.pid);
|
|
Packit |
d68d13 |
register_spawn(main_server, procnode);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
apr_status_t pm_main(server_rec * main_server, apr_pool_t * configpool)
|
|
Packit |
d68d13 |
{
|
|
Packit |
d68d13 |
fcgid_command command;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
while (1) {
|
|
Packit |
d68d13 |
if (procmgr_must_exit())
|
|
Packit |
d68d13 |
break;
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Wait for command */
|
|
Packit |
d68d13 |
if (procmgr_fetch_cmd(&command, main_server) == APR_SUCCESS) {
|
|
Packit |
d68d13 |
if (is_spawn_allowed(main_server, &command))
|
|
Packit |
d68d13 |
fastcgi_spawn(&command, main_server, configpool);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
procmgr_finish_notify(main_server);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Move matched node to error list */
|
|
Packit |
d68d13 |
scan_idlelist_zombie(main_server);
|
|
Packit |
d68d13 |
scan_idlelist(main_server);
|
|
Packit |
d68d13 |
scan_busylist(main_server);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Kill() and wait() nodes in error list */
|
|
Packit |
d68d13 |
scan_errorlist(main_server);
|
|
Packit |
d68d13 |
}
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
/* Stop all processes */
|
|
Packit |
d68d13 |
kill_all_subprocess(main_server);
|
|
Packit |
d68d13 |
|
|
Packit |
d68d13 |
return APR_SUCCESS;
|
|
Packit |
d68d13 |
}
|