1159045Smaxim/*	$NetBSD: showq.c,v 1.5 2023/12/23 20:30:45 christos Exp $	*/
2159045Smaxim
3159045Smaxim/*++
4159045Smaxim/* NAME
5159045Smaxim/*	showq 8
6159045Smaxim/* SUMMARY
7159045Smaxim/*	list the Postfix mail queue
8159045Smaxim/* SYNOPSIS
9159045Smaxim/*	\fBshowq\fR [generic Postfix daemon options]
10159045Smaxim/* DESCRIPTION
11159045Smaxim/*	The \fBshowq\fR(8) daemon reports the Postfix mail queue status.
12159045Smaxim/*	The output is meant to be formatted by the postqueue(1) command,
13159045Smaxim/*	as it emulates the Sendmail `mailq' command.
14159045Smaxim/*
15159045Smaxim/*	The \fBshowq\fR(8) daemon can also be run in stand-alone mode
16159045Smaxim/*	by the superuser. This mode of operation is used to emulate
17159045Smaxim/*	the `mailq' command while the Postfix mail system is down.
18159045Smaxim/* SECURITY
19159045Smaxim/* .ad
20159045Smaxim/* .fi
21159045Smaxim/*	The \fBshowq\fR(8) daemon can run in a chroot jail at fixed low
22159045Smaxim/*	privilege, and takes no input from the client. Its service port
23159045Smaxim/*	is accessible to local untrusted users, so the service can be
24159045Smaxim/*	susceptible to denial of service attacks.
25159045Smaxim/* STANDARDS
26159045Smaxim/* .ad
27159045Smaxim/* .fi
28159045Smaxim/*	None. The \fBshowq\fR(8) daemon does not interact with the
29159045Smaxim/*	outside world.
30246670Spluknet/* DIAGNOSTICS
31159045Smaxim/*	Problems and transactions are logged to \fBsyslogd\fR(8)
32159045Smaxim/*	or \fBpostlogd\fR(8).
33246670Spluknet/* CONFIGURATION PARAMETERS
34159045Smaxim/* .ad
35246670Spluknet/* .fi
36159045Smaxim/*	Changes to \fBmain.cf\fR are picked up automatically as \fBshowq\fR(8)
37159045Smaxim/*	processes run for only a limited amount of time. Use the command
38159045Smaxim/*	"\fBpostfix reload\fR" to speed up a change.
39159045Smaxim/*
40159045Smaxim/*	The text below provides only a parameter summary. See
41159045Smaxim/*	\fBpostconf\fR(5) for more details including examples.
42246670Spluknet/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
43159045Smaxim/*	The default location of the Postfix main.cf and master.cf
44159045Smaxim/*	configuration files.
45246670Spluknet/* .IP "\fBdaemon_timeout (18000s)\fR"
46159045Smaxim/*	How much time a Postfix daemon process may take to handle a
47159045Smaxim/*	request before it is terminated by a built-in watchdog timer.
48246670Spluknet/* .IP "\fBduplicate_filter_limit (1000)\fR"
49159045Smaxim/*	The maximal number of addresses remembered by the address
50159045Smaxim/*	duplicate filter for \fBaliases\fR(5) or \fBvirtual\fR(5) alias expansion, or
51159045Smaxim/*	for \fBshowq\fR(8) queue displays.
52159045Smaxim/* .IP "\fBempty_address_recipient (MAILER-DAEMON)\fR"
53159045Smaxim/*	The recipient of mail addressed to the null address.
54159045Smaxim/* .IP "\fBipc_timeout (3600s)\fR"
55159045Smaxim/*	The time limit for sending or receiving information over an internal
56159045Smaxim/*	communication channel.
57159045Smaxim/* .IP "\fBmax_idle (100s)\fR"
58246670Spluknet/*	The maximum amount of time that an idle Postfix daemon process waits
59246670Spluknet/*	for an incoming connection before terminating voluntarily.
60159045Smaxim/* .IP "\fBmax_use (100)\fR"
61246670Spluknet/*	The maximal number of incoming connections that a Postfix daemon
62246670Spluknet/*	process will service before terminating voluntarily.
63246670Spluknet/* .IP "\fBprocess_id (read-only)\fR"
64246670Spluknet/*	The process ID of a Postfix command or daemon process.
65246670Spluknet/* .IP "\fBprocess_name (read-only)\fR"
66159045Smaxim/*	The process name of a Postfix command or daemon process.
67246670Spluknet/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
68246670Spluknet/*	The location of the Postfix top-level queue directory.
69246670Spluknet/* .IP "\fBsyslog_facility (mail)\fR"
70159045Smaxim/*	The syslog facility of Postfix logging.
71159045Smaxim/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
72159045Smaxim/*	A prefix that is prepended to the process name in syslog
73159045Smaxim/*	records, so that, for example, "smtpd" becomes "prefix/smtpd".
74159045Smaxim/* .PP
75159045Smaxim/*	Available in Postfix version 2.9 and later:
76159045Smaxim/* .IP "\fBenable_long_queue_ids (no)\fR"
77246670Spluknet/*	Enable long, non-repeating, queue IDs (queue file names).
78159045Smaxim/* .PP
79159045Smaxim/*	Available in Postfix 3.3 and later:
80246670Spluknet/* .IP "\fBservice_name (read-only)\fR"
81246670Spluknet/*	The master.cf service name of a Postfix daemon process.
82246670Spluknet/* FILES
83246670Spluknet/*	/var/spool/postfix, queue directories
84246670Spluknet/* SEE ALSO
85246670Spluknet/*	pickup(8), local mail pickup service
86246670Spluknet/*	cleanup(8), canonicalize and enqueue mail
87246670Spluknet/*	qmgr(8), queue manager
88159045Smaxim/*	postconf(5), configuration parameters
89159045Smaxim/*	master(8), process manager
90246670Spluknet/*	postlogd(8), Postfix logging
91246670Spluknet/*	syslogd(8), system logging
92159045Smaxim/* LICENSE
93159045Smaxim/* .ad
94246670Spluknet/* .fi
95246670Spluknet/*	The Secure Mailer license must be distributed with this software.
96246670Spluknet/* AUTHOR(S)
97246670Spluknet/*	Wietse Venema
98246670Spluknet/*	IBM T.J. Watson Research
99246670Spluknet/*	P.O. Box 704
100246670Spluknet/*	Yorktown Heights, NY 10598, USA
101246670Spluknet/*
102246670Spluknet/*	Wietse Venema
103246670Spluknet/*	Google, Inc.
104246670Spluknet/*	111 8th Avenue
105246670Spluknet/*	New York, NY 10011, USA
106246670Spluknet/*--*/
107246670Spluknet
108246670Spluknet/* System library. */
109246670Spluknet
110246670Spluknet#include <sys_defs.h>
111246670Spluknet#include <sys/stat.h>
112246670Spluknet#include <dirent.h>
113246670Spluknet#include <stdlib.h>
114246670Spluknet#include <unistd.h>
115246670Spluknet#include <errno.h>
116246670Spluknet#include <fcntl.h>
117246670Spluknet#include <time.h>
118246670Spluknet#include <string.h>
119246670Spluknet#include <ctype.h>
120246670Spluknet
121246670Spluknet/* Utility library. */
122246670Spluknet
123246670Spluknet#include <msg.h>
124246670Spluknet#include <scan_dir.h>
125246670Spluknet#include <vstring.h>
126246670Spluknet#include <vstream.h>
127246670Spluknet#include <vstring_vstream.h>
128246670Spluknet#include <stringops.h>
129246670Spluknet#include <mymalloc.h>
130246670Spluknet#include <htable.h>
131159045Smaxim
132159045Smaxim/* Global library. */
133246670Spluknet
134246670Spluknet#include <mail_queue.h>
135246670Spluknet#include <mail_open_ok.h>
136246670Spluknet#include <mail_proto.h>
137246670Spluknet#include <mail_date.h>
138246670Spluknet#include <mail_params.h>
139246670Spluknet#include <mail_version.h>
140246670Spluknet#include <mail_scan_dir.h>
141246670Spluknet#include <mail_conf.h>
142246670Spluknet#include <record.h>
143246670Spluknet#include <rec_type.h>
144246670Spluknet#include <quote_822_local.h>
145246670Spluknet#include <mail_addr.h>
146246670Spluknet#include <bounce_log.h>
147246670Spluknet
148246670Spluknet/* Single-threaded server skeleton. */
149246670Spluknet
150246670Spluknet#include <mail_server.h>
151246670Spluknet
152246670Spluknet/* Application-specific. */
153246670Spluknet
154246670Spluknetint     var_dup_filter_limit;
155246670Spluknetchar   *var_empty_addr;
156246670Spluknet
157246670Spluknetstatic void showq_reasons(VSTREAM *, BOUNCE_LOG *, RCPT_BUF *, DSN_BUF *,
158246670Spluknet			          HTABLE *);
159246670Spluknet
160246670Spluknet#define STR(x)	vstring_str(x)
161246670Spluknet
162246670Spluknet/* showq_report - report status of sender and recipients */
163246670Spluknet
164246670Spluknetstatic void showq_report(VSTREAM *client, char *queue, char *id,
165159045Smaxim			         VSTREAM *qfile, long size, time_t mtime,
166159045Smaxim			         mode_t mode)
167246670Spluknet{
168246670Spluknet    VSTRING *buf = vstring_alloc(100);
169159045Smaxim    VSTRING *printable_quoted_addr = vstring_alloc(100);
170246670Spluknet    int     rec_type;
171246670Spluknet    time_t  arrival_time = 0;
172246670Spluknet    char   *start;
173246670Spluknet    long    msg_size = size;
174246670Spluknet    BOUNCE_LOG *logfile;
175159045Smaxim    HTABLE *dup_filter = 0;
176246670Spluknet    RCPT_BUF *rcpt_buf = 0;
177246670Spluknet    DSN_BUF *dsn_buf = 0;
178159045Smaxim    int     sender_seen = 0;
179246670Spluknet    int     msg_size_ok = 0;
180159045Smaxim
181246670Spluknet    /*
182246670Spluknet     * Let the optimizer worry about eliminating duplicate code.
183246670Spluknet     */
184159045Smaxim#define SHOWQ_CLEANUP_AND_RETURN { \
185246670Spluknet	if (sender_seen > 0) \
186246670Spluknet	    attr_print(client, ATTR_FLAG_NONE, ATTR_TYPE_END); \
187246670Spluknet	vstring_free(buf); \
188246670Spluknet	vstring_free(printable_quoted_addr); \
189246670Spluknet	if (rcpt_buf) \
190246670Spluknet	    rcpb_free(rcpt_buf); \
191159045Smaxim	if (dsn_buf) \
192246670Spluknet	    dsb_free(dsn_buf); \
193246670Spluknet	if (dup_filter) \
194246670Spluknet	    htable_free(dup_filter, (void (*) (void *)) 0); \
195246670Spluknet    }
196159045Smaxim
197246670Spluknet    /*
198246670Spluknet     * XXX addresses in defer logfiles are in printable quoted form, while
199246670Spluknet     * addresses in message envelope records are in raw unquoted form. This
200246670Spluknet     * may change once we replace the present ad-hoc bounce/defer logfile
201246670Spluknet     * format by one that is transparent for control etc. characters. See
202246670Spluknet     * also: bounce/bounce_append_service.c.
203246670Spluknet     *
204246670Spluknet     * XXX With Postfix <= 2.0, "postsuper -r" results in obsolete size records
205159045Smaxim     * from previous cleanup runs. Skip the obsolete size records.
206246670Spluknet     */
207159045Smaxim    while (!vstream_ferror(client) && (rec_type = rec_get(qfile, buf, 0)) > 0) {
208246670Spluknet	start = vstring_str(buf);
209246670Spluknet	if (msg_verbose)
210246670Spluknet	    msg_info("record %c %s", rec_type, printable(start, '?'));
211246670Spluknet	switch (rec_type) {
212159045Smaxim	case REC_TYPE_TIME:
213246670Spluknet	    /* TODO: parse seconds and microseconds. */
214159045Smaxim	    if (arrival_time == 0)
215246670Spluknet		arrival_time = atol(start);
216159045Smaxim	    break;
217159045Smaxim	case REC_TYPE_SIZE:
218159045Smaxim	    if (msg_size_ok == 0) {
219246670Spluknet		msg_size_ok = (start[strspn(start, "0123456789 ")] == 0
220159045Smaxim			       && (msg_size = atol(start)) >= 0);
221159045Smaxim		if (msg_size_ok == 0) {
222159045Smaxim		    msg_warn("%s: malformed size record: %.100s "
223246670Spluknet			     "-- using file size instead",
224159045Smaxim			     id, printable(start, '?'));
225246670Spluknet		    msg_size = size;
226159045Smaxim		}
227246670Spluknet	    }
228246670Spluknet	    break;
229246670Spluknet	case REC_TYPE_FROM:
230159095Smaxim	    if (*start == 0)
231246670Spluknet		start = var_empty_addr;
232246670Spluknet	    quote_822_local(printable_quoted_addr, start);
233246670Spluknet	    /* For consistency with REC_TYPE_RCPT below. */
234246670Spluknet	    printable(STR(printable_quoted_addr), '?');
235246670Spluknet	    if (sender_seen++ > 0) {
236246670Spluknet		msg_warn("%s: duplicate sender address: %s "
237246670Spluknet			 "-- skipping remainder of this file",
238246670Spluknet			 id, STR(printable_quoted_addr));
239246670Spluknet		SHOWQ_CLEANUP_AND_RETURN;
240246670Spluknet	    }
241246670Spluknet	    attr_print(client, ATTR_FLAG_MORE,
242246670Spluknet		       SEND_ATTR_STR(MAIL_ATTR_QUEUE, queue),
243246670Spluknet		       SEND_ATTR_STR(MAIL_ATTR_QUEUEID, id),
244246670Spluknet		       SEND_ATTR_LONG(MAIL_ATTR_TIME, arrival_time > 0 ?
245246670Spluknet				      arrival_time : mtime),
246159045Smaxim		       SEND_ATTR_LONG(MAIL_ATTR_SIZE, msg_size),
247159045Smaxim		       SEND_ATTR_INT(MAIL_ATTR_FORCED_EXPIRE,
248159045Smaxim				     (mode & MAIL_QUEUE_STAT_EXPIRE) != 0),
249159045Smaxim		       SEND_ATTR_STR(MAIL_ATTR_SENDER,
250159045Smaxim				     STR(printable_quoted_addr)),
251246670Spluknet		       ATTR_TYPE_END);
252159045Smaxim	    break;
253159045Smaxim	case REC_TYPE_RCPT:
254159045Smaxim	    if (sender_seen == 0) {
255159045Smaxim		msg_warn("%s: missing sender address: %s "
256246670Spluknet			 "-- skipping remainder of this file",
257159045Smaxim			 id, STR(printable_quoted_addr));
258159045Smaxim		SHOWQ_CLEANUP_AND_RETURN;
259159045Smaxim	    }
260159045Smaxim	    if (*start == 0)			/* can't happen? */
261159045Smaxim		start = var_empty_addr;
262159045Smaxim	    quote_822_local(printable_quoted_addr, start);
263159045Smaxim	    /* For consistency with recipients in bounce logfile. */
264246670Spluknet	    printable(STR(printable_quoted_addr), '?');
265159045Smaxim	    if (dup_filter == 0
266159045Smaxim	      || htable_locate(dup_filter, STR(printable_quoted_addr)) == 0)
267159045Smaxim		attr_print(client, ATTR_FLAG_MORE,
268246670Spluknet			   SEND_ATTR_STR(MAIL_ATTR_RECIP,
269159045Smaxim					 STR(printable_quoted_addr)),
270159045Smaxim			   SEND_ATTR_STR(MAIL_ATTR_WHY, ""),
271246670Spluknet			   ATTR_TYPE_END);
272159045Smaxim	    break;
273246670Spluknet	case REC_TYPE_MESG:
274159045Smaxim	    if (msg_size_ok && vstream_fseek(qfile, msg_size, SEEK_CUR) < 0)
275246670Spluknet		msg_fatal("seek file %s: %m", VSTREAM_PATH(qfile));
276159045Smaxim	    break;
277246670Spluknet	case REC_TYPE_END:
278246670Spluknet	    break;
279159045Smaxim	}
280246670Spluknet
281246670Spluknet	/*
282246670Spluknet	 * Before listing any recipients from the queue file, try to list
283246670Spluknet	 * recipients from the corresponding defer logfile with per-recipient
284246670Spluknet	 * descriptions why delivery was deferred.
285246670Spluknet	 *
286246670Spluknet	 * The defer logfile is not necessarily complete: delivery may be
287246670Spluknet	 * interrupted (postfix stop or reload) before all recipients have
288246670Spluknet	 * been tried.
289159045Smaxim	 *
290159045Smaxim	 * Therefore we keep a record of recipients found in the defer logfile,
291159045Smaxim	 * and try to avoid listing those recipients again when processing
292159045Smaxim	 * recipients from the queue file.
293159045Smaxim	 */
294159045Smaxim	if (rec_type == REC_TYPE_FROM
295159045Smaxim	    && (logfile = bounce_log_open(MAIL_QUEUE_DEFER, id, O_RDONLY, 0)) != 0) {
296159045Smaxim	    if (dup_filter != 0)
297246670Spluknet		msg_panic("showq_report: attempt to reuse duplicate filter");
298159045Smaxim	    dup_filter = htable_create(var_dup_filter_limit);
299159045Smaxim	    if (rcpt_buf == 0)
300159045Smaxim		rcpt_buf = rcpb_create();
301246670Spluknet	    if (dsn_buf == 0)
302246670Spluknet		dsn_buf = dsb_create();
303246670Spluknet	    showq_reasons(client, logfile, rcpt_buf, dsn_buf, dup_filter);
304246670Spluknet	    if (bounce_log_close(logfile))
305246670Spluknet		msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id);
306246670Spluknet	}
307246670Spluknet    }
308246670Spluknet    SHOWQ_CLEANUP_AND_RETURN;
309246670Spluknet}
310246670Spluknet
311246670Spluknet/* showq_reasons - show deferral reasons */
312246670Spluknet
313159045Smaximstatic void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf,
314246670Spluknet			          DSN_BUF *dsn_buf, HTABLE *dup_filter)
315159045Smaxim{
316246670Spluknet    RECIPIENT *rcpt = &rcpt_buf->rcpt;
317246670Spluknet    DSN    *dsn = &dsn_buf->dsn;
318159045Smaxim
319246670Spluknet    while (bounce_log_read(bp, rcpt_buf, dsn_buf) != 0) {
320246670Spluknet
321246670Spluknet	/*
322246670Spluknet	 * Update the duplicate filter.
323246670Spluknet	 */
324246670Spluknet	if (var_dup_filter_limit == 0
325246670Spluknet	    || dup_filter->used < var_dup_filter_limit)
326246670Spluknet	    if (htable_locate(dup_filter, rcpt->address) == 0)
327246670Spluknet		htable_enter(dup_filter, rcpt->address, (void *) 0);
328246670Spluknet
329246670Spluknet	attr_print(client, ATTR_FLAG_MORE,
330246670Spluknet		   SEND_ATTR_STR(MAIL_ATTR_RECIP, rcpt->address),
331246670Spluknet		   SEND_ATTR_STR(MAIL_ATTR_WHY, dsn->reason),
332246670Spluknet		   ATTR_TYPE_END);
333246670Spluknet    }
334246670Spluknet}
335159045Smaxim
336246670Spluknet
337246670Spluknet/* showq_service - service client */
338246670Spluknet
339246670Spluknetstatic void showq_service(VSTREAM *client, char *unused_service, char **argv)
340159045Smaxim{
341246670Spluknet    VSTREAM *qfile;
342159045Smaxim    const char *path;
343159045Smaxim    int     status;
344246670Spluknet    char   *id;
345159045Smaxim    struct stat st;
346159045Smaxim    struct queue_info {
347159045Smaxim	char   *name;			/* queue name */
348159045Smaxim	char   *(*scan_next) (SCAN_DIR *);	/* flat or recursive */
349246670Spluknet    };
350246670Spluknet    struct queue_info *qp;
351159045Smaxim
352159045Smaxim    static struct queue_info queue_info[] = {
353246670Spluknet	MAIL_QUEUE_MAILDROP, scan_dir_next,
354246670Spluknet	MAIL_QUEUE_ACTIVE, mail_scan_dir_next,
355159045Smaxim	MAIL_QUEUE_INCOMING, mail_scan_dir_next,
356246670Spluknet	MAIL_QUEUE_DEFERRED, mail_scan_dir_next,
357246670Spluknet	MAIL_QUEUE_HOLD, mail_scan_dir_next,
358159045Smaxim	0,
359246670Spluknet    };
360246670Spluknet
361159045Smaxim    /*
362159045Smaxim     * Sanity check. This service takes no command-line arguments.
363159045Smaxim     */
364159045Smaxim    if (argv[0])
365159045Smaxim	msg_fatal("unexpected command-line argument: %s", argv[0]);
366159045Smaxim
367159045Smaxim    /*
368246670Spluknet     * Protocol identification.
369246670Spluknet     */
370159045Smaxim    (void) attr_print(client, ATTR_FLAG_NONE,
371246670Spluknet		      SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_SHOWQ),
372246670Spluknet		      ATTR_TYPE_END);
373246670Spluknet
374246670Spluknet    /*
375246670Spluknet     * Skip any files that have the wrong permissions. If we can't open an
376246670Spluknet     * existing file, assume the system is out of resources or that it is
377246670Spluknet     * mis-configured, and force backoff by raising a fatal error.
378246670Spluknet     */
379246670Spluknet    for (qp = queue_info; qp->name != 0; qp++) {
380246670Spluknet	SCAN_DIR *scan = scan_dir_open(qp->name);
381246670Spluknet	char   *saved_id = 0;
382246670Spluknet
383246670Spluknet	while ((id = qp->scan_next(scan)) != 0) {
384246670Spluknet
385246670Spluknet	    /*
386246670Spluknet	     * XXX I have seen showq loop on the same queue id. That would be
387246670Spluknet	     * an operating system bug, but who cares whose fault it is. Make
388246670Spluknet	     * sure this will never happen again.
389246670Spluknet	     */
390246670Spluknet	    if (saved_id) {
391246670Spluknet		if (strcmp(saved_id, id) == 0) {
392246670Spluknet		    msg_warn("readdir loop on queue %s id %s", qp->name, id);
393246670Spluknet		    break;
394246670Spluknet		}
395246670Spluknet		myfree(saved_id);
396246670Spluknet	    }
397246670Spluknet	    saved_id = mystrdup(id);
398246670Spluknet	    status = mail_open_ok(qp->name, id, &st, &path);
399246670Spluknet	    if (status == MAIL_OPEN_YES) {
400246670Spluknet		if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) {
401246670Spluknet		    showq_report(client, qp->name, id, qfile, (long) st.st_size,
402246670Spluknet				 st.st_mtime, st.st_mode);
403246670Spluknet		    if (vstream_fclose(qfile))
404246670Spluknet			msg_warn("close file %s %s: %m", qp->name, id);
405246670Spluknet		} else if (errno != ENOENT) {
406246670Spluknet		    msg_warn("open %s %s: %m", qp->name, id);
407246670Spluknet		}
408246670Spluknet	    }
409246670Spluknet	    vstream_fflush(client);
410246670Spluknet	}
411246670Spluknet	if (saved_id)
412246670Spluknet	    myfree(saved_id);
413246670Spluknet	scan_dir_close(scan);
414246670Spluknet    }
415246670Spluknet    attr_print(client, ATTR_FLAG_NONE, ATTR_TYPE_END);
416246670Spluknet}
417246670Spluknet
418246670SpluknetMAIL_VERSION_STAMP_DECLARE;
419246670Spluknet
420246670Spluknet/* main - pass control to the single-threaded server skeleton */
421246670Spluknet
422246670Spluknetint     main(int argc, char **argv)
423246670Spluknet{
424246670Spluknet    static const CONFIG_INT_TABLE int_table[] = {
425246670Spluknet	VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
426246670Spluknet	0,
427246670Spluknet    };
428246670Spluknet    CONFIG_STR_TABLE str_table[] = {
429246670Spluknet	VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0,
430246670Spluknet	0,
431246670Spluknet    };
432246670Spluknet
433159045Smaxim    /*
434159045Smaxim     * Fingerprint executables and core dumps.
435246670Spluknet     */
436246670Spluknet    MAIL_VERSION_STAMP_ALLOCATE;
437246670Spluknet
438246670Spluknet    single_server_main(argc, argv, showq_service,
439246670Spluknet		       CA_MAIL_SERVER_INT_TABLE(int_table),
440246670Spluknet		       CA_MAIL_SERVER_STR_TABLE(str_table),
441246670Spluknet		       0);
442246670Spluknet}
443246670Spluknet