Beispiel #1
0
static void master_avail_event(int event, char *context)
{
    MASTER_SERV *serv = (MASTER_SERV *) context;
    time_t  now;

    if (event == 0)				/* XXX Can this happen? */
	msg_panic("master_avail_event: null event");
    else {

	/*
	 * When all servers for a public internet service are busy, we start
	 * creating server processes with "-o stress=yes" on the command
	 * line, and keep creating such processes until the process count is
	 * below the limit for at least 1000 seconds. This provides a minimal
	 * solution that can be adopted into legacy and stable Postfix
	 * releases.
	 * 
	 * This is not the right place to update serv->stress_param_val in
	 * response to stress level changes. Doing so would would contaminate
	 * the "postfix reload" code with stress management implementation
	 * details, creating a source of future bugs. Instead, we update
	 * simple counters or flags here, and use their values to determine
	 * the proper serv->stress_param_val value when exec-ing a server
	 * process.
	 */
	if (serv->stress_param_val != 0
	    && !MASTER_LIMIT_OK(serv->max_proc, serv->total_proc + 1)) {
	    now = event_time();
	    if (serv->stress_expire_time < now)
		master_restart_service(serv, NO_CONF_RELOAD);
	    serv->stress_expire_time = now + 1000;
	}
	master_spawn(serv);
    }
}
Beispiel #2
0
void    master_avail_listen(MASTER_SERV *serv)
{
    const char *myname = "master_avail_listen";
    int     listen_flag;
    time_t  now;
    int     n;

    /*
     * Caution: several other master_XXX modules call master_avail_listen(),
     * master_avail_more() or master_avail_less(). To avoid mutual dependency
     * problems, the code below invokes no code in other master_XXX modules,
     * and modifies no data that is maintained by other master_XXX modules.
     * 
     * When no-one else is monitoring the service's listen socket, start
     * monitoring the socket for connection requests. All this under the
     * restriction that we have sufficient resources to service a connection
     * request.
     */
    if (msg_verbose)
	msg_info("%s: %s avail %d total %d max %d", myname, serv->name,
		 serv->avail_proc, serv->total_proc, serv->max_proc);
    if (MASTER_THROTTLED(serv) || serv->avail_proc > 0) {
	listen_flag = 0;
    } else if (MASTER_LIMIT_OK(serv->max_proc, serv->total_proc)) {
	listen_flag = 1;
    } else {
	listen_flag = 0;
	if (serv->stress_param_val != 0) {
	    now = event_time();
	    if (serv->busy_warn_time < now - 1000) {
		serv->busy_warn_time = now;
		msg_warn("service \"%s\" (%s) has reached its process limit \"%d\": "
			 "new clients may experience noticeable delays",
			 serv->ext_name, serv->name, serv->max_proc);
		msg_warn("to avoid this condition, increase the process count "
		      "in master.cf or reduce the service time per client");
		msg_warn("see http://www.postfix.org/STRESS_README.html for "
		      "examples of stress-adapting configuration settings");
	    }
	}
    }
    if (listen_flag && !MASTER_LISTENING(serv)) {
	if (msg_verbose)
	    msg_info("%s: enable events %s", myname, serv->name);
	for (n = 0; n < serv->listen_fd_count; n++)
	    event_enable_read(serv->listen_fd[n], master_avail_event,
			      (char *) serv);
	serv->flags |= MASTER_FLAG_LISTEN;
    } else if (!listen_flag && MASTER_LISTENING(serv)) {
	if (msg_verbose)
	    msg_info("%s: disable events %s", myname, serv->name);
	for (n = 0; n < serv->listen_fd_count; n++)
	    event_disable_readwrite(serv->listen_fd[n]);
	serv->flags &= ~MASTER_FLAG_LISTEN;
    }
}
Beispiel #3
0
void    master_spawn(MASTER_SERV *serv)
{
    const char *myname = "master_spawn";
    MASTER_PROC *proc;
    MASTER_PID pid;
    int     n;
    static unsigned master_generation = 0;
    static VSTRING *env_gen = 0;

    if (master_child_table == 0)
	master_child_table = binhash_create(0);
    if (env_gen == 0)
	env_gen = vstring_alloc(100);

    /*
     * Sanity checks. The master_avail module is supposed to know what it is
     * doing.
     */
    if (!MASTER_LIMIT_OK(serv->max_proc, serv->total_proc))
	msg_panic("%s: at process limit %d", myname, serv->total_proc);
    if (serv->avail_proc > 0)
	msg_panic("%s: processes available: %d", myname, serv->avail_proc);
    if (serv->flags & MASTER_FLAG_THROTTLE)
	msg_panic("%s: throttled service: %s", myname, serv->path);

    /*
     * Create a child process and connect parent and child via the status
     * pipe.
     */
    master_generation += 1;
    switch (pid = fork()) {

	/*
	 * Error. We're out of some essential resource. Best recourse is to
	 * try again later.
	 */
    case -1:
	msg_warn("%s: fork: %m -- throttling", myname);
	master_throttle(serv);
	return;

	/*
	 * Child process. Redirect child stdin/stdout to the parent-child
	 * connection and run the requested command. Leave child stderr
	 * alone. Disable exit handlers: they should be executed by the
	 * parent only.
	 * 
	 * When we reach the process limit on a public internet service, we
	 * create stress-mode processes until the process count stays below
	 * the limit for some amount of time. See master_avail_listen().
	 */
    case 0:
	msg_cleanup((void (*) (void)) 0);	/* disable exit handler */
	closelog();				/* avoid filedes leak */

	if (master_flow_pipe[0] <= MASTER_FLOW_READ)
	    msg_fatal("%s: flow pipe read descriptor <= %d",
		      myname, MASTER_FLOW_READ);
	if (DUP2(master_flow_pipe[0], MASTER_FLOW_READ) < 0)
	    msg_fatal("%s: dup2: %m", myname);
	if (close(master_flow_pipe[0]) < 0)
	    msg_fatal("close %d: %m", master_flow_pipe[0]);

	if (master_flow_pipe[1] <= MASTER_FLOW_WRITE)
	    msg_fatal("%s: flow pipe read descriptor <= %d",
		      myname, MASTER_FLOW_WRITE);
	if (DUP2(master_flow_pipe[1], MASTER_FLOW_WRITE) < 0)
	    msg_fatal("%s: dup2: %m", myname);
	if (close(master_flow_pipe[1]) < 0)
	    msg_fatal("close %d: %m", master_flow_pipe[1]);

	close(serv->status_fd[0]);		/* status channel */
	if (serv->status_fd[1] <= MASTER_STATUS_FD)
	    msg_fatal("%s: status file descriptor collision", myname);
	if (DUP2(serv->status_fd[1], MASTER_STATUS_FD) < 0)
	    msg_fatal("%s: dup2 status_fd: %m", myname);
	(void) close(serv->status_fd[1]);

	for (n = 0; n < serv->listen_fd_count; n++) {
	    if (serv->listen_fd[n] <= MASTER_LISTEN_FD + n)
		msg_fatal("%s: listen file descriptor collision", myname);
	    if (DUP2(serv->listen_fd[n], MASTER_LISTEN_FD + n) < 0)
		msg_fatal("%s: dup2 listen_fd %d: %m",
			  myname, serv->listen_fd[n]);
	    (void) close(serv->listen_fd[n]);
	}
	vstring_sprintf(env_gen, "%s=%o", MASTER_GEN_NAME, master_generation);
	if (putenv(vstring_str(env_gen)) < 0)
	    msg_fatal("%s: putenv: %m", myname);
	if (serv->stress_param_val && serv->stress_expire_time > event_time())
	    serv->stress_param_val[0] = CONFIG_BOOL_YES[0];

	execvp(serv->path, serv->args->argv);
	msg_fatal("%s: exec %s: %m", myname, serv->path);
	/* NOTREACHED */

	/*
	 * Parent. Fill in a process member data structure and set up links
	 * between child and process. Say this process has become available.
	 * If this service has a wakeup timer that is turned on only when the
	 * service is actually used, turn on the wakeup timer.
	 */
    default:
	if (msg_verbose)
	    msg_info("spawn command %s; pid %d", serv->path, pid);
	proc = (MASTER_PROC *) mymalloc(sizeof(MASTER_PROC));
	proc->serv = serv;
	proc->pid = pid;
	proc->gen = master_generation;
	proc->use_count = 0;
	proc->avail = 0;
	binhash_enter(master_child_table, (void *) &pid,
		      sizeof(pid), (void *) proc);
	serv->total_proc++;
	master_avail_more(serv, proc);
	if (serv->flags & MASTER_FLAG_CONDWAKE) {
	    serv->flags &= ~MASTER_FLAG_CONDWAKE;
	    master_wakeup_init(serv);
	    if (msg_verbose)
		msg_info("start conditional timer for %s", serv->name);
	}
	return;
    }
}