Quelle master_spawn.c
Sprache: C
/*++
/* NAME
/* master_spawn 3
/* SUMMARY
/* Postfix master - child process birth and death
/* SYNOPSIS
/* #include "master.h"
/*
/* void master_spawn(serv)
/* MASTER_SERV *serv;
/*
/* void master_reap_child()
/*
/* void master_delete_children(serv)
/* MASTER_SERV *serv;
/* DESCRIPTION
/* This module creates and cleans up child processes, and applies
/* a process creation throttle in case of serious trouble.
/* This module is the working horse for the master_avail(3) process
/* creation policy module.
/*
/* master_spawn() spawns off a child process for the specified service,
/* making the child process available for servicing connection requests.
/* It is an error to call this function then the specified service is
/* throttled.
/*
/* master_reap_child() cleans up all dead child processes. One typically
/* runs this function at a convenient moment after receiving a SIGCHLD
/* signal. When a child process terminates abnormally after being used
/* for the first time, process creation for that service is throttled
/* for a configurable amount of time.
/*
/* master_delete_children() deletes all child processes that provide
/* the named service. Upon exit, the process creation throttle for that
/* service is released.
/* DIAGNOSTICS
/* Panic: interface violations, internal inconsistencies.
/* Fatal errors: out of memory. Warnings: throttle on/off.
/* BUGS
/* SEE ALSO
/* master_avail(3), process creation policy.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System libraries. */
#include <sys_defs.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h> /* closelog() */
#include <signal.h>
#include <stdarg.h>
#include <syslog.h>
/* Utility libraries. */
#include <msg.h>
#include <binhash.h>
#include <mymalloc.h>
#include <events.h>
#include <vstring.h>
#include <argv.h>
/* Global library. */
#include <mail_conf.h>
/* Application-specific. */
#include "master_proto.h"
#include "master.h"
BINHASH *master_child_table;
static void master_unthrottle(MASTER_SERV *serv);
/* master_unthrottle_wrapper - in case (char *) != (struct *) */
static void master_unthrottle_wrapper(int unused_event, void *ptr)
{
MASTER_SERV *serv = (MASTER_SERV *) ptr;
/*
* This routine runs after expiry of the timer set in master_throttle ( ) ,
* which gets called when it appears that the world is falling apart .
*/
master_unthrottle(serv);
}
/* master_unthrottle - enable process creation */
static void master_unthrottle(MASTER_SERV *serv)
{
/*
* Enable process creation within this class . Disable the " unthrottle "
* timer just in case we ' re being called directly from the cleanup
* routine , instead of from the event manager .
*/
if ((serv->flags & MASTER_FLAG_THROTTLE) != 0 ) {
serv->flags &= ~MASTER_FLAG_THROTTLE;
event_cancel_timer(master_unthrottle_wrapper, (void *) serv);
if (msg_verbose)
msg_info("throttle released for command %s" , serv->path);
master_avail_listen(serv);
}
}
/* master_throttle - suspend process creation */
static void master_throttle(MASTER_SERV *serv)
{
/*
* Perhaps the command to be run is defective , perhaps some configuration
* is wrong , or perhaps the system is out of resources . Disable further
* process creation attempts for a while .
*/
if ((serv->flags & MASTER_FLAG_THROTTLE) == 0 ) {
serv->flags |= MASTER_FLAG_THROTTLE;
event_request_timer(master_unthrottle_wrapper, (void *) serv,
serv->throttle_delay);
if (msg_verbose)
msg_info("throttling command %s" , serv->path);
master_avail_listen(serv);
}
}
/* master_spawn - spawn off new child process if we can */
void master_spawn(MASTER_SERV *serv)
{
const char *myname = "master_spawn" ;
MASTER_PROC *proc;
MASTER_PID pid;
int n;
static unsigned master_generation = 0 ;
static VSTRING *env_gen = 0 ;
if (master_child_table == 0 )
master_child_table = binhash_create(0 );
if (env_gen == 0 )
env_gen = vstring_alloc(100 );
/*
* Sanity checks . The master_avail module is supposed to know what it is
* doing .
*/
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);
/*
* Create a child process and connect parent and child via the status
* pipe .
*/
master_generation += 1 ;
switch (pid = fork()) {
/*
* Error . We ' re out of some essential resource . Best recourse is to
* try again later .
*/
case -1 :
msg_warn("%s: fork: %m -- throttling" , myname);
master_throttle(serv);
return ;
/*
* Child process . Redirect child stdin / stdout to the parent - child
* connection and run the requested command . Leave child stderr
* alone . Disable exit handlers : they should be executed by the
* parent only .
*
* When we reach the process limit on a public internet service , we
* create stress - mode processes until the process count stays below
* the limit for some amount of time . See master_avail_listen ( ) .
*/
case 0 :
msg_cleanup((void (*) (void )) 0 ); /* disable exit handler */
closelog(); /* avoid filedes leak */
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 ]); /* status channel */
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]);
}
vstring_sprintf(env_gen, "%s=%o" , MASTER_GEN_NAME, master_generation);
if (putenv(vstring_str(env_gen)) < 0 )
msg_fatal("%s: putenv: %m" , myname);
if (serv->stress_param_val && serv->stress_expire_time > event_time())
serv->stress_param_val[0 ] = CONFIG_BOOL_YES[0 ];
execvp(serv->path, serv->args->argv);
msg_fatal("%s: exec %s: %m" , myname, serv->path);
/* NOTREACHED */
/*
* Parent . Fill in a process member data structure and set up links
* between child and process . Say this process has become available .
* If this service has a wakeup timer that is turned on only when the
* service is actually used , turn on the wakeup timer .
*/
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->gen = master_generation;
proc->use_count = 0 ;
proc->avail = 0 ;
binhash_enter(master_child_table, (void *) &pid,
sizeof (pid), (void *) 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 ;
}
}
/* master_delete_child - destroy child process info */
static void master_delete_child(MASTER_PROC *proc)
{
MASTER_SERV *serv;
/*
* Undo the things that master_spawn did . Stop the process if it still
* exists , and remove it from the lookup tables . Update the number of
* available processes .
*/
serv = proc->serv;
serv->total_proc--;
if (proc->avail == MASTER_STAT_AVAIL)
master_avail_less(serv, proc);
else
master_avail_listen(serv);
binhash_delete(master_child_table, (void *) &proc->pid,
sizeof (proc->pid), (void (*) (void *)) 0 );
myfree((void *) proc);
}
/* master_reap_child - reap dead children */
void master_reap_child(void )
{
MASTER_SERV *serv;
MASTER_PROC *proc;
MASTER_PID pid;
WAIT_STATUS_T status;
/*
* Pick up termination status of all dead children . When a process failed
* on its first job , assume we see the symptom of a structural problem
* ( configuration problem , system running out of resources ) and back off .
*/
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,
(void *) &pid, sizeof (pid))) == 0 ) {
if (init_mode)
continue ; /* non-Postfix process */
msg_panic("master_reap: unknown pid: %d" , pid);
}
serv = proc->serv;
#define MASTER_KILL_SIGNAL SIGTERM
#define MASTER_SENT_SIGNAL(serv, status) \
(MASTER_MARKED_FOR_DELETION(serv) \
&& WTERMSIG(status) == MASTER_KILL_SIGNAL)
/*
* XXX The code for WIFSTOPPED ( ) is here in case some buggy kernel
* reports WIFSTOPPED ( ) events to a Postfix daemon ' s parent process
* ( the master ( 8 ) daemon ) instead of the tracing process ( e . g . , gdb ) .
*
* The WIFSTOPPED ( ) test prevents master ( 8 ) from deleting its record of
* a child process that is stopped . That would cause a master ( 8 )
* panic ( unknown child ) when the child terminates .
*/
if (!NORMAL_EXIT_STATUS(status)) {
if (WIFSTOPPED(status)) {
msg_warn("process %s pid %d stopped by signal %d" ,
serv->path, pid, WSTOPSIG(status));
continue ;
}
if (WIFEXITED(status))
msg_warn("process %s pid %d exit status %d" ,
serv->path, pid, WEXITSTATUS(status));
if (WIFSIGNALED(status) && !MASTER_SENT_SIGNAL(serv, status))
msg_warn("process %s pid %d killed by signal %d" ,
serv->path, pid, WTERMSIG(status));
/* master_delete_children() throttles first, then kills. */
if (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);
}
}
/* master_delete_children - delete all child processes of service */
void master_delete_children(MASTER_SERV *serv)
{
BINHASH_INFO **list;
BINHASH_INFO **info;
MASTER_PROC *proc;
/*
* XXX turn on the throttle so that master_reap_child ( ) doesn ' t . Someone
* has to turn off the throttle in order to stop the associated timer
* request , so we might just as well do it at the end .
*/
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, MASTER_KILL_SIGNAL);
}
while (serv->total_proc > 0 )
master_reap_child();
myfree((void *) list);
master_unthrottle(serv);
}
Messung V0.5 in Prozent C=75 H=95 G=85
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland
2026-08-25
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=277311
#Domains=752002