void master_reap_child(void) { MASTER_SERV *serv; MASTER_PROC *proc; MASTER_PID pid; WAIT_STATUS_T status; /* * Pick up termination status of all dead children. When a process failed * on its first job, assume we see the symptom of a structural problem * (configuration problem, system running out of resources) and back off. */ while ((pid = waitpid((pid_t) - 1, &status, WNOHANG)) > 0) { if (msg_verbose) msg_info("master_reap_child: pid %d", pid); if ((proc = (MASTER_PROC *) binhash_find(master_child_table, (void *) &pid, sizeof(pid))) == 0) msg_panic("master_reap: unknown pid: %d", pid); serv = proc->serv; #define MASTER_KILL_SIGNAL SIGTERM #define MASTER_SENT_SIGNAL(serv, status) \ (MASTER_MARKED_FOR_DELETION(serv) \ && WTERMSIG(status) == MASTER_KILL_SIGNAL) /* * XXX The code for WIFSTOPPED() is here in case some buggy kernel * reports WIFSTOPPED() events to a Postfix daemon's parent process * (the master(8) daemon) instead of the tracing process (e.g., gdb). * * The WIFSTOPPED() test prevents master(8) from deleting its record of * a child process that is stopped. That would cause a master(8) * panic (unknown child) when the child terminates. */ if (!NORMAL_EXIT_STATUS(status)) { if (WIFSTOPPED(status)) { msg_warn("process %s pid %d stopped by signal %d", serv->path, pid, WSTOPSIG(status)); continue; } if (WIFEXITED(status)) msg_warn("process %s pid %d exit status %d", serv->path, pid, WEXITSTATUS(status)); if (WIFSIGNALED(status) && !MASTER_SENT_SIGNAL(serv, status)) msg_warn("process %s pid %d killed by signal %d", serv->path, pid, WTERMSIG(status)); /* master_delete_children() throttles first, then kills. */ if (proc->use_count == 0 && (serv->flags & MASTER_FLAG_THROTTLE) == 0) { msg_warn("%s: bad command startup -- throttling", serv->path); master_throttle(serv); } } master_delete_child(proc); } }
static void spawn_service(VSTREAM *client_stream, char *service, char **argv) { const char *myname = "spawn_service"; static SPAWN_ATTR attr; WAIT_STATUS_T status; ARGV *export_env; /* * This routine runs whenever a client connects to the UNIX-domain socket * dedicated to running an external command. */ if (msg_verbose) msg_info("%s: service=%s, command=%s...", myname, service, argv[0]); /* * Look up service attributes and config information only once. This is * safe since the information comes from a trusted source. */ if (attr.argv == 0) { get_service_attr(&attr, service, argv); } /* * Execute the command. */ export_env = mail_parm_split(VAR_EXPORT_ENVIRON, var_export_environ); status = spawn_command(CA_SPAWN_CMD_STDIN(vstream_fileno(client_stream)), CA_SPAWN_CMD_STDOUT(vstream_fileno(client_stream)), CA_SPAWN_CMD_STDERR(vstream_fileno(client_stream)), CA_SPAWN_CMD_UID(attr.uid), CA_SPAWN_CMD_GID(attr.gid), CA_SPAWN_CMD_ARGV(attr.argv), CA_SPAWN_CMD_TIME_LIMIT(attr.time_limit), CA_SPAWN_CMD_EXPORT(export_env->argv), CA_SPAWN_CMD_END); argv_free(export_env); /* * Warn about unsuccessful completion. */ if (!NORMAL_EXIT_STATUS(status)) { if (WIFEXITED(status)) msg_warn("command %s exit status %d", attr.argv[0], WEXITSTATUS(status)); if (WIFSIGNALED(status)) msg_warn("command %s killed by signal %d", attr.argv[0], WTERMSIG(status)); } }