7 /* Postfix delivery status reports
9 /* \fBbounce\fR [generic Postfix daemon options]
11 /* The \fBbounce\fR(8) daemon maintains per-message log files with
12 /* delivery status information. Each log file is named after the
13 /* queue file that it corresponds to, and is kept in a queue subdirectory
14 /* named after the service name in the \fBmaster.cf\fR file (either
15 /* \fBbounce\fR, \fBdefer\fR or \fBtrace\fR).
16 /* This program expects to be run from the \fBmaster\fR(8) process
19 /* The \fBbounce\fR(8) daemon processes two types of service requests:
21 /* Append a recipient (non-)delivery status record to a per-message
24 /* Enqueue a delivery status notification message, with a copy
25 /* of a per-message log file and of the corresponding message.
26 /* When the delivery status notification message is
27 /* enqueued successfully, the per-message log file is deleted.
29 /* The software does a best notification effort. A non-delivery
30 /* notification is sent even when the log file or the original
31 /* message cannot be read.
33 /* Optionally, a bounce (defer, trace) client can request that the
34 /* per-message log file be deleted when the requested operation fails.
35 /* This is used by clients that cannot retry transactions by
36 /* themselves, and that depend on retry logic in their own client.
38 /* RFC 822 (ARPA Internet Text Messages)
39 /* RFC 2045 (Format of Internet Message Bodies)
40 /* RFC 2822 (ARPA Internet Text Messages)
41 /* RFC 3462 (Delivery Status Notifications)
42 /* RFC 3464 (Delivery Status Notifications)
43 /* RFC 3834 (Auto-Submitted: message header)
45 /* Problems and transactions are logged to \fBsyslogd\fR(8).
46 /* CONFIGURATION PARAMETERS
49 /* Changes to \fBmain.cf\fR are picked up automatically, as \fBbounce\fR(8)
50 /* processes run for only a limited amount of time. Use the command
51 /* "\fBpostfix reload\fR" to speed up a change.
53 /* The text below provides only a parameter summary. See
54 /* \fBpostconf\fR(5) for more details including examples.
55 /* .IP "\fB2bounce_notice_recipient (postmaster)\fR"
56 /* The recipient of undeliverable mail that cannot be returned to
58 /* .IP "\fBbackwards_bounce_logfile_compatibility (yes)\fR"
59 /* Produce additional \fBbounce\fR(8) logfile records that can be read by
60 /* Postfix versions before 2.0.
61 /* .IP "\fBbounce_notice_recipient (postmaster)\fR"
62 /* The recipient of postmaster notifications with the message headers
63 /* of mail that Postfix did not deliver and of SMTP conversation
64 /* transcripts of mail that Postfix did not receive.
65 /* .IP "\fBbounce_size_limit (50000)\fR"
66 /* The maximal amount of original message text that is sent in a
67 /* non-delivery notification.
68 /* .IP "\fBbounce_template_file (empty)\fR"
69 /* Pathname of a configuration file with bounce message templates.
70 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
71 /* The default location of the Postfix main.cf and master.cf
72 /* configuration files.
73 /* .IP "\fBdaemon_timeout (18000s)\fR"
74 /* How much time a Postfix daemon process may take to handle a
75 /* request before it is terminated by a built-in watchdog timer.
76 /* .IP "\fBdelay_notice_recipient (postmaster)\fR"
77 /* The recipient of postmaster notifications with the message headers
78 /* of mail that cannot be delivered within $delay_warning_time time
80 /* .IP "\fBdeliver_lock_attempts (20)\fR"
81 /* The maximal number of attempts to acquire an exclusive lock on a
82 /* mailbox file or \fBbounce\fR(8) logfile.
83 /* .IP "\fBdeliver_lock_delay (1s)\fR"
84 /* The time between attempts to acquire an exclusive lock on a mailbox
85 /* file or \fBbounce\fR(8) logfile.
86 /* .IP "\fBipc_timeout (3600s)\fR"
87 /* The time limit for sending or receiving information over an internal
88 /* communication channel.
89 /* .IP "\fBinternal_mail_filter_classes (empty)\fR"
90 /* What categories of Postfix-generated mail are subject to
91 /* before-queue content inspection by non_smtpd_milters, header_checks
93 /* .IP "\fBmail_name (Postfix)\fR"
94 /* The mail system name that is displayed in Received: headers, in
95 /* the SMTP greeting banner, and in bounced mail.
96 /* .IP "\fBmax_idle (100s)\fR"
97 /* The maximum amount of time that an idle Postfix daemon process waits
98 /* for an incoming connection before terminating voluntarily.
99 /* .IP "\fBmax_use (100)\fR"
100 /* The maximal number of incoming connections that a Postfix daemon
101 /* process will service before terminating voluntarily.
102 /* .IP "\fBnotify_classes (resource, software)\fR"
103 /* The list of error classes that are reported to the postmaster.
104 /* .IP "\fBprocess_id (read-only)\fR"
105 /* The process ID of a Postfix command or daemon process.
106 /* .IP "\fBprocess_name (read-only)\fR"
107 /* The process name of a Postfix command or daemon process.
108 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
109 /* The location of the Postfix top-level queue directory.
110 /* .IP "\fBsyslog_facility (mail)\fR"
111 /* The syslog facility of Postfix logging.
112 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
113 /* The mail system name that is prepended to the process name in syslog
114 /* records, so that "smtpd" becomes, for example, "postfix/smtpd".
116 /* /var/spool/postfix/bounce/* non-delivery records
117 /* /var/spool/postfix/defer/* non-delivery records
118 /* /var/spool/postfix/trace/* delivery status records
120 /* bounce(5), bounce message template format
121 /* qmgr(8), queue manager
122 /* postconf(5), configuration parameters
123 /* master(5), generic daemon options
124 /* master(8), process manager
125 /* syslogd(8), system logging
129 /* The Secure Mailer license must be distributed with this software.
132 /* IBM T.J. Watson Research
134 /* Yorktown Heights, NY 10598, USA
137 /* System library. */
139 #include <sys_defs.h>
143 #ifdef STRCASECMP_IN_STRINGS_H
147 /* Utility library. */
152 #include <stringops.h>
153 #include <load_file.h>
155 /* Global library. */
157 #include <mail_proto.h>
158 #include <mail_queue.h>
159 #include <mail_params.h>
160 #include <mail_version.h>
161 #include <mail_conf.h>
163 #include <mail_addr.h>
164 #include <rcpt_buf.h>
165 #include <dsb_scan.h>
167 /* Single-threaded server skeleton. */
169 #include <mail_server.h>
171 /* Application-specific. */
173 #include <bounce_service.h>
178 int var_bounce_limit
;
179 int var_max_queue_time
;
180 int var_delay_warn_time
;
181 char *var_notify_classes
;
182 char *var_bounce_rcpt
;
183 char *var_2bounce_rcpt
;
184 char *var_delay_rcpt
;
185 char *var_bounce_tmpl
;
188 * We're single threaded, so we can avoid some memory allocation overhead.
190 static VSTRING
*queue_id
;
191 static VSTRING
*queue_name
;
192 static RCPT_BUF
*rcpt_buf
;
193 static VSTRING
*encoding
;
194 static VSTRING
*sender
;
195 static VSTRING
*dsn_envid
;
196 static VSTRING
*verp_delims
;
197 static DSN_BUF
*dsn_buf
;
202 BOUNCE_TEMPLATES
*bounce_templates
;
204 #define STR vstring_str
206 #define VS_NEUTER(s) printable(vstring_str(s), '?')
208 /* bounce_append_proto - bounce_append server protocol */
210 static int bounce_append_proto(char *service_name
, VSTREAM
*client
)
212 const char *myname
= "bounce_append_proto";
216 * Read and validate the client request.
218 if (mail_command_server(client
,
219 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, &flags
,
220 ATTR_TYPE_STR
, MAIL_ATTR_QUEUEID
, queue_id
,
221 ATTR_TYPE_FUNC
, rcpb_scan
, (void *) rcpt_buf
,
222 ATTR_TYPE_FUNC
, dsb_scan
, (void *) dsn_buf
,
223 ATTR_TYPE_END
) != 4) {
224 msg_warn("malformed request");
231 if (mail_queue_id_ok(STR(queue_id
)) == 0) {
232 msg_warn("malformed queue id: %s", printable(STR(queue_id
), '?'));
235 VS_NEUTER(rcpt_buf
->address
);
236 VS_NEUTER(rcpt_buf
->orig_addr
);
237 VS_NEUTER(rcpt_buf
->dsn_orcpt
);
238 VS_NEUTER(dsn_buf
->status
);
239 VS_NEUTER(dsn_buf
->action
);
240 VS_NEUTER(dsn_buf
->reason
);
241 VS_NEUTER(dsn_buf
->dtype
);
242 VS_NEUTER(dsn_buf
->dtext
);
243 VS_NEUTER(dsn_buf
->mtype
);
244 VS_NEUTER(dsn_buf
->mname
);
245 (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf
);
246 (void) DSN_FROM_DSN_BUF(dsn_buf
);
249 * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
250 * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
251 * RECIPIENT_FROM_RCPT_BUF().
254 msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s",
255 myname
, flags
, service_name
, STR(queue_id
),
256 STR(rcpt_buf
->orig_addr
), STR(rcpt_buf
->address
),
257 rcpt_buf
->offset
, STR(rcpt_buf
->dsn_orcpt
),
258 rcpt_buf
->dsn_notify
, STR(dsn_buf
->status
),
259 STR(dsn_buf
->action
), STR(dsn_buf
->reason
));
262 * On request by the client, set up a trap to delete the log file in case
265 if (flags
& BOUNCE_FLAG_CLEAN
)
266 bounce_cleanup_register(service_name
, STR(queue_id
));
269 * Execute the request.
271 return (bounce_append_service(flags
, service_name
, STR(queue_id
),
272 &rcpt_buf
->rcpt
, &dsn_buf
->dsn
));
275 /* bounce_notify_proto - bounce_notify server protocol */
277 static int bounce_notify_proto(char *service_name
, VSTREAM
*client
,
278 int (*service
) (int, char *, char *, char *,
279 char *, char *, char *, int,
282 const char *myname
= "bounce_notify_proto";
287 * Read and validate the client request.
289 if (mail_command_server(client
,
290 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, &flags
,
291 ATTR_TYPE_STR
, MAIL_ATTR_QUEUE
, queue_name
,
292 ATTR_TYPE_STR
, MAIL_ATTR_QUEUEID
, queue_id
,
293 ATTR_TYPE_STR
, MAIL_ATTR_ENCODING
, encoding
,
294 ATTR_TYPE_STR
, MAIL_ATTR_SENDER
, sender
,
295 ATTR_TYPE_STR
, MAIL_ATTR_DSN_ENVID
, dsn_envid
,
296 ATTR_TYPE_INT
, MAIL_ATTR_DSN_RET
, &dsn_ret
,
297 ATTR_TYPE_END
) != 7) {
298 msg_warn("malformed request");
305 if (mail_queue_name_ok(STR(queue_name
)) == 0) {
306 msg_warn("malformed queue name: %s", printable(STR(queue_name
), '?'));
309 if (mail_queue_id_ok(STR(queue_id
)) == 0) {
310 msg_warn("malformed queue id: %s", printable(STR(queue_id
), '?'));
313 printable(STR(dsn_envid
), '?');
315 msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s envid=%s ret=0x%x",
316 myname
, flags
, service_name
, STR(queue_name
), STR(queue_id
),
317 STR(encoding
), STR(sender
), STR(dsn_envid
), dsn_ret
);
320 * On request by the client, set up a trap to delete the log file in case
323 if (flags
& BOUNCE_FLAG_CLEAN
)
324 bounce_cleanup_register(service_name
, STR(queue_id
));
327 * Execute the request.
329 return (service(flags
, service_name
, STR(queue_name
),
330 STR(queue_id
), STR(encoding
),
331 STR(sender
), STR(dsn_envid
), dsn_ret
,
335 /* bounce_verp_proto - bounce_notify server protocol, VERP style */
337 static int bounce_verp_proto(char *service_name
, VSTREAM
*client
)
339 const char *myname
= "bounce_verp_proto";
344 * Read and validate the client request.
346 if (mail_command_server(client
,
347 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, &flags
,
348 ATTR_TYPE_STR
, MAIL_ATTR_QUEUE
, queue_name
,
349 ATTR_TYPE_STR
, MAIL_ATTR_QUEUEID
, queue_id
,
350 ATTR_TYPE_STR
, MAIL_ATTR_ENCODING
, encoding
,
351 ATTR_TYPE_STR
, MAIL_ATTR_SENDER
, sender
,
352 ATTR_TYPE_STR
, MAIL_ATTR_DSN_ENVID
, dsn_envid
,
353 ATTR_TYPE_INT
, MAIL_ATTR_DSN_RET
, &dsn_ret
,
354 ATTR_TYPE_STR
, MAIL_ATTR_VERPDL
, verp_delims
,
355 ATTR_TYPE_END
) != 8) {
356 msg_warn("malformed request");
363 if (mail_queue_name_ok(STR(queue_name
)) == 0) {
364 msg_warn("malformed queue name: %s", printable(STR(queue_name
), '?'));
367 if (mail_queue_id_ok(STR(queue_id
)) == 0) {
368 msg_warn("malformed queue id: %s", printable(STR(queue_id
), '?'));
371 printable(STR(dsn_envid
), '?');
372 if (strlen(STR(verp_delims
)) != 2) {
373 msg_warn("malformed verp delimiter string: %s",
374 printable(STR(verp_delims
), '?'));
378 msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s envid=%s ret=0x%x delim=%s",
379 myname
, flags
, service_name
, STR(queue_name
),
380 STR(queue_id
), STR(encoding
), STR(sender
),
381 STR(dsn_envid
), dsn_ret
, STR(verp_delims
));
384 * On request by the client, set up a trap to delete the log file in case
387 if (flags
& BOUNCE_FLAG_CLEAN
)
388 bounce_cleanup_register(service_name
, STR(queue_id
));
391 * Execute the request. Fall back to traditional notification if a bounce
392 * was returned as undeliverable, because we don't want to VERPify those.
394 if (!*STR(sender
) || !strcasecmp(STR(sender
), mail_addr_double_bounce())) {
395 msg_warn("request to send VERP-style notification of bounced mail");
396 return (bounce_notify_service(flags
, service_name
, STR(queue_name
),
397 STR(queue_id
), STR(encoding
),
398 STR(sender
), STR(dsn_envid
), dsn_ret
,
401 return (bounce_notify_verp(flags
, service_name
, STR(queue_name
),
402 STR(queue_id
), STR(encoding
),
403 STR(sender
), STR(dsn_envid
), dsn_ret
,
404 STR(verp_delims
), bounce_templates
));
407 /* bounce_one_proto - bounce_one server protocol */
409 static int bounce_one_proto(char *service_name
, VSTREAM
*client
)
411 const char *myname
= "bounce_one_proto";
416 * Read and validate the client request.
418 if (mail_command_server(client
,
419 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, &flags
,
420 ATTR_TYPE_STR
, MAIL_ATTR_QUEUE
, queue_name
,
421 ATTR_TYPE_STR
, MAIL_ATTR_QUEUEID
, queue_id
,
422 ATTR_TYPE_STR
, MAIL_ATTR_ENCODING
, encoding
,
423 ATTR_TYPE_STR
, MAIL_ATTR_SENDER
, sender
,
424 ATTR_TYPE_STR
, MAIL_ATTR_DSN_ENVID
, dsn_envid
,
425 ATTR_TYPE_INT
, MAIL_ATTR_DSN_RET
, &dsn_ret
,
426 ATTR_TYPE_FUNC
, rcpb_scan
, (void *) rcpt_buf
,
427 ATTR_TYPE_FUNC
, dsb_scan
, (void *) dsn_buf
,
428 ATTR_TYPE_END
) != 9) {
429 msg_warn("malformed request");
436 if (strcmp(service_name
, MAIL_SERVICE_BOUNCE
) != 0) {
437 msg_warn("wrong service name \"%s\" for one-recipient bouncing",
441 if (mail_queue_name_ok(STR(queue_name
)) == 0) {
442 msg_warn("malformed queue name: %s", printable(STR(queue_name
), '?'));
445 if (mail_queue_id_ok(STR(queue_id
)) == 0) {
446 msg_warn("malformed queue id: %s", printable(STR(queue_id
), '?'));
449 printable(STR(dsn_envid
), '?');
450 VS_NEUTER(rcpt_buf
->address
);
451 VS_NEUTER(rcpt_buf
->orig_addr
);
452 VS_NEUTER(rcpt_buf
->dsn_orcpt
);
453 VS_NEUTER(dsn_buf
->status
);
454 VS_NEUTER(dsn_buf
->action
);
455 VS_NEUTER(dsn_buf
->reason
);
456 VS_NEUTER(dsn_buf
->dtype
);
457 VS_NEUTER(dsn_buf
->dtext
);
458 VS_NEUTER(dsn_buf
->mtype
);
459 VS_NEUTER(dsn_buf
->mname
);
460 (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf
);
461 (void) DSN_FROM_DSN_BUF(dsn_buf
);
464 * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
465 * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
466 * RECIPIENT_FROM_RCPT_BUF().
469 msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s",
470 myname
, flags
, STR(queue_name
), STR(queue_id
),
471 STR(encoding
), STR(sender
), STR(dsn_envid
), dsn_ret
,
472 STR(rcpt_buf
->orig_addr
), STR(rcpt_buf
->address
),
473 rcpt_buf
->offset
, STR(rcpt_buf
->dsn_orcpt
),
474 rcpt_buf
->dsn_notify
, STR(dsn_buf
->status
),
475 STR(dsn_buf
->action
), STR(dsn_buf
->reason
));
478 * Execute the request.
480 return (bounce_one_service(flags
, STR(queue_name
), STR(queue_id
),
481 STR(encoding
), STR(sender
), STR(dsn_envid
),
482 dsn_ret
, rcpt_buf
, dsn_buf
, bounce_templates
));
485 /* bounce_service - parse bounce command type and delegate */
487 static void bounce_service(VSTREAM
*client
, char *service_name
, char **argv
)
493 * Sanity check. This service takes no command-line arguments. The
494 * service name should be usable as a subdirectory name.
497 msg_fatal("unexpected command-line argument: %s", argv
[0]);
498 if (mail_queue_name_ok(service_name
) == 0)
499 msg_fatal("malformed service name: %s", service_name
);
502 * Read and validate the first parameter of the client request. Let the
503 * request-specific protocol routines take care of the remainder.
505 if (attr_scan(client
, ATTR_FLAG_STRICT
| ATTR_FLAG_MORE
,
506 ATTR_TYPE_INT
, MAIL_ATTR_NREQ
, &command
, 0) != 1) {
507 msg_warn("malformed request");
509 } else if (command
== BOUNCE_CMD_VERP
) {
510 status
= bounce_verp_proto(service_name
, client
);
511 } else if (command
== BOUNCE_CMD_FLUSH
) {
512 status
= bounce_notify_proto(service_name
, client
,
513 bounce_notify_service
);
514 } else if (command
== BOUNCE_CMD_WARN
) {
515 status
= bounce_notify_proto(service_name
, client
,
516 bounce_warn_service
);
517 } else if (command
== BOUNCE_CMD_TRACE
) {
518 status
= bounce_notify_proto(service_name
, client
,
519 bounce_trace_service
);
520 } else if (command
== BOUNCE_CMD_APPEND
) {
521 status
= bounce_append_proto(service_name
, client
);
522 } else if (command
== BOUNCE_CMD_ONE
) {
523 status
= bounce_one_proto(service_name
, client
);
525 msg_warn("unknown command: %d", command
);
530 * When the request has completed, send the completion status to the
533 attr_print(client
, ATTR_FLAG_NONE
,
534 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, status
,
536 vstream_fflush(client
);
539 * When a cleanup trap was set, delete the log file in case of error.
540 * This includes errors while sending the completion status to the
543 if (bounce_cleanup_path
) {
544 if (status
|| vstream_ferror(client
))
545 bounce_cleanup_log();
546 bounce_cleanup_unregister();
550 static void load_helper(VSTREAM
*stream
, void *context
)
552 BOUNCE_TEMPLATES
*templates
= (BOUNCE_TEMPLATES
*) context
;
554 bounce_templates_load(stream
, templates
);
557 /* pre_jail_init - pre-jail initialization */
559 static void pre_jail_init(char *unused_name
, char **unused_argv
)
563 * Bundle up a bunch of bounce template information.
565 bounce_templates
= bounce_templates_create();
568 * Load the alternate message files (if specified) before entering the
571 if (*var_bounce_tmpl
)
572 load_file(var_bounce_tmpl
, load_helper
, (char *) bounce_templates
);
575 /* post_jail_init - initialize after entering chroot jail */
577 static void post_jail_init(char *service_name
, char **unused_argv
)
581 * Special case: dump bounce templates. This is not part of the master(5)
582 * public interface. This internal interface is used by the postconf
583 * command. It was implemented before bounce templates were isolated into
584 * modules that could have been called directly.
586 if (strcmp(service_name
, "dump_templates") == 0) {
587 bounce_templates_dump(VSTREAM_OUT
, bounce_templates
);
588 vstream_fflush(VSTREAM_OUT
);
591 if (strcmp(service_name
, "expand_templates") == 0) {
592 bounce_templates_expand(VSTREAM_OUT
, bounce_templates
);
593 vstream_fflush(VSTREAM_OUT
);
598 * Initialize. We're single threaded so we can reuse some memory upon
599 * successive requests.
601 queue_id
= vstring_alloc(10);
602 queue_name
= vstring_alloc(10);
603 rcpt_buf
= rcpb_create();
604 encoding
= vstring_alloc(10);
605 sender
= vstring_alloc(10);
606 dsn_envid
= vstring_alloc(10);
607 verp_delims
= vstring_alloc(10);
608 dsn_buf
= dsb_create();
611 MAIL_VERSION_STAMP_DECLARE
;
613 /* main - the main program */
615 int main(int argc
, char **argv
)
617 static const CONFIG_INT_TABLE int_table
[] = {
618 VAR_BOUNCE_LIMIT
, DEF_BOUNCE_LIMIT
, &var_bounce_limit
, 1, 0,
621 static const CONFIG_TIME_TABLE time_table
[] = {
622 VAR_MAX_QUEUE_TIME
, DEF_MAX_QUEUE_TIME
, &var_max_queue_time
, 0, 8640000,
623 VAR_DELAY_WARN_TIME
, DEF_DELAY_WARN_TIME
, &var_delay_warn_time
, 0, 0,
626 static const CONFIG_STR_TABLE str_table
[] = {
627 VAR_NOTIFY_CLASSES
, DEF_NOTIFY_CLASSES
, &var_notify_classes
, 0, 0,
628 VAR_BOUNCE_RCPT
, DEF_BOUNCE_RCPT
, &var_bounce_rcpt
, 1, 0,
629 VAR_2BOUNCE_RCPT
, DEF_2BOUNCE_RCPT
, &var_2bounce_rcpt
, 1, 0,
630 VAR_DELAY_RCPT
, DEF_DELAY_RCPT
, &var_delay_rcpt
, 1, 0,
631 VAR_BOUNCE_TMPL
, DEF_BOUNCE_TMPL
, &var_bounce_tmpl
, 0, 0,
636 * Fingerprint executables and core dumps.
638 MAIL_VERSION_STAMP_ALLOCATE
;
641 * Pass control to the single-threaded service skeleton.
643 single_server_main(argc
, argv
, bounce_service
,
644 MAIL_SERVER_INT_TABLE
, int_table
,
645 MAIL_SERVER_STR_TABLE
, str_table
,
646 MAIL_SERVER_TIME_TABLE
, time_table
,
647 MAIL_SERVER_PRE_INIT
, pre_jail_init
,
648 MAIL_SERVER_POST_INIT
, post_jail_init
,
649 MAIL_SERVER_UNLIMITED
,