7 /* Postfix master - child process birth and death
11 /* void master_spawn(serv)
14 /* void master_reap_child()
16 /* void master_delete_children(serv)
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.
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
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.
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.
39 /* Panic: interface violations, internal inconsistencies.
40 /* Fatal errors: out of memory. Warnings: throttle on/off.
43 /* master_avail(3), process creation policy.
47 /* The Secure Mailer license must be distributed with this software.
50 /* IBM T.J. Watson Research
52 /* Yorktown Heights, NY 10598, USA
55 /* System libraries. */
61 #include <syslog.h> /* closelog() */
66 /* Utility libraries. */
77 #include <mail_conf.h>
79 /* Application-specific. */
81 #include "master_proto.h"
84 BINHASH
*master_child_table
;
85 static void master_unthrottle(MASTER_SERV
*serv
);
87 /* master_unthrottle_wrapper - in case (char *) != (struct *) */
89 static void master_unthrottle_wrapper(int unused_event
, char *ptr
)
91 MASTER_SERV
*serv
= (MASTER_SERV
*) ptr
;
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.
97 master_unthrottle(serv
);
100 /* master_unthrottle - enable process creation */
102 static void master_unthrottle(MASTER_SERV
*serv
)
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.
110 if ((serv
->flags
& MASTER_FLAG_THROTTLE
) != 0) {
111 serv
->flags
&= ~MASTER_FLAG_THROTTLE
;
112 event_cancel_timer(master_unthrottle_wrapper
, (char *) serv
);
114 msg_info("throttle released for command %s", serv
->path
);
115 master_avail_listen(serv
); /* XXX interface botch */
119 /* master_throttle - suspend process creation */
121 static void master_throttle(MASTER_SERV
*serv
)
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.
129 if ((serv
->flags
& MASTER_FLAG_THROTTLE
) == 0) {
130 serv
->flags
|= MASTER_FLAG_THROTTLE
;
131 event_request_timer(master_unthrottle_wrapper
, (char *) serv
,
132 serv
->throttle_delay
);
134 msg_info("throttling command %s", serv
->path
);
138 /* master_spawn - spawn off new child process if we can */
140 void master_spawn(MASTER_SERV
*serv
)
142 const char *myname
= "master_spawn";
146 static unsigned master_generation
= 0;
147 static VSTRING
*env_gen
= 0;
149 if (master_child_table
== 0)
150 master_child_table
= binhash_create(0);
152 env_gen
= vstring_alloc(100);
155 * Sanity checks. The master_avail module is supposed to know what it is
158 if (!MASTER_LIMIT_OK(serv
->max_proc
, serv
->total_proc
))
159 msg_panic("%s: at process limit %d", myname
, serv
->total_proc
);
160 if (serv
->avail_proc
> 0)
161 msg_panic("%s: processes available: %d", myname
, serv
->avail_proc
);
162 if (serv
->flags
& MASTER_FLAG_THROTTLE
)
163 msg_panic("%s: throttled service: %s", myname
, serv
->path
);
166 * Create a child process and connect parent and child via the status
169 master_generation
+= 1;
170 switch (pid
= fork()) {
173 * Error. We're out of some essential resource. Best recourse is to
177 msg_warn("%s: fork: %m -- throttling", myname
);
178 master_throttle(serv
);
182 * Child process. Redirect child stdin/stdout to the parent-child
183 * connection and run the requested command. Leave child stderr
184 * alone. Disable exit handlers: they should be executed by the
187 * When we reach the process limit on a public internet service, we
188 * create stress-mode processes until the process count stays below
189 * the limit for some amount of time. See master_avail_listen().
192 msg_cleanup((void (*) (void)) 0); /* disable exit handler */
193 closelog(); /* avoid filedes leak */
195 if (master_flow_pipe
[0] <= MASTER_FLOW_READ
)
196 msg_fatal("%s: flow pipe read descriptor <= %d",
197 myname
, MASTER_FLOW_READ
);
198 if (DUP2(master_flow_pipe
[0], MASTER_FLOW_READ
) < 0)
199 msg_fatal("%s: dup2: %m", myname
);
200 if (close(master_flow_pipe
[0]) < 0)
201 msg_fatal("close %d: %m", master_flow_pipe
[0]);
203 if (master_flow_pipe
[1] <= MASTER_FLOW_WRITE
)
204 msg_fatal("%s: flow pipe read descriptor <= %d",
205 myname
, MASTER_FLOW_WRITE
);
206 if (DUP2(master_flow_pipe
[1], MASTER_FLOW_WRITE
) < 0)
207 msg_fatal("%s: dup2: %m", myname
);
208 if (close(master_flow_pipe
[1]) < 0)
209 msg_fatal("close %d: %m", master_flow_pipe
[1]);
211 close(serv
->status_fd
[0]); /* status channel */
212 if (serv
->status_fd
[1] <= MASTER_STATUS_FD
)
213 msg_fatal("%s: status file descriptor collision", myname
);
214 if (DUP2(serv
->status_fd
[1], MASTER_STATUS_FD
) < 0)
215 msg_fatal("%s: dup2 status_fd: %m", myname
);
216 (void) close(serv
->status_fd
[1]);
218 for (n
= 0; n
< serv
->listen_fd_count
; n
++) {
219 if (serv
->listen_fd
[n
] <= MASTER_LISTEN_FD
+ n
)
220 msg_fatal("%s: listen file descriptor collision", myname
);
221 if (DUP2(serv
->listen_fd
[n
], MASTER_LISTEN_FD
+ n
) < 0)
222 msg_fatal("%s: dup2 listen_fd %d: %m",
223 myname
, serv
->listen_fd
[n
]);
224 (void) close(serv
->listen_fd
[n
]);
226 vstring_sprintf(env_gen
, "%s=%o", MASTER_GEN_NAME
, master_generation
);
227 if (putenv(vstring_str(env_gen
)) < 0)
228 msg_fatal("%s: putenv: %m", myname
);
229 if (serv
->stress_param_val
&& serv
->stress_expire_time
> event_time())
230 serv
->stress_param_val
[0] = CONFIG_BOOL_YES
[0];
232 execvp(serv
->path
, serv
->args
->argv
);
233 msg_fatal("%s: exec %s: %m", myname
, serv
->path
);
237 * Parent. Fill in a process member data structure and set up links
238 * between child and process. Say this process has become available.
239 * If this service has a wakeup timer that is turned on only when the
240 * service is actually used, turn on the wakeup timer.
244 msg_info("spawn command %s; pid %d", serv
->path
, pid
);
245 proc
= (MASTER_PROC
*) mymalloc(sizeof(MASTER_PROC
));
248 proc
->gen
= master_generation
;
251 binhash_enter(master_child_table
, (char *) &pid
,
252 sizeof(pid
), (char *) proc
);
254 master_avail_more(serv
, proc
);
255 if (serv
->flags
& MASTER_FLAG_CONDWAKE
) {
256 serv
->flags
&= ~MASTER_FLAG_CONDWAKE
;
257 master_wakeup_init(serv
);
259 msg_info("start conditional timer for %s", serv
->name
);
265 /* master_delete_child - destroy child process info */
267 static void master_delete_child(MASTER_PROC
*proc
)
272 * Undo the things that master_spawn did. Stop the process if it still
273 * exists, and remove it from the lookup tables. Update the number of
274 * available processes.
278 if (proc
->avail
== MASTER_STAT_AVAIL
)
279 master_avail_less(serv
, proc
);
280 else if (MASTER_LIMIT_OK(serv
->max_proc
, serv
->total_proc
)
281 && serv
->avail_proc
< 1)
282 master_avail_listen(serv
);
283 binhash_delete(master_child_table
, (char *) &proc
->pid
,
284 sizeof(proc
->pid
), (void (*) (char *)) 0);
285 myfree((char *) proc
);
288 /* master_reap_child - reap dead children */
290 void master_reap_child(void)
295 WAIT_STATUS_T status
;
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.
302 while ((pid
= waitpid((pid_t
) - 1, &status
, WNOHANG
)) > 0) {
304 msg_info("master_reap_child: pid %d", pid
);
305 if ((proc
= (MASTER_PROC
*) binhash_find(master_child_table
,
306 (char *) &pid
, sizeof(pid
))) == 0)
307 msg_panic("master_reap: unknown pid: %d", pid
);
309 if (!NORMAL_EXIT_STATUS(status
)) {
310 if (WIFEXITED(status
))
311 msg_warn("process %s pid %d exit status %d",
312 serv
->path
, pid
, WEXITSTATUS(status
));
313 if (WIFSIGNALED(status
))
314 msg_warn("process %s pid %d killed by signal %d",
315 serv
->path
, pid
, WTERMSIG(status
));
317 if (!NORMAL_EXIT_STATUS(status
) && proc
->use_count
== 0
318 && (serv
->flags
& MASTER_FLAG_THROTTLE
) == 0) {
319 msg_warn("%s: bad command startup -- throttling", serv
->path
);
320 master_throttle(serv
);
322 master_delete_child(proc
);
326 /* master_delete_children - delete all child processes of service */
328 void master_delete_children(MASTER_SERV
*serv
)
335 * XXX turn on the throttle so that master_reap_child() doesn't. Someone
336 * has to turn off the throttle in order to stop the associated timer
337 * request, so we might just as well do it at the end.
339 master_throttle(serv
);
340 for (info
= list
= binhash_list(master_child_table
); *info
; info
++) {
341 proc
= (MASTER_PROC
*) info
[0]->value
;
342 if (proc
->serv
== serv
)
343 (void) kill(proc
->pid
, SIGTERM
);
345 while (serv
->total_proc
> 0)
347 myfree((char *) list
);
348 master_unthrottle(serv
);