Example #1
0
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);
    }
}
Example #2
0
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);
}
Example #3
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;
    }
}