1 /*
2 * sma -- Sendmail log analyser
3 *
4 * Copyright (c) 2000 - 2003 Jarkko Turkulainen. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY JARKKO TURKULAINEN ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL JARKKO TURKULAINEN BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Date: 2002/09/12 10:46:00 $
30 */
31
32 extern const char *sma_optarg;
33 extern int sma_optind;
34
35 #include "sma.h"
36
37 /* pointer to program name: */
38 char *pname;
39
40 /* current time: */
41 time_t tval;
42 struct tm *curr;
43 struct tm tp;
44
45 /*
46 * Command line arguments
47 * xflag sets argument x on/off
48 * xchar is pointer to argument string if x requires an argument
49 */
50 int aflag;
51 int cflag;
52 int dflag;
53 int hflag;
54 int nflag;
55 int sflag;
56 int qflag;
57 int lflag;
58 int vflag;
59 int wflag;
60 unsigned int lnum;
61 unsigned int lrnum;
62 int rflag;
63 unsigned int rnum;
64 unsigned int rrnum;
65 int bflag;
66 const char *bchar;
67 int fflag;
68 const char *fchar;
69 int oflag;
70 const char *ochar;
71 int Lflag;
72 const char *Lchar;
73 int Oflag;
74 const char *Ochar;
75 int Dflag;
76 const char *Dchar;
77 int pflag;
78 int iflag;
79 int tflag;
80 int dcaddrflag;
81 const char *tchar;
82
83 /* Configuration file parameters: */
84 int Hflag;
85 const char *Hchar;
86 int Cflag;
87 const char *Cchar;
88 int Fflag;
89 const char *tbchar;
90 int pgflag;
91 const char *bechar;
92 const char *cfchar;
93 const char *puchar;
94 const char *pachar;
95 const char *plchar;
96 const char *ppchar;
97 const char *htchar;
98 const char *ftchar;
99 int csflag;
100 int lrflag;
101 int rrflag;
102 int clsflag;
103 unsigned int stnum;
104 unsigned int rsnum;
105 unsigned int rsrnum;
106 unsigned int epnum;
107 unsigned int rpnum;
108
109 /* Start and end times: */
110 char *sstring;
111 char *estring;
112 char *tstring;
113 time_t sstime;
114 time_t eetime;
115 int syear;
116 int smonth;
117 int sday;
118 int shour;
119 int sminute;
120 int ssecond;
121 int eyear;
122 int emonth;
123 int eday;
124 int ehour;
125 int eminute;
126 int esecond;
127
128 /* hash table sizes: */
129 int asize;
130 int rsize;
131 char *hsstring;
132 char *hastring;
133 char *hrstring;
134
135 /* Filters */
136 char *sef;
137 char *ref;
138 char *srf;
139 char *rrf;
140 #ifdef USE_REGEXP
141 regex_t csef;
142 regex_t cref;
143 regex_t csrf;
144 regex_t crrf;
145 #endif
146
147 /* Output file handle: */
148 FILE *ofp;
149
150 /* total number of hosts: */
151 int hosts;
152
153 /* inital host structure: */
154 struct host first;
155
156 int
main(int argc,char ** argv)157 main(int argc, char **argv) {
158 FILE *fp = NULL;
159 struct host *hptr;
160 int c;
161
162 #ifdef _WIN32
163 int getopt(int, char **, char *);
164 #endif
165
166 /* initialize global variables: */
167 hosts = 0;
168 tval = time((time_t *)NULL);
169 curr = localtime(&tval);
170 first.next = NULL;
171 pname = argv[0];
172
173 /* get arguments: */
174 while ((c = getopt(argc, argv, "C:D:H:L:b:f:l:r:t:o:O:AFacdhinpsqvw")) != -1)
175 switch(c) {
176 case 'A':
177 dcaddrflag = 1;
178 break;
179 case 'D':
180 Dflag = 1;
181 Dchar = sma_optarg;
182 break;
183 case 'C':
184 Cflag = 1;
185 Cchar = sma_optarg;
186 break;
187 case 'F':
188 Fflag = 1;
189 break;
190 case 'H':
191 Hflag = 1;
192 Hchar = sma_optarg;
193 break;
194 case 'a':
195 aflag = 1;
196 break;
197 case 'b':
198 bflag = 1;
199 bchar = sma_optarg;
200 break;
201 case 'c':
202 cflag = 1;
203 break;
204 case 'd':
205 dflag = 1;
206 break;
207 case 'f':
208 fflag = 1;
209 fchar = sma_optarg;
210 break;
211 case 'h':
212 hflag = 1;
213 break;
214 case 'i':
215 iflag = 1;
216 break;
217 case 'l':
218 lflag = 1;
219 lnum = atoi(sma_optarg);
220 break;
221 case 'L':
222 Lflag = 1;
223 Lchar = sma_optarg;
224 break;
225 case 'r':
226 rflag = 1;
227 rnum = atoi(sma_optarg);
228 break;
229 case 'n':
230 nflag = 1;
231 break;
232 case 'o':
233 oflag = 1;
234 ochar = sma_optarg;
235 break;
236 case 'O':
237 Oflag = 1;
238 Ochar = sma_optarg;
239 break;
240 case 'p':
241 pflag = 1;
242 break;
243 case 's':
244 sflag = 1;
245 break;
246 case 't':
247 tflag = 1;
248 tchar = sma_optarg;
249 break;
250 case 'q':
251 qflag = 1;
252 break;
253 case 'v':
254 vflag = 1;
255 break;
256 case 'w':
257 wflag = 1;
258 break;
259 default:
260 usage();
261 }
262 argc -= sma_optind;
263 argv += sma_optind;
264
265 /* Debug */
266 if (vflag)
267 fprintf(stderr, "%s: running in Debug mode\n", pname);
268
269 /* Check argument logic: */
270
271 if (hflag) usage();
272 if (cflag) copying();
273 if (!pgflag) pgflag = 1;
274
275 /* Read in the configuration file: */
276 if (fchar) {
277 if (!(fp = fopen(fchar, "r")))
278 fprintf(stderr, "%s: cannot open %s\n", pname, fchar);
279 else {
280 init(fp);
281 (void)fclose(fp);
282 }
283 } else if (!Fflag) {
284 if (!(fp = fopen(DEFAULT_CONF, "r")))
285 fprintf(stderr, "%s: cannot open %s, using defaults\n",
286 pname, "configuration file");
287 else {
288 fchar = DEFAULT_CONF;
289 init(fp);
290 (void)fclose(fp);
291 }
292 }
293
294 /* Hash table size: */
295 if (!tflag || !strncmp(tchar, "normal", 6)) {
296 asize = ASIZE_NORMAL;
297 rsize = RSIZE_NORMAL;
298 } else if (!strncmp(tchar, "big", 3)) {
299 asize = ASIZE_BIG;
300 rsize = RSIZE_BIG;
301 } else if (!strncmp(tchar, "huge", 4)) {
302 asize = ASIZE_HUGE;
303 rsize = RSIZE_HUGE;
304 } else {
305 if (!(hsstring = strdup(tchar)))
306 error_memory();
307 hastring = hsstring;
308 if (*hsstring == ',') {
309 hastring = NULL;
310 if (*(hsstring + 1) == '\0')
311 hrstring = NULL;
312 else
313 hrstring = ++hsstring;
314 }
315 while (*hsstring != '\0') {
316 if (*hsstring == ',') {
317 *hsstring = '\0';
318 if (*(hsstring + 1) == '\0')
319 hrstring = NULL;
320 else
321 hrstring = ++hsstring;
322 break;
323 }
324 hsstring++;
325 }
326 /* Address Hash table: */
327 if (hastring != NULL)
328 asize = atoi(hastring);
329 else asize = ASIZE_NORMAL;
330 /* Relay Hash table: */
331 if (hrstring != NULL)
332 rsize = atoi(hrstring);
333 else rsize = RSIZE_NORMAL;
334
335 }
336 if (asize <= 0 || rsize <= 0) {
337 fprintf(stderr, "%s: Illegal hash table size\n", pname);
338 usage();
339 }
340
341 if (!Oflag) Oflag = FORMAT_ASCII;
342 else if (!strncmp(Ochar, "ascii", 5)) Oflag = FORMAT_ASCII;
343 else if (!strncmp(Ochar, "html", 4)) Oflag = FORMAT_HTML;
344 else if (!strncmp(Ochar, "clog", 4)) Oflag = FORMAT_CLOG;
345 else {
346 fprintf(stderr, "%s: illegal output format \"%s\".\n",
347 pname, Ochar);
348 exit (1);
349 }
350 if (aflag) Oflag = FORMAT_ASCII;
351 if (wflag) Oflag = FORMAT_HTML;
352
353 if (!Cchar) Cchar = COMMENT;
354 if (aflag) wflag = 0;
355 if (!bchar) bchar = BG_COLOR;
356 if (!tbchar) tbchar = TB_COLOR;
357 if (!bechar) bechar = BOUNCE_ADDR;
358
359 if (lflag) {
360 if (lnum <= 0) lnum = 0;
361 lrnum = lnum;
362 } else if (!lrflag) {
363 lnum = LDEF;
364 if (lnum <= 0) lnum = 0;
365 lrnum = LRDEF;
366 if (lrnum <= 0) lrnum = 0;
367 }
368 if (rflag) {
369 if (rnum <= 0) rnum = 0;
370 rrnum = rnum;
371 } else if (!rrflag) {
372 rnum = RDEF;
373 if (rnum <= 0) rnum = 0;
374 rrnum = RRDEF;
375 if (rrnum <= 0) rrnum = 0;
376 }
377
378 /* Set output: */
379 if (ochar) {
380 if (!(ofp = fopen(ochar, "w"))) {
381 fprintf(stderr, "%s: cannot open %s\n", pname, ochar);
382 ofp = stdout;
383 }
384 } else
385 ofp = stdout;
386
387 /* Check filter logic: */
388
389 /* Envelope and relay filters: */
390 if (sef || srf || ref || rrf) {
391 if (!qflag) fprintf(stderr, "%s: setting filters\n", pname);
392 #ifdef USE_REGEXP
393 if (csflag)
394 csflag = REG_EXTENDED|REG_NOSUB;
395 else
396 csflag = REG_EXTENDED|REG_ICASE|REG_NOSUB;
397 #endif
398 }
399
400 if (!sef || !strncmp(sef, "*", 1)) {
401 sef = (char *)NULL;
402 } else {
403 #ifdef USE_REGEXP
404 if (*sef == '!') {
405 if (*(sef+1) == '\0')
406 sef = NULL;
407 regcomp(&csef, sef+1, csflag);
408 } else
409 regcomp(&csef, sef, csflag);
410 #else
411 if (*sef == '!' && *(sef+1) == '\0')
412 sef = (char *)NULL;
413 #endif
414 if (!qflag) fprintf(stderr,
415 " envelope sender filter: %s\n", sef);
416 }
417
418 if (!srf || !strncmp(srf, "*", 1)) srf = (char *)NULL;
419 else {
420 #ifdef USE_REGEXP
421 if (*srf == '!') {
422 if (*(srf+1) == '\0')
423 srf = NULL;
424 regcomp(&csrf, srf+1, csflag);
425 } else
426 regcomp(&csrf, srf, csflag);
427 #else
428 if (*srf == '!' && *(srf+1) == '\0')
429 srf = (char *)NULL;
430 #endif
431 if (!qflag) fprintf(stderr,
432 " relay sender filter: %s\n", srf);
433 }
434
435 if (!ref || !strncmp(ref, "*", 1)) ref = (char *)NULL;
436 else {
437 #ifdef USE_REGEXP
438 if (*ref == '!')
439 regcomp(&cref, ref+1, csflag);
440 else
441 regcomp(&cref, ref, csflag);
442 #else
443 if (*ref == '!' && *(ref+1) == '\0')
444 ref = (char *)NULL;
445
446 #endif
447 if (!qflag) fprintf(stderr,
448 " envelope recipient filter: %s\n", ref);
449 }
450
451 if (!rrf || !strncmp(rrf, "*", 1)) rrf = (char *)NULL;
452 else {
453 #ifdef USE_REGEXP
454 if (*rrf == '!')
455 regcomp(&crrf, rrf+1, csflag);
456 else
457 regcomp(&crrf, rrf, csflag);
458 #else
459 if (*rrf == '!' && *(rrf+1) == '\0')
460 rrf = (char *)NULL;
461 #endif
462 if (!qflag) fprintf(stderr,
463 " relay recipient filter: %s\n", rrf);
464 }
465
466 /* Start -and end times: */
467 if (Dflag) {
468 /* Read from command line.. */
469 if (!(sstring = strdup(Dchar)))
470 error_memory();
471 tstring = sstring;
472 if (*sstring == ',') {
473 tstring = NULL;
474 if (*(sstring + 1) == '\0')
475 estring = NULL;
476 else
477 estring = ++sstring;
478 }
479 while (*sstring != '\0') {
480 if (*sstring == ',') {
481 *sstring = '\0';
482 if (*(sstring + 1) == '\0')
483 estring = NULL;
484 else
485 estring = ++sstring;
486 break;
487 }
488 sstring++;
489 }
490 /* StartTime: */
491 if (tstring != NULL)
492 scan_time(&sstime, tstring);
493 /* EndTime: */
494 if (estring != NULL)
495 scan_time(&eetime, estring);
496 } else {
497 if (!sstring || !(strncmp(sstring, "*", 1)))
498 sstring = NULL;
499 else if (sscanf(sstring, "%d/%d/%d-%d:%d:%d",
500 &syear, &smonth, &sday, &shour, &sminute, &ssecond) != 6) {
501 sstring = NULL;
502 if (!qflag) {
503 fprintf(stderr, "%s: illegal StartTime, "
504 "using (*)\n", pname);
505 }
506 } else {
507 sstime = (time_t)conv_time(smonth-1, sday, shour,
508 sminute, ssecond);
509 if (!sstime && !qflag)
510 fprintf(stderr, "%s: illegal StartTime, "
511 "using (*)\n", pname);
512 }
513 if (!estring || !(strncmp(estring, "*", 1)))
514 estring = NULL;
515 else if (sscanf(estring, "%d/%d/%d-%d:%d:%d",
516 &eyear, &emonth, &eday, &ehour, &eminute, &esecond) != 6) {
517 if (!qflag) {
518 fprintf(stderr, "%s: illegal EndTime, "
519 "using (*)\n", pname);
520 }
521 } else {
522 eetime = (time_t)conv_time(emonth-1, eday, ehour,
523 eminute, esecond);
524 if (!eetime && !qflag)
525 fprintf(stderr, "%s: illegal EndTime, "
526 "using (*)\n", pname);
527 }
528 }
529 /* Print out configuration to stdout: */
530 if (pflag) {
531 fprintf(stderr, "%s: configuration:\n", pname);
532 dump_config(stdout);
533 exit (0);
534 }
535
536 /* Print configuration in debug mode: */
537 if (vflag) {
538 fprintf(stderr, "%s: configuration:\n", pname);
539 dump_config(stderr);
540 }
541
542 /* loop through remaining arguments (if any) and do parsing: */
543 if (*argv)
544 for (; *argv; ++argv) {
545 if (!qflag) fprintf(stderr,
546 "%s: opening file %s\n", pname, *argv);
547 if (!(fp = fopen(*argv, "r")))
548 fprintf(stderr,
549 "%s: cannot open %s\n", pname, *argv);
550 else {
551 parse(fp, *argv);
552 (void)fclose(fp);
553 }
554 }
555 else {
556 if (!qflag)
557 fprintf(stderr, "%s: reading from stdin\n", pname);
558 parse(stdin, "stdin");
559 }
560
561 /* If output format is clog, nothing to do here: */
562 if (Oflag == FORMAT_CLOG) exit (0);
563
564 /* check host structure: */
565 hptr = first.next;
566 if (!hptr || !(hptr->inum) || !(hptr->onum)) {
567 if (!qflag)
568 fprintf(stderr, "%s: nothing to report\n", pname);
569 exit(1);
570 }
571 for (hptr = first.next; hptr; hptr = hptr->next) {
572
573 /* rearrange and sort: */
574 sort(hptr);
575
576 /* seconds between first and last log entry: */
577 hptr->dtime = difftime(hptr->ltime, hptr->ftime);
578
579 /* first hour and day: */
580 hptr->fday = (localtime(&hptr->ftime))->tm_wday;
581 hptr->fhour = (localtime(&hptr->ftime))->tm_hour;
582
583 /* time distributions: */
584 average(hptr, hptr->idd, hptr->fidd,
585 hptr->ihh, hptr->fihh);
586 average(hptr, hptr->odd, hptr->fodd,
587 hptr->ohh, hptr->fohh);
588 }
589
590 /* print: */
591 if (Oflag == FORMAT_HTML) html(ofp);
592 else if (Oflag == FORMAT_ASCII) ascii(ofp);
593 (void)fclose(ofp);
594
595 /* OK: */
596 exit(0);
597 }
598