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