5
/* skeleton single-threaded mail subsystem
7
/* #include <mail_server.h>
9
/* NORETURN single_server_main(argc, argv, service, key, value, ...)
12
/* void (*service)(VSTREAM *stream, char *service_name, char **argv);
15
/* This module implements a skeleton for single-threaded
16
/* mail subsystems: mail subsystem programs that service one
17
/* client at a time. The resulting program expects to be run
18
/* from the \fBmaster\fR process.
20
/* single_server_main() is the skeleton entry point. It should be
21
/* called from the application main program. The skeleton does the
22
/* generic command-line options processing, initialization of
23
/* configurable parameters, and connection management.
24
/* The skeleton never returns.
27
/* .IP "void (*service)(VSTREAM *fp, char *service_name, char **argv)"
28
/* A pointer to a function that is called by the skeleton each time
29
/* a client connects to the program's service port. The function is
30
/* run after the program has irrevocably dropped its privileges.
31
/* The stream initial state is non-blocking mode.
32
/* The service name argument corresponds to the service name in the
34
/* The argv argument specifies command-line arguments left over
35
/* after options processing.
37
/* Optional arguments are specified as a null-terminated (key, value)
38
/* list. Keys and expected values are:
39
/* .IP "MAIL_SERVER_INT_TABLE (CONFIG_INT_TABLE *)"
40
/* A table with configurable parameters, to be loaded from the
41
/* global Postfix configuration file. Tables are loaded in the
42
/* order as specified, and multiple instances of the same type
44
/* .IP "MAIL_SERVER_STR_TABLE (CONFIG_STR_TABLE *)"
45
/* A table with configurable parameters, to be loaded from the
46
/* global Postfix configuration file. Tables are loaded in the
47
/* order as specified, and multiple instances of the same type
49
/* .IP "MAIL_SERVER_BOOL_TABLE (CONFIG_BOOL_TABLE *)"
50
/* A table with configurable parameters, to be loaded from the
51
/* global Postfix configuration file. Tables are loaded in the
52
/* order as specified, and multiple instances of the same type
54
/* .IP "MAIL_SERVER_TIME_TABLE (CONFIG_TIME_TABLE *)"
55
/* A table with configurable parameters, to be loaded from the
56
/* global Postfix configuration file. Tables are loaded in the
57
/* order as specified, and multiple instances of the same type
59
/* .IP "MAIL_SERVER_RAW_TABLE (CONFIG_RAW_TABLE *)"
60
/* A table with configurable parameters, to be loaded from the
61
/* global Postfix configuration file. Tables are loaded in the
62
/* order as specified, and multiple instances of the same type
63
/* are allowed. Raw parameters are not subjected to $name
65
/* .IP "MAIL_SERVER_PRE_INIT (void *(char *service_name, char **argv))"
66
/* A pointer to a function that is called once
67
/* by the skeleton after it has read the global configuration file
68
/* and after it has processed command-line arguments, but before
69
/* the skeleton has optionally relinquished the process privileges.
71
/* Only the last instance of this parameter type is remembered.
72
/* .IP "MAIL_SERVER_POST_INIT (void *(char *service_name, char **argv))"
73
/* A pointer to a function that is called once
74
/* by the skeleton after it has optionally relinquished the process
75
/* privileges, but before servicing client connection requests.
77
/* Only the last instance of this parameter type is remembered.
78
/* .IP "MAIL_SERVER_LOOP (int *(char *service_name, char **argv))"
79
/* A pointer to function that is executed from
80
/* within the event loop, whenever an I/O or timer event has happened,
81
/* or whenever nothing has happened for a specified amount of time.
82
/* The result value of the function specifies how long to wait until
83
/* the next event. Specify -1 to wait for "as long as it takes".
85
/* Only the last instance of this parameter type is remembered.
86
/* .IP "MAIL_SERVER_EXIT (void *(void))"
87
/* A pointer to function that is executed immediately before normal
88
/* process termination.
90
/* Only the last instance of this parameter type is remembered.
91
/* .IP "MAIL_SERVER_PRE_ACCEPT (void *(char *service_name, char **argv))"
92
/* Function to be executed prior to accepting a new connection.
94
/* Only the last instance of this parameter type is remembered.
95
/* .IP "MAIL_SERVER_IN_FLOW_DELAY (none)"
96
/* Pause $in_flow_delay seconds when no "mail flow control token"
97
/* is available. A token is consumed for each connection request.
98
/* .IP MAIL_SERVER_SOLITARY
99
/* This service must be configured with process limit of 1.
100
/* .IP MAIL_SERVER_UNLIMITED
101
/* This service must be configured with process limit of 0.
103
/* The var_use_limit variable limits the number of clients that
104
/* a server can service before it commits suicide.
105
/* This value is taken from the global \fBmain.cf\fR configuration
106
/* file. Setting \fBvar_idle_limit\fR to zero disables the client limit.
108
/* The var_idle_limit variable limits the time that a service
109
/* receives no client connection requests before it commits suicide.
110
/* This value is taken from the global \fBmain.cf\fR configuration
111
/* file. Setting \fBvar_use_limit\fR to zero disables the idle limit.
113
/* Problems and transactions are logged to \fBsyslogd\fR(8).
116
/* master(8), master process
117
/* syslogd(8) system logging
121
/* The Secure Mailer license must be distributed with this software.
124
/* IBM T.J. Watson Research
126
/* Yorktown Heights, NY 10598, USA
129
/* System library. */
131
#include <sys_defs.h>
132
#include <sys/socket.h>
141
#ifdef STRCASECMP_IN_STRINGS_H
145
/* Utility library. */
148
#include <msg_syslog.h>
149
#include <chroot_uid.h>
152
#include <msg_vstream.h>
153
#include <mymalloc.h>
156
#include <stringops.h>
157
#include <sane_accept.h>
159
#include <safe_open.h>
161
#include <watchdog.h>
162
#include <split_at.h>
164
/* Global library. */
166
#include <mail_params.h>
167
#include <mail_task.h>
168
#include <debug_process.h>
169
#include <mail_conf.h>
170
#include <mail_dict.h>
171
#include <timed_ipc.h>
172
#include <resolve_local.h>
173
#include <mail_flow.h>
175
/* Process manager. */
177
#include "master_proto.h"
179
/* Application-specific */
181
#include "mail_server.h"
186
static int use_count;
188
static void (*single_server_service) (VSTREAM *, char *, char **);
189
static char *single_server_name;
190
static char **single_server_argv;
191
static void (*single_server_accept) (int, char *);
192
static void (*single_server_onexit) (char *, char **);
193
static void (*single_server_pre_accept) (char *, char **);
194
static VSTREAM *single_server_lock;
195
static int single_server_in_flow_delay;
197
/* single_server_exit - normal termination */
199
static NORETURN single_server_exit(void)
201
if (single_server_onexit)
202
single_server_onexit(single_server_name, single_server_argv);
206
/* single_server_abort - terminate after abnormal master exit */
208
static void single_server_abort(int unused_event, char *unused_context)
211
msg_info("master disconnect -- exiting");
212
single_server_exit();
215
/* single_server_timeout - idle time exceeded */
217
static void single_server_timeout(int unused_event, char *unused_context)
220
msg_info("idle timeout -- exiting");
221
single_server_exit();
224
/* single_server_wakeup - wake up application */
226
static void single_server_wakeup(int fd)
232
* If the accept() succeeds, be sure to disable non-blocking I/O, because
233
* the application is supposed to be single-threaded. Notice the master
234
* of our (un)availability to service connection requests. Commit suicide
235
* when the master process disconnected from us.
238
msg_info("connection established");
239
non_blocking(fd, BLOCKING);
240
close_on_exec(fd, CLOSE_ON_EXEC);
241
stream = vstream_fdopen(fd, O_RDWR);
242
tmp = concatenate(single_server_name, " socket", (char *) 0);
243
vstream_control(stream, VSTREAM_CTL_PATH, tmp, VSTREAM_CTL_END);
245
timed_ipc_setup(stream);
246
if (master_notify(var_pid, MASTER_STAT_TAKEN) < 0)
247
single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
248
if (single_server_in_flow_delay && mail_flow_get(1) < 0)
249
doze(var_in_flow_delay * 1000000);
250
single_server_service(stream, single_server_name, single_server_argv);
251
(void) vstream_fclose(stream);
252
if (master_notify(var_pid, MASTER_STAT_AVAIL) < 0)
253
single_server_abort(EVENT_NULL_TYPE, EVENT_NULL_CONTEXT);
255
msg_info("connection closed");
257
if (var_idle_limit > 0)
258
event_request_timer(single_server_timeout, (char *) 0, var_idle_limit);
261
/* single_server_accept_local - accept client connection request */
263
static void single_server_accept_local(int unused_event, char *context)
265
int listen_fd = CAST_CHAR_PTR_TO_INT(context);
270
* Be prepared for accept() to fail because some other process already
271
* got the connection. We use select() + accept(), instead of simply
272
* blocking in accept(), because we must be able to detect that the
273
* master process has gone away unexpectedly.
275
if (var_idle_limit > 0)
276
time_left = event_cancel_timer(single_server_timeout, (char *) 0);
278
if (single_server_pre_accept)
279
single_server_pre_accept(single_server_name, single_server_argv);
280
fd = LOCAL_ACCEPT(listen_fd);
281
if (single_server_lock != 0
282
&& myflock(vstream_fileno(single_server_lock), INTERNAL_LOCK,
283
MYFLOCK_OP_NONE) < 0)
284
msg_fatal("select unlock: %m");
287
msg_fatal("accept connection: %m");
289
event_request_timer(single_server_timeout, (char *) 0, time_left);
292
single_server_wakeup(fd);
295
/* single_server_accept_inet - accept client connection request */
297
static void single_server_accept_inet(int unused_event, char *context)
299
int listen_fd = CAST_CHAR_PTR_TO_INT(context);
304
* Be prepared for accept() to fail because some other process already
305
* got the connection. We use select() + accept(), instead of simply
306
* blocking in accept(), because we must be able to detect that the
307
* master process has gone away unexpectedly.
309
if (var_idle_limit > 0)
310
time_left = event_cancel_timer(single_server_timeout, (char *) 0);
312
if (single_server_pre_accept)
313
single_server_pre_accept(single_server_name, single_server_argv);
314
fd = inet_accept(listen_fd);
315
if (single_server_lock != 0
316
&& myflock(vstream_fileno(single_server_lock), INTERNAL_LOCK,
317
MYFLOCK_OP_NONE) < 0)
318
msg_fatal("select unlock: %m");
321
msg_fatal("accept connection: %m");
323
event_request_timer(single_server_timeout, (char *) 0, time_left);
326
single_server_wakeup(fd);
329
/* single_server_main - the real main program */
331
NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...)
333
char *myname = "single_server_main";
338
char *service_name = basename(argv[0]);
341
int socket_count = 1;
344
MAIL_SERVER_INIT_FN pre_init = 0;
345
MAIL_SERVER_INIT_FN post_init = 0;
346
MAIL_SERVER_LOOP_FN loop = 0;
357
* Process environment options as early as we can.
359
if (getenv(CONF_ENV_VERB))
361
if (getenv(CONF_ENV_DEBUG))
365
* Don't die when a process goes away unexpectedly.
367
signal(SIGPIPE, SIG_IGN);
370
* Don't die for frivolous reasons.
373
signal(SIGXFSZ, SIG_IGN);
377
* May need this every now and then.
379
var_procname = mystrdup(basename(argv[0]));
380
set_mail_conf_str(VAR_PROCNAME, var_procname);
383
* Initialize logging and exit handler. Do the syslog first, so that its
384
* initialization completes before we enter the optional chroot jail.
386
msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY);
388
msg_info("daemon started");
391
* Initialize from the configuration file. Allow command-line options to
392
* override compiled-in defaults or configured parameter values.
397
* Register dictionaries that use higher-level interfaces and protocols.
402
* Pick up policy settings from master process. Shut up error messages to
403
* stderr, because no-one is going to see them.
406
while ((c = GETOPT(argc, argv, "cDi:lm:n:o:s:St:uvz")) > 0) {
415
mail_conf_update(VAR_MAX_IDLE, optarg);
421
mail_conf_update(VAR_MAX_USE, optarg);
424
service_name = optarg;
427
if ((oval = split_at(optarg, '=')) == 0)
429
mail_conf_update(optarg, oval);
432
if ((socket_count = atoi(optarg)) <= 0)
433
msg_fatal("invalid socket_count: %s", optarg);
451
msg_fatal("invalid option: %c", c);
457
* Initialize generic parameters.
462
* Application-specific initialization.
464
va_start(ap, service);
465
while ((key = va_arg(ap, int)) != 0) {
467
case MAIL_SERVER_INT_TABLE:
468
get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
470
case MAIL_SERVER_STR_TABLE:
471
get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
473
case MAIL_SERVER_BOOL_TABLE:
474
get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
476
case MAIL_SERVER_TIME_TABLE:
477
get_mail_conf_time_table(va_arg(ap, CONFIG_TIME_TABLE *));
479
case MAIL_SERVER_RAW_TABLE:
480
get_mail_conf_raw_table(va_arg(ap, CONFIG_RAW_TABLE *));
482
case MAIL_SERVER_PRE_INIT:
483
pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
485
case MAIL_SERVER_POST_INIT:
486
post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
488
case MAIL_SERVER_LOOP:
489
loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
491
case MAIL_SERVER_EXIT:
492
single_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
494
case MAIL_SERVER_PRE_ACCEPT:
495
single_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
497
case MAIL_SERVER_IN_FLOW_DELAY:
498
single_server_in_flow_delay = 1;
500
case MAIL_SERVER_SOLITARY:
502
msg_fatal("service %s requires a process limit of 1",
505
case MAIL_SERVER_UNLIMITED:
507
msg_fatal("service %s requires a process limit of 0",
511
msg_panic("%s: unknown argument type: %d", myname, key);
517
root_dir = var_queue_dir;
519
user_name = var_mail_owner;
522
* If not connected to stdin, stdin must not be a terminal.
524
if (stream == 0 && isatty(STDIN_FILENO)) {
525
msg_vstream_init(var_procname, VSTREAM_ERR);
526
msg_fatal("do not run this command by hand");
530
* Can options be required?
534
msg_fatal("no transport type specified");
535
if (strcasecmp(transport, MASTER_XPORT_NAME_INET) == 0)
536
single_server_accept = single_server_accept_inet;
537
else if (strcasecmp(transport, MASTER_XPORT_NAME_UNIX) == 0)
538
single_server_accept = single_server_accept_local;
540
msg_fatal("unsupported transport type: %s", transport);
544
* Optionally start the debugger on ourself.
550
* Traditionally, BSD select() can't handle multiple processes selecting
551
* on the same socket, and wakes up every process in select(). See TCP/IP
552
* Illustrated volume 2 page 532. We avoid select() collisions with an
553
* external lock file.
555
if (stream == 0 && !alone) {
556
lock_path = concatenate(DEF_PID_DIR, "/", transport,
557
".", service_name, (char *) 0);
558
why = vstring_alloc(1);
559
if ((single_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
560
(struct stat *) 0, -1, -1, why)) == 0)
561
msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
562
close_on_exec(vstream_fileno(single_server_lock), CLOSE_ON_EXEC);
568
* Set up call-back info.
570
single_server_service = service;
571
single_server_name = service_name;
572
single_server_argv = argv + optind;
575
* Run pre-jail initialization.
577
if (chdir(var_queue_dir) < 0)
578
msg_fatal("chdir(\"%s\"): %m", var_queue_dir);
580
pre_init(single_server_name, single_server_argv);
583
* Optionally, restrict the damage that this process can do.
585
resolve_local_init();
586
chroot_uid(root_dir, user_name);
589
* Run post-jail initialization.
592
post_init(single_server_name, single_server_argv);
595
* Are we running as a one-shot server with the client connection on
596
* standard input? If so, make sure the output is written to stdout so as
597
* to satisfy common expectation.
600
vstream_control(stream,
602
VSTREAM_CTL_WRITE_FD, STDOUT_FILENO,
604
service(stream, single_server_name, single_server_argv);
605
vstream_fflush(stream);
606
single_server_exit();
610
* Running as a semi-resident server. Service connection requests.
611
* Terminate when we have serviced a sufficient number of clients, when
612
* no-one has been talking to us for a configurable amount of time, or
613
* when the master process terminated abnormally.
615
if (var_idle_limit > 0)
616
event_request_timer(single_server_timeout, (char *) 0, var_idle_limit);
617
for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) {
618
event_enable_read(fd, single_server_accept, CAST_INT_TO_CHAR_PTR(fd));
619
close_on_exec(fd, CLOSE_ON_EXEC);
621
event_enable_read(MASTER_STATUS_FD, single_server_abort, (char *) 0);
622
close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC);
623
close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC);
624
close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC);
625
watchdog = watchdog_create(var_daemon_timeout, (WATCHDOG_FN) 0, (char *) 0);
628
* The event loop, at last.
630
while (var_use_limit == 0 || use_count < var_use_limit) {
631
if (single_server_lock != 0) {
632
watchdog_stop(watchdog);
633
if (myflock(vstream_fileno(single_server_lock), INTERNAL_LOCK,
634
MYFLOCK_OP_EXCLUSIVE) < 0)
635
msg_fatal("select lock: %m");
637
watchdog_start(watchdog);
638
delay = loop ? loop(single_server_name, single_server_argv) : -1;
641
single_server_exit();