1 /* $NetBSD: showq_compat.c,v 1.2 2017/02/14 01:16:47 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 42 /* Utility library. */ 43 44 #include <vstring.h> 45 #include <vstream.h> 46 #include <stringops.h> 47 #include <mymalloc.h> 48 #include <msg.h> 49 50 /* Global library. */ 51 52 #include <mail_proto.h> 53 #include <mail_queue.h> 54 #include <mail_date.h> 55 #include <mail_params.h> 56 57 /* Application-specific. */ 58 59 #include <postqueue.h> 60 61 /* 62 * The enable_long_queue_ids parameter determines the output format. 63 * 64 * The historical output format for short queue IDs (inode number and time in 65 * microseconds modulo 1) is not suitable for large inode numbers, but we 66 * won't change it to avoid breaking compatibility with programs that parse 67 * this output. 68 */ 69 #define S_STRING_FORMAT "%-11s %7s %-20s %s\n" 70 #define S_SENDER_FORMAT "%-11s %7ld %20.20s %s\n" 71 #define S_HEADINGS "-Queue ID-", "--Size--", \ 72 "----Arrival Time----", "-Sender/Recipient-------" 73 74 #define L_STRING_FORMAT "%-17s %8s %-19s %s\n" 75 #define L_SENDER_FORMAT "%-17s %8ld %19.19s %s\n" 76 #define L_HEADINGS "----Queue ID-----", "--Size--", \ 77 "---Arrival Time----", "--Sender/Recipient------" 78 79 #define STR(x) vstring_str(x) 80 81 /* showq_message - report status for one message */ 82 83 static unsigned long showq_message(VSTREAM *showq_stream) 84 { 85 static VSTRING *queue_name = 0; 86 static VSTRING *queue_id = 0; 87 static VSTRING *id_status = 0; 88 static VSTRING *addr = 0; 89 static VSTRING *why = 0; 90 long arrival_time; 91 long message_size; 92 int message_status; 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 99 /* 100 * One-time initialization. 101 */ 102 if (queue_name == 0) { 103 queue_name = vstring_alloc(100); 104 queue_id = vstring_alloc(100); 105 id_status = vstring_alloc(100); 106 addr = vstring_alloc(100); 107 why = vstring_alloc(100); 108 } 109 110 /* 111 * Read the message properties and sender address. 112 */ 113 if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, 114 RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), 115 RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), 116 RECV_ATTR_LONG(MAIL_ATTR_TIME, &arrival_time), 117 RECV_ATTR_LONG(MAIL_ATTR_SIZE, &message_size), 118 RECV_ATTR_STR(MAIL_ATTR_SENDER, addr), 119 ATTR_TYPE_END) != 5) 120 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 121 122 /* 123 * Decorate queue file names in specific states, then print the result 124 * left-aligned, followed by other status info and the sender address 125 * which is already in externalized RFC 5321 form. 126 */ 127 message_status = (strcmp(STR(queue_name), MAIL_QUEUE_ACTIVE) == 0 ? '*' : 128 strcmp(STR(queue_name), MAIL_QUEUE_HOLD) == 0 ? '!' : ' '); 129 vstring_sprintf(id_status, "%s%c", STR(queue_id), message_status); 130 time_t_arrival_time = arrival_time; 131 vstream_printf(var_long_queue_ids ? 132 L_SENDER_FORMAT : S_SENDER_FORMAT, STR(id_status), 133 message_size, asctime(localtime(&time_t_arrival_time)), 134 STR(addr)); 135 136 /* 137 * Read zero or more (recipient, reason) pair(s) until attr_scan_more() 138 * consumes a terminator. If the showq daemon messes up, don't try to 139 * resynchronize. 140 */ 141 while ((showq_status = attr_scan_more(showq_stream)) > 0) { 142 if (attr_scan(showq_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, 143 RECV_ATTR_STR(MAIL_ATTR_RECIP, addr), 144 RECV_ATTR_STR(MAIL_ATTR_WHY, why), 145 ATTR_TYPE_END) != 2) 146 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 147 148 /* 149 * Don't output a "(reason)" line when no recipient has a reason, or 150 * when the previous recipient has the same (non)reason as the 151 * current recipient. Do output a "(reason unavailable)" when the 152 * previous recipient has a reason, and the current recipient has 153 * none. 154 */ 155 if (strcmp(saved_reason, STR(why)) != 0) { 156 myfree(saved_reason); 157 saved_reason = mystrdup(STR(why)); 158 show_reason = *saved_reason ? saved_reason : "reason unavailable"; 159 if ((padding = 76 - strlen(show_reason)) < 0) 160 padding = 0; 161 vstream_printf("%*s(%s)\n", padding, "", show_reason); 162 } 163 vstream_printf(var_long_queue_ids ? 164 L_STRING_FORMAT : S_STRING_FORMAT, 165 "", "", "", STR(addr)); 166 } 167 if (showq_status < 0) 168 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 169 myfree(saved_reason); 170 return (message_size); 171 } 172 173 /* showq_compat - legacy mailq-style output adapter */ 174 175 void showq_compat(VSTREAM *showq_stream) 176 { 177 unsigned long file_count = 0; 178 unsigned long queue_size = 0; 179 int showq_status; 180 181 /* 182 * Process zero or more queue file objects until attr_scan_more() 183 * consumes a terminator. 184 */ 185 while ((showq_status = attr_scan_more(showq_stream)) > 0) { 186 if (file_count > 0) { 187 vstream_printf("\n"); 188 } else if (var_long_queue_ids) { 189 vstream_printf(L_STRING_FORMAT, L_HEADINGS); 190 } else { 191 vstream_printf(S_STRING_FORMAT, S_HEADINGS); 192 } 193 queue_size += showq_message(showq_stream); 194 file_count++; 195 vstream_fflush(VSTREAM_OUT); 196 } 197 if (showq_status < 0) 198 msg_fatal_status(EX_SOFTWARE, "malformed showq server response"); 199 200 /* 201 * Print the queue summary. 202 */ 203 if (file_count == 0) 204 vstream_printf("Mail queue is empty\n"); 205 else { 206 vstream_printf("\n-- %lu Kbytes in %lu Request%s.\n", 207 queue_size / 1024, file_count, 208 file_count == 1 ? "" : "s"); 209 } 210 vstream_fflush(VSTREAM_OUT); 211 } 212