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