Home | History | Annotate | Line # | Download | only in master
      1 /*	$NetBSD: master_spawn.c,v 1.3 2020/03/18 19:05:16 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	master_spawn 3
      6 /* SUMMARY
      7 /*	Postfix master - child process birth and death
      8 /* SYNOPSIS
      9 /*	#include "master.h"
     10 /*
     11 /*	void	master_spawn(serv)
     12 /*	MASTER_SERV *serv;
     13 /*
     14 /*	void	master_reap_child()
     15 /*
     16 /*	void	master_delete_children(serv)
     17 /*	MASTER_SERV *serv;
     18 /* DESCRIPTION
     19 /*	This module creates and cleans up child processes, and applies
     20 /*	a process creation throttle in case of serious trouble.
     21 /*	This module is the working horse for the master_avail(3) process
     22 /*	creation policy module.
     23 /*
     24 /*	master_spawn() spawns off a child process for the specified service,
     25 /*	making the child process available for servicing connection requests.
     26 /*	It is an error to call this function then the specified service is
     27 /*	throttled.
     28 /*
     29 /*	master_reap_child() cleans up all dead child processes.  One typically
     30 /*	runs this function at a convenient moment after receiving a SIGCHLD
     31 /*	signal. When a child process terminates abnormally after being used
     32 /*	for the first time, process creation for that service is throttled
     33 /*	for a configurable amount of time.
     34 /*
     35 /*	master_delete_children() deletes all child processes that provide
     36 /*	the named service. Upon exit, the process creation throttle for that
     37 /*	service is released.
     38 /* DIAGNOSTICS
     39 /*	Panic: interface violations, internal inconsistencies.
     40 /*	Fatal errors: out of memory.  Warnings: throttle on/off.
     41 /* BUGS
     42 /* SEE ALSO
     43 /*	master_avail(3), process creation policy.
     44 /* LICENSE
     45 /* .ad
     46 /* .fi
     47 /*	The Secure Mailer license must be distributed with this software.
     48 /* AUTHOR(S)
     49 /*	Wietse Venema
     50 /*	IBM T.J. Watson Research
     51 /*	P.O. Box 704
     52 /*	Yorktown Heights, NY 10598, USA
     53 /*--*/
     54 
     55 /* System libraries. */
     56 
     57 #include <sys_defs.h>
     58 #include <sys/wait.h>
     59 #include <stdlib.h>
     60 #include <unistd.h>
     61 #include <syslog.h>			/* closelog() */
     62 #include <signal.h>
     63 #include <stdarg.h>
     64 #include <syslog.h>
     65 
     66 /* Utility libraries. */
     67 
     68 #include <msg.h>
     69 #include <binhash.h>
     70 #include <mymalloc.h>
     71 #include <events.h>
     72 #include <vstring.h>
     73 #include <argv.h>
     74 
     75 /* Global library. */
     76 
     77 #include <mail_conf.h>
     78 
     79 /* Application-specific. */
     80 
     81 #include "master_proto.h"
     82 #include "master.h"
     83 
     84 BINHASH *master_child_table;
     85 static void master_unthrottle(MASTER_SERV *serv);
     86 
     87 /* master_unthrottle_wrapper - in case (char *) != (struct *) */
     88 
     89 static void master_unthrottle_wrapper(int unused_event, void *ptr)
     90 {
     91     MASTER_SERV *serv = (MASTER_SERV *) ptr;
     92 
     93     /*
     94      * This routine runs after expiry of the timer set in master_throttle(),
     95      * which gets called when it appears that the world is falling apart.
     96      */
     97     master_unthrottle(serv);
     98 }
     99 
    100 /* master_unthrottle - enable process creation */
    101 
    102 static void master_unthrottle(MASTER_SERV *serv)
    103 {
    104 
    105     /*
    106      * Enable process creation within this class. Disable the "unthrottle"
    107      * timer just in case we're being called directly from the cleanup
    108      * routine, instead of from the event manager.
    109      */
    110     if ((serv->flags & MASTER_FLAG_THROTTLE) != 0) {
    111 	serv->flags &= ~MASTER_FLAG_THROTTLE;
    112 	event_cancel_timer(master_unthrottle_wrapper, (void *) serv);
    113 	if (msg_verbose)
    114 	    msg_info("throttle released for command %s", serv->path);
    115 	master_avail_listen(serv);
    116     }
    117 }
    118 
    119 /* master_throttle - suspend process creation */
    120 
    121 static void master_throttle(MASTER_SERV *serv)
    122 {
    123 
    124     /*
    125      * Perhaps the command to be run is defective, perhaps some configuration
    126      * is wrong, or perhaps the system is out of resources. Disable further
    127      * process creation attempts for a while.
    128      */
    129     if ((serv->flags & MASTER_FLAG_THROTTLE) == 0) {
    130 	serv->flags |= MASTER_FLAG_THROTTLE;
    131 	event_request_timer(master_unthrottle_wrapper, (void *) serv,
    132 			    serv->throttle_delay);
    133 	if (msg_verbose)
    134 	    msg_info("throttling command %s", serv->path);
    135 	master_avail_listen(serv);
    136     }
    137 }
    138 
    139 /* master_spawn - spawn off new child process if we can */
    140 
    141 void    master_spawn(MASTER_SERV *serv)
    142 {
    143     const char *myname = "master_spawn";
    144     MASTER_PROC *proc;
    145     MASTER_PID pid;
    146     int     n;
    147     static unsigned master_generation = 0;
    148     static VSTRING *env_gen = 0;
    149 
    150     if (master_child_table == 0)
    151 	master_child_table = binhash_create(0);
    152     if (env_gen == 0)
    153 	env_gen = vstring_alloc(100);
    154 
    155     /*
    156      * Sanity checks. The master_avail module is supposed to know what it is
    157      * doing.
    158      */
    159     if (!MASTER_LIMIT_OK(serv->max_proc, serv->total_proc))
    160 	msg_panic("%s: at process limit %d", myname, serv->total_proc);
    161     if (serv->avail_proc > 0)
    162 	msg_panic("%s: processes available: %d", myname, serv->avail_proc);
    163     if (serv->flags & MASTER_FLAG_THROTTLE)
    164 	msg_panic("%s: throttled service: %s", myname, serv->path);
    165 
    166     /*
    167      * Create a child process and connect parent and child via the status
    168      * pipe.
    169      */
    170     master_generation += 1;
    171     switch (pid = fork()) {
    172 
    173 	/*
    174 	 * Error. We're out of some essential resource. Best recourse is to
    175 	 * try again later.
    176 	 */
    177     case -1:
    178 	msg_warn("%s: fork: %m -- throttling", myname);
    179 	master_throttle(serv);
    180 	return;
    181 
    182 	/*
    183 	 * Child process. Redirect child stdin/stdout to the parent-child
    184 	 * connection and run the requested command. Leave child stderr
    185 	 * alone. Disable exit handlers: they should be executed by the
    186 	 * parent only.
    187 	 *
    188 	 * When we reach the process limit on a public internet service, we
    189 	 * create stress-mode processes until the process count stays below
    190 	 * the limit for some amount of time. See master_avail_listen().
    191 	 */
    192     case 0:
    193 	msg_cleanup((void (*) (void)) 0);	/* disable exit handler */
    194 	closelog();				/* avoid filedes leak */
    195 
    196 	if (master_flow_pipe[0] <= MASTER_FLOW_READ)
    197 	    msg_fatal("%s: flow pipe read descriptor <= %d",
    198 		      myname, MASTER_FLOW_READ);
    199 	if (DUP2(master_flow_pipe[0], MASTER_FLOW_READ) < 0)
    200 	    msg_fatal("%s: dup2: %m", myname);
    201 	if (close(master_flow_pipe[0]) < 0)
    202 	    msg_fatal("close %d: %m", master_flow_pipe[0]);
    203 
    204 	if (master_flow_pipe[1] <= MASTER_FLOW_WRITE)
    205 	    msg_fatal("%s: flow pipe read descriptor <= %d",
    206 		      myname, MASTER_FLOW_WRITE);
    207 	if (DUP2(master_flow_pipe[1], MASTER_FLOW_WRITE) < 0)
    208 	    msg_fatal("%s: dup2: %m", myname);
    209 	if (close(master_flow_pipe[1]) < 0)
    210 	    msg_fatal("close %d: %m", master_flow_pipe[1]);
    211 
    212 	close(serv->status_fd[0]);		/* status channel */
    213 	if (serv->status_fd[1] <= MASTER_STATUS_FD)
    214 	    msg_fatal("%s: status file descriptor collision", myname);
    215 	if (DUP2(serv->status_fd[1], MASTER_STATUS_FD) < 0)
    216 	    msg_fatal("%s: dup2 status_fd: %m", myname);
    217 	(void) close(serv->status_fd[1]);
    218 
    219 	for (n = 0; n < serv->listen_fd_count; n++) {
    220 	    if (serv->listen_fd[n] <= MASTER_LISTEN_FD + n)
    221 		msg_fatal("%s: listen file descriptor collision", myname);
    222 	    if (DUP2(serv->listen_fd[n], MASTER_LISTEN_FD + n) < 0)
    223 		msg_fatal("%s: dup2 listen_fd %d: %m",
    224 			  myname, serv->listen_fd[n]);
    225 	    (void) close(serv->listen_fd[n]);
    226 	}
    227 	vstring_sprintf(env_gen, "%s=%o", MASTER_GEN_NAME, master_generation);
    228 	if (putenv(vstring_str(env_gen)) < 0)
    229 	    msg_fatal("%s: putenv: %m", myname);
    230 	if (serv->stress_param_val && serv->stress_expire_time > event_time())
    231 	    serv->stress_param_val[0] = CONFIG_BOOL_YES[0];
    232 
    233 	execvp(serv->path, serv->args->argv);
    234 	msg_fatal("%s: exec %s: %m", myname, serv->path);
    235 	/* NOTREACHED */
    236 
    237 	/*
    238 	 * Parent. Fill in a process member data structure and set up links
    239 	 * between child and process. Say this process has become available.
    240 	 * If this service has a wakeup timer that is turned on only when the
    241 	 * service is actually used, turn on the wakeup timer.
    242 	 */
    243     default:
    244 	if (msg_verbose)
    245 	    msg_info("spawn command %s; pid %d", serv->path, pid);
    246 	proc = (MASTER_PROC *) mymalloc(sizeof(MASTER_PROC));
    247 	proc->serv = serv;
    248 	proc->pid = pid;
    249 	proc->gen = master_generation;
    250 	proc->use_count = 0;
    251 	proc->avail = 0;
    252 	binhash_enter(master_child_table, (void *) &pid,
    253 		      sizeof(pid), (void *) proc);
    254 	serv->total_proc++;
    255 	master_avail_more(serv, proc);
    256 	if (serv->flags & MASTER_FLAG_CONDWAKE) {
    257 	    serv->flags &= ~MASTER_FLAG_CONDWAKE;
    258 	    master_wakeup_init(serv);
    259 	    if (msg_verbose)
    260 		msg_info("start conditional timer for %s", serv->name);
    261 	}
    262 	return;
    263     }
    264 }
    265 
    266 /* master_delete_child - destroy child process info */
    267 
    268 static void master_delete_child(MASTER_PROC *proc)
    269 {
    270     MASTER_SERV *serv;
    271 
    272     /*
    273      * Undo the things that master_spawn did. Stop the process if it still
    274      * exists, and remove it from the lookup tables. Update the number of
    275      * available processes.
    276      */
    277     serv = proc->serv;
    278     serv->total_proc--;
    279     if (proc->avail == MASTER_STAT_AVAIL)
    280 	master_avail_less(serv, proc);
    281     else
    282 	master_avail_listen(serv);
    283     binhash_delete(master_child_table, (void *) &proc->pid,
    284 		   sizeof(proc->pid), (void (*) (void *)) 0);
    285     myfree((void *) proc);
    286 }
    287 
    288 /* master_reap_child - reap dead children */
    289 
    290 void    master_reap_child(void)
    291 {
    292     MASTER_SERV *serv;
    293     MASTER_PROC *proc;
    294     MASTER_PID pid;
    295     WAIT_STATUS_T status;
    296 
    297     /*
    298      * Pick up termination status of all dead children. When a process failed
    299      * on its first job, assume we see the symptom of a structural problem
    300      * (configuration problem, system running out of resources) and back off.
    301      */
    302     while ((pid = waitpid((pid_t) - 1, &status, WNOHANG)) > 0) {
    303 	if (msg_verbose)
    304 	    msg_info("master_reap_child: pid %d", pid);
    305 	if ((proc = (MASTER_PROC *) binhash_find(master_child_table,
    306 					(void *) &pid, sizeof(pid))) == 0) {
    307 	    if (init_mode)
    308 		continue;			/* non-Postfix process */
    309 	    msg_panic("master_reap: unknown pid: %d", pid);
    310 	}
    311 	serv = proc->serv;
    312 
    313 #define MASTER_KILL_SIGNAL	SIGTERM
    314 #define MASTER_SENT_SIGNAL(serv, status) \
    315 	(MASTER_MARKED_FOR_DELETION(serv) \
    316 	    && WTERMSIG(status) == MASTER_KILL_SIGNAL)
    317 
    318 	/*
    319 	 * XXX The code for WIFSTOPPED() is here in case some buggy kernel
    320 	 * reports WIFSTOPPED() events to a Postfix daemon's parent process
    321 	 * (the master(8) daemon) instead of the tracing process (e.g., gdb).
    322 	 *
    323 	 * The WIFSTOPPED() test prevents master(8) from deleting its record of
    324 	 * a child process that is stopped. That would cause a master(8)
    325 	 * panic (unknown child) when the child terminates.
    326 	 */
    327 	if (!NORMAL_EXIT_STATUS(status)) {
    328 	    if (WIFSTOPPED(status)) {
    329 		msg_warn("process %s pid %d stopped by signal %d",
    330 			 serv->path, pid, WSTOPSIG(status));
    331 		continue;
    332 	    }
    333 	    if (WIFEXITED(status))
    334 		msg_warn("process %s pid %d exit status %d",
    335 			 serv->path, pid, WEXITSTATUS(status));
    336 	    if (WIFSIGNALED(status) && !MASTER_SENT_SIGNAL(serv, status))
    337 		msg_warn("process %s pid %d killed by signal %d",
    338 			 serv->path, pid, WTERMSIG(status));
    339 	    /* master_delete_children() throttles first, then kills. */
    340 	    if (proc->use_count == 0
    341 		&& (serv->flags & MASTER_FLAG_THROTTLE) == 0) {
    342 		msg_warn("%s: bad command startup -- throttling", serv->path);
    343 		master_throttle(serv);
    344 	    }
    345 	}
    346 	master_delete_child(proc);
    347     }
    348 }
    349 
    350 /* master_delete_children - delete all child processes of service */
    351 
    352 void    master_delete_children(MASTER_SERV *serv)
    353 {
    354     BINHASH_INFO **list;
    355     BINHASH_INFO **info;
    356     MASTER_PROC *proc;
    357 
    358     /*
    359      * XXX turn on the throttle so that master_reap_child() doesn't. Someone
    360      * has to turn off the throttle in order to stop the associated timer
    361      * request, so we might just as well do it at the end.
    362      */
    363     master_throttle(serv);
    364     for (info = list = binhash_list(master_child_table); *info; info++) {
    365 	proc = (MASTER_PROC *) info[0]->value;
    366 	if (proc->serv == serv)
    367 	    (void) kill(proc->pid, MASTER_KILL_SIGNAL);
    368     }
    369     while (serv->total_proc > 0)
    370 	master_reap_child();
    371     myfree((void *) list);
    372     master_unthrottle(serv);
    373 }
    374