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