5
/* list the Postfix mail queue
7
/* \fBshowq\fR [generic Postfix daemon options]
9
/* The \fBshowq\fR daemon reports the Postfix mail queue status.
10
/* It is the program that emulates the sendmail `mailq' command.
12
/* The \fBshowq\fR daemon can also be run in stand-alone mode
13
/* by the superuser. This mode of operation is used to emulate
14
/* the `mailq' command while the Postfix mail system is down.
18
/* The \fBshowq\fR daemon can run in a chroot jail at fixed low
19
/* privilege, and takes no input from the client. Its service port
20
/* is accessible to local untrusted users, so the service can be
21
/* susceptible to denial of service attacks.
25
/* None. The showq daemon does not interact with the outside world.
27
/* Problems and transactions are logged to \fBsyslogd\fR(8).
29
/* The \fBshowq\fR daemon runs at a fixed low privilege; consequently,
30
/* it cannot extract information from queue files in the
31
/* \fBmaildrop\fR directory.
32
/* CONFIGURATION PARAMETERS
35
/* Changes to \fBmain.cf\fR are picked up automatically as showq(8)
36
/* processes run for only a limited amount of time. Use the command
37
/* "\fBpostfix reload\fR" to speed up a change.
39
/* The text below provides only a parameter summary. See
40
/* postconf(5) for more details including examples.
41
/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
42
/* The default location of the Postfix main.cf and master.cf
43
/* configuration files.
44
/* .IP "\fBdaemon_timeout (18000s)\fR"
45
/* How much time a Postfix daemon process may take to handle a
46
/* request before it is terminated by a built-in watchdog timer.
47
/* .IP "\fBduplicate_filter_limit (1000)\fR"
48
/* The maximal number of addresses remembered by the address
49
/* duplicate filter for aliases(5) or virtual(5) alias expansion, or
50
/* for showq(8) queue displays.
51
/* .IP "\fBempty_address_recipient (MAILER-DAEMON)\fR"
52
/* The recipient of mail addressed to the null address.
53
/* .IP "\fBipc_timeout (3600s)\fR"
54
/* The time limit for sending or receiving information over an internal
55
/* communication channel.
56
/* .IP "\fBmax_idle (100s)\fR"
57
/* The maximum amount of time that an idle Postfix daemon process
58
/* waits for the next service request before exiting.
59
/* .IP "\fBmax_use (100)\fR"
60
/* The maximal number of connection requests before a Postfix daemon
61
/* process terminates.
62
/* .IP "\fBprocess_id (read-only)\fR"
63
/* The process ID of a Postfix command or daemon process.
64
/* .IP "\fBprocess_name (read-only)\fR"
65
/* The process name of a Postfix command or daemon process.
66
/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
67
/* The location of the Postfix top-level queue directory.
68
/* .IP "\fBsyslog_facility (mail)\fR"
69
/* The syslog facility of Postfix logging.
70
/* .IP "\fBsyslog_name (postfix)\fR"
71
/* The mail system name that is prepended to the process name in syslog
72
/* records, so that "smtpd" becomes, for example, "postfix/smtpd".
74
/* /var/spool/postfix, queue directories
76
/* pickup(8), local mail pickup service
77
/* cleanup(8), canonicalize and enqueue mail
78
/* qmgr(8), queue manager
79
/* postconf(5), configuration parameters
80
/* master(8), process manager
81
/* syslogd(8), system logging
85
/* The Secure Mailer license must be distributed with this software.
88
/* IBM T.J. Watson Research
90
/* Yorktown Heights, NY 10598, USA
106
/* Utility library. */
109
#include <scan_dir.h>
112
#include <vstring_vstream.h>
113
#include <stringops.h>
114
#include <mymalloc.h>
117
/* Global library. */
119
#include <mail_queue.h>
120
#include <mail_open_ok.h>
121
#include <mail_proto.h>
122
#include <mail_date.h>
123
#include <mail_params.h>
124
#include <mail_scan_dir.h>
125
#include <mail_conf.h>
127
#include <rec_type.h>
128
#include <quote_822_local.h>
129
#include <mail_addr.h>
130
#include <bounce_log.h>
132
/* Single-threaded server skeleton. */
134
#include <mail_server.h>
136
/* Application-specific. */
138
int var_dup_filter_limit;
139
char *var_empty_addr;
141
#define STRING_FORMAT "%-10s %8s %-20s %s\n"
142
#define SENDER_FORMAT "%-11s%8ld %20.20s %s\n"
143
#define DROP_FORMAT "%-10s%c%8ld %20.20s (maildrop queue, sender UID %u)\n"
145
static void showq_reasons(VSTREAM *, BOUNCE_LOG *, HTABLE *);
147
#define STR(x) vstring_str(x)
149
/* showq_report - report status of sender and recipients */
151
static void showq_report(VSTREAM *client, char *queue, char *id,
152
VSTREAM *qfile, long size, time_t mtime)
154
VSTRING *buf = vstring_alloc(100);
155
VSTRING *printable_quoted_addr = vstring_alloc(100);
157
time_t arrival_time = 0;
161
HTABLE *dup_filter = 0;
162
char status = (strcmp(queue, MAIL_QUEUE_ACTIVE) == 0 ? '*' :
163
strcmp(queue, MAIL_QUEUE_HOLD) == 0 ? '!' : ' ');
167
* XXX addresses in defer logfiles are in printable quoted form, while
168
* addresses in message envelope records are in raw unquoted form. This
169
* may change once we replace the present ad-hoc bounce/defer logfile
170
* format by one that is transparent for control etc. characters. See
171
* also: bounce/bounce_append_service.c.
173
* XXX With Postfix <= 2.0, "postsuper -r" results in obsolete size records
174
* from previous cleanup runs. Skip the obsolete size records.
176
while (!vstream_ferror(client) && (rec_type = rec_get(qfile, buf, 0)) > 0) {
177
start = vstring_str(buf);
179
msg_info("record %c %s", rec_type, printable(start, '?'));
182
arrival_time = atol(start);
186
if ((msg_size_ok = ((msg_size = atol(start)) > 0)) == 0) {
187
msg_warn("%s: malformed size record: %.100s",
188
id, printable(start, '?'));
195
start = var_empty_addr;
196
quote_822_local(printable_quoted_addr, start);
197
printable(STR(printable_quoted_addr), '?');
198
/* quote_822_local() saves buf, so we can reuse its space. */
199
vstring_sprintf(buf, "%s%c", id, status);
200
vstream_fprintf(client, SENDER_FORMAT, STR(buf),
201
msg_size > 0 ? msg_size : size, arrival_time > 0 ?
202
asctime(localtime(&arrival_time)) :
203
asctime(localtime(&mtime)),
204
STR(printable_quoted_addr));
207
if (*start == 0) /* can't happen? */
208
start = var_empty_addr;
209
quote_822_local(printable_quoted_addr, start);
210
printable(STR(printable_quoted_addr), '?');
212
|| htable_locate(dup_filter, STR(printable_quoted_addr)) == 0)
213
vstream_fprintf(client, STRING_FORMAT,
214
"", "", "", STR(printable_quoted_addr));
217
if (msg_size_ok && vstream_fseek(qfile, msg_size, SEEK_CUR) < 0)
218
msg_fatal("seek file %s: %m", VSTREAM_PATH(qfile));
225
* With the heading printed, see if there is a defer logfile. The
226
* defer logfile is not necessarily complete: delivery may be
227
* interrupted (postfix stop or reload) before all recipients have
230
* Therefore we keep a record of recipients found in the defer logfile,
231
* and try to avoid listing those recipients again when processing
232
* the remainder of the queue file.
234
if (rec_type == REC_TYPE_FROM
236
&& (logfile = bounce_log_open(MAIL_QUEUE_DEFER, id, O_RDONLY, 0)) != 0) {
237
dup_filter = htable_create(var_dup_filter_limit);
238
showq_reasons(client, logfile, dup_filter);
239
if (bounce_log_close(logfile))
240
msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id);
244
vstring_free(printable_quoted_addr);
246
htable_free(dup_filter, (void (*) (char *)) 0);
249
/* showq_reasons - show deferral reasons */
251
static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, HTABLE *dup_filter)
253
char *saved_reason = 0;
256
while (bounce_log_read(bp) != 0) {
259
* Update the duplicate filter.
261
if (var_dup_filter_limit == 0
262
|| dup_filter->used < var_dup_filter_limit)
263
if (htable_locate(dup_filter, bp->recipient) == 0)
264
htable_enter(dup_filter, bp->recipient, (char *) 0);
267
* Don't print the reason when the previous recipient had the same
270
if (saved_reason == 0 || strcmp(saved_reason, bp->text) != 0) {
272
myfree(saved_reason);
273
saved_reason = mystrdup(bp->text);
274
if ((padding = 76 - strlen(saved_reason)) < 0)
276
vstream_fprintf(client, "%*s(%s)\n", padding, "", saved_reason);
278
vstream_fprintf(client, STRING_FORMAT, "", "", "", bp->recipient);
281
myfree(saved_reason);
285
/* showq_service - service client */
287
static void showq_service(VSTREAM *client, char *unused_service, char **argv)
294
unsigned long queue_size = 0;
297
char *name; /* queue name */
298
char *(*scan_next) (SCAN_DIR *); /* flat or recursive */
300
struct queue_info *qp;
302
static struct queue_info queue_info[] = {
303
MAIL_QUEUE_MAILDROP, scan_dir_next,
304
MAIL_QUEUE_ACTIVE, mail_scan_dir_next,
305
MAIL_QUEUE_INCOMING, mail_scan_dir_next,
306
MAIL_QUEUE_DEFERRED, mail_scan_dir_next,
307
MAIL_QUEUE_HOLD, mail_scan_dir_next,
312
* Sanity check. This service takes no command-line arguments.
315
msg_fatal("unexpected command-line argument: %s", argv[0]);
318
* Skip any files that have the wrong permissions. If we can't open an
319
* existing file, assume the system is out of resources or that it is
320
* mis-configured, and force backoff by raising a fatal error.
323
for (qp = queue_info; qp->name != 0; qp++) {
324
SCAN_DIR *scan = scan_dir_open(qp->name);
327
while ((id = qp->scan_next(scan)) != 0) {
330
* XXX I have seen showq loop on the same queue id. That would be
331
* an operating system bug, but who cares whose fault it is. Make
332
* sure this will never happen again.
335
if (strcmp(saved_id, id) == 0) {
336
msg_warn("readdir loop on queue %s id %s", qp->name, id);
341
saved_id = mystrdup(id);
342
status = mail_open_ok(qp->name, id, &st, &path);
343
if (status == MAIL_OPEN_YES) {
345
vstream_fprintf(client, STRING_FORMAT,
346
"-Queue ID-", "--Size--",
347
"----Arrival Time----",
348
"-Sender/Recipient-------");
350
vstream_fprintf(client, "\n");
351
if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) {
352
queue_size += st.st_size;
353
showq_report(client, qp->name, id, qfile, (long) st.st_size,
355
if (vstream_fclose(qfile))
356
msg_warn("close file %s %s: %m", qp->name, id);
357
} else if (strcmp(qp->name, MAIL_QUEUE_MAILDROP) == 0) {
358
queue_size += st.st_size;
359
vstream_fprintf(client, DROP_FORMAT, id, ' ',
361
asctime(localtime(&st.st_mtime)),
362
(unsigned) st.st_uid);
363
} else if (errno != ENOENT)
364
msg_fatal("open %s %s: %m", qp->name, id);
366
vstream_fflush(client);
368
vstream_fflush(client);
372
scan_dir_close(scan);
375
vstream_fprintf(client, "Mail queue is empty\n");
377
vstream_fprintf(client, "\n-- %lu Kbytes in %d Request%s.\n",
378
queue_size / 1024, file_count,
379
file_count == 1 ? "" : "s");
383
/* main - pass control to the single-threaded server skeleton */
385
int main(int argc, char **argv)
387
static CONFIG_INT_TABLE int_table[] = {
388
VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
391
CONFIG_STR_TABLE str_table[] = {
392
VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0,
396
single_server_main(argc, argv, showq_service,
397
MAIL_SERVER_INT_TABLE, int_table,
398
MAIL_SERVER_STR_TABLE, str_table,