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