1 /* $NetBSD: showq_compat.c,v 1.3 2020/03/18 19:05:19 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* showq_compat 8 6 /* SUMMARY 7 /* Sendmail mailq compatibitily 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 RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), 116 RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), 117 RECV_ATTR_LONG(MAIL_ATTR_TIME, &arrival_time), 118 RECV_ATTR_LONG(MAIL_ATTR_SIZE, &message_size), 119 RECV_ATTR_INT(MAIL_ATTR_FORCED_EXPIRE, &forced_expire), 120 RECV_ATTR_STR(MAIL_ATTR_SENDER, addr), 121 ATTR_TYPE_END) != 6) 122 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 123 124 /* 125 * Decorate queue file names in specific states, then print the result 126 * left-aligned, followed by other status info and the sender address 127 * which is already in externalized RFC 5321 form. 128 */ 129 vstring_strcpy(id_status, STR(queue_id)); 130 if (strcmp(STR(queue_name), MAIL_QUEUE_ACTIVE) == 0) 131 vstring_strcat(id_status, "*"); 132 else if (strcmp(STR(queue_name), MAIL_QUEUE_HOLD) == 0) 133 vstring_strcat(id_status, "!"); 134 if (forced_expire) 135 vstring_strcat(id_status, "#"); 136 time_t_arrival_time = arrival_time; 137 vstream_printf(var_long_queue_ids ? 138 L_SENDER_FORMAT : S_SENDER_FORMAT, STR(id_status), 139 message_size, asctime(localtime(&time_t_arrival_time)), 140 STR(addr)); 141 142 /* 143 * Read zero or more (recipient, reason) pair(s) until attr_scan_more() 144 * consumes a terminator. If the showq daemon messes up, don't try to 145 * resynchronize. 146 */ 147 while ((showq_status = attr_scan_more(showq_stream)) > 0) { 148 if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, 149 RECV_ATTR_STR(MAIL_ATTR_RECIP, addr), 150 RECV_ATTR_STR(MAIL_ATTR_WHY, why), 151 ATTR_TYPE_END) != 2) 152 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 153 154 /* 155 * Don't output a "(reason)" line when no recipient has a reason, or 156 * when the previous recipient has the same (non)reason as the 157 * current recipient. Do output a "(reason unavailable)" when the 158 * previous recipient has a reason, and the current recipient has 159 * none. 160 */ 161 if (strcmp(saved_reason, STR(why)) != 0) { 162 myfree(saved_reason); 163 saved_reason = mystrdup(STR(why)); 164 show_reason = *saved_reason ? saved_reason : "reason unavailable"; 165 if ((padding = 76 - (int) strlen(show_reason)) < 0) 166 padding = 0; 167 vstream_printf("%*s(%s)\n", padding, "", show_reason); 168 } 169 vstream_printf(var_long_queue_ids ? 170 L_STRING_FORMAT : S_STRING_FORMAT, 171 "", "", "", STR(addr)); 172 } 173 if (showq_status < 0) 174 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 175 myfree(saved_reason); 176 return (message_size); 177 } 178 179 /* showq_compat - legacy mailq-style output adapter */ 180 181 void showq_compat(VSTREAM *showq_stream) 182 { 183 unsigned long file_count = 0; 184 unsigned long queue_size = 0; 185 int showq_status; 186 187 /* 188 * Process zero or more queue file objects until attr_scan_more() 189 * consumes a terminator. 190 */ 191 while ((showq_status = attr_scan_more(showq_stream)) > 0) { 192 if (file_count > 0) { 193 vstream_printf("\n"); 194 } else if (var_long_queue_ids) { 195 vstream_printf(L_STRING_FORMAT, L_HEADINGS); 196 } else { 197 vstream_printf(S_STRING_FORMAT, S_HEADINGS); 198 } 199 queue_size += showq_message(showq_stream); 200 file_count++; 201 if (vstream_fflush(VSTREAM_OUT)) { 202 if (errno != EPIPE) 203 msg_fatal_status(EX_IOERR, "output write error: %m"); 204 return; 205 } 206 } 207 if (showq_status < 0) 208 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 209 210 /* 211 * Print the queue summary. 212 */ 213 if (file_count == 0) 214 vstream_printf("Mail queue is empty\n"); 215 else { 216 vstream_printf("\n-- %lu Kbytes in %lu Request%s.\n", 217 queue_size / 1024, file_count, 218 file_count == 1 ? "" : "s"); 219 } 220 if (vstream_fflush(VSTREAM_OUT) && errno != EPIPE) 221 msg_fatal_status(EX_IOERR, "output write error: %m"); 222 } 223