xref: /netbsd-src/external/ibm-public/postfix/dist/src/postqueue/showq_compat.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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