1 /* 2 * Copyright (c) 1998-2001 Sendmail, 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-2001 Sendmail, 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.95 2001/12/30 04:59:40 gshapiro Exp $") 24 25 /* $FreeBSD$ */ 26 27 #include <unistd.h> 28 #include <stddef.h> 29 #include <stdlib.h> 30 #include <ctype.h> 31 #include <string.h> 32 #include <time.h> 33 #ifdef EX_OK 34 # undef EX_OK /* unistd.h may have another use for this */ 35 #endif /* EX_OK */ 36 #include <sysexits.h> 37 38 #include <sm/errstring.h> 39 #include <sm/limits.h> 40 #include <sendmail/sendmail.h> 41 #include <sendmail/mailstats.h> 42 #include <sendmail/pathnames.h> 43 44 45 #define MNAMELEN 20 /* max length of mailer name */ 46 47 int 48 main(argc, argv) 49 int argc; 50 char **argv; 51 { 52 register int i; 53 int mno; 54 int save_errno; 55 int ch, fd; 56 char *sfile; 57 char *cfile; 58 SM_FILE_T *cfp; 59 bool mnames; 60 bool progmode; 61 bool trunc; 62 long frmsgs = 0, frbytes = 0, tomsgs = 0, tobytes = 0, rejmsgs = 0; 63 long dismsgs = 0; 64 #if _FFR_QUARANTINE 65 long quarmsgs = 0; 66 #endif /* _FFR_QUARANTINE */ 67 time_t now; 68 char mtable[MAXMAILERS][MNAMELEN + 1]; 69 char sfilebuf[MAXLINE]; 70 char buf[MAXLINE]; 71 struct statistics stats; 72 extern char *ctime(); 73 extern char *optarg; 74 extern int optind; 75 76 cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); 77 sfile = NULL; 78 mnames = true; 79 progmode = false; 80 trunc = false; 81 while ((ch = getopt(argc, argv, "cC:f:opP")) != -1) 82 { 83 switch (ch) 84 { 85 case 'c': 86 cfile = getcfname(0, 0, SM_GET_SUBMIT_CF, NULL); 87 break; 88 89 case 'C': 90 cfile = optarg; 91 break; 92 93 case 'f': 94 sfile = optarg; 95 break; 96 97 case 'o': 98 mnames = false; 99 break; 100 101 case 'p': 102 trunc = true; 103 /* FALLTHROUGH */ 104 105 case 'P': 106 progmode = true; 107 break; 108 109 case '?': 110 default: 111 usage: 112 (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, 113 "usage: mailstats [-C cffile] [-P] [-f stfile] [-o] [-p]\n"); 114 exit(EX_USAGE); 115 } 116 } 117 argc -= optind; 118 argv += optind; 119 120 if (argc != 0) 121 goto usage; 122 123 if ((cfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, cfile, SM_IO_RDONLY, 124 NULL)) == NULL) 125 { 126 save_errno = errno; 127 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, "mailstats: "); 128 errno = save_errno; 129 sm_perror(cfile); 130 exit(EX_NOINPUT); 131 } 132 133 mno = 0; 134 (void) sm_strlcpy(mtable[mno++], "prog", MNAMELEN + 1); 135 (void) sm_strlcpy(mtable[mno++], "*file*", MNAMELEN + 1); 136 (void) sm_strlcpy(mtable[mno++], "*include*", MNAMELEN + 1); 137 138 while (sm_io_fgets(cfp, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL) 139 { 140 register char *b; 141 char *s; 142 register char *m; 143 144 b = buf; 145 switch (*b++) 146 { 147 case 'M': /* mailer definition */ 148 break; 149 150 case 'O': /* option -- see if .st file */ 151 if (sm_strncasecmp(b, " StatusFile", 11) == 0 && 152 !(isascii(b[11]) && isalnum(b[11]))) 153 { 154 /* new form -- find value */ 155 b = strchr(b, '='); 156 if (b == NULL) 157 continue; 158 while (isascii(*++b) && isspace(*b)) 159 continue; 160 } 161 else if (*b++ != 'S') 162 { 163 /* something else boring */ 164 continue; 165 } 166 167 /* this is the S or StatusFile option -- save it */ 168 if (sm_strlcpy(sfilebuf, b, sizeof sfilebuf) >= 169 sizeof sfilebuf) 170 { 171 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 172 "StatusFile filename too long: %.30s...\n", 173 b); 174 exit(EX_CONFIG); 175 } 176 b = strchr(sfilebuf, '#'); 177 if (b == NULL) 178 b = strchr(sfilebuf, '\n'); 179 if (b == NULL) 180 b = &sfilebuf[strlen(sfilebuf)]; 181 while (isascii(*--b) && isspace(*b)) 182 continue; 183 *++b = '\0'; 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); 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 if (progmode) 277 { 278 (void) time(&now); 279 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%ld %ld\n", 280 (long) stats.stat_itime, (long) now); 281 } 282 else 283 { 284 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 285 "Statistics from %s", 286 ctime(&stats.stat_itime)); 287 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 288 " M msgsfr bytes_from msgsto bytes_to msgsrej msgsdis"); 289 #if _FFR_QUARANTINE 290 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " msgsqur"); 291 #endif /* _FFR_QUARANTINE */ 292 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", 293 mnames ? " Mailer" : ""); 294 } 295 for (i = 0; i < MAXMAILERS; i++) 296 { 297 if (stats.stat_nf[i] || stats.stat_nt[i] || 298 #if _FFR_QUARANTINE 299 stats.stat_nq[i] || 300 #endif /* _FFR_QUARANTINE */ 301 stats.stat_nr[i] || stats.stat_nd[i]) 302 { 303 char *format; 304 305 if (progmode) 306 format = "%2d %8ld %10ld %8ld %10ld %6ld %6ld"; 307 else 308 format = "%2d %8ld %10ldK %8ld %10ldK %6ld %6ld"; 309 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 310 format, i, 311 stats.stat_nf[i], 312 stats.stat_bf[i], 313 stats.stat_nt[i], 314 stats.stat_bt[i], 315 stats.stat_nr[i], 316 stats.stat_nd[i]); 317 #if _FFR_QUARANTINE 318 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 319 " %6ld", stats.stat_nq[i]); 320 #endif /* _FFR_QUARANTINE */ 321 if (mnames) 322 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 323 " %s", 324 mtable[i]); 325 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 326 frmsgs += stats.stat_nf[i]; 327 frbytes += stats.stat_bf[i]; 328 tomsgs += stats.stat_nt[i]; 329 tobytes += stats.stat_bt[i]; 330 rejmsgs += stats.stat_nr[i]; 331 dismsgs += stats.stat_nd[i]; 332 #if _FFR_QUARANTINE 333 quarmsgs += stats.stat_nq[i]; 334 #endif /* _FFR_QUARANTINE */ 335 } 336 } 337 if (progmode) 338 { 339 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 340 " T %8ld %10ld %8ld %10ld %6ld %6ld", 341 frmsgs, frbytes, tomsgs, tobytes, rejmsgs, 342 dismsgs); 343 #if _FFR_QUARANTINE 344 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 345 " %6ld", quarmsgs); 346 #endif /* _FFR_QUARANTINE */ 347 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 348 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 349 " C %8ld %8ld %6ld\n", 350 stats.stat_cf, stats.stat_ct, 351 stats.stat_cr); 352 (void) close(fd); 353 if (trunc) 354 { 355 fd = open(sfile, O_RDWR | O_TRUNC); 356 if (fd >= 0) 357 (void) close(fd); 358 } 359 } 360 else 361 { 362 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 363 "============================================================="); 364 #if _FFR_QUARANTINE 365 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "========"); 366 #endif /* _FFR_QUARANTINE */ 367 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 368 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 369 " T %8ld %10ldK %8ld %10ldK %6ld %6ld", 370 frmsgs, frbytes, tomsgs, tobytes, rejmsgs, 371 dismsgs); 372 #if _FFR_QUARANTINE 373 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 374 " %6ld", quarmsgs); 375 #endif /* _FFR_QUARANTINE */ 376 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 377 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 378 " C %8ld %10s %8ld %10s %6ld\n", 379 stats.stat_cf, "", stats.stat_ct, "", 380 stats.stat_cr); 381 } 382 exit(EX_OK); 383 /* NOTREACHED */ 384 return EX_OK; 385 } 386