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 * $FreeBSD$ 13 * 14 */ 15 16 #include <sm/gen.h> 17 18 SM_IDSTR(copyright, 19 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\ 20 All rights reserved.\n\ 21 Copyright (c) 1988, 1993\n\ 22 The Regents of the University of California. All rights reserved.\n") 23 24 SM_IDSTR(id, "@(#)$Id: mailstats.c,v 8.98 2002/05/24 23:10:15 gshapiro Exp $") 25 26 #include <unistd.h> 27 #include <stddef.h> 28 #include <stdlib.h> 29 #include <ctype.h> 30 #include <string.h> 31 #include <time.h> 32 #ifdef EX_OK 33 # undef EX_OK /* unistd.h may have another use for this */ 34 #endif /* EX_OK */ 35 #include <sysexits.h> 36 37 #include <sm/errstring.h> 38 #include <sm/limits.h> 39 #include <sendmail/sendmail.h> 40 #include <sendmail/mailstats.h> 41 #include <sendmail/pathnames.h> 42 43 44 #define MNAMELEN 20 /* max length of mailer name */ 45 46 int 47 main(argc, argv) 48 int argc; 49 char **argv; 50 { 51 register int i; 52 int mno; 53 int save_errno; 54 int ch, fd; 55 char *sfile; 56 char *cfile; 57 SM_FILE_T *cfp; 58 bool mnames; 59 bool progmode; 60 bool trunc; 61 long frmsgs = 0, frbytes = 0, tomsgs = 0, tobytes = 0, rejmsgs = 0; 62 long dismsgs = 0; 63 #if _FFR_QUARANTINE 64 long quarmsgs = 0; 65 #endif /* _FFR_QUARANTINE */ 66 time_t now; 67 char mtable[MAXMAILERS][MNAMELEN + 1]; 68 char sfilebuf[MAXPATHLEN]; 69 char buf[MAXLINE]; 70 struct statistics stats; 71 extern char *ctime(); 72 extern char *optarg; 73 extern int optind; 74 75 cfile = getcfname(0, 0, SM_GET_SENDMAIL_CF, NULL); 76 sfile = NULL; 77 mnames = true; 78 progmode = false; 79 trunc = false; 80 while ((ch = getopt(argc, argv, "cC:f:opP")) != -1) 81 { 82 switch (ch) 83 { 84 case 'c': 85 cfile = getcfname(0, 0, SM_GET_SUBMIT_CF, NULL); 86 break; 87 88 case 'C': 89 cfile = optarg; 90 break; 91 92 case 'f': 93 sfile = optarg; 94 break; 95 96 case 'o': 97 mnames = false; 98 break; 99 100 case 'p': 101 trunc = true; 102 /* FALLTHROUGH */ 103 104 case 'P': 105 progmode = true; 106 break; 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)) != NULL) 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 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, 0600); 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