Postfix3.3.1
showq.c
[詳解]
1 /*++
2 /* NAME
3 /* showq 8
4 /* SUMMARY
5 /* list the Postfix mail queue
6 /* SYNOPSIS
7 /* \fBshowq\fR [generic Postfix daemon options]
8 /* DESCRIPTION
9 /* The \fBshowq\fR(8) daemon reports the Postfix mail queue status.
10 /* The output is meant to be formatted by the postqueue(1) command,
11 /* as it emulates the Sendmail `mailq' command.
12 /*
13 /* The \fBshowq\fR(8) daemon can also be run in stand-alone mode
14 /* by the superuser. This mode of operation is used to emulate
15 /* the `mailq' command while the Postfix mail system is down.
16 /* SECURITY
17 /* .ad
18 /* .fi
19 /* The \fBshowq\fR(8) daemon can run in a chroot jail at fixed low
20 /* privilege, and takes no input from the client. Its service port
21 /* is accessible to local untrusted users, so the service can be
22 /* susceptible to denial of service attacks.
23 /* STANDARDS
24 /* .ad
25 /* .fi
26 /* None. The \fBshowq\fR(8) daemon does not interact with the
27 /* outside world.
28 /* DIAGNOSTICS
29 /* Problems and transactions are logged to \fBsyslogd\fR(8).
30 /* CONFIGURATION PARAMETERS
31 /* .ad
32 /* .fi
33 /* Changes to \fBmain.cf\fR are picked up automatically as \fBshowq\fR(8)
34 /* processes run for only a limited amount of time. Use the command
35 /* "\fBpostfix reload\fR" to speed up a change.
36 /*
37 /* The text below provides only a parameter summary. See
38 /* \fBpostconf\fR(5) for more details including examples.
39 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
40 /* The default location of the Postfix main.cf and master.cf
41 /* configuration files.
42 /* .IP "\fBdaemon_timeout (18000s)\fR"
43 /* How much time a Postfix daemon process may take to handle a
44 /* request before it is terminated by a built-in watchdog timer.
45 /* .IP "\fBduplicate_filter_limit (1000)\fR"
46 /* The maximal number of addresses remembered by the address
47 /* duplicate filter for \fBaliases\fR(5) or \fBvirtual\fR(5) alias expansion, or
48 /* for \fBshowq\fR(8) queue displays.
49 /* .IP "\fBempty_address_recipient (MAILER-DAEMON)\fR"
50 /* The recipient of mail addressed to the null address.
51 /* .IP "\fBipc_timeout (3600s)\fR"
52 /* The time limit for sending or receiving information over an internal
53 /* communication channel.
54 /* .IP "\fBmax_idle (100s)\fR"
55 /* The maximum amount of time that an idle Postfix daemon process waits
56 /* for an incoming connection before terminating voluntarily.
57 /* .IP "\fBmax_use (100)\fR"
58 /* The maximal number of incoming connections that a Postfix daemon
59 /* process will service before terminating voluntarily.
60 /* .IP "\fBprocess_id (read-only)\fR"
61 /* The process ID of a Postfix command or daemon process.
62 /* .IP "\fBprocess_name (read-only)\fR"
63 /* The process name of a Postfix command or daemon process.
64 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
65 /* The location of the Postfix top-level queue directory.
66 /* .IP "\fBsyslog_facility (mail)\fR"
67 /* The syslog facility of Postfix logging.
68 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
69 /* A prefix that is prepended to the process name in syslog
70 /* records, so that, for example, "smtpd" becomes "prefix/smtpd".
71 /* .PP
72 /* Available in Postfix version 2.9 and later:
73 /* .IP "\fBenable_long_queue_ids (no)\fR"
74 /* Enable long, non-repeating, queue IDs (queue file names).
75 /* .PP
76 /* Available in Postfix 3.3 and later:
77 /* .IP "\fBservice_name (read-only)\fR"
78 /* The master.cf service name of a Postfix daemon process.
79 /* FILES
80 /* /var/spool/postfix, queue directories
81 /* SEE ALSO
82 /* pickup(8), local mail pickup service
83 /* cleanup(8), canonicalize and enqueue mail
84 /* qmgr(8), queue manager
85 /* postconf(5), configuration parameters
86 /* master(8), process manager
87 /* syslogd(8), system logging
88 /* LICENSE
89 /* .ad
90 /* .fi
91 /* The Secure Mailer license must be distributed with this software.
92 /* AUTHOR(S)
93 /* Wietse Venema
94 /* IBM T.J. Watson Research
95 /* P.O. Box 704
96 /* Yorktown Heights, NY 10598, USA
97 /*
98 /* Wietse Venema
99 /* Google, Inc.
100 /* 111 8th Avenue
101 /* New York, NY 10011, USA
102 /*--*/
103 
104 /* System library. */
105 
106 #include <sys_defs.h>
107 #include <sys/stat.h>
108 #include <dirent.h>
109 #include <stdlib.h>
110 #include <unistd.h>
111 #include <errno.h>
112 #include <fcntl.h>
113 #include <time.h>
114 #include <string.h>
115 #include <ctype.h>
116 
117 /* Utility library. */
118 
119 #include <msg.h>
120 #include <scan_dir.h>
121 #include <vstring.h>
122 #include <vstream.h>
123 #include <vstring_vstream.h>
124 #include <stringops.h>
125 #include <mymalloc.h>
126 #include <htable.h>
127 
128 /* Global library. */
129 
130 #include <mail_queue.h>
131 #include <mail_open_ok.h>
132 #include <mail_proto.h>
133 #include <mail_date.h>
134 #include <mail_params.h>
135 #include <mail_version.h>
136 #include <mail_scan_dir.h>
137 #include <mail_conf.h>
138 #include <record.h>
139 #include <rec_type.h>
140 #include <quote_822_local.h>
141 #include <mail_addr.h>
142 #include <bounce_log.h>
143 
144 /* Single-threaded server skeleton. */
145 
146 #include <mail_server.h>
147 
148 /* Application-specific. */
149 
152 
153 static void showq_reasons(VSTREAM *, BOUNCE_LOG *, RCPT_BUF *, DSN_BUF *,
154  HTABLE *);
155 
156 #define STR(x) vstring_str(x)
157 
158 /* showq_report - report status of sender and recipients */
159 
160 static void showq_report(VSTREAM *client, char *queue, char *id,
161  VSTREAM *qfile, long size, time_t mtime)
162 {
163  VSTRING *buf = vstring_alloc(100);
164  VSTRING *printable_quoted_addr = vstring_alloc(100);
165  int rec_type;
166  time_t arrival_time = 0;
167  char *start;
168  long msg_size = size;
169  BOUNCE_LOG *logfile;
170  HTABLE *dup_filter = 0;
171  RCPT_BUF *rcpt_buf = 0;
172  DSN_BUF *dsn_buf = 0;
173  int sender_seen = 0;
174  int msg_size_ok = 0;
175 
176  /*
177  * Let the optimizer worry about eliminating duplicate code.
178  */
179 #define SHOWQ_CLEANUP_AND_RETURN { \
180  if (sender_seen > 0) \
181  attr_print(client, ATTR_FLAG_NONE, ATTR_TYPE_END); \
182  vstring_free(buf); \
183  vstring_free(printable_quoted_addr); \
184  if (rcpt_buf) \
185  rcpb_free(rcpt_buf); \
186  if (dsn_buf) \
187  dsb_free(dsn_buf); \
188  if (dup_filter) \
189  htable_free(dup_filter, (void (*) (void *)) 0); \
190  }
191 
192  /*
193  * XXX addresses in defer logfiles are in printable quoted form, while
194  * addresses in message envelope records are in raw unquoted form. This
195  * may change once we replace the present ad-hoc bounce/defer logfile
196  * format by one that is transparent for control etc. characters. See
197  * also: bounce/bounce_append_service.c.
198  *
199  * XXX With Postfix <= 2.0, "postsuper -r" results in obsolete size records
200  * from previous cleanup runs. Skip the obsolete size records.
201  */
202  while (!vstream_ferror(client) && (rec_type = rec_get(qfile, buf, 0)) > 0) {
203  start = vstring_str(buf);
204  if (msg_verbose)
205  msg_info("record %c %s", rec_type, printable(start, '?'));
206  switch (rec_type) {
207  case REC_TYPE_TIME:
208  /* TODO: parse seconds and microseconds. */
209  if (arrival_time == 0)
210  arrival_time = atol(start);
211  break;
212  case REC_TYPE_SIZE:
213  if (msg_size_ok == 0) {
214  msg_size_ok = (start[strspn(start, "0123456789 ")] == 0
215  && (msg_size = atol(start)) >= 0);
216  if (msg_size_ok == 0) {
217  msg_warn("%s: malformed size record: %.100s "
218  "-- using file size instead",
219  id, printable(start, '?'));
220  msg_size = size;
221  }
222  }
223  break;
224  case REC_TYPE_FROM:
225  if (*start == 0)
226  start = var_empty_addr;
227  quote_822_local(printable_quoted_addr, start);
228  printable(STR(printable_quoted_addr), '?');
229  if (sender_seen++ > 0) {
230  msg_warn("%s: duplicate sender address: %s "
231  "-- skipping remainder of this file",
232  id, STR(printable_quoted_addr));
234  }
235  attr_print(client, ATTR_FLAG_MORE,
238  SEND_ATTR_LONG(MAIL_ATTR_TIME, arrival_time > 0 ?
239  arrival_time : mtime),
240  SEND_ATTR_LONG(MAIL_ATTR_SIZE, msg_size),
242  STR(printable_quoted_addr)),
243  ATTR_TYPE_END);
244  break;
245  case REC_TYPE_RCPT:
246  if (sender_seen == 0) {
247  msg_warn("%s: missing sender address: %s "
248  "-- skipping remainder of this file",
249  id, STR(printable_quoted_addr));
251  }
252  if (*start == 0) /* can't happen? */
253  start = var_empty_addr;
254  quote_822_local(printable_quoted_addr, start);
255  printable(STR(printable_quoted_addr), '?');
256  if (dup_filter == 0
257  || htable_locate(dup_filter, STR(printable_quoted_addr)) == 0)
258  attr_print(client, ATTR_FLAG_MORE,
260  STR(printable_quoted_addr)),
262  ATTR_TYPE_END);
263  break;
264  case REC_TYPE_MESG:
265  if (msg_size_ok && vstream_fseek(qfile, msg_size, SEEK_CUR) < 0)
266  msg_fatal("seek file %s: %m", VSTREAM_PATH(qfile));
267  break;
268  case REC_TYPE_END:
269  break;
270  }
271 
272  /*
273  * Before listing any recipients from the queue file, try to list
274  * recipients from the corresponding defer logfile with per-recipient
275  * descriptions why delivery was deferred.
276  *
277  * The defer logfile is not necessarily complete: delivery may be
278  * interrupted (postfix stop or reload) before all recipients have
279  * been tried.
280  *
281  * Therefore we keep a record of recipients found in the defer logfile,
282  * and try to avoid listing those recipients again when processing
283  * recipients from the queue file.
284  */
285  if (rec_type == REC_TYPE_FROM
286  && (logfile = bounce_log_open(MAIL_QUEUE_DEFER, id, O_RDONLY, 0)) != 0) {
287  if (dup_filter != 0)
288  msg_panic("showq_report: attempt to reuse duplicate filter");
289  dup_filter = htable_create(var_dup_filter_limit);
290  if (rcpt_buf == 0)
291  rcpt_buf = rcpb_create();
292  if (dsn_buf == 0)
293  dsn_buf = dsb_create();
294  showq_reasons(client, logfile, rcpt_buf, dsn_buf, dup_filter);
295  if (bounce_log_close(logfile))
296  msg_warn("close %s %s: %m", MAIL_QUEUE_DEFER, id);
297  }
298  }
300 }
301 
302 /* showq_reasons - show deferral reasons */
303 
304 static void showq_reasons(VSTREAM *client, BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf,
305  DSN_BUF *dsn_buf, HTABLE *dup_filter)
306 {
307  RECIPIENT *rcpt = &rcpt_buf->rcpt;
308  DSN *dsn = &dsn_buf->dsn;
309 
310  while (bounce_log_read(bp, rcpt_buf, dsn_buf) != 0) {
311 
312  /*
313  * Update the duplicate filter.
314  */
315  if (var_dup_filter_limit == 0
316  || dup_filter->used < var_dup_filter_limit)
317  if (htable_locate(dup_filter, rcpt->address) == 0)
318  htable_enter(dup_filter, rcpt->address, (void *) 0);
319 
320  attr_print(client, ATTR_FLAG_MORE,
323  ATTR_TYPE_END);
324  }
325 }
326 
327 
328 /* showq_service - service client */
329 
330 static void showq_service(VSTREAM *client, char *unused_service, char **argv)
331 {
332  VSTREAM *qfile;
333  const char *path;
334  int status;
335  char *id;
336  struct stat st;
337  struct queue_info {
338  char *name; /* queue name */
339  char *(*scan_next) (SCAN_DIR *); /* flat or recursive */
340  };
341  struct queue_info *qp;
342 
343  static struct queue_info queue_info[] = {
349  0,
350  };
351 
352  /*
353  * Sanity check. This service takes no command-line arguments.
354  */
355  if (argv[0])
356  msg_fatal("unexpected command-line argument: %s", argv[0]);
357 
358  /*
359  * Skip any files that have the wrong permissions. If we can't open an
360  * existing file, assume the system is out of resources or that it is
361  * mis-configured, and force backoff by raising a fatal error.
362  */
363  for (qp = queue_info; qp->name != 0; qp++) {
364  SCAN_DIR *scan = scan_dir_open(qp->name);
365  char *saved_id = 0;
366 
367  while ((id = qp->scan_next(scan)) != 0) {
368 
369  /*
370  * XXX I have seen showq loop on the same queue id. That would be
371  * an operating system bug, but who cares whose fault it is. Make
372  * sure this will never happen again.
373  */
374  if (saved_id) {
375  if (strcmp(saved_id, id) == 0) {
376  msg_warn("readdir loop on queue %s id %s", qp->name, id);
377  break;
378  }
379  myfree(saved_id);
380  }
381  saved_id = mystrdup(id);
382  status = mail_open_ok(qp->name, id, &st, &path);
383  if (status == MAIL_OPEN_YES) {
384  if ((qfile = mail_queue_open(qp->name, id, O_RDONLY, 0)) != 0) {
385  showq_report(client, qp->name, id, qfile, (long) st.st_size,
386  st.st_mtime);
387  if (vstream_fclose(qfile))
388  msg_warn("close file %s %s: %m", qp->name, id);
389  } else if (errno != ENOENT) {
390  msg_warn("open %s %s: %m", qp->name, id);
391  }
392  }
393  vstream_fflush(client);
394  }
395  if (saved_id)
396  myfree(saved_id);
397  scan_dir_close(scan);
398  }
400 }
401 
403 
404 /* main - pass control to the single-threaded server skeleton */
405 
406 int main(int argc, char **argv)
407 {
408  static const CONFIG_INT_TABLE int_table[] = {
410  0,
411  };
412  CONFIG_STR_TABLE str_table[] = {
414  0,
415  };
416 
417  /*
418  * Fingerprint executables and core dumps.
419  */
421 
422  single_server_main(argc, argv, showq_service,
423  CA_MAIL_SERVER_INT_TABLE(int_table),
424  CA_MAIL_SERVER_STR_TABLE(str_table),
425  0);
426 }
int msg_verbose
Definition: msg.c:177
#define ATTR_FLAG_NONE
Definition: attr.h:98
void myfree(void *ptr)
Definition: mymalloc.c:207
HTABLE_INFO * htable_locate(HTABLE *table, const char *key)
Definition: htable.c:242
DSN_BUF * dsb_create(void)
Definition: dsn_buf.c:169
#define STR(x)
Definition: showq.c:156
char * mystrdup(const char *str)
Definition: mymalloc.c:225
char * name
Definition: postsuper.c:350
const char * address
#define VAR_EMPTY_ADDR
Definition: mail_params.h:79
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define CA_MAIL_SERVER_STR_TABLE(v)
Definition: mail_server.h:57
const char * reason
Definition: dsn.h:20
#define MAIL_QUEUE_ACTIVE
Definition: mail_queue.h:31
#define vstring_str(vp)
Definition: vstring.h:71
#define VAR_DUP_FILTER_LIMIT
Definition: mail_params.h:686
#define MAIL_OPEN_YES
Definition: mail_open_ok.h:19
#define stat(p, s)
Definition: warn_stat.h:18
ssize_t used
Definition: htable.h:27
#define MAIL_ATTR_SIZE
Definition: mail_proto.h:139
#define REC_TYPE_SIZE
Definition: rec_type.h:37
DSN dsn
Definition: dsn_buf.h:28
#define ATTR_TYPE_END
Definition: attr.h:39
#define VSTREAM_PATH(vp)
Definition: vstream.h:126
#define REC_TYPE_FROM
Definition: rec_type.h:43
#define REC_TYPE_END
Definition: rec_type.h:77
#define MAIL_QUEUE_HOLD
Definition: mail_queue.h:29
Definition: htable.h:25
char * var_empty_addr
Definition: showq.c:151
#define DEF_EMPTY_ADDR
Definition: mail_params.h:80
RECIPIENT rcpt
Definition: rcpt_buf.h:30
#define MAIL_ATTR_QUEUE
Definition: mail_proto.h:129
SCAN_DIR * scan_dir_open(const char *path)
Definition: scan_dir.c:165
HTABLE * htable_create(ssize_t size)
Definition: htable.c:179
#define CA_MAIL_SERVER_INT_TABLE(v)
Definition: mail_server.h:56
RCPT_BUF * rcpb_create(void)
Definition: rcpt_buf.c:80
#define attr_print
Definition: attr.h:109
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
#define REC_TYPE_RCPT
Definition: rec_type.h:45
SCAN_DIR * scan_dir_close(SCAN_DIR *scan)
Definition: scan_dir.c:210
VSTREAM * mail_queue_open(const char *queue_name, const char *queue_id, int flags, mode_t mode)
Definition: mail_queue.c:424
#define REC_TYPE_MESG
Definition: rec_type.h:56
int bounce_log_close(BOUNCE_LOG *bp)
Definition: bounce_log.c:305
#define MAIL_QUEUE_INCOMING
Definition: mail_queue.h:30
#define MAIL_ATTR_WHY
Definition: mail_proto.h:135
int mail_open_ok(const char *queue_name, const char *queue_id, struct stat *statp, const char **path)
Definition: mail_open_ok.c:77
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define MAIL_VERSION_STAMP_ALLOCATE
Definition: mail_version.h:67
MAIL_VERSION_STAMP_DECLARE
Definition: showq.c:402
#define SHOWQ_CLEANUP_AND_RETURN
#define MAIL_ATTR_TIME
Definition: mail_proto.h:142
#define MAIL_QUEUE_MAILDROP
Definition: mail_queue.h:28
#define SEND_ATTR_LONG(name, val)
Definition: attr.h:67
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence)
Definition: vstream.c:1093
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
#define MAIL_QUEUE_DEFERRED
Definition: mail_queue.h:32
#define MAIL_QUEUE_DEFER
Definition: mail_queue.h:34
#define quote_822_local(dst, src)
#define MAIL_ATTR_RECIP
Definition: mail_proto.h:134
Definition: dsn.h:17
#define MAIL_ATTR_SENDER
Definition: mail_proto.h:131
NORETURN single_server_main(int, char **, SINGLE_SERVER_FN,...)
int main(int argc, char **argv)
Definition: showq.c:406
#define REC_TYPE_TIME
Definition: rec_type.h:38
BOUNCE_LOG * bounce_log_open(const char *queue_name, const char *queue_id, int flags, mode_t mode)
Definition: bounce_log.c:114
#define MAIL_ATTR_QUEUEID
Definition: mail_proto.h:130
char * printable(char *string, int replacement)
Definition: printable.c:49
#define rec_get(fp, buf, limit)
Definition: record.h:56
#define vstream_ferror(vp)
Definition: vstream.h:120
#define ATTR_FLAG_MORE
Definition: attr.h:101
#define SEND_ATTR_STR(name, val)
Definition: attr.h:64
BOUNCE_LOG * bounce_log_read(BOUNCE_LOG *bp, RCPT_BUF *rcpt_buf, DSN_BUF *dsn_buf)
Definition: bounce_log.c:147
char * mail_scan_dir_next(SCAN_DIR *scan)
Definition: mail_scan_dir.c:43
int var_dup_filter_limit
Definition: showq.c:150
#define DEF_DUP_FILTER_LIMIT
Definition: mail_params.h:687
char * scan_dir_next(SCAN_DIR *scan)
Definition: scan_dir.c:177
HTABLE_INFO * htable_enter(HTABLE *table, const char *key, void *value)
Definition: htable.c:212
void msg_info(const char *fmt,...)
Definition: msg.c:199