blob: b4e4eff98c6bd3c5353437fe60d610dd9b832590 [file] [log] [blame]
/*
* 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 "apr_thread_proc.h"
#include "apr_strings.h"
#include "apr_queue.h"
#include "fcgid_pm.h"
#include "fcgid_pm_main.h"
#include "fcgid_conf.h"
#include "fcgid_spawn_ctl.h"
#define FCGID_MSGQUEUE_SIZE 10
static apr_thread_t *g_thread = NULL;
static apr_queue_t *g_msgqueue = NULL;
static apr_queue_t *g_notifyqueue = NULL;
static apr_thread_mutex_t *g_reqlock = NULL;
static apr_thread_t *g_wakeup_thread = NULL;
static int g_must_exit = 0;
static int g_wakeup_timeout = 0;
static void *APR_THREAD_FUNC wakeup_thread(apr_thread_t * thd, void *data)
{
while (!g_must_exit) {
/* Wake up every second to check g_must_exit flag */
int i;
for (i = 0; i < g_wakeup_timeout; i++) {
if (g_must_exit)
break;
apr_sleep(apr_time_from_sec(1));
}
/* Send a wake up message to procmgr_peek_cmd() */
if (!g_must_exit && g_msgqueue)
apr_queue_trypush(g_msgqueue, NULL);
}
return NULL;
}
static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *data)
{
server_rec *main_server = data;
pm_main(main_server, main_server->process->pconf);
return NULL;
}
apr_status_t procmgr_pre_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp)
{
return APR_SUCCESS;
}
apr_status_t
procmgr_post_config(server_rec * main_server, apr_pool_t * pconf)
{
apr_status_t rv;
fcgid_server_conf *sconf = ap_get_module_config(main_server->module_config,
&fcgid_module);
/* Initialize spawn controler */
spawn_control_init(main_server, pconf);
/* Create a message queues */
if ((rv = apr_queue_create(&g_msgqueue, FCGID_MSGQUEUE_SIZE,
pconf)) != APR_SUCCESS
|| (rv = apr_queue_create(&g_notifyqueue, FCGID_MSGQUEUE_SIZE,
pconf)) != APR_SUCCESS) {
/* Fatal error */
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server,
"mod_fcgid: can't create message queue");
exit(1);
}
/* Create request lock */
if ((rv = apr_thread_mutex_create(&g_reqlock,
APR_THREAD_MUTEX_DEFAULT,
pconf)) != APR_SUCCESS) {
/* Fatal error */
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server,
"mod_fcgid: Can't create request mutex");
exit(1);
}
/* Calculate procmgr_peek_cmd wake up interval */
g_wakeup_timeout = min(sconf->error_scan_interval,
sconf->busy_scan_interval);
g_wakeup_timeout = min(sconf->idle_scan_interval,
g_wakeup_timeout);
if (g_wakeup_timeout == 0)
g_wakeup_timeout = 1; /* Make it reasonable */
/* Create process manager worker thread */
if ((rv = apr_thread_create(&g_thread, NULL, worker_thread,
main_server, pconf)) != APR_SUCCESS) {
/* It's a fatal error */
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server,
"mod_fcgid: can't create process manager thread");
exit(1);
}
/* Create wake up thread */
/* XXX If there was a function such like apr_queue_pop_timedwait(),
then I don't need such an ugly thread to do the wake up job */
if ((rv = apr_thread_create(&g_wakeup_thread, NULL, wakeup_thread,
NULL, pconf)) != APR_SUCCESS) {
/* It's a fatal error */
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server,
"mod_fcgid: can't create wake up thread");
exit(1);
}
return APR_SUCCESS;
}
void procmgr_init_spawn_cmd(fcgid_command * command, request_rec * r,
const char *argv0, dev_t deviceid,
apr_ino_t inode, apr_size_t share_grp_id)
{
fcgid_wrapper_conf *wrapperconf;
const char *cmd_to_spawn;
memset(command, 0, sizeof(*command));
apr_cpystrn(command->cgipath, argv0, _POSIX_PATH_MAX);
command->deviceid = deviceid;
command->inode = inode;
command->share_grp_id = share_grp_id;
command->uid = (uid_t) - 1;
command->gid = (gid_t) - 1;
command->userdir = 0;
command->virtualhost = r->server->server_hostname;
/* Update fcgid_command with wrapper info */
command->wrapper_cmdline[0] = '\0';
if ((wrapperconf = get_wrapper_info(argv0, r))) {
apr_cpystrn(command->wrapper_cmdline, wrapperconf->args, _POSIX_PATH_MAX);
command->deviceid = wrapperconf->deviceid;
command->inode = wrapperconf->inode;
command->share_grp_id = wrapperconf->share_group_id;
cmd_to_spawn = wrapperconf->exe;
}
else {
cmd_to_spawn = command->cgipath;
}
get_cmd_options(r, cmd_to_spawn, &command->cmdopts, &command->cmdenv);
}
apr_status_t procmgr_post_spawn_cmd(fcgid_command * command,
request_rec * r)
{
if (g_thread && g_msgqueue && !g_must_exit
&& g_reqlock && g_notifyqueue) {
apr_status_t rv;
/*
Prepare the message send to another thread
destroy the message if I can't push to message
*/
fcgid_command *postcmd =
(fcgid_command *) malloc(sizeof(fcgid_command));
if (!postcmd)
return APR_ENOMEM;
memcpy(postcmd, command, sizeof(*command));
/* Get request lock first */
if ((rv = apr_thread_mutex_lock(g_reqlock)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r,
"mod_fcgid: can't get request lock");
return rv;
}
/* Try push the message */
if ((rv = apr_queue_push(g_msgqueue, postcmd)) != APR_SUCCESS) {
apr_thread_mutex_unlock(g_reqlock);
free(postcmd);
ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r,
"mod_fcgid: can't push request message");
return rv;
} else {
/* Wait the respond from process manager */
char *notifybyte = NULL;
if ((rv =
apr_queue_pop(g_notifyqueue,
&notifybyte)) != APR_SUCCESS) {
apr_thread_mutex_unlock(g_reqlock);
ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r,
"mod_fcgid: can't pop notify message");
return rv;
}
}
/* Release the lock now */
if ((rv = apr_thread_mutex_unlock(g_reqlock)) != APR_SUCCESS) {
/* It's a fatal error */
ap_log_rerror(APLOG_MARK, APLOG_EMERG, rv, r,
"mod_fcgid: can't release request lock");
exit(1);
}
}
return APR_SUCCESS;
}
apr_status_t procmgr_finish_notify(server_rec * main_server)
{
apr_status_t rv;
char *notify = NULL;
if ((rv = apr_queue_push(g_notifyqueue, notify)) != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_EMERG, rv, main_server,
"mod_fcgid: can't send spawn notify");
}
return rv;
}
apr_status_t procmgr_peek_cmd(fcgid_command * command,
server_rec * main_server)
{
apr_status_t rv = APR_SUCCESS;
fcgid_command *peakcmd = NULL;
if (!g_must_exit && g_msgqueue) {
if (apr_queue_pop(g_msgqueue, &peakcmd) == APR_SUCCESS) {
if (!peakcmd)
return APR_TIMEUP; /* This a wake up message */
else {
/* Copy the command, and then free the memory */
memcpy(command, peakcmd, sizeof(*peakcmd));
free(peakcmd);
return APR_SUCCESS;
}
}
}
return APR_TIMEUP;
}
apr_status_t
procmgr_child_init(server_rec * main_server, apr_pool_t * pchild)
{
apr_pool_cleanup_register(pchild, main_server,
procmgr_stop_procmgr, apr_pool_cleanup_null);
return APR_SUCCESS;
}
int procmgr_must_exit()
{
return g_must_exit;
}
apr_status_t procmgr_stop_procmgr(void *server)
{
apr_status_t status;
/* Tell the world to die */
g_must_exit = 1;
if (g_msgqueue)
apr_queue_push(g_msgqueue, NULL);
/* Wait */
if (g_thread && apr_thread_join(&status, g_thread) == APR_SUCCESS) {
/* Free the memory left in queue */
fcgid_command *peakcmd = NULL;
while (apr_queue_trypop(g_msgqueue, &peakcmd) == APR_SUCCESS) {
if (peakcmd)
free(peakcmd);
}
}
if (g_wakeup_thread)
return apr_thread_join(&status, g_wakeup_thread);
return APR_SUCCESS;
}