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