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 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 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