event_server.c revision 1.4
1/*	$NetBSD: event_server.c,v 1.4 2022/10/08 16:12:46 christos Exp $	*/
2
3/*++
4/* NAME
5/*	event_server 3
6/* SUMMARY
7/*	skeleton multi-threaded mail subsystem
8/* SYNOPSIS
9/*	#include <mail_server.h>
10/*
11/*	NORETURN event_server_main(argc, argv, service, key, value, ...)
12/*	int	argc;
13/*	char	**argv;
14/*	void	(*service)(VSTREAM *stream, char *service_name, char **argv);
15/*	int	key;
16/*
17/*	void	event_server_disconnect(fd)
18/*	VSTREAM	*stream;
19/*
20/*	void	event_server_drain()
21/* DESCRIPTION
22/*	This module implements a skeleton for event-driven
23/*	mail subsystems: mail subsystem programs that service multiple
24/*	clients at the same time. The resulting program expects to be run
25/*	from the \fBmaster\fR process.
26/*
27/*	event_server_main() is the skeleton entry point. It should be
28/*	called from the application main program.  The skeleton does all
29/*	the generic command-line processing, initialization of
30/*	configurable parameters, and connection management.
31/*	Unlike multi_server, this skeleton does not attempt to manage
32/*	all the events on a client connection.
33/*	The skeleton never returns.
34/*
35/*	Arguments:
36/* .IP "void (*service)(VSTREAM *stream, char *service_name, char **argv)"
37/*	A pointer to a function that is called by the skeleton each
38/*	time a client connects to the program's service port. The
39/*	function is run after the program has optionally dropped
40/*	its privileges. The application is responsible for managing
41/*	subsequent I/O events on the stream, and is responsible for
42/*	calling event_server_disconnect() when the stream is closed.
43/*	The stream initial state is non-blocking mode.
44/*	Optional connection attributes are provided as a hash that
45/*	is attached as stream context. NOTE: the attributes are
46/*	destroyed after this function is called. The service
47/*	name argument corresponds to the service name in the master.cf
48/*	file.  The argv argument specifies command-line arguments
49/*	left over after options processing.
50/* .PP
51/*	Optional arguments are specified as a null-terminated list
52/*	with macros that have zero or more arguments:
53/* .IP "CA_MAIL_SERVER_REQ_INT_TABLE(CONFIG_INT_TABLE *)"
54/*	A table with configurable parameters, to be loaded from the
55/*	global Postfix configuration file. Tables are loaded in the
56/*	order as specified, and multiple instances of the same type
57/*	are allowed.
58/* .IP "CA_MAIL_SERVER_REQ_LONG_TABLE(CONFIG_LONG_TABLE *)"
59/*	A table with configurable parameters, to be loaded from the
60/*	global Postfix configuration file. Tables are loaded in the
61/*	order as specified, and multiple instances of the same type
62/*	are allowed.
63/* .IP "CA_MAIL_SERVER_REQ_STR_TABLE(CONFIG_STR_TABLE *)"
64/*	A table with configurable parameters, to be loaded from the
65/*	global Postfix configuration file. Tables are loaded in the
66/*	order as specified, and multiple instances of the same type
67/*	are allowed.
68/* .IP "CA_MAIL_SERVER_REQ_BOOL_TABLE(CONFIG_BOOL_TABLE *)"
69/*	A table with configurable parameters, to be loaded from the
70/*	global Postfix configuration file. Tables are loaded in the
71/*	order as specified, and multiple instances of the same type
72/*	are allowed.
73/* .IP "CA_MAIL_SERVER_REQ_TIME_TABLE(CONFIG_TIME_TABLE *)"
74/*	A table with configurable parameters, to be loaded from the
75/*	global Postfix configuration file. Tables are loaded in the
76/*	order as specified, and multiple instances of the same type
77/*	are allowed.
78/* .IP "CA_MAIL_SERVER_REQ_RAW_TABLE(CONFIG_RAW_TABLE *)"
79/*	A table with configurable parameters, to be loaded from the
80/*	global Postfix configuration file. Tables are loaded in the
81/*	order as specified, and multiple instances of the same type
82/*	are allowed. Raw parameters are not subjected to $name
83/*	evaluation.
84/* .IP "CA_MAIL_SERVER_REQ_NINT_TABLE(CONFIG_NINT_TABLE *)"
85/*	A table with configurable parameters, to be loaded from the
86/*	global Postfix configuration file. Tables are loaded in the
87/*	order as specified, and multiple instances of the same type
88/*	are allowed.
89/* .IP "CA_MAIL_SERVER_REQ_NBOOL_TABLE(CONFIG_NBOOL_TABLE *)"
90/*	A table with configurable parameters, to be loaded from the
91/*	global Postfix configuration file. Tables are loaded in the
92/*	order as specified, and multiple instances of the same type
93/*	are allowed.
94/* .IP "CA_MAIL_SERVER_REQ_PRE_INIT(void *(char *service_name, char **argv))"
95/*	A pointer to a function that is called once
96/*	by the skeleton after it has read the global configuration file
97/*	and after it has processed command-line arguments, but before
98/*	the skeleton has optionally relinquished the process privileges.
99/* .sp
100/*	Only the last instance of this parameter type is remembered.
101/* .IP "CA_MAIL_SERVER_REQ_POST_INIT(void *(char *service_name, char **argv))"
102/*	A pointer to a function that is called once
103/*	by the skeleton after it has optionally relinquished the process
104/*	privileges, but before servicing client connection requests.
105/* .sp
106/*	Only the last instance of this parameter type is remembered.
107/* .IP "CA_MAIL_SERVER_REQ_LOOP(int *(char *service_name, char **argv))"
108/*	A pointer to function that is executed from
109/*	within the event loop, whenever an I/O or timer event has happened,
110/*	or whenever nothing has happened for a specified amount of time.
111/*	The result value of the function specifies how long to wait until
112/*	the next event. Specify -1 to wait for "as long as it takes".
113/* .sp
114/*	Only the last instance of this parameter type is remembered.
115/* .IP "CA_MAIL_SERVER_EXIT(void *(char *service_name, char **argv))"
116/*	A pointer to function that is executed immediately before normal
117/*	process termination.
118/* .IP "CA_MAIL_SERVER_PRE_ACCEPT(void *(char *service_name, char **argv))"
119/*	Function to be executed prior to accepting a new connection.
120/* .sp
121/*	Only the last instance of this parameter type is remembered.
122/* .IP "CA_MAIL_SERVER_PRE_DISCONN(VSTREAM *, char *service_name, char **argv)"
123/*	A pointer to a function that is called
124/*	by the event_server_disconnect() function (see below).
125/* .sp
126/*	Only the last instance of this parameter type is remembered.
127/* .IP CA_MAIL_SERVER_IN_FLOW_DELAY
128/*	Pause $in_flow_delay seconds when no "mail flow control token"
129/*	is available. A token is consumed for each connection request.
130/* .IP CA_MAIL_SERVER_SOLITARY
131/*	This service must be configured with process limit of 1.
132/* .IP CA_MAIL_SERVER_UNLIMITED
133/*	This service must be configured with process limit of 0.
134/* .IP CA_MAIL_SERVER_PRIVILEGED
135/*	This service must be configured as privileged.
136/* .IP "CA_MAIL_SERVER_SLOW_EXIT(void *(char *service_name, char **argv))"
137/*	A pointer to a function that is called after "postfix reload"
138/*	or "master exit".  The application can call event_server_drain()
139/*	(see below) to finish ongoing activities in the background.
140/* .IP "CA_MAIL_SERVER_WATCHDOG(int *)"
141/*	Override the default 1000s watchdog timeout. The value is
142/*	used after command-line and main.cf file processing.
143/* .IP "CA_MAIL_SERVER_BOUNCE_INIT(const char *, const char **)"
144/*	Initialize the DSN filter for the bounce/defer service
145/*	clients with the specified map source and map names.
146/* .IP "CA_MAIL_SERVER_RETIRE_ME"
147/*	Prevent a process from being reused indefinitely. After
148/*	(var_max_use * var_max_idle) seconds or some sane constant,
149/*	stop accepting new connections and terminate voluntarily
150/*	when the process becomes idle.
151/* .PP
152/*	event_server_disconnect() should be called by the application
153/*	to close a client connection.
154/*
155/*	event_server_drain() should be called when the application
156/*	no longer wishes to accept new client connections. Existing
157/*	clients are handled in a background process, and the process
158/*	terminates when the last client is disconnected. A non-zero
159/*	result means this call should be tried again later.
160/*
161/*	The var_use_limit variable limits the number of clients
162/*	that a server can service before it commits suicide. This
163/*	value is taken from the global \fBmain.cf\fR configuration
164/*	file. Setting \fBvar_use_limit\fR to zero disables the
165/*	client limit.
166/*
167/*	The var_idle_limit variable limits the time that a service
168/*	receives no client connection requests before it commits
169/*	suicide.  This value is taken from the global \fBmain.cf\fR
170/*	configuration file. Setting \fBvar_idle_limit\fR to zero
171/*	disables the idle limit.
172/* DIAGNOSTICS
173/*	Problems and transactions are logged to \fBsyslogd\fR(8)
174/*	or \fBpostlogd\fR(8).
175/* SEE ALSO
176/*	master(8), master process
177/*	postlogd(8), Postfix logging
178/*	syslogd(8), system logging
179/* LICENSE
180/* .ad
181/* .fi
182/*	The Secure Mailer license must be distributed with this software.
183/* AUTHOR(S)
184/*	Wietse Venema
185/*	IBM T.J. Watson Research
186/*	P.O. Box 704
187/*	Yorktown Heights, NY 10598, USA
188/*
189/*	Wietse Venema
190/*	Google, Inc.
191/*	111 8th Avenue
192/*	New York, NY 10011, USA
193/*--*/
194
195/* System library. */
196
197#include <sys_defs.h>
198#include <sys/socket.h>
199#include <sys/time.h>			/* select() */
200#include <unistd.h>
201#include <signal.h>
202#include <stdlib.h>
203#include <limits.h>
204#include <string.h>
205#include <errno.h>
206#include <fcntl.h>
207#include <stdarg.h>
208#ifdef STRCASECMP_IN_STRINGS_H
209#include <strings.h>
210#endif
211#include <time.h>
212
213#ifdef USE_SYS_SELECT_H
214#include <sys/select.h>			/* select() */
215#endif
216
217/* Utility library. */
218
219#include <msg.h>
220#include <msg_vstream.h>
221#include <chroot_uid.h>
222#include <listen.h>
223#include <events.h>
224#include <vstring.h>
225#include <vstream.h>
226#include <msg_vstream.h>
227#include <mymalloc.h>
228#include <iostuff.h>
229#include <stringops.h>
230#include <sane_accept.h>
231#include <myflock.h>
232#include <safe_open.h>
233#include <listen.h>
234#include <watchdog.h>
235#include <split_at.h>
236
237/* Global library. */
238
239#include <mail_task.h>
240#include <debug_process.h>
241#include <mail_params.h>
242#include <mail_conf.h>
243#include <mail_dict.h>
244#include <timed_ipc.h>
245#include <resolve_local.h>
246#include <mail_flow.h>
247#include <mail_version.h>
248#include <bounce.h>
249#include <maillog_client.h>
250
251/* Process manager. */
252
253#include "master_proto.h"
254
255/* Application-specific */
256
257#include "mail_server.h"
258
259 /*
260  * Global state.
261  */
262static int client_count;
263static int use_count;
264static int socket_count = 1;
265
266static void (*event_server_service) (VSTREAM *, char *, char **);
267static char *event_server_name;
268static char **event_server_argv;
269static void (*event_server_accept) (int, void *);
270static void (*event_server_onexit) (char *, char **);
271static void (*event_server_pre_accept) (char *, char **);
272static VSTREAM *event_server_lock;
273static int event_server_in_flow_delay;
274static unsigned event_server_generation;
275static void (*event_server_pre_disconn) (VSTREAM *, char *, char **);
276static void (*event_server_slow_exit) (char *, char **);
277static int event_server_watchdog = 1000;
278
279/* event_server_exit - normal termination */
280
281static NORETURN event_server_exit(void)
282{
283    if (event_server_onexit)
284	event_server_onexit(event_server_name, event_server_argv);
285    exit(0);
286}
287
288/* event_server_retire - retire when idle */
289
290static void event_server_retire(int unused_event, void *unused_context)
291{
292    if (msg_verbose)
293	msg_info("time to retire -- %s", event_server_slow_exit ?
294		 "draining" : "exiting");
295    event_disable_readwrite(MASTER_STATUS_FD);
296    if (event_server_slow_exit)
297	event_server_slow_exit(event_server_name, event_server_argv);
298    else
299	event_server_exit();
300}
301
302/* event_server_abort - terminate after abnormal master exit */
303
304static void event_server_abort(int unused_event, void *unused_context)
305{
306    if (msg_verbose)
307	msg_info("master disconnect -- %s", event_server_slow_exit ?
308		 "draining" : "exiting");
309    event_disable_readwrite(MASTER_STATUS_FD);
310    if (event_server_slow_exit)
311	event_server_slow_exit(event_server_name, event_server_argv);
312    else
313	event_server_exit();
314}
315
316/* event_server_timeout - idle time exceeded */
317
318static void event_server_timeout(int unused_event, void *unused_context)
319{
320    if (msg_verbose)
321	msg_info("idle timeout -- exiting");
322    event_server_exit();
323}
324
325/* event_server_drain - stop accepting new clients */
326
327int     event_server_drain(void)
328{
329    const char *myname = "event_server_drain";
330    int     fd;
331
332    switch (fork()) {
333	/* Try again later. */
334    case -1:
335	return (-1);
336	/* Finish existing clients in the background, then terminate. */
337    case 0:
338	(void) msg_cleanup((MSG_CLEANUP_FN) 0);
339	event_fork();
340	for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) {
341	    event_disable_readwrite(fd);
342	    (void) close(fd);
343	    /* Play safe - don't reuse this file number. */
344	    if (DUP2(STDIN_FILENO, fd) < 0)
345		msg_warn("%s: dup2(%d, %d): %m", myname, STDIN_FILENO, fd);
346	}
347	var_use_limit = 1;
348	return (0);
349	/* Let the master start a new process. */
350    default:
351	exit(0);
352    }
353}
354
355/* event_server_disconnect - terminate client session */
356
357void    event_server_disconnect(VSTREAM *stream)
358{
359    if (msg_verbose)
360	msg_info("connection closed fd %d", vstream_fileno(stream));
361    if (event_server_pre_disconn)
362	event_server_pre_disconn(stream, event_server_name, event_server_argv);
363    (void) vstream_fclose(stream);
364    client_count--;
365    /* Avoid integer wrap-around in a persistent process.  */
366    if (use_count < INT_MAX)
367	use_count++;
368    if (client_count == 0 && var_idle_limit > 0)
369	event_request_timer(event_server_timeout, (void *) 0, var_idle_limit);
370}
371
372/* event_server_execute - in case (char *) != (struct *) */
373
374static void event_server_execute(int unused_event, void *context)
375{
376    VSTREAM *stream = (VSTREAM *) context;
377    HTABLE *attr = (HTABLE *) vstream_context(stream);
378
379    if (event_server_lock != 0
380	&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
381		   MYFLOCK_OP_NONE) < 0)
382	msg_fatal("select unlock: %m");
383
384    /*
385     * Do bother the application when the client disconnected. Don't drop the
386     * already accepted client request after "postfix reload"; that would be
387     * rude.
388     */
389    if (master_notify(var_pid, event_server_generation, MASTER_STAT_TAKEN) < 0)
390	 /* void */ ;
391    event_server_service(stream, event_server_name, event_server_argv);
392    if (master_notify(var_pid, event_server_generation, MASTER_STAT_AVAIL) < 0)
393	event_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
394    if (attr)
395	htable_free(attr, myfree);
396}
397
398/* event_server_wakeup - wake up application */
399
400static void event_server_wakeup(int fd, HTABLE *attr)
401{
402    VSTREAM *stream;
403    char   *tmp;
404
405#if defined(F_DUPFD) && (EVENTS_STYLE != EVENTS_STYLE_SELECT)
406#ifndef THRESHOLD_FD_WORKAROUND
407#define THRESHOLD_FD_WORKAROUND 128
408#endif
409    int     new_fd;
410
411    /*
412     * Leave some handles < FD_SETSIZE for DBMS libraries, in the unlikely
413     * case of a multi-server with a thousand clients.
414     */
415    if (fd < THRESHOLD_FD_WORKAROUND) {
416	if ((new_fd = fcntl(fd, F_DUPFD, THRESHOLD_FD_WORKAROUND)) < 0)
417	    msg_fatal("fcntl F_DUPFD: %m");
418	(void) close(fd);
419	fd = new_fd;
420    }
421#endif
422    if (msg_verbose)
423	msg_info("connection established fd %d", fd);
424    non_blocking(fd, BLOCKING);
425    close_on_exec(fd, CLOSE_ON_EXEC);
426    client_count++;
427    stream = vstream_fdopen(fd, O_RDWR);
428    tmp = concatenate(event_server_name, " socket", (char *) 0);
429    vstream_control(stream,
430		    CA_VSTREAM_CTL_PATH(tmp),
431		    CA_VSTREAM_CTL_CONTEXT((void *) attr),
432		    CA_VSTREAM_CTL_END);
433    myfree(tmp);
434    timed_ipc_setup(stream);
435    if (event_server_in_flow_delay && mail_flow_get(1) < 0)
436	event_request_timer(event_server_execute, (void *) stream,
437			    var_in_flow_delay);
438    else
439	event_server_execute(0, (void *) stream);
440}
441
442/* event_server_accept_local - accept client connection request */
443
444static void event_server_accept_local(int unused_event, void *context)
445{
446    int     listen_fd = CAST_ANY_PTR_TO_INT(context);
447    int     time_left = -1;
448    int     fd;
449
450    /*
451     * Be prepared for accept() to fail because some other process already
452     * got the connection (the number of processes competing for clients is
453     * kept small, so this is not a "thundering herd" problem). If the
454     * accept() succeeds, be sure to disable non-blocking I/O, in order to
455     * minimize confusion.
456     */
457    if (client_count == 0 && var_idle_limit > 0)
458	time_left = event_cancel_timer(event_server_timeout, (void *) 0);
459
460    if (event_server_pre_accept)
461	event_server_pre_accept(event_server_name, event_server_argv);
462    fd = LOCAL_ACCEPT(listen_fd);
463    if (event_server_lock != 0
464	&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
465		   MYFLOCK_OP_NONE) < 0)
466	msg_fatal("select unlock: %m");
467    if (fd < 0) {
468	if (errno != EAGAIN)
469	    msg_error("accept connection: %m");
470	if (time_left >= 0)
471	    event_request_timer(event_server_timeout, (void *) 0, time_left);
472	return;
473    }
474    event_server_wakeup(fd, (HTABLE *) 0);
475}
476
477#ifdef MASTER_XPORT_NAME_PASS
478
479/* event_server_accept_pass - accept descriptor */
480
481static void event_server_accept_pass(int unused_event, void *context)
482{
483    int     listen_fd = CAST_ANY_PTR_TO_INT(context);
484    int     time_left = -1;
485    int     fd;
486    HTABLE *attr = 0;
487
488    /*
489     * Be prepared for accept() to fail because some other process already
490     * got the connection (the number of processes competing for clients is
491     * kept small, so this is not a "thundering herd" problem). If the
492     * accept() succeeds, be sure to disable non-blocking I/O, in order to
493     * minimize confusion.
494     */
495    if (client_count == 0 && var_idle_limit > 0)
496	time_left = event_cancel_timer(event_server_timeout, (void *) 0);
497
498    if (event_server_pre_accept)
499	event_server_pre_accept(event_server_name, event_server_argv);
500    fd = pass_accept_attr(listen_fd, &attr);
501    if (event_server_lock != 0
502	&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
503		   MYFLOCK_OP_NONE) < 0)
504	msg_fatal("select unlock: %m");
505    if (fd < 0) {
506	if (errno != EAGAIN)
507	    msg_error("accept connection: %m");
508	if (time_left >= 0)
509	    event_request_timer(event_server_timeout, (void *) 0, time_left);
510	return;
511    }
512    event_server_wakeup(fd, attr);
513}
514
515#endif
516
517/* event_server_accept_inet - accept client connection request */
518
519static void event_server_accept_inet(int unused_event, void *context)
520{
521    int     listen_fd = CAST_ANY_PTR_TO_INT(context);
522    int     time_left = -1;
523    int     fd;
524
525    /*
526     * Be prepared for accept() to fail because some other process already
527     * got the connection (the number of processes competing for clients is
528     * kept small, so this is not a "thundering herd" problem). If the
529     * accept() succeeds, be sure to disable non-blocking I/O, in order to
530     * minimize confusion.
531     */
532    if (client_count == 0 && var_idle_limit > 0)
533	time_left = event_cancel_timer(event_server_timeout, (void *) 0);
534
535    if (event_server_pre_accept)
536	event_server_pre_accept(event_server_name, event_server_argv);
537    fd = inet_accept(listen_fd);
538    if (event_server_lock != 0
539	&& myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
540		   MYFLOCK_OP_NONE) < 0)
541	msg_fatal("select unlock: %m");
542    if (fd < 0) {
543	if (errno != EAGAIN)
544	    msg_error("accept connection: %m");
545	if (time_left >= 0)
546	    event_request_timer(event_server_timeout, (void *) 0, time_left);
547	return;
548    }
549    event_server_wakeup(fd, (HTABLE *) 0);
550}
551
552/* event_server_main - the real main program */
553
554NORETURN event_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
555{
556    const char *myname = "event_server_main";
557    VSTREAM *stream = 0;
558    char   *root_dir = 0;
559    char   *user_name = 0;
560    int     debug_me = 0;
561    int     daemon_mode = 1;
562    char   *service_name = basename(argv[0]);
563    int     delay;
564    int     c;
565    int     fd;
566    va_list ap;
567    MAIL_SERVER_INIT_FN pre_init = 0;
568    MAIL_SERVER_INIT_FN post_init = 0;
569    MAIL_SERVER_LOOP_FN loop = 0;
570    int     key;
571    char   *transport = 0;
572
573#if 0
574    char   *lock_path;
575    VSTRING *why;
576
577#endif
578    int     alone = 0;
579    int     zerolimit = 0;
580    WATCHDOG *watchdog;
581    char   *oname_val;
582    char   *oname;
583    char   *oval;
584    const char *err;
585    char   *generation;
586    int     msg_vstream_needed = 0;
587    const char *dsn_filter_title;
588    const char **dsn_filter_maps;
589    int     retire_me_from_flags = 0;
590    int     retire_me = 0;
591
592    /*
593     * Process environment options as early as we can.
594     */
595    if (getenv(CONF_ENV_VERB))
596	msg_verbose = 1;
597    if (getenv(CONF_ENV_DEBUG))
598	debug_me = 1;
599
600    /*
601     * Don't die when a process goes away unexpectedly.
602     */
603    signal(SIGPIPE, SIG_IGN);
604
605    /*
606     * Don't die for frivolous reasons.
607     */
608#ifdef SIGXFSZ
609    signal(SIGXFSZ, SIG_IGN);
610#endif
611
612    /*
613     * May need this every now and then.
614     */
615    var_procname = mystrdup(basename(argv[0]));
616    set_mail_conf_str(VAR_PROCNAME, var_procname);
617
618    /*
619     * Initialize logging and exit handler. Do the syslog first, so that its
620     * initialization completes before we enter the optional chroot jail.
621     */
622    maillog_client_init(mail_task(var_procname), MAILLOG_CLIENT_FLAG_NONE);
623    if (msg_verbose)
624	msg_info("daemon started");
625
626    /*
627     * Check the Postfix library version as soon as we enable logging.
628     */
629    MAIL_VERSION_CHECK;
630
631    /*
632     * Initialize from the configuration file. Allow command-line options to
633     * override compiled-in defaults or configured parameter values.
634     */
635    mail_conf_suck();
636
637    /*
638     * After database open error, continue execution with reduced
639     * functionality.
640     */
641    dict_allow_surrogate = 1;
642
643    /*
644     * Pick up policy settings from master process. Shut up error messages to
645     * stderr, because no-one is going to see them.
646     */
647    opterr = 0;
648    while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:r:s:St:uvVz")) > 0) {
649	switch (c) {
650	case 'c':
651	    root_dir = "setme";
652	    break;
653	case 'd':
654	    daemon_mode = 0;
655	    break;
656	case 'D':
657	    debug_me = 1;
658	    break;
659	case 'i':
660	    mail_conf_update(VAR_MAX_IDLE, optarg);
661	    break;
662	case 'l':
663	    alone = 1;
664	    break;
665	case 'm':
666	    mail_conf_update(VAR_MAX_USE, optarg);
667	    break;
668	case 'n':
669	    service_name = optarg;
670	    break;
671	case 'o':
672	    oname_val = mystrdup(optarg);
673	    if ((err = split_nameval(oname_val, &oname, &oval)) != 0)
674		msg_fatal("invalid \"-o %s\" option value: %s", optarg, err);
675	    mail_conf_update(oname, oval);
676	    myfree(oname_val);
677	    break;
678	case 'r':
679	    if ((retire_me_from_flags = atoi(optarg)) <= 0)
680		msg_fatal("invalid retirement time: %s", optarg);
681	    break;
682	case 's':
683	    if ((socket_count = atoi(optarg)) <= 0)
684		msg_fatal("invalid socket_count: %s", optarg);
685	    break;
686	case 'S':
687	    stream = VSTREAM_IN;
688	    break;
689	case 'u':
690	    user_name = "setme";
691	    break;
692	case 't':
693	    transport = optarg;
694	    break;
695	case 'v':
696	    msg_verbose++;
697	    break;
698	case 'V':
699	    if (++msg_vstream_needed == 1)
700		msg_vstream_init(mail_task(var_procname), VSTREAM_ERR);
701	    break;
702	case 'z':
703	    zerolimit = 1;
704	    break;
705	default:
706	    msg_fatal("invalid option: %c", optopt);
707	    break;
708	}
709    }
710    set_mail_conf_str(VAR_SERVNAME, service_name);
711
712    /*
713     * Initialize generic parameters and re-initialize logging in case of a
714     * non-default program name or logging destination.
715     */
716    mail_params_init();
717    maillog_client_init(mail_task(var_procname), MAILLOG_CLIENT_FLAG_NONE);
718
719    /*
720     * Register higher-level dictionaries and initialize the support for
721     * dynamically-loaded dictionaries.
722     */
723    mail_dict_init();
724
725    /*
726     * If not connected to stdin, stdin must not be a terminal.
727     */
728    if (daemon_mode && stream == 0 && isatty(STDIN_FILENO)) {
729	msg_vstream_init(var_procname, VSTREAM_ERR);
730	msg_fatal("do not run this command by hand");
731    }
732
733    /*
734     * Application-specific initialization.
735     */
736    va_start(ap, service);
737    while ((key = va_arg(ap, int)) != 0) {
738	switch (key) {
739	case MAIL_SERVER_INT_TABLE:
740	    get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
741	    break;
742	case MAIL_SERVER_LONG_TABLE:
743	    get_mail_conf_long_table(va_arg(ap, CONFIG_LONG_TABLE *));
744	    break;
745	case MAIL_SERVER_STR_TABLE:
746	    get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
747	    break;
748	case MAIL_SERVER_BOOL_TABLE:
749	    get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
750	    break;
751	case MAIL_SERVER_TIME_TABLE:
752	    get_mail_conf_time_table(va_arg(ap, CONFIG_TIME_TABLE *));
753	    break;
754	case MAIL_SERVER_RAW_TABLE:
755	    get_mail_conf_raw_table(va_arg(ap, CONFIG_RAW_TABLE *));
756	    break;
757	case MAIL_SERVER_NINT_TABLE:
758	    get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *));
759	    break;
760	case MAIL_SERVER_NBOOL_TABLE:
761	    get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *));
762	    break;
763	case MAIL_SERVER_PRE_INIT:
764	    pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
765	    break;
766	case MAIL_SERVER_POST_INIT:
767	    post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
768	    break;
769	case MAIL_SERVER_LOOP:
770	    loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
771	    break;
772	case MAIL_SERVER_EXIT:
773	    event_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
774	    break;
775	case MAIL_SERVER_PRE_ACCEPT:
776	    event_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
777	    break;
778	case MAIL_SERVER_PRE_DISCONN:
779	    event_server_pre_disconn = va_arg(ap, MAIL_SERVER_DISCONN_FN);
780	    break;
781	case MAIL_SERVER_IN_FLOW_DELAY:
782	    event_server_in_flow_delay = 1;
783	    break;
784	case MAIL_SERVER_SOLITARY:
785	    if (stream == 0 && !alone)
786		msg_fatal("service %s requires a process limit of 1",
787			  service_name);
788	    break;
789	case MAIL_SERVER_UNLIMITED:
790	    if (stream == 0 && !zerolimit)
791		msg_fatal("service %s requires a process limit of 0",
792			  service_name);
793	    break;
794	case MAIL_SERVER_PRIVILEGED:
795	    if (user_name)
796		msg_fatal("service %s requires privileged operation",
797			  service_name);
798	    break;
799	case MAIL_SERVER_WATCHDOG:
800	    event_server_watchdog = *va_arg(ap, int *);
801	    break;
802	case MAIL_SERVER_SLOW_EXIT:
803	    event_server_slow_exit = va_arg(ap, MAIL_SERVER_SLOW_EXIT_FN);
804	    break;
805	case MAIL_SERVER_BOUNCE_INIT:
806	    dsn_filter_title = va_arg(ap, const char *);
807	    dsn_filter_maps = va_arg(ap, const char **);
808	    bounce_client_init(dsn_filter_title, *dsn_filter_maps);
809	    break;
810	case MAIL_SERVER_RETIRE_ME:
811	    if (retire_me_from_flags > 0)
812		retire_me = retire_me_from_flags;
813	    else if (var_idle_limit == 0 || var_use_limit == 0
814		     || var_idle_limit > 18000 / var_use_limit)
815		retire_me = 18000;
816	    else
817		retire_me = var_idle_limit * var_use_limit;
818	    break;
819	default:
820	    msg_panic("%s: unknown argument type: %d", myname, key);
821	}
822    }
823    va_end(ap);
824
825    if (root_dir)
826	root_dir = var_queue_dir;
827    if (user_name)
828	user_name = var_mail_owner;
829
830    /*
831     * Can options be required?
832     */
833    if (stream == 0) {
834	if (transport == 0)
835	    msg_fatal("no transport type specified");
836	if (strcasecmp(transport, MASTER_XPORT_NAME_INET) == 0)
837	    event_server_accept = event_server_accept_inet;
838	else if (strcasecmp(transport, MASTER_XPORT_NAME_UNIX) == 0)
839	    event_server_accept = event_server_accept_local;
840#ifdef MASTER_XPORT_NAME_PASS
841	else if (strcasecmp(transport, MASTER_XPORT_NAME_PASS) == 0)
842	    event_server_accept = event_server_accept_pass;
843#endif
844	else
845	    msg_fatal("unsupported transport type: %s", transport);
846    }
847
848    /*
849     * Retrieve process generation from environment.
850     */
851    if ((generation = getenv(MASTER_GEN_NAME)) != 0) {
852	if (!alldig(generation))
853	    msg_fatal("bad generation: %s", generation);
854	OCTAL_TO_UNSIGNED(event_server_generation, generation);
855	if (msg_verbose)
856	    msg_info("process generation: %s (%o)",
857		     generation, event_server_generation);
858    }
859
860    /*
861     * Optionally start the debugger on ourself.
862     */
863    if (debug_me)
864	debug_process();
865
866    /*
867     * Traditionally, BSD select() can't handle multiple processes selecting
868     * on the same socket, and wakes up every process in select(). See TCP/IP
869     * Illustrated volume 2 page 532. We avoid select() collisions with an
870     * external lock file.
871     */
872
873    /*
874     * XXX Can't compete for exclusive access to the listen socket because we
875     * also have to monitor existing client connections for service requests.
876     */
877#if 0
878    if (stream == 0 && !alone) {
879	lock_path = concatenate(DEF_PID_DIR, "/", transport,
880				".", service_name, (char *) 0);
881	why = vstring_alloc(1);
882	if ((event_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
883				      (struct stat *) 0, -1, -1, why)) == 0)
884	    msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
885	close_on_exec(vstream_fileno(event_server_lock), CLOSE_ON_EXEC);
886	myfree(lock_path);
887	vstring_free(why);
888    }
889#endif
890
891    /*
892     * Set up call-back info.
893     */
894    event_server_service = service;
895    event_server_name = service_name;
896    event_server_argv = argv + optind;
897
898    /*
899     * Run pre-jail initialization.
900     */
901    if (chdir(var_queue_dir) < 0)
902	msg_fatal("chdir(\"%s\"): %m", var_queue_dir);
903    if (pre_init)
904	pre_init(event_server_name, event_server_argv);
905
906    /*
907     * Optionally, restrict the damage that this process can do.
908     */
909    resolve_local_init();
910    tzset();
911    chroot_uid(root_dir, user_name);
912
913    /*
914     * Run post-jail initialization.
915     */
916    if (post_init)
917	post_init(event_server_name, event_server_argv);
918
919    /*
920     * Are we running as a one-shot server with the client connection on
921     * standard input? If so, make sure the output is written to stdout so as
922     * to satisfy common expectation.
923     */
924    if (stream != 0) {
925	vstream_control(stream,
926			CA_VSTREAM_CTL_DOUBLE,
927			CA_VSTREAM_CTL_WRITE_FD(STDOUT_FILENO),
928			CA_VSTREAM_CTL_END);
929	service(stream, event_server_name, event_server_argv);
930	vstream_fflush(stream);
931	event_server_exit();
932    }
933
934    /*
935     * Running as a semi-resident server. Service connection requests.
936     * Terminate when we have serviced a sufficient number of clients, when
937     * no-one has been talking to us for a configurable amount of time, or
938     * when the master process terminated abnormally.
939     */
940    if (var_idle_limit > 0)
941	event_request_timer(event_server_timeout, (void *) 0, var_idle_limit);
942    if (retire_me)
943	event_request_timer(event_server_retire, (void *) 0, retire_me);
944    for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) {
945	event_enable_read(fd, event_server_accept, CAST_INT_TO_VOID_PTR(fd));
946	close_on_exec(fd, CLOSE_ON_EXEC);
947    }
948    event_enable_read(MASTER_STATUS_FD, event_server_abort, (void *) 0);
949    close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC);
950    close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC);
951    close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC);
952    watchdog = watchdog_create(event_server_watchdog,
953			       (WATCHDOG_FN) 0, (void *) 0);
954
955    /*
956     * The event loop, at last.
957     */
958    while (var_use_limit == 0 || use_count < var_use_limit || client_count > 0) {
959	if (event_server_lock != 0) {
960	    watchdog_stop(watchdog);
961	    if (myflock(vstream_fileno(event_server_lock), INTERNAL_LOCK,
962			MYFLOCK_OP_EXCLUSIVE) < 0)
963		msg_fatal("select lock: %m");
964	}
965	watchdog_start(watchdog);
966	delay = loop ? loop(event_server_name, event_server_argv) : -1;
967	event_loop(delay);
968    }
969    event_server_exit();
970}
971