1 /* $NetBSD: showq_compat.c,v 1.4 2022/10/08 16:12:48 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* showq_compat 8
6 /* SUMMARY
7 /* Sendmail mailq compatibility adapter
8 /* SYNOPSIS
9 /* void showq_compat(
10 /* VSTREAM *showq)
11 /* DESCRIPTION
12 /* This function converts a record stream from the showq(8)
13 /* daemon to of an approximation of Sendmail mailq command
14 /* output.
15 /* DIAGNOSTICS
16 /* Fatal errors: out of memory, malformed showq(8) daemon output.
17 /* LICENSE
18 /* .ad
19 /* .fi
20 /* The Secure Mailer license must be distributed with this software.
21 /* AUTHOR(S)
22 /* Wietse Venema
23 /* IBM T.J. Watson Research
24 /* P.O. Box 704
25 /* Yorktown Heights, NY 10598, USA
26 /*
27 /* Wietse Venema
28 /* Google, Inc.
29 /* 111 8th Avenue
30 /* New York, NY 10011, USA
31 /*--*/
32
33 /* System library. */
34
35 #include <sys_defs.h>
36 #include <stdlib.h>
37 #include <unistd.h>
38 #include <time.h>
39 #include <string.h>
40 #include <sysexits.h>
41 #include <errno.h>
42
43 /* Utility library. */
44
45 #include <vstring.h>
46 #include <vstream.h>
47 #include <stringops.h>
48 #include <mymalloc.h>
49 #include <msg.h>
50
51 /* Global library. */
52
53 #include <mail_proto.h>
54 #include <mail_queue.h>
55 #include <mail_date.h>
56 #include <mail_params.h>
57
58 /* Application-specific. */
59
60 #include <postqueue.h>
61
62 /*
63 * The enable_long_queue_ids parameter determines the output format.
64 *
65 * The historical output format for short queue IDs (inode number and time in
66 * microseconds modulo 1) is not suitable for large inode numbers, but we
67 * won't change it to avoid breaking compatibility with programs that parse
68 * this output.
69 */
70 #define S_STRING_FORMAT "%-11s %7s %-20s %s\n"
71 #define S_SENDER_FORMAT "%-11s %7ld %20.20s %s\n"
72 #define S_HEADINGS "-Queue ID-", "--Size--", \
73 "----Arrival Time----", "-Sender/Recipient-------"
74
75 #define L_STRING_FORMAT "%-17s %8s %-19s %s\n"
76 #define L_SENDER_FORMAT "%-17s %8ld %19.19s %s\n"
77 #define L_HEADINGS "----Queue ID-----", "--Size--", \
78 "---Arrival Time----", "--Sender/Recipient------"
79
80 #define STR(x) vstring_str(x)
81
82 /* showq_message - report status for one message */
83
showq_message(VSTREAM * showq_stream)84 static unsigned long showq_message(VSTREAM *showq_stream)
85 {
86 static VSTRING *queue_name = 0;
87 static VSTRING *queue_id = 0;
88 static VSTRING *id_status = 0;
89 static VSTRING *addr = 0;
90 static VSTRING *why = 0;
91 long arrival_time;
92 long message_size;
93 char *saved_reason = mystrdup("");
94 const char *show_reason;
95 int padding;
96 int showq_status;
97 time_t time_t_arrival_time;
98 int forced_expire;
99
100 /*
101 * One-time initialization.
102 */
103 if (queue_name == 0) {
104 queue_name = vstring_alloc(100);
105 queue_id = vstring_alloc(100);
106 id_status = vstring_alloc(100);
107 addr = vstring_alloc(100);
108 why = vstring_alloc(100);
109 }
110
111 /*
112 * Read the message properties and sender address.
113 */
114 if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT
115 | ATTR_FLAG_PRINTABLE,
116 RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name),
117 RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id),
118 RECV_ATTR_LONG(MAIL_ATTR_TIME, &arrival_time),
119 RECV_ATTR_LONG(MAIL_ATTR_SIZE, &message_size),
120 RECV_ATTR_INT(MAIL_ATTR_FORCED_EXPIRE, &forced_expire),
121 RECV_ATTR_STR(MAIL_ATTR_SENDER, addr),
122 ATTR_TYPE_END) != 6)
123 msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
124
125 /*
126 * Decorate queue file names in specific states, then print the result
127 * left-aligned, followed by other status info and the sender address
128 * which is already in externalized RFC 5321 form.
129 */
130 vstring_strcpy(id_status, STR(queue_id));
131 if (strcmp(STR(queue_name), MAIL_QUEUE_ACTIVE) == 0)
132 vstring_strcat(id_status, "*");
133 else if (strcmp(STR(queue_name), MAIL_QUEUE_HOLD) == 0)
134 vstring_strcat(id_status, "!");
135 if (forced_expire)
136 vstring_strcat(id_status, "#");
137 time_t_arrival_time = arrival_time;
138 vstream_printf(var_long_queue_ids ?
139 L_SENDER_FORMAT : S_SENDER_FORMAT, STR(id_status),
140 message_size, asctime(localtime(&time_t_arrival_time)),
141 STR(addr));
142
143 /*
144 * Read zero or more (recipient, reason) pair(s) until attr_scan_more()
145 * consumes a terminator. If the showq daemon messes up, don't try to
146 * resynchronize.
147 */
148 while ((showq_status = attr_scan_more(showq_stream)) > 0) {
149 if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT
150 | ATTR_FLAG_PRINTABLE,
151 RECV_ATTR_STR(MAIL_ATTR_RECIP, addr),
152 RECV_ATTR_STR(MAIL_ATTR_WHY, why),
153 ATTR_TYPE_END) != 2)
154 msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
155
156 /*
157 * Don't output a "(reason)" line when no recipient has a reason, or
158 * when the previous recipient has the same (non)reason as the
159 * current recipient. Do output a "(reason unavailable)" when the
160 * previous recipient has a reason, and the current recipient has
161 * none.
162 */
163 if (strcmp(saved_reason, STR(why)) != 0) {
164 myfree(saved_reason);
165 saved_reason = mystrdup(STR(why));
166 show_reason = *saved_reason ? saved_reason : "reason unavailable";
167 if ((padding = 76 - (int) strlen(show_reason)) < 0)
168 padding = 0;
169 vstream_printf("%*s(%s)\n", padding, "", show_reason);
170 }
171 vstream_printf(var_long_queue_ids ?
172 L_STRING_FORMAT : S_STRING_FORMAT,
173 "", "", "", STR(addr));
174 }
175 if (showq_status < 0)
176 msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
177 myfree(saved_reason);
178 return (message_size);
179 }
180
181 /* showq_compat - legacy mailq-style output adapter */
182
showq_compat(VSTREAM * showq_stream)183 void showq_compat(VSTREAM *showq_stream)
184 {
185 unsigned long file_count = 0;
186 unsigned long queue_size = 0;
187 int showq_status;
188
189 /*
190 * Process zero or more queue file objects until attr_scan_more()
191 * consumes a terminator.
192 */
193 while ((showq_status = attr_scan_more(showq_stream)) > 0) {
194 if (file_count > 0) {
195 vstream_printf("\n");
196 } else if (var_long_queue_ids) {
197 vstream_printf(L_STRING_FORMAT, L_HEADINGS);
198 } else {
199 vstream_printf(S_STRING_FORMAT, S_HEADINGS);
200 }
201 queue_size += showq_message(showq_stream);
202 file_count++;
203 if (vstream_fflush(VSTREAM_OUT)) {
204 if (errno != EPIPE)
205 msg_fatal_status(EX_IOERR, "output write error: %m");
206 return;
207 }
208 }
209 if (showq_status < 0)
210 msg_fatal_status(EX_SOFTWARE, "malformed showq server response");
211
212 /*
213 * Print the queue summary.
214 */
215 if (file_count == 0)
216 vstream_printf("Mail queue is empty\n");
217 else {
218 vstream_printf("\n-- %lu Kbytes in %lu Request%s.\n",
219 queue_size / 1024, file_count,
220 file_count == 1 ? "" : "s");
221 }
222 if (vstream_fflush(VSTREAM_OUT) && errno != EPIPE)
223 msg_fatal_status(EX_IOERR, "output write error: %m");
224 }
225