Home | History | Annotate | Line # | Download | only in master
      1 /*	$NetBSD: dgram_server.c,v 1.4 2025/02/25 19:15:46 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	dgram_server 3
      6 /* SUMMARY
      7 /*	skeleton datagram server subsystem
      8 /* SYNOPSIS
      9 /*	#include <mail_server.h>
     10 /*
     11 /*	NORETURN dgram_server_main(argc, argv, service, key, value, ...)
     12 /*	int	argc;
     13 /*	char	**argv;
     14 /*	void	(*service)(int sock, char *service_name, char **argv);
     15 /*	int	key;
     16 /* DESCRIPTION
     17 /*	This module implements a skeleton for mail subsystem programs
     18 /*	that wake up on client request and perform some activity
     19 /*	without further client interaction.  This module supports
     20 /*	local IPC via a UNIX-domain datagram socket. The resulting
     21 /*	program expects to be run from the \fBmaster\fR process.
     22 /*
     23 /*	dgram_server_main() is the skeleton entry point. It should
     24 /*	be called from the application main program. The skeleton
     25 /*	does the generic command-line options processing, initialization
     26 /*	of configurable parameters, and monitors a datagram socket. The
     27 /*	skeleton never returns.
     28 /*
     29 /*	Arguments:
     30 /* .IP "void (*service)(int sock, char *service_name, char **argv)"
     31 /*	A pointer to a function that is called by the skeleton each
     32 /*	time a client sends a datagram to the program's service
     33 /*	port. The function is run after the program has irrevocably
     34 /*	dropped its privileges. The sock argument specifies the socket
     35 /*	that the client should receive data from. The service name
     36 /*	argument corresponds to the service name in the master.cf
     37 /*	file.  The argv argument specifies command-line arguments
     38 /*	left over after options processing.
     39 /* .PP
     40 /*	Optional arguments are specified as a null-terminated list
     41 /*	with macros that have zero or more arguments:
     42 /* .IP "CA_MAIL_SERVER_INT_TABLE(CONFIG_INT_TABLE *)"
     43 /*	A table with configurable parameters, to be loaded from the
     44 /*	global Postfix configuration file. Tables are loaded in the
     45 /*	order as specified, and multiple instances of the same type
     46 /*	are allowed.
     47 /* .IP "CA_MAIL_SERVER_LONG_TABLE(CONFIG_LONG_TABLE *)"
     48 /*	A table with configurable parameters, to be loaded from the
     49 /*	global Postfix configuration file. Tables are loaded in the
     50 /*	order as specified, and multiple instances of the same type
     51 /*	are allowed.
     52 /* .IP "CA_MAIL_SERVER_STR_TABLE(CONFIG_STR_TABLE *)"
     53 /*	A table with configurable parameters, to be loaded from the
     54 /*	global Postfix configuration file. Tables are loaded in the
     55 /*	order as specified, and multiple instances of the same type
     56 /*	are allowed.
     57 /* .IP "CA_MAIL_SERVER_BOOL_TABLE(CONFIG_BOOL_TABLE *)"
     58 /*	A table with configurable parameters, to be loaded from the
     59 /*	global Postfix configuration file. Tables are loaded in the
     60 /*	order as specified, and multiple instances of the same type
     61 /*	are allowed.
     62 /* .IP "CA_MAIL_SERVER_TIME_TABLE(CONFIG_TIME_TABLE *)"
     63 /*	A table with configurable parameters, to be loaded from the
     64 /*	global Postfix configuration file. Tables are loaded in the
     65 /*	order as specified, and multiple instances of the same type
     66 /*	are allowed.
     67 /* .IP "CA_MAIL_SERVER_RAW_TABLE(CONFIG_RAW_TABLE *)"
     68 /*	A table with configurable parameters, to be loaded from the
     69 /*	global Postfix configuration file. Tables are loaded in the
     70 /*	order as specified, and multiple instances of the same type
     71 /*	are allowed. Raw parameters are not subjected to $name
     72 /*	evaluation.
     73 /* .IP "CA_MAIL_SERVER_NINT_TABLE(CONFIG_NINT_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_NBOOL_TABLE(CONFIG_NBOOL_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.
     83 /* .IP "CA_MAIL_SERVER_PRE_INIT(void *(char *service_name, char **argv))"
     84 /*	A pointer to a function that is called once
     85 /*	by the skeleton after it has read the global configuration file
     86 /*	and after it has processed command-line arguments, but before
     87 /*	the skeleton has optionally relinquished the process privileges.
     88 /* .sp
     89 /*	Only the last instance of this parameter type is remembered.
     90 /* .IP "CA_MAIL_SERVER_POST_INIT(void *(char *service_name, char **argv))"
     91 /*	A pointer to a function that is called once
     92 /*	by the skeleton after it has optionally relinquished the process
     93 /*	privileges, but before servicing client connection requests.
     94 /* .sp
     95 /*	Only the last instance of this parameter type is remembered.
     96 /* .IP "CA_MAIL_SERVER_LOOP(int *(char *service_name, char **argv))"
     97 /*	A pointer to function that is executed from
     98 /*	within the event loop, whenever an I/O or timer event has happened,
     99 /*	or whenever nothing has happened for a specified amount of time.
    100 /*	The result value of the function specifies how long to wait until
    101 /*	the next event. Specify -1 to wait for "as long as it takes".
    102 /* .sp
    103 /*	Only the last instance of this parameter type is remembered.
    104 /* .IP "CA_MAIL_SERVER_EXIT(void *(char *service_name, char **argv))"
    105 /*	A pointer to function that is executed immediately before normal
    106 /*	process termination.
    107 /* .sp
    108 /*	Only the last instance of this parameter type is remembered.
    109 /* .IP "CA_MAIL_SERVER_PRE_ACCEPT(void *(char *service_name, char **argv))"
    110 /*	Function to be executed prior to accepting a new request.
    111 /* .sp
    112 /*	Only the last instance of this parameter type is remembered.
    113 /* .IP "CA_MAIL_SERVER_IN_FLOW_DELAY(none)"
    114 /*	Pause $in_flow_delay seconds when no "mail flow control token"
    115 /*	is available. A token is consumed for each connection request.
    116 /* .IP CA_MAIL_SERVER_SOLITARY
    117 /*	This service must be configured with process limit of 1.
    118 /* .IP CA_MAIL_SERVER_UNLIMITED
    119 /*	This service must be configured with process limit of 0.
    120 /* .IP CA_MAIL_SERVER_PRIVILEGED
    121 /*	This service must be configured as privileged.
    122 /* .IP "CA_MAIL_SERVER_WATCHDOG(int *)"
    123 /*	Override the default 1000s watchdog timeout. The value is
    124 /*	used after command-line and main.cf file processing.
    125 /* .IP "CA_MAIL_SERVER_BOUNCE_INIT(const char *, const char **)"
    126 /*	Initialize the DSN filter for the bounce/defer service
    127 /*	clients with the specified map source and map names.
    128 /* .PP
    129 /*	The var_use_limit variable limits the number of clients that
    130 /*	a server can service before it commits suicide.
    131 /*	This value is taken from the global \fBmain.cf\fR configuration
    132 /*	file. Setting \fBvar_use_limit\fR to zero disables the client limit.
    133 /*
    134 /*	The var_idle_limit variable limits the time that a service
    135 /*	receives no client connection requests before it commits suicide.
    136 /*	This value is taken from the global \fBmain.cf\fR configuration
    137 /*	file. Setting \fBvar_use_limit\fR to zero disables the idle limit.
    138 /* DIAGNOSTICS
    139 /*	Problems and transactions are logged to \fBsyslogd\fR(8)
    140 /*	or \fBpostlogd\fR(8).
    141 /* SEE ALSO
    142 /*	master(8), master process
    143 /*	postlogd(8), Postfix logging
    144 /*	syslogd(8), system logging
    145 /* LICENSE
    146 /* .ad
    147 /* .fi
    148 /*	The Secure Mailer license must be distributed with this software.
    149 /* AUTHOR(S)
    150 /*	Wietse Venema
    151 /*	Google, Inc.
    152 /*	111 8th Avenue
    153 /*	New York, NY 10011, USA
    154 /*
    155 /*	Wietse Venema
    156 /*	porcupine.org
    157 /*--*/
    158 
    159 /* System library. */
    160 
    161 #include <sys_defs.h>
    162 #include <sys/socket.h>
    163 #include <unistd.h>
    164 #include <signal.h>
    165 #include <stdlib.h>
    166 #include <limits.h>
    167 #include <string.h>
    168 #include <errno.h>
    169 #include <fcntl.h>
    170 #include <stdarg.h>
    171 #ifdef STRCASECMP_IN_STRINGS_H
    172 #include <strings.h>
    173 #endif
    174 #include <time.h>
    175 
    176 /* Utility library. */
    177 
    178 #include <msg.h>
    179 #include <msg_vstream.h>
    180 #include <chroot_uid.h>
    181 #include <vstring.h>
    182 #include <vstream.h>
    183 #include <msg_vstream.h>
    184 #include <mymalloc.h>
    185 #include <events.h>
    186 #include <iostuff.h>
    187 #include <stringops.h>
    188 #include <sane_accept.h>
    189 #include <myflock.h>
    190 #include <safe_open.h>
    191 #include <listen.h>
    192 #include <watchdog.h>
    193 #include <split_at.h>
    194 
    195 /* Global library. */
    196 
    197 #include <mail_params.h>
    198 #include <mail_task.h>
    199 #include <debug_process.h>
    200 #include <mail_conf.h>
    201 #include <mail_dict.h>
    202 #include <resolve_local.h>
    203 #include <mail_flow.h>
    204 #include <mail_version.h>
    205 #include <bounce.h>
    206 #include <maillog_client.h>
    207 
    208 /* Process manager. */
    209 
    210 #include "master_proto.h"
    211 
    212 /* Application-specific */
    213 
    214 #include "mail_server.h"
    215 
    216  /*
    217   * Global state.
    218   */
    219 static int use_count;
    220 
    221 static DGRAM_SERVER_FN dgram_server_service;
    222 static char *dgram_server_name;
    223 static char **dgram_server_argv;
    224 static void (*dgram_server_accept) (int, void *);
    225 static void (*dgram_server_onexit) (char *, char **);
    226 static void (*dgram_server_pre_accept) (char *, char **);
    227 static VSTREAM *dgram_server_lock;
    228 static int dgram_server_in_flow_delay;
    229 static unsigned dgram_server_generation;
    230 static int dgram_server_watchdog = 1000;
    231 
    232 /* dgram_server_exit - normal termination */
    233 
    234 static NORETURN dgram_server_exit(void)
    235 {
    236     if (dgram_server_onexit)
    237 	dgram_server_onexit(dgram_server_name, dgram_server_argv);
    238     exit(0);
    239 }
    240 
    241 /* dgram_server_abort - terminate after abnormal master exit */
    242 
    243 static void dgram_server_abort(int unused_event, void *unused_context)
    244 {
    245     if (msg_verbose)
    246 	msg_info("master disconnect -- exiting");
    247     dgram_server_exit();
    248 }
    249 
    250 /* dgram_server_timeout - idle time exceeded */
    251 
    252 static void dgram_server_timeout(int unused_event, void *unused_context)
    253 {
    254     if (msg_verbose)
    255 	msg_info("idle timeout -- exiting");
    256     dgram_server_exit();
    257 }
    258 
    259 /* dgram_server_wakeup - wake up application */
    260 
    261 static void dgram_server_wakeup(int fd)
    262 {
    263 
    264     /*
    265      * Commit suicide when the master process disconnected from us, after
    266      * handling the client request.
    267      */
    268     if (master_notify(var_pid, dgram_server_generation, MASTER_STAT_TAKEN) < 0)
    269 	 /* void */ ;
    270     if (dgram_server_in_flow_delay && mail_flow_get(1) < 0)
    271 	doze(var_in_flow_delay * 1000000);
    272     dgram_server_service(fd, dgram_server_name, dgram_server_argv);
    273     if (master_notify(var_pid, dgram_server_generation, MASTER_STAT_AVAIL) < 0)
    274 	dgram_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
    275     if (var_idle_limit > 0)
    276 	event_request_timer(dgram_server_timeout, (void *) 0, var_idle_limit);
    277     /* Avoid integer wrap-around in a persistent process.  */
    278     if (use_count < INT_MAX)
    279 	use_count++;
    280 }
    281 
    282 /* dgram_server_accept_unix - handle UNIX-domain socket event */
    283 
    284 static void dgram_server_accept_unix(int unused_event, void *context)
    285 {
    286     const char *myname = "dgram_server_accept";
    287     int     listen_fd = CAST_ANY_PTR_TO_INT(context);
    288 
    289     if (dgram_server_lock != 0
    290 	&& myflock(vstream_fileno(dgram_server_lock), INTERNAL_LOCK,
    291 		   MYFLOCK_OP_NONE) < 0)
    292 	msg_fatal("select unlock: %m");
    293 
    294     if (msg_verbose)
    295 	msg_info("%s: request arrived", myname);
    296 
    297     /*
    298      * Read whatever the other side wrote. The socket is non-blocking so we
    299      * won't get stuck when multiple processes wake up.
    300      */
    301     if (dgram_server_pre_accept)
    302 	dgram_server_pre_accept(dgram_server_name, dgram_server_argv);
    303     dgram_server_wakeup(listen_fd);
    304 }
    305 
    306 /* dgram_server_main - the real main program */
    307 
    308 NORETURN dgram_server_main(int argc, char **argv, DGRAM_SERVER_FN service,...)
    309 {
    310     const char *myname = "dgram_server_main";
    311     char   *root_dir = 0;
    312     char   *user_name = 0;
    313     int     debug_me = 0;
    314     int     daemon_mode = 1;
    315     char   *service_name = basename(argv[0]);
    316     int     delay;
    317     int     c;
    318     int     socket_count = 1;
    319     int     fd;
    320     va_list ap;
    321     MAIL_SERVER_INIT_FN pre_init = 0;
    322     MAIL_SERVER_INIT_FN post_init = 0;
    323     MAIL_SERVER_LOOP_FN loop = 0;
    324     int     key;
    325     char   *transport = 0;
    326     char   *lock_path;
    327     VSTRING *why;
    328     int     alone = 0;
    329     int     zerolimit = 0;
    330     WATCHDOG *watchdog;
    331     char   *oname_val;
    332     char   *oname;
    333     char   *oval;
    334     const char *err;
    335     char   *generation;
    336     int     msg_vstream_needed = 0;
    337     const char *dsn_filter_title;
    338     const char **dsn_filter_maps;
    339 
    340     /*
    341      * Process environment options as early as we can.
    342      */
    343     if (getenv(CONF_ENV_VERB))
    344 	msg_verbose = 1;
    345     if (getenv(CONF_ENV_DEBUG))
    346 	debug_me = 1;
    347 
    348     /*
    349      * Don't die when a process goes away unexpectedly.
    350      */
    351     signal(SIGPIPE, SIG_IGN);
    352 
    353     /*
    354      * Don't die for frivolous reasons.
    355      */
    356 #ifdef SIGXFSZ
    357     signal(SIGXFSZ, SIG_IGN);
    358 #endif
    359 
    360     /*
    361      * May need this every now and then.
    362      */
    363     var_procname = mystrdup(basename(argv[0]));
    364     set_mail_conf_str(VAR_PROCNAME, var_procname);
    365 
    366     /*
    367      * Initialize logging and exit handler. Do the syslog first, so that its
    368      * initialization completes before we enter the optional chroot jail.
    369      */
    370     maillog_client_init(mail_task(var_procname), MAILLOG_CLIENT_FLAG_NONE);
    371     if (msg_verbose)
    372 	msg_info("daemon started");
    373 
    374     /*
    375      * Check the Postfix library version as soon as we enable logging.
    376      */
    377     MAIL_VERSION_CHECK;
    378 
    379     /*
    380      * Initialize from the configuration file. Allow command-line options to
    381      * override compiled-in defaults or configured parameter values.
    382      */
    383     mail_conf_suck();
    384 
    385     /*
    386      * After database open error, continue execution with reduced
    387      * functionality.
    388      */
    389     dict_allow_surrogate = 1;
    390 
    391     /*
    392      * Pick up policy settings from master process. Shut up error messages to
    393      * stderr, because no-one is going to see them.
    394      */
    395     opterr = 0;
    396     while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:s:t:uvVz")) > 0) {
    397 	switch (c) {
    398 	case 'c':
    399 	    root_dir = "setme";
    400 	    break;
    401 	case 'd':
    402 	    daemon_mode = 0;
    403 	    break;
    404 	case 'D':
    405 	    debug_me = 1;
    406 	    break;
    407 	case 'i':
    408 	    mail_conf_update(VAR_MAX_IDLE, optarg);
    409 	    break;
    410 	case 'l':
    411 	    alone = 1;
    412 	    break;
    413 	case 'm':
    414 	    mail_conf_update(VAR_MAX_USE, optarg);
    415 	    break;
    416 	case 'n':
    417 	    service_name = optarg;
    418 	    break;
    419 	case 'o':
    420 	    oname_val = mystrdup(optarg);
    421 	    if ((err = split_nameval(oname_val, &oname, &oval)) != 0)
    422 		msg_fatal("invalid \"-o %s\" option value: %s", optarg, err);
    423 	    mail_conf_update(oname, oval);
    424 	    myfree(oname_val);
    425 	    break;
    426 	case 's':
    427 	    if ((socket_count = atoi(optarg)) <= 0)
    428 		msg_fatal("invalid socket_count: %s", optarg);
    429 	    break;
    430 	case 't':
    431 	    transport = optarg;
    432 	    break;
    433 	case 'u':
    434 	    user_name = "setme";
    435 	    break;
    436 	case 'v':
    437 	    msg_verbose++;
    438 	    break;
    439 	case 'V':
    440 	    if (++msg_vstream_needed == 1)
    441 		msg_vstream_init(mail_task(var_procname), VSTREAM_ERR);
    442 	    break;
    443 	case 'z':
    444 	    zerolimit = 1;
    445 	    break;
    446 	default:
    447 	    msg_fatal("invalid option: %c", optopt);
    448 	    break;
    449 	}
    450     }
    451     set_mail_conf_str(VAR_SERVNAME, service_name);
    452 
    453     /*
    454      * Initialize generic parameters and re-initialize logging in case of a
    455      * non-default program name or logging destination.
    456      */
    457     mail_params_init();
    458     maillog_client_init(mail_task(var_procname), MAILLOG_CLIENT_FLAG_NONE);
    459 
    460     /*
    461      * Register higher-level dictionaries and initialize the support for
    462      * dynamically-loaded dictionaries.
    463      */
    464     mail_dict_init();
    465 
    466     /*
    467      * If not connected to stdin, stdin must not be a terminal.
    468      */
    469     if (daemon_mode && isatty(STDIN_FILENO)) {
    470 	msg_vstream_init(var_procname, VSTREAM_ERR);
    471 	msg_fatal("do not run this command by hand");
    472     }
    473 
    474     /*
    475      * Application-specific initialization.
    476      */
    477     va_start(ap, service);
    478     while ((key = va_arg(ap, int)) != 0) {
    479 	switch (key) {
    480 	case MAIL_SERVER_INT_TABLE:
    481 	    get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
    482 	    break;
    483 	case MAIL_SERVER_LONG_TABLE:
    484 	    get_mail_conf_long_table(va_arg(ap, CONFIG_LONG_TABLE *));
    485 	    break;
    486 	case MAIL_SERVER_STR_TABLE:
    487 	    get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
    488 	    break;
    489 	case MAIL_SERVER_BOOL_TABLE:
    490 	    get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
    491 	    break;
    492 	case MAIL_SERVER_TIME_TABLE:
    493 	    get_mail_conf_time_table(va_arg(ap, CONFIG_TIME_TABLE *));
    494 	    break;
    495 	case MAIL_SERVER_RAW_TABLE:
    496 	    get_mail_conf_raw_table(va_arg(ap, CONFIG_RAW_TABLE *));
    497 	    break;
    498 	case MAIL_SERVER_NINT_TABLE:
    499 	    get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *));
    500 	    break;
    501 	case MAIL_SERVER_NBOOL_TABLE:
    502 	    get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *));
    503 	    break;
    504 	case MAIL_SERVER_PRE_INIT:
    505 	    pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
    506 	    break;
    507 	case MAIL_SERVER_POST_INIT:
    508 	    post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
    509 	    break;
    510 	case MAIL_SERVER_LOOP:
    511 	    loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
    512 	    break;
    513 	case MAIL_SERVER_EXIT:
    514 	    dgram_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
    515 	    break;
    516 	case MAIL_SERVER_PRE_ACCEPT:
    517 	    dgram_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
    518 	    break;
    519 	case MAIL_SERVER_IN_FLOW_DELAY:
    520 	    dgram_server_in_flow_delay = 1;
    521 	    break;
    522 	case MAIL_SERVER_SOLITARY:
    523 	    if (!alone)
    524 		msg_fatal("service %s requires a process limit of 1",
    525 			  service_name);
    526 	    break;
    527 	case MAIL_SERVER_UNLIMITED:
    528 	    if (!zerolimit)
    529 		msg_fatal("service %s requires a process limit of 0",
    530 			  service_name);
    531 	    break;
    532 	case MAIL_SERVER_PRIVILEGED:
    533 	    if (user_name)
    534 		msg_fatal("service %s requires privileged operation",
    535 			  service_name);
    536 	    break;
    537 	case MAIL_SERVER_WATCHDOG:
    538 	    dgram_server_watchdog = *va_arg(ap, int *);
    539 	    break;
    540 	case MAIL_SERVER_BOUNCE_INIT:
    541 	    dsn_filter_title = va_arg(ap, const char *);
    542 	    dsn_filter_maps = va_arg(ap, const char **);
    543 	    bounce_client_init(dsn_filter_title, *dsn_filter_maps);
    544 	    break;
    545 	default:
    546 	    msg_panic("%s: unknown argument type: %d", myname, key);
    547 	}
    548     }
    549     va_end(ap);
    550 
    551     if (root_dir)
    552 	root_dir = var_queue_dir;
    553     if (user_name)
    554 	user_name = var_mail_owner;
    555 
    556     /*
    557      * Can options be required?
    558      */
    559     if (transport == 0)
    560 	msg_fatal("no transport type specified");
    561     else if (strcasecmp(transport, MASTER_XPORT_NAME_UXDG) == 0)
    562 	dgram_server_accept = dgram_server_accept_unix;
    563     else
    564 	msg_fatal("unsupported transport type: %s", transport);
    565 
    566     /*
    567      * Retrieve process generation from environment.
    568      */
    569     if ((generation = getenv(MASTER_GEN_NAME)) != 0) {
    570 	if (!alldig(generation))
    571 	    msg_fatal("bad generation: %s", generation);
    572 	OCTAL_TO_UNSIGNED(dgram_server_generation, generation);
    573 	if (msg_verbose)
    574 	    msg_info("process generation: %s (%o)",
    575 		     generation, dgram_server_generation);
    576     }
    577 
    578     /*
    579      * Optionally start the debugger on ourself.
    580      */
    581     if (debug_me)
    582 	debug_process();
    583 
    584     /*
    585      * Traditionally, BSD select() can't handle multiple processes selecting
    586      * on the same socket, and wakes up every process in select(). See TCP/IP
    587      * Illustrated volume 2 page 532. We avoid select() collisions with an
    588      * external lock file.
    589      */
    590     if (!alone) {
    591 	lock_path = concatenate(DEF_PID_DIR, "/", transport,
    592 				".", service_name, (char *) 0);
    593 	why = vstring_alloc(1);
    594 	if ((dgram_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
    595 				      (struct stat *) 0, -1, -1, why)) == 0)
    596 	    msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
    597 	close_on_exec(vstream_fileno(dgram_server_lock), CLOSE_ON_EXEC);
    598 	myfree(lock_path);
    599 	vstring_free(why);
    600     }
    601 
    602     /*
    603      * Set up call-back info.
    604      */
    605     dgram_server_service = service;
    606     dgram_server_name = service_name;
    607     dgram_server_argv = argv + optind;
    608 
    609     /*
    610      * Run pre-jail initialization.
    611      */
    612     if (chdir(var_queue_dir) < 0)
    613 	msg_fatal("chdir(\"%s\"): %m", var_queue_dir);
    614     if (pre_init)
    615 	pre_init(dgram_server_name, dgram_server_argv);
    616 
    617     /*
    618      * Optionally, restrict the damage that this process can do.
    619      */
    620     resolve_local_init();
    621     tzset();
    622     chroot_uid(root_dir, user_name);
    623 
    624     /*
    625      * Run post-jail initialization.
    626      */
    627     if (post_init)
    628 	post_init(dgram_server_name, dgram_server_argv);
    629 
    630     /*
    631      * Running as a semi-resident server. Service requests. Terminate when we
    632      * have serviced a sufficient number of requests, when no-one has been
    633      * talking to us for a configurable amount of time, or when the master
    634      * process terminated abnormally.
    635      */
    636     if (var_idle_limit > 0)
    637 	event_request_timer(dgram_server_timeout, (void *) 0, var_idle_limit);
    638     for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) {
    639 	event_enable_read(fd, dgram_server_accept, CAST_INT_TO_VOID_PTR(fd));
    640 	close_on_exec(fd, CLOSE_ON_EXEC);
    641     }
    642     event_enable_read(MASTER_STATUS_FD, dgram_server_abort, (void *) 0);
    643     close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC);
    644     close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC);
    645     close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC);
    646     watchdog = watchdog_create(dgram_server_watchdog,
    647 			       (WATCHDOG_FN) 0, (void *) 0);
    648 
    649     /*
    650      * The event loop, at last.
    651      */
    652     while (var_use_limit == 0 || use_count < var_use_limit) {
    653 	if (dgram_server_lock != 0) {
    654 	    watchdog_stop(watchdog);
    655 	    if (myflock(vstream_fileno(dgram_server_lock), INTERNAL_LOCK,
    656 			MYFLOCK_OP_EXCLUSIVE) < 0)
    657 		msg_fatal("select lock: %m");
    658 	}
    659 	watchdog_start(watchdog);
    660 	delay = loop ? loop(dgram_server_name, dgram_server_argv) : -1;
    661 	event_loop(delay);
    662     }
    663     dgram_server_exit();
    664 }
    665