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); } }
int mypwuid_err(uid_t uid, struct mypasswd ** result) { struct passwd *pwd; struct mypasswd *mypwd; /* * See if this is the same user as last time. */ if (last_pwd != 0) { if (last_pwd->pw_uid != uid) { mypwfree(last_pwd); last_pwd = 0; } else { *result = mypwd = last_pwd; mypwd->refcount++; return (0); } } /* * Find the info in the cache or in the password database. */ if ((mypwd = (struct mypasswd *) binhash_find(mypwcache_uid, (char *) &uid, sizeof(uid))) == 0) { #ifdef HAVE_POSIX_GETPW_R char pwstore[GETPW_R_BUFSIZ]; struct passwd pwbuf; int err; err = getpwuid_r(uid, &pwbuf, pwstore, sizeof(pwstore), &pwd); if (err != 0) return (err); if (pwd == 0) { *result = 0; return (0); } #else if ((pwd = getpwuid(uid)) == 0) { *result = 0; return (0); } #endif mypwd = mypwenter(pwd); } *result = last_pwd = mypwd; mypwd->refcount += 2; return (0); }
static void master_status_event(int event, char *context) { const char *myname = "master_status_event"; MASTER_SERV *serv = (MASTER_SERV *) context; MASTER_STATUS stat; MASTER_PROC *proc; MASTER_PID pid; int n; if (event == 0) /* XXX Can this happen? */ return; /* * We always keep the child end of the status pipe open, so an EOF read * condition means that we're seriously confused. We use non-blocking * reads so that we don't get stuck when someone sends a partial message. * Messages are short, so a partial read means someone wrote less than a * whole status message. Hopefully the next read will be in sync again... * We use a global child process status table because when a child dies * only its pid is known - we do not know what service it came from. */ switch (n = read(serv->status_fd[0], (char *) &stat, sizeof(stat))) { case -1: msg_warn("%s: read: %m", myname); return; case 0: msg_panic("%s: read EOF status", myname); /* NOTREACHED */ default: msg_warn("service %s(%s): child (pid %d) sent partial status update (%d bytes)", serv->ext_name, serv->name, stat.pid, n); return; case sizeof(stat): pid = stat.pid; if (msg_verbose) msg_info("%s: pid %d gen %u avail %d", myname, stat.pid, stat.gen, stat.avail); } /* * Sanity checks. Do not freak out when the child sends garbage because * it is confused or for other reasons. However, be sure to freak out * when our own data structures are inconsistent. A process not found * condition can happen when we reap a process before receiving its * status update, so this is not an error. */ if ((proc = (MASTER_PROC *) binhash_find(master_child_table, (char *) &pid, sizeof(pid))) == 0) { if (msg_verbose) msg_info("%s: process id not found: %d", myname, stat.pid); return; } if (proc->gen != stat.gen) { msg_info("ignoring status update from child pid %d generation %u", pid, stat.gen); return; } if (proc->serv != serv) msg_panic("%s: pointer corruption: %p != %p", myname, (void *) proc->serv, (void *) serv); /* * Update our idea of the child process status. Allow redundant status * updates, because different types of events may be processed out of * order. Otherwise, warn about weird status updates but do not take * action. It's all gossip after all. */ if (proc->avail == stat.avail) return; switch (stat.avail) { case MASTER_STAT_AVAIL: proc->use_count++; master_avail_more(serv, proc); break; case MASTER_STAT_TAKEN: master_avail_less(serv, proc); break; default: msg_warn("%s: ignoring unknown status: %d allegedly from pid: %d", myname, stat.pid, stat.avail); break; } }