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
84BINHASH *master_child_table;
85static void master_unthrottle(MASTER_SERV *serv);
86
87/* master_unthrottle_wrapper - in case (char *) != (struct *) */
88
89static 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
102static 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
121static 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
141void    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
268static 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
290void    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
352void    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