1/* $NetBSD$ */ 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, char *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, (char *) serv); 113 if (msg_verbose) 114 msg_info("throttle released for command %s", serv->path); 115 master_avail_listen(serv); /* XXX interface botch */ 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, (char *) serv, 132 serv->throttle_delay); 133 if (msg_verbose) 134 msg_info("throttling command %s", serv->path); 135 } 136} 137 138/* master_spawn - spawn off new child process if we can */ 139 140void master_spawn(MASTER_SERV *serv) 141{ 142 const char *myname = "master_spawn"; 143 MASTER_PROC *proc; 144 MASTER_PID pid; 145 int n; 146 static unsigned master_generation = 0; 147 static VSTRING *env_gen = 0; 148 149 if (master_child_table == 0) 150 master_child_table = binhash_create(0); 151 if (env_gen == 0) 152 env_gen = vstring_alloc(100); 153 154 /* 155 * Sanity checks. The master_avail module is supposed to know what it is 156 * doing. 157 */ 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); 164 165 /* 166 * Create a child process and connect parent and child via the status 167 * pipe. 168 */ 169 master_generation += 1; 170 switch (pid = fork()) { 171 172 /* 173 * Error. We're out of some essential resource. Best recourse is to 174 * try again later. 175 */ 176 case -1: 177 msg_warn("%s: fork: %m -- throttling", myname); 178 master_throttle(serv); 179 return; 180 181 /* 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 185 * parent only. 186 * 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(). 190 */ 191 case 0: 192 msg_cleanup((void (*) (void)) 0); /* disable exit handler */ 193 closelog(); /* avoid filedes leak */ 194 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]); 202 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]); 210 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]); 217 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]); 225 } 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]; 231 232 execvp(serv->path, serv->args->argv); 233 msg_fatal("%s: exec %s: %m", myname, serv->path); 234 /* NOTREACHED */ 235 236 /* 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. 241 */ 242 default: 243 if (msg_verbose) 244 msg_info("spawn command %s; pid %d", serv->path, pid); 245 proc = (MASTER_PROC *) mymalloc(sizeof(MASTER_PROC)); 246 proc->serv = serv; 247 proc->pid = pid; 248 proc->gen = master_generation; 249 proc->use_count = 0; 250 proc->avail = 0; 251 binhash_enter(master_child_table, (char *) &pid, 252 sizeof(pid), (char *) proc); 253 serv->total_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); 258 if (msg_verbose) 259 msg_info("start conditional timer for %s", serv->name); 260 } 261 return; 262 } 263} 264 265/* master_delete_child - destroy child process info */ 266 267static void master_delete_child(MASTER_PROC *proc) 268{ 269 MASTER_SERV *serv; 270 271 /* 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. 275 */ 276 serv = proc->serv; 277 serv->total_proc--; 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); 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 (char *) &pid, sizeof(pid))) == 0) 307 msg_panic("master_reap: unknown pid: %d", pid); 308 serv = proc->serv; 309 310#define MASTER_KILL_SIGNAL SIGTERM 311#define MASTER_SENT_SIGNAL(serv, status) \ 312 (MASTER_MARKED_FOR_DELETION(serv) \ 313 && WTERMSIG(status) == MASTER_KILL_SIGNAL) 314 315 if (!NORMAL_EXIT_STATUS(status)) { 316 if (WIFEXITED(status)) 317 msg_warn("process %s pid %d exit status %d", 318 serv->path, pid, WEXITSTATUS(status)); 319 if (WIFSIGNALED(status) && !MASTER_SENT_SIGNAL(serv, status)) 320 msg_warn("process %s pid %d killed by signal %d", 321 serv->path, pid, WTERMSIG(status)); 322 /* master_delete_children() throttles first, then kills. */ 323 if (proc->use_count == 0 324 && (serv->flags & MASTER_FLAG_THROTTLE) == 0) { 325 msg_warn("%s: bad command startup -- throttling", serv->path); 326 master_throttle(serv); 327 } 328 } 329 master_delete_child(proc); 330 } 331} 332 333/* master_delete_children - delete all child processes of service */ 334 335void master_delete_children(MASTER_SERV *serv) 336{ 337 BINHASH_INFO **list; 338 BINHASH_INFO **info; 339 MASTER_PROC *proc; 340 341 /* 342 * XXX turn on the throttle so that master_reap_child() doesn't. Someone 343 * has to turn off the throttle in order to stop the associated timer 344 * request, so we might just as well do it at the end. 345 */ 346 master_throttle(serv); 347 for (info = list = binhash_list(master_child_table); *info; info++) { 348 proc = (MASTER_PROC *) info[0]->value; 349 if (proc->serv == serv) 350 (void) kill(proc->pid, MASTER_KILL_SIGNAL); 351 } 352 while (serv->total_proc > 0) 353 master_reap_child(); 354 myfree((char *) list); 355 master_unthrottle(serv); 356} 357