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