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