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