1 /* 2 * Copyright (c) 1998-2002, 2013 Proofpoint, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 * 13 */ 14 15 #include <sm/gen.h> 16 17 SM_IDSTR(copyright, 18 "@(#) Copyright (c) 1998-2002 Proofpoint, Inc. and its suppliers.\n\ 19 All rights reserved.\n\ 20 Copyright (c) 1988, 1993\n\ 21 The Regents of the University of California. All rights reserved.\n") 22 23 SM_IDSTR(id, "@(#)$Id: mailstats.c,v 8.103 2013/11/22 20:51:51 ca Exp $") 24 25 #include <unistd.h> 26 #include <stddef.h> 27 #include <stdlib.h> 28 #include <ctype.h> 29 #include <string.h> 30 #include <time.h> 31 #ifdef EX_OK 32 # undef EX_OK /* unistd.h may have another use for this */ 33 #endif /* EX_OK */ 34 #include <sysexits.h> 35 36 #include <sm/errstring.h> 37 #include <sm/limits.h> 38 #include <sendmail/sendmail.h> 39 #include <sendmail/mailstats.h> 40 #include <sendmail/pathnames.h> 41 42 43 #define MNAMELEN 20 /* max length of mailer name */ 44 45 int 46 main(argc, argv) 47 int argc; 48 char **argv; 49 { 50 register int i; 51 int mno; 52 int save_errno; 53 int ch, fd; 54 char *sfile; 55 char *cfile; 56 SM_FILE_T *cfp; 57 bool mnames; 58 bool progmode; 59 bool trunc; 60 long frmsgs = 0, frbytes = 0, tomsgs = 0, tobytes = 0, rejmsgs = 0; 61 long dismsgs = 0; 62 long quarmsgs = 0; 63 time_t now; 64 char mtable[MAXMAILERS][MNAMELEN + 1]; 65 char sfilebuf[MAXPATHLEN]; 66 char buf[MAXLINE]; 67 struct statistics stats; 68 extern char *ctime(); 69 extern char *optarg; 70 extern int optind; 71 # define MSOPTS "cC:f:opP" 72 73 cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); 74 sfile = NULL; 75 mnames = true; 76 progmode = false; 77 trunc = false; 78 while ((ch = getopt(argc, argv, MSOPTS)) != -1) 79 { 80 switch (ch) 81 { 82 case 'c': 83 cfile = getcfname(0, 0, SM_GET_SUBMIT_CF, NULL); 84 break; 85 86 case 'C': 87 cfile = optarg; 88 break; 89 90 case 'f': 91 sfile = optarg; 92 break; 93 94 95 case 'o': 96 mnames = false; 97 break; 98 99 case 'p': 100 trunc = true; 101 /* FALLTHROUGH */ 102 103 case 'P': 104 progmode = true; 105 break; 106 107 108 case '?': 109 default: 110 usage: 111 (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, 112 "usage: mailstats [-C cffile] [-c] [-P] [-f stfile] [-o] [-p]\n"); 113 exit(EX_USAGE); 114 } 115 } 116 argc -= optind; 117 argv += optind; 118 119 if (argc != 0) 120 goto usage; 121 122 if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY, 123 NULL)) == NULL) 124 { 125 save_errno = errno; 126 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "mailstats: "); 127 errno = save_errno; 128 sm_perror(cfile); 129 exit(EX_NOINPUT); 130 } 131 132 mno = 0; 133 (void) sm_strlcpy(mtable[mno++], "prog", MNAMELEN + 1); 134 (void) sm_strlcpy(mtable[mno++], "*file*", MNAMELEN + 1); 135 (void) sm_strlcpy(mtable[mno++], "*include*", MNAMELEN + 1); 136 137 while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0) 138 { 139 register char *b; 140 char *s; 141 register char *m; 142 143 b = strchr(buf, '#'); 144 if (b == NULL) 145 b = strchr(buf, '\n'); 146 if (b == NULL) 147 b = &buf[strlen(buf)]; 148 while (isascii(*--b) && isspace(*b)) 149 continue; 150 *++b = '\0'; 151 152 b = buf; 153 switch (*b++) 154 { 155 case 'M': /* mailer definition */ 156 break; 157 158 case 'O': /* option -- see if .st file */ 159 if (sm_strncasecmp(b, " StatusFile", 11) == 0 && 160 !(isascii(b[11]) && isalnum(b[11]))) 161 { 162 /* new form -- find value */ 163 b = strchr(b, '='); 164 if (b == NULL) 165 continue; 166 while (isascii(*++b) && isspace(*b)) 167 continue; 168 } 169 else if (*b++ != 'S') 170 { 171 /* something else boring */ 172 continue; 173 } 174 175 /* this is the S or StatusFile option -- save it */ 176 if (sm_strlcpy(sfilebuf, b, sizeof sfilebuf) >= 177 sizeof sfilebuf) 178 { 179 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 180 "StatusFile filename too long: %.30s...\n", 181 b); 182 exit(EX_CONFIG); 183 } 184 if (sfile == NULL) 185 sfile = sfilebuf; 186 187 default: 188 continue; 189 } 190 191 if (mno >= MAXMAILERS) 192 { 193 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 194 "Too many mailers defined, %d max.\n", 195 MAXMAILERS); 196 exit(EX_SOFTWARE); 197 } 198 m = mtable[mno]; 199 s = m + MNAMELEN; /* is [MNAMELEN + 1] */ 200 while (*b != ',' && !(isascii(*b) && isspace(*b)) && 201 *b != '\0' && m < s) 202 *m++ = *b++; 203 *m = '\0'; 204 for (i = 0; i < mno; i++) 205 { 206 if (strcmp(mtable[i], mtable[mno]) == 0) 207 break; 208 } 209 if (i == mno) 210 mno++; 211 } 212 (void) sm_io_close(cfp, SM_TIME_DEFAULT); 213 for (; mno < MAXMAILERS; mno++) 214 mtable[mno][0] = '\0'; 215 216 if (sfile == NULL) 217 { 218 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 219 "mailstats: no statistics file located\n"); 220 exit(EX_OSFILE); 221 } 222 223 fd = open(sfile, O_RDONLY, 0600); 224 if ((fd < 0) || (i = read(fd, &stats, sizeof stats)) < 0) 225 { 226 save_errno = errno; 227 (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, "mailstats: "); 228 errno = save_errno; 229 sm_perror(sfile); 230 exit(EX_NOINPUT); 231 } 232 if (i == 0) 233 { 234 (void) sleep(1); 235 if ((i = read(fd, &stats, sizeof stats)) < 0) 236 { 237 save_errno = errno; 238 (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, 239 "mailstats: "); 240 errno = save_errno; 241 sm_perror(sfile); 242 exit(EX_NOINPUT); 243 } 244 else if (i == 0) 245 { 246 memset((ARBPTR_T) &stats, '\0', sizeof stats); 247 (void) time(&stats.stat_itime); 248 } 249 } 250 if (i != 0) 251 { 252 if (stats.stat_magic != STAT_MAGIC) 253 { 254 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 255 "mailstats: incorrect magic number in %s\n", 256 sfile); 257 exit(EX_OSERR); 258 } 259 else if (stats.stat_version != STAT_VERSION) 260 { 261 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 262 "mailstats version (%d) incompatible with %s version (%d)\n", 263 STAT_VERSION, sfile, 264 stats.stat_version); 265 266 exit(EX_OSERR); 267 } 268 else if (i != sizeof stats || stats.stat_size != sizeof(stats)) 269 { 270 (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, 271 "mailstats: file size changed.\n"); 272 exit(EX_OSERR); 273 } 274 } 275 276 277 if (progmode) 278 { 279 (void) time(&now); 280 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%ld %ld\n", 281 (long) stats.stat_itime, (long) now); 282 } 283 else 284 { 285 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 286 "Statistics from %s", 287 ctime(&stats.stat_itime)); 288 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 289 " M msgsfr bytes_from msgsto bytes_to msgsrej msgsdis"); 290 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " msgsqur"); 291 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", 292 mnames ? " Mailer" : ""); 293 } 294 for (i = 0; i < MAXMAILERS; i++) 295 { 296 if (stats.stat_nf[i] || stats.stat_nt[i] || 297 stats.stat_nq[i] || 298 stats.stat_nr[i] || stats.stat_nd[i]) 299 { 300 char *format; 301 302 if (progmode) 303 format = "%2d %8ld %10ld %8ld %10ld %6ld %6ld"; 304 else 305 format = "%2d %8ld %10ldK %8ld %10ldK %6ld %6ld"; 306 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 307 format, i, 308 stats.stat_nf[i], 309 stats.stat_bf[i], 310 stats.stat_nt[i], 311 stats.stat_bt[i], 312 stats.stat_nr[i], 313 stats.stat_nd[i]); 314 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 315 " %6ld", stats.stat_nq[i]); 316 if (mnames) 317 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 318 " %s", 319 mtable[i]); 320 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 321 frmsgs += stats.stat_nf[i]; 322 frbytes += stats.stat_bf[i]; 323 tomsgs += stats.stat_nt[i]; 324 tobytes += stats.stat_bt[i]; 325 rejmsgs += stats.stat_nr[i]; 326 dismsgs += stats.stat_nd[i]; 327 quarmsgs += stats.stat_nq[i]; 328 } 329 } 330 if (progmode) 331 { 332 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 333 " T %8ld %10ld %8ld %10ld %6ld %6ld", 334 frmsgs, frbytes, tomsgs, tobytes, rejmsgs, 335 dismsgs); 336 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 337 " %6ld", quarmsgs); 338 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 339 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 340 " C %8ld %8ld %6ld\n", 341 stats.stat_cf, stats.stat_ct, 342 stats.stat_cr); 343 (void) close(fd); 344 if (trunc) 345 { 346 fd = open(sfile, O_RDWR | O_TRUNC, 0600); 347 if (fd >= 0) 348 (void) close(fd); 349 } 350 } 351 else 352 { 353 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 354 "============================================================="); 355 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "========"); 356 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 357 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 358 " T %8ld %10ldK %8ld %10ldK %6ld %6ld", 359 frmsgs, frbytes, tomsgs, tobytes, rejmsgs, 360 dismsgs); 361 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 362 " %6ld", quarmsgs); 363 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 364 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 365 " C %8ld %10s %8ld %10s %6ld\n", 366 stats.stat_cf, "", stats.stat_ct, "", 367 stats.stat_cr); 368 } 369 exit(EX_OK); 370 /* NOTREACHED */ 371 return EX_OK; 372 } 373