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