#include <sys_defs.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#include <stdarg.h>
#include <msg.h>
#include <binhash.h>
#include <mymalloc.h>
#include <events.h>
#include <argv.h>
#include <syslog.h>
#include "master_proto.h"
#include "master.h"
BINHASH *master_child_table;
static void master_unthrottle(MASTER_SERV *serv);
static void master_unthrottle_wrapper(int unused_event, char *ptr)
{
MASTER_SERV *serv = (MASTER_SERV *) ptr;
master_unthrottle(serv);
}
static void master_unthrottle(MASTER_SERV *serv)
{
if ((serv->flags & MASTER_FLAG_THROTTLE) != 0) {
serv->flags &= ~MASTER_FLAG_THROTTLE;
event_cancel_timer(master_unthrottle_wrapper, (char *) serv);
if (msg_verbose)
msg_info("throttle released for command %s", serv->path);
master_avail_listen(serv);
}
}
static void master_throttle(MASTER_SERV *serv)
{
if ((serv->flags & MASTER_FLAG_THROTTLE) == 0) {
serv->flags |= MASTER_FLAG_THROTTLE;
event_request_timer(master_unthrottle_wrapper, (char *) serv,
serv->throttle_delay);
if (msg_verbose)
msg_info("throttling command %s", serv->path);
}
}
void master_spawn(MASTER_SERV *serv)
{
char *myname = "master_spawn";
MASTER_PROC *proc;
MASTER_PID pid;
int n;
if (master_child_table == 0)
master_child_table = binhash_create(0);
if (!MASTER_LIMIT_OK(serv->max_proc, serv->total_proc))
msg_panic("%s: at process limit %d", myname, serv->total_proc);
if (serv->avail_proc > 0)
msg_panic("%s: processes available: %d", myname, serv->avail_proc);
if (serv->flags & MASTER_FLAG_THROTTLE)
msg_panic("%s: throttled service: %s", myname, serv->path);
switch (pid = fork()) {
case -1:
msg_warn("%s: fork: %m -- throttling", myname);
master_throttle(serv);
return;
case 0:
msg_cleanup((void (*) (void)) 0);
closelog();
if (master_flow_pipe[0] <= MASTER_FLOW_READ)
msg_fatal("%s: flow pipe read descriptor <= %d",
myname, MASTER_FLOW_READ);
if (DUP2(master_flow_pipe[0], MASTER_FLOW_READ) < 0)
msg_fatal("%s: dup2: %m", myname);
if (close(master_flow_pipe[0]) < 0)
msg_fatal("close %d: %m", master_flow_pipe[0]);
if (master_flow_pipe[1] <= MASTER_FLOW_WRITE)
msg_fatal("%s: flow pipe read descriptor <= %d",
myname, MASTER_FLOW_WRITE);
if (DUP2(master_flow_pipe[1], MASTER_FLOW_WRITE) < 0)
msg_fatal("%s: dup2: %m", myname);
if (close(master_flow_pipe[1]) < 0)
msg_fatal("close %d: %m", master_flow_pipe[1]);
close(serv->status_fd[0]);
if (serv->status_fd[1] <= MASTER_STATUS_FD)
msg_fatal("%s: status file descriptor collision", myname);
if (DUP2(serv->status_fd[1], MASTER_STATUS_FD) < 0)
msg_fatal("%s: dup2 status_fd: %m", myname);
(void) close(serv->status_fd[1]);
for (n = 0; n < serv->listen_fd_count; n++) {
if (serv->listen_fd[n] <= MASTER_LISTEN_FD + n)
msg_fatal("%s: listen file descriptor collision", myname);
if (DUP2(serv->listen_fd[n], MASTER_LISTEN_FD + n) < 0)
msg_fatal("%s: dup2 listen_fd %d: %m",
myname, serv->listen_fd[n]);
(void) close(serv->listen_fd[n]);
}
execvp(serv->path, serv->args->argv);
msg_fatal("%s: exec %s: %m", myname, serv->path);
exit(1);
default:
if (msg_verbose)
msg_info("spawn command %s; pid %d", serv->path, pid);
proc = (MASTER_PROC *) mymalloc(sizeof(MASTER_PROC));
proc->serv = serv;
proc->pid = pid;
proc->use_count = 0;
proc->avail = 0;
binhash_enter(master_child_table, (char *) &pid,
sizeof(pid), (char *) proc);
serv->total_proc++;
master_avail_more(serv, proc);
if (serv->flags & MASTER_FLAG_CONDWAKE) {
serv->flags &= ~MASTER_FLAG_CONDWAKE;
master_wakeup_init(serv);
if (msg_verbose)
msg_info("start conditional timer for %s", serv->name);
}
return;
}
}
static void master_delete_child(MASTER_PROC *proc)
{
MASTER_SERV *serv;
serv = proc->serv;
serv->total_proc--;
if (proc->avail == MASTER_STAT_AVAIL)
master_avail_less(serv, proc);
else if (MASTER_LIMIT_OK(serv->max_proc, serv->total_proc)
&& serv->avail_proc < 1)
master_avail_listen(serv);
binhash_delete(master_child_table, (char *) &proc->pid,
sizeof(proc->pid), (void (*) (char *)) 0);
myfree((char *) proc);
}
void master_reap_child(void)
{
MASTER_SERV *serv;
MASTER_PROC *proc;
MASTER_PID pid;
WAIT_STATUS_T status;
while ((pid = waitpid((pid_t) - 1, &status, WNOHANG)) > 0) {
if (msg_verbose)
msg_info("master_reap_child: pid %d", pid);
if ((proc = (MASTER_PROC *) binhash_find(master_child_table,
(char *) &pid, sizeof(pid))) == 0) {
msg_panic("master_reap: unknown pid: %d", pid);
continue;
}
serv = proc->serv;
if (!NORMAL_EXIT_STATUS(status)) {
if (WIFEXITED(status))
msg_warn("process %s pid %d exit status %d",
serv->path, pid, WEXITSTATUS(status));
if (WIFSIGNALED(status))
msg_warn("process %s pid %d killed by signal %d",
serv->path, pid, WTERMSIG(status));
}
if (!NORMAL_EXIT_STATUS(status) && proc->use_count == 0
&& (serv->flags & MASTER_FLAG_THROTTLE) == 0) {
msg_warn("%s: bad command startup -- throttling", serv->path);
master_throttle(serv);
}
master_delete_child(proc);
}
}
void master_delete_children(MASTER_SERV *serv)
{
BINHASH_INFO **list;
BINHASH_INFO **info;
MASTER_PROC *proc;
master_throttle(serv);
for (info = list = binhash_list(master_child_table); *info; info++) {
proc = (MASTER_PROC *) info[0]->value;
if (proc->serv == serv)
(void) kill(proc->pid, SIGTERM);
}
while (serv->total_proc > 0)
master_reap_child();
myfree((char *) list);
master_unthrottle(serv);
}