1*68e5c67bSchristos /*	$NetBSD: master_avail.c,v 1.2 2017/02/14 01:16:45 christos Exp $	*/
241fbaed0Stron 
341fbaed0Stron /*++
441fbaed0Stron /* NAME
541fbaed0Stron /*	master_avail 3
641fbaed0Stron /* SUMMARY
741fbaed0Stron /*	Postfix master - process creation policy
841fbaed0Stron /* SYNOPSIS
941fbaed0Stron /*	#include "master.h"
1041fbaed0Stron /*
1141fbaed0Stron /*	void	master_avail_listen(serv)
1241fbaed0Stron /*	MASTER_SERV *serv;
1341fbaed0Stron /*
1441fbaed0Stron /*	void	master_avail_cleanup(serv)
1541fbaed0Stron /*	MASTER_SERV *serv;
1641fbaed0Stron /*
1741fbaed0Stron /*	void	master_avail_more(serv, proc)
1841fbaed0Stron /*	MASTER_SERV *serv;
1941fbaed0Stron /*	MASTER_PROC *proc;
2041fbaed0Stron /*
2141fbaed0Stron /*	void	master_avail_less(serv, proc)
2241fbaed0Stron /*	MASTER_SERV *serv;
2341fbaed0Stron /*	MASTER_PROC *proc;
2441fbaed0Stron /* DESCRIPTION
2541fbaed0Stron /*	This module implements the process creation policy. As long as
2641fbaed0Stron /*	the allowed number of processes for the given service is not
2741fbaed0Stron /*	exceeded, a connection request is either handled by an existing
2841fbaed0Stron /*	available process, or this module causes a new process to be
2941fbaed0Stron /*	created to service the request.
3041fbaed0Stron /*
31e694ac3bStron /*	When the service runs out of process slots, and the service
32e694ac3bStron /*	is eligible for stress-mode operation, a warning is logged,
33e694ac3bStron /*	servers are asked to restart at their convenience, and new
34e694ac3bStron /*	servers are created with stress mode enabled.
3541fbaed0Stron /*
3641fbaed0Stron /*	master_avail_listen() ensures that someone monitors the service's
3741fbaed0Stron /*	listen socket for connection requests (as long as resources
3841fbaed0Stron /*	to handle connection requests are available).  This function may
39e694ac3bStron /*	be called at random times, but it must be called after each status
40e694ac3bStron /*	change of a service (throttled, process limit, etc.) or child
41e694ac3bStron /*	process (taken, available, dead, etc.).
4241fbaed0Stron /*
4341fbaed0Stron /*	master_avail_cleanup() should be called when the named service
4441fbaed0Stron /*	is taken out of operation. It terminates child processes by
4541fbaed0Stron /*	sending SIGTERM.
4641fbaed0Stron /*
4741fbaed0Stron /*	master_avail_more() should be called when the named process
4841fbaed0Stron /*	has become available for servicing new connection requests.
49e694ac3bStron /*	This function updates the process availability status and
50e694ac3bStron /*	counter, and implicitly calls master_avail_listen().
5141fbaed0Stron /*
5241fbaed0Stron /*	master_avail_less() should be called when the named process
5341fbaed0Stron /*	has become unavailable for servicing new connection requests.
54e694ac3bStron /*	This function updates the process availability status and
55e694ac3bStron /*	counter, and implicitly calls master_avail_listen().
5641fbaed0Stron /* DIAGNOSTICS
5741fbaed0Stron /*	Panic: internal inconsistencies.
5841fbaed0Stron /* BUGS
5941fbaed0Stron /* SEE ALSO
6041fbaed0Stron /*	master_spawn(3), child process birth and death
6141fbaed0Stron /* LICENSE
6241fbaed0Stron /* .ad
6341fbaed0Stron /* .fi
6441fbaed0Stron /*	The Secure Mailer license must be distributed with this software.
6541fbaed0Stron /* AUTHOR(S)
6641fbaed0Stron /*	Wietse Venema
6741fbaed0Stron /*	IBM T.J. Watson Research
6841fbaed0Stron /*	P.O. Box 704
6941fbaed0Stron /*	Yorktown Heights, NY 10598, USA
7041fbaed0Stron /*--*/
7141fbaed0Stron 
7241fbaed0Stron /* System libraries. */
7341fbaed0Stron 
7441fbaed0Stron #include <sys_defs.h>
7541fbaed0Stron 
7641fbaed0Stron /* Utility library. */
7741fbaed0Stron 
7841fbaed0Stron #include <events.h>
7941fbaed0Stron #include <msg.h>
8041fbaed0Stron 
8141fbaed0Stron /* Application-specific. */
8241fbaed0Stron 
8341fbaed0Stron #include "master_proto.h"
8441fbaed0Stron #include "master.h"
8541fbaed0Stron 
8641fbaed0Stron /* master_avail_event - create child process to handle connection request */
8741fbaed0Stron 
master_avail_event(int event,void * context)88837e7c1aSchristos static void master_avail_event(int event, void *context)
8941fbaed0Stron {
9041fbaed0Stron     MASTER_SERV *serv = (MASTER_SERV *) context;
9141fbaed0Stron     time_t  now;
9241fbaed0Stron 
9341fbaed0Stron     if (event == 0)				/* XXX Can this happen? */
94e694ac3bStron 	msg_panic("master_avail_event: null event");
95e694ac3bStron     else {
9641fbaed0Stron 
9741fbaed0Stron 	/*
9841fbaed0Stron 	 * When all servers for a public internet service are busy, we start
9941fbaed0Stron 	 * creating server processes with "-o stress=yes" on the command
10041fbaed0Stron 	 * line, and keep creating such processes until the process count is
10141fbaed0Stron 	 * below the limit for at least 1000 seconds. This provides a minimal
10241fbaed0Stron 	 * solution that can be adopted into legacy and stable Postfix
10341fbaed0Stron 	 * releases.
10441fbaed0Stron 	 *
10541fbaed0Stron 	 * This is not the right place to update serv->stress_param_val in
10641fbaed0Stron 	 * response to stress level changes. Doing so would would contaminate
10741fbaed0Stron 	 * the "postfix reload" code with stress management implementation
10841fbaed0Stron 	 * details, creating a source of future bugs. Instead, we update
10941fbaed0Stron 	 * simple counters or flags here, and use their values to determine
11041fbaed0Stron 	 * the proper serv->stress_param_val value when exec-ing a server
11141fbaed0Stron 	 * process.
11241fbaed0Stron 	 */
11341fbaed0Stron 	if (serv->stress_param_val != 0
11441fbaed0Stron 	    && !MASTER_LIMIT_OK(serv->max_proc, serv->total_proc + 1)) {
11541fbaed0Stron 	    now = event_time();
11641fbaed0Stron 	    if (serv->stress_expire_time < now)
117e694ac3bStron 		master_restart_service(serv, NO_CONF_RELOAD);
11841fbaed0Stron 	    serv->stress_expire_time = now + 1000;
11941fbaed0Stron 	}
12041fbaed0Stron 	master_spawn(serv);
12141fbaed0Stron     }
12241fbaed0Stron }
12341fbaed0Stron 
124e694ac3bStron /* master_avail_listen - enforce the socket monitoring policy */
12541fbaed0Stron 
master_avail_listen(MASTER_SERV * serv)12641fbaed0Stron void    master_avail_listen(MASTER_SERV *serv)
12741fbaed0Stron {
12841fbaed0Stron     const char *myname = "master_avail_listen";
129e694ac3bStron     int     listen_flag;
13041fbaed0Stron     time_t  now;
13141fbaed0Stron     int     n;
13241fbaed0Stron 
13341fbaed0Stron     /*
134e694ac3bStron      * Caution: several other master_XXX modules call master_avail_listen(),
135e694ac3bStron      * master_avail_more() or master_avail_less(). To avoid mutual dependency
136e694ac3bStron      * problems, the code below invokes no code in other master_XXX modules,
137e694ac3bStron      * and modifies no data that is maintained by other master_XXX modules.
138e694ac3bStron      *
13941fbaed0Stron      * When no-one else is monitoring the service's listen socket, start
14041fbaed0Stron      * monitoring the socket for connection requests. All this under the
14141fbaed0Stron      * restriction that we have sufficient resources to service a connection
14241fbaed0Stron      * request.
14341fbaed0Stron      */
14441fbaed0Stron     if (msg_verbose)
145e694ac3bStron 	msg_info("%s: %s avail %d total %d max %d", myname, serv->name,
14641fbaed0Stron 		 serv->avail_proc, serv->total_proc, serv->max_proc);
147e694ac3bStron     if (MASTER_THROTTLED(serv) || serv->avail_proc > 0) {
148e694ac3bStron 	listen_flag = 0;
149e694ac3bStron     } else if (MASTER_LIMIT_OK(serv->max_proc, serv->total_proc)) {
150e694ac3bStron 	listen_flag = 1;
151e694ac3bStron     } else {
152e694ac3bStron 	listen_flag = 0;
153e694ac3bStron 	if (serv->stress_param_val != 0) {
154e694ac3bStron 	    now = event_time();
155e694ac3bStron 	    if (serv->busy_warn_time < now - 1000) {
15641fbaed0Stron 		serv->busy_warn_time = now;
15741fbaed0Stron 		msg_warn("service \"%s\" (%s) has reached its process limit \"%d\": "
15841fbaed0Stron 			 "new clients may experience noticeable delays",
15941fbaed0Stron 			 serv->ext_name, serv->name, serv->max_proc);
16041fbaed0Stron 		msg_warn("to avoid this condition, increase the process count "
16141fbaed0Stron 		      "in master.cf or reduce the service time per client");
16241fbaed0Stron 		msg_warn("see http://www.postfix.org/STRESS_README.html for "
16341fbaed0Stron 		      "examples of stress-adapting configuration settings");
16441fbaed0Stron 	    }
16541fbaed0Stron 	}
16641fbaed0Stron     }
167e694ac3bStron     if (listen_flag && !MASTER_LISTENING(serv)) {
168e694ac3bStron 	if (msg_verbose)
169e694ac3bStron 	    msg_info("%s: enable events %s", myname, serv->name);
170e694ac3bStron 	for (n = 0; n < serv->listen_fd_count; n++)
171e694ac3bStron 	    event_enable_read(serv->listen_fd[n], master_avail_event,
172837e7c1aSchristos 			      (void *) serv);
173e694ac3bStron 	serv->flags |= MASTER_FLAG_LISTEN;
174e694ac3bStron     } else if (!listen_flag && MASTER_LISTENING(serv)) {
175e694ac3bStron 	if (msg_verbose)
176e694ac3bStron 	    msg_info("%s: disable events %s", myname, serv->name);
177e694ac3bStron 	for (n = 0; n < serv->listen_fd_count; n++)
178e694ac3bStron 	    event_disable_readwrite(serv->listen_fd[n]);
179e694ac3bStron 	serv->flags &= ~MASTER_FLAG_LISTEN;
180e694ac3bStron     }
181e694ac3bStron }
18241fbaed0Stron 
18341fbaed0Stron /* master_avail_cleanup - cleanup */
18441fbaed0Stron 
master_avail_cleanup(MASTER_SERV * serv)18541fbaed0Stron void    master_avail_cleanup(MASTER_SERV *serv)
18641fbaed0Stron {
18741fbaed0Stron     int     n;
18841fbaed0Stron 
18941fbaed0Stron     master_delete_children(serv);		/* XXX calls
19041fbaed0Stron 						 * master_avail_listen */
191e694ac3bStron 
192e694ac3bStron     /*
193e694ac3bStron      * This code is redundant because master_delete_children() throttles the
194e694ac3bStron      * service temporarily before calling master_avail_listen/less(), which
195e694ac3bStron      * then turn off read events. This temporary throttling is not documented
196e694ac3bStron      * (it is only an optimization), and therefore we must not depend on it.
197e694ac3bStron      */
198e694ac3bStron     if (MASTER_LISTENING(serv)) {
19941fbaed0Stron 	for (n = 0; n < serv->listen_fd_count; n++)
200e694ac3bStron 	    event_disable_readwrite(serv->listen_fd[n]);
201e694ac3bStron 	serv->flags &= ~MASTER_FLAG_LISTEN;
202e694ac3bStron     }
20341fbaed0Stron }
20441fbaed0Stron 
20541fbaed0Stron /* master_avail_more - one more available child process */
20641fbaed0Stron 
master_avail_more(MASTER_SERV * serv,MASTER_PROC * proc)20741fbaed0Stron void    master_avail_more(MASTER_SERV *serv, MASTER_PROC *proc)
20841fbaed0Stron {
20941fbaed0Stron     const char *myname = "master_avail_more";
21041fbaed0Stron 
21141fbaed0Stron     /*
212e694ac3bStron      * Caution: several other master_XXX modules call master_avail_listen(),
213e694ac3bStron      * master_avail_more() or master_avail_less(). To avoid mutual dependency
214e694ac3bStron      * problems, the code below invokes no code in other master_XXX modules,
215e694ac3bStron      * and modifies no data that is maintained by other master_XXX modules.
216e694ac3bStron      *
21741fbaed0Stron      * This child process has become available for servicing connection
21841fbaed0Stron      * requests, so we can stop monitoring the service's listen socket. The
21941fbaed0Stron      * child will do it for us.
22041fbaed0Stron      */
22141fbaed0Stron     if (msg_verbose)
22241fbaed0Stron 	msg_info("%s: pid %d (%s)", myname, proc->pid, proc->serv->name);
22341fbaed0Stron     if (proc->avail == MASTER_STAT_AVAIL)
22441fbaed0Stron 	msg_panic("%s: process already available", myname);
22541fbaed0Stron     serv->avail_proc++;
22641fbaed0Stron     proc->avail = MASTER_STAT_AVAIL;
227e694ac3bStron     master_avail_listen(serv);
22841fbaed0Stron }
22941fbaed0Stron 
23041fbaed0Stron /* master_avail_less - one less available child process */
23141fbaed0Stron 
master_avail_less(MASTER_SERV * serv,MASTER_PROC * proc)23241fbaed0Stron void    master_avail_less(MASTER_SERV *serv, MASTER_PROC *proc)
23341fbaed0Stron {
23441fbaed0Stron     const char *myname = "master_avail_less";
23541fbaed0Stron 
23641fbaed0Stron     /*
237e694ac3bStron      * Caution: several other master_XXX modules call master_avail_listen(),
238e694ac3bStron      * master_avail_more() or master_avail_less(). To avoid mutual dependency
239e694ac3bStron      * problems, the code below invokes no code in other master_XXX modules,
240e694ac3bStron      * and modifies no data that is maintained by other master_XXX modules.
241e694ac3bStron      *
242837e7c1aSchristos      * This child is no longer available for servicing connection requests. When
243837e7c1aSchristos      * no child processes are available, start monitoring the service's
24441fbaed0Stron      * listen socket for new connection requests.
24541fbaed0Stron      */
24641fbaed0Stron     if (msg_verbose)
24741fbaed0Stron 	msg_info("%s: pid %d (%s)", myname, proc->pid, proc->serv->name);
24841fbaed0Stron     if (proc->avail != MASTER_STAT_AVAIL)
24941fbaed0Stron 	msg_panic("%s: process not available", myname);
25041fbaed0Stron     serv->avail_proc--;
25141fbaed0Stron     proc->avail = MASTER_STAT_TAKEN;
25241fbaed0Stron     master_avail_listen(serv);
25341fbaed0Stron }
254