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