1 /* 2 * Copyright (c) 1998-2002 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.98 2002/05/24 23:10:15 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[MAXPATHLEN]; 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] [-c] [-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 = strchr(buf, '#'); 145 if (b == NULL) 146 b = strchr(buf, '\n'); 147 if (b == NULL) 148 b = &buf[strlen(buf)]; 149 while (isascii(*--b) && isspace(*b)) 150 continue; 151 *++b = '\0'; 152 153 b = buf; 154 switch (*b++) 155 { 156 case 'M': /* mailer definition */ 157 break; 158 159 case 'O': /* option -- see if .st file */ 160 if (sm_strncasecmp(b, " StatusFile", 11) == 0 && 161 !(isascii(b[11]) && isalnum(b[11]))) 162 { 163 /* new form -- find value */ 164 b = strchr(b, '='); 165 if (b == NULL) 166 continue; 167 while (isascii(*++b) && isspace(*b)) 168 continue; 169 } 170 else if (*b++ != 'S') 171 { 172 /* something else boring */ 173 continue; 174 } 175 176 /* this is the S or StatusFile option -- save it */ 177 if (sm_strlcpy(sfilebuf, b, sizeof sfilebuf) >= 178 sizeof sfilebuf) 179 { 180 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 181 "StatusFile filename too long: %.30s...\n", 182 b); 183 exit(EX_CONFIG); 184 } 185 if (sfile == NULL) 186 sfile = sfilebuf; 187 188 default: 189 continue; 190 } 191 192 if (mno >= MAXMAILERS) 193 { 194 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 195 "Too many mailers defined, %d max.\n", 196 MAXMAILERS); 197 exit(EX_SOFTWARE); 198 } 199 m = mtable[mno]; 200 s = m + MNAMELEN; /* is [MNAMELEN + 1] */ 201 while (*b != ',' && !(isascii(*b) && isspace(*b)) && 202 *b != '\0' && m < s) 203 *m++ = *b++; 204 *m = '\0'; 205 for (i = 0; i < mno; i++) 206 { 207 if (strcmp(mtable[i], mtable[mno]) == 0) 208 break; 209 } 210 if (i == mno) 211 mno++; 212 } 213 (void) sm_io_close(cfp, SM_TIME_DEFAULT); 214 for (; mno < MAXMAILERS; mno++) 215 mtable[mno][0] = '\0'; 216 217 if (sfile == NULL) 218 { 219 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 220 "mailstats: no statistics file located\n"); 221 exit(EX_OSFILE); 222 } 223 224 fd = open(sfile, O_RDONLY, 0600); 225 if ((fd < 0) || (i = read(fd, &stats, sizeof stats)) < 0) 226 { 227 save_errno = errno; 228 (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, "mailstats: "); 229 errno = save_errno; 230 sm_perror(sfile); 231 exit(EX_NOINPUT); 232 } 233 if (i == 0) 234 { 235 (void) sleep(1); 236 if ((i = read(fd, &stats, sizeof stats)) < 0) 237 { 238 save_errno = errno; 239 (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, 240 "mailstats: "); 241 errno = save_errno; 242 sm_perror(sfile); 243 exit(EX_NOINPUT); 244 } 245 else if (i == 0) 246 { 247 memset((ARBPTR_T) &stats, '\0', sizeof stats); 248 (void) time(&stats.stat_itime); 249 } 250 } 251 if (i != 0) 252 { 253 if (stats.stat_magic != STAT_MAGIC) 254 { 255 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 256 "mailstats: incorrect magic number in %s\n", 257 sfile); 258 exit(EX_OSERR); 259 } 260 else if (stats.stat_version != STAT_VERSION) 261 { 262 (void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT, 263 "mailstats version (%d) incompatible with %s version (%d)\n", 264 STAT_VERSION, sfile, 265 stats.stat_version); 266 267 exit(EX_OSERR); 268 } 269 else if (i != sizeof stats || stats.stat_size != sizeof(stats)) 270 { 271 (void) sm_io_fputs(smioerr, SM_TIME_DEFAULT, 272 "mailstats: file size changed.\n"); 273 exit(EX_OSERR); 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 #if _FFR_QUARANTINE 291 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, " msgsqur"); 292 #endif /* _FFR_QUARANTINE */ 293 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "%s\n", 294 mnames ? " Mailer" : ""); 295 } 296 for (i = 0; i < MAXMAILERS; i++) 297 { 298 if (stats.stat_nf[i] || stats.stat_nt[i] || 299 #if _FFR_QUARANTINE 300 stats.stat_nq[i] || 301 #endif /* _FFR_QUARANTINE */ 302 stats.stat_nr[i] || stats.stat_nd[i]) 303 { 304 char *format; 305 306 if (progmode) 307 format = "%2d %8ld %10ld %8ld %10ld %6ld %6ld"; 308 else 309 format = "%2d %8ld %10ldK %8ld %10ldK %6ld %6ld"; 310 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 311 format, i, 312 stats.stat_nf[i], 313 stats.stat_bf[i], 314 stats.stat_nt[i], 315 stats.stat_bt[i], 316 stats.stat_nr[i], 317 stats.stat_nd[i]); 318 #if _FFR_QUARANTINE 319 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 320 " %6ld", stats.stat_nq[i]); 321 #endif /* _FFR_QUARANTINE */ 322 if (mnames) 323 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 324 " %s", 325 mtable[i]); 326 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 327 frmsgs += stats.stat_nf[i]; 328 frbytes += stats.stat_bf[i]; 329 tomsgs += stats.stat_nt[i]; 330 tobytes += stats.stat_bt[i]; 331 rejmsgs += stats.stat_nr[i]; 332 dismsgs += stats.stat_nd[i]; 333 #if _FFR_QUARANTINE 334 quarmsgs += stats.stat_nq[i]; 335 #endif /* _FFR_QUARANTINE */ 336 } 337 } 338 if (progmode) 339 { 340 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 341 " T %8ld %10ld %8ld %10ld %6ld %6ld", 342 frmsgs, frbytes, tomsgs, tobytes, rejmsgs, 343 dismsgs); 344 #if _FFR_QUARANTINE 345 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 346 " %6ld", quarmsgs); 347 #endif /* _FFR_QUARANTINE */ 348 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 349 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 350 " C %8ld %8ld %6ld\n", 351 stats.stat_cf, stats.stat_ct, 352 stats.stat_cr); 353 (void) close(fd); 354 if (trunc) 355 { 356 fd = open(sfile, O_RDWR | O_TRUNC, 0600); 357 if (fd >= 0) 358 (void) close(fd); 359 } 360 } 361 else 362 { 363 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 364 "============================================================="); 365 #if _FFR_QUARANTINE 366 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "========"); 367 #endif /* _FFR_QUARANTINE */ 368 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 369 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 370 " T %8ld %10ldK %8ld %10ldK %6ld %6ld", 371 frmsgs, frbytes, tomsgs, tobytes, rejmsgs, 372 dismsgs); 373 #if _FFR_QUARANTINE 374 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 375 " %6ld", quarmsgs); 376 #endif /* _FFR_QUARANTINE */ 377 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, "\n"); 378 (void) sm_io_fprintf(smioout, SM_TIME_DEFAULT, 379 " C %8ld %10s %8ld %10s %6ld\n", 380 stats.stat_cf, "", stats.stat_ct, "", 381 stats.stat_cr); 382 } 383 exit(EX_OK); 384 /* NOTREACHED */ 385 return EX_OK; 386 } 387