root/src/apachetop.cc

Revision 6, 24.2 KB (checked in by nick, 3 years ago)
Line 
1/* APACHETOP
2**
3**
4*/
5#include "apachetop.h"
6
7/* die and report why */
8#define DIE(msg) fprintf(stderr, "%s: %s\n", msg, strerror(errno)); catchsig(1);
9/* die with no strerror */
10#define DIE_N(msg) fprintf(stderr, "%s\n", msg); catchsig(1);
11
12#if HAVE_ADNS_H
13/* resolver master state */
14adns_state adns;
15#endif
16
17/* global stats */
18struct gstat gstats;
19
20time_t now;
21
22struct itemlist *items = NULL;
23map *last_display_map;
24
25Circle *c;
26Timed_Circle *tc;
27Hits_Circle *hc;
28
29map *um, /* urlmap */
30    *im, /* ipmap */
31    *hm, /* hostmap */
32    *rm, /* referrermap */
33    *fm; /* filemap */
34
35struct config cf;
36
37/* inputs */
38struct input *in;
39
40WINDOW *win;
41
42#if (POLLING_METHOD == USING_KQUEUE)
43int kq;
44#elif (POLLING_METHOD == USING_FAM)
45/* master fd for talking to FAM */
46FAMConnection fc;
47#endif
48
49int main(int argc, char *argv[])
50{
51        int fd, buflen, ch, x;
52        char buf[32768], *bufcp, *nextline;
53        time_t last_display = 0;
54        struct logbits lb;
55        struct input *curfile;
56        bool seen_h = false, seen_t = false;
57        extern char *optarg;
58#if (POLLING_METHOD == USING_KQUEUE)
59        /* we have and are using kqueue */
60        struct timespec stv;
61        struct kevent *ev, *evptr;
62#elif (POLLING_METHOD == USING_FAM)
63        /* we have and are using FAM */
64        int fam_fd;
65        FAMEvent fe;
66        struct timeval tv;
67        tv.tv_sec = 1; tv.tv_usec = 0;
68        fd_set readfds;
69
70#else /* stat() then */
71        struct stat sb;
72        struct timeval tv;
73        tv.tv_sec = 1; tv.tv_usec = 0;
74#endif
75
76        LogParser *p;
77        CommonLogParser *cmmp; //AtopLogParser *atpp;
78
79
80        /* some init stuff will need to know the time */
81        now = time(NULL);
82
83        /* set up initial configuration {{{ */
84        memset(&cf, 0, sizeof(cf));
85        cf.debug = true;
86        cf.current_display_size = 0;
87        cf.input_count = 0;
88        cf.circle_size = DEFAULT_CIRCLE_SIZE;
89        cf.circle_mode = DEFAULT_CIRCLE_MODE;
90        cf.sort = DEFAULT_SORT;
91        cf.retcodes_sort = DEFAULT_RETCODES_SORT;
92        cf.refresh_delay = DEFAULT_REFRESH_DELAY;
93        cf.display_mode = DEFAULT_DISPLAY_MODE;
94        cf.numbers_mode = DEFAULT_NUMBERS_MODE;
95        cf.detail_display_urls = true;
96        cf.detail_display_hosts = true;
97        cf.detail_display_refs = true;
98        cf.display_paused = false;
99        cf.keep_querystring = false;
100        cf.lowercase_urls = false;
101        cf.keep_segments = 0;
102        cf.preserve_ref_protocol = 0;
103        cf.do_immed_display = false;
104        cf.do_resolving = false;
105        cf.urlfilter = new Filter();
106        cf.reffilter = new Filter();
107        cf.hostfilter = new Filter();
108        /* }}} */
109
110        /* support MAX_INPUT_FILES input files */
111        in = (struct input *)calloc(MAX_INPUT_FILES, sizeof(struct input));
112
113#if (POLLING_METHOD == USING_KQUEUE) /* then create kqueue {{{ */
114        if ((kq = kqueue()) < 0)
115        {
116                DIE("cannot create kqueue");
117                exit(1);
118        }
119
120        /* space in ev for all inputs */
121        ev = (struct kevent *)calloc(MAX_INPUT_FILES, sizeof(struct kevent));
122/* }}} */
123#elif (POLLING_METHOD == USING_FAM) /* open FAM connection */
124        if (FAMOpen(&fc))
125        {
126                DIE("cannot connect to fam");
127                exit(1);
128        }
129
130        fam_fd = FAMCONNECTION_GETFD(&fc);
131#endif
132
133#if HAVE_ADNS_H
134        /* open adns connection */
135        adns_init(&adns, adns_if_noerrprint, 0);
136#endif
137
138        /* process commandline {{{ */
139        while ((ch = getopt(argc, argv, "f:H:T:hqlrs:pd:")) != -1)
140        {
141                switch(ch)
142                {
143                        case 'f':
144                                if (new_file(optarg, SEEK_TO_END) == -1)
145                                {
146                                        fprintf(stderr, "opening %s: %s\n",
147                                                optarg, strerror(errno));
148                                        sleep(2);
149                                }
150                                else
151                                        cf.input_count++;
152                                break;
153                        case 'T':
154                                x = atoi(optarg);
155                                seen_t = true;
156                                if (x > 0)
157                                {
158                                        cf.circle_mode = TIMED_CIRCLE;
159                                        cf.circle_size = x;
160                                }
161                                break;
162                        case 'H':
163                                x = atoi(optarg);
164                                seen_h = true;
165                                if (x > 0)
166                                {
167                                        cf.circle_mode = HITS_CIRCLE;
168                                        cf.circle_size = x;
169                                }
170                                break;
171
172                        case 'q':
173                                cf.keep_querystring = true;
174                                break;
175
176                        case 'l':
177                                cf.lowercase_urls = true;
178                                break;
179
180                        case 'r':
181                                cf.do_resolving = true;
182                                break;
183
184                        case 's':
185                                x = atoi(optarg);
186                                if (x > 0)
187                                        cf.keep_segments = x;
188                                break;
189
190                        case 'p':
191                                cf.preserve_ref_protocol = 1;
192                                break;
193
194                        case 'd':
195                                x = atoi(optarg);
196                                if (x > 0)
197                                        cf.refresh_delay = x;
198                                break;
199
200                        case 'h':
201                        case '?':
202                                usage();
203                                exit(1);
204                                break;
205                }
206        } /* }}} */
207
208        /* if no files have been specified, we'll use DEFAULT_LOGFILE */
209        if (cf.input_count == 0)
210        {
211                if (new_file(DEFAULT_LOGFILE, SEEK_TO_END) != -1)
212                        cf.input_count++;
213
214                /* if it's still zero, fail */
215                if (cf.input_count == 0)
216                {
217                        fprintf(stderr, "opening %s: %s\n",
218                                DEFAULT_LOGFILE, strerror(errno));
219                        DIE_N("No input files could be opened");
220                        exit(1);
221                }
222        }
223
224        if (seen_t && seen_h)
225        {
226                DIE_N("-T and -H are mutually exclusive. Specify only one.");
227                exit(1);
228        }
229
230
231        /* set up circle */
232        switch(cf.circle_mode)
233        {
234                default:
235                case HITS_CIRCLE:
236                        hc = new Hits_Circle;
237                        hc->create(cf.circle_size);
238                        c = hc;
239                        break;
240       
241                case TIMED_CIRCLE:
242                        tc = new Timed_Circle;
243                        tc->create(cf.circle_size);
244                        c = tc;
245                        break;
246        }
247
248        /* set up one of each parser */
249        /* CommonLogParser handles combined too */
250        cmmp = new CommonLogParser;
251#if 0
252/* this isn't written yet */
253        atpp = new AtopLogParser;
254#endif
255
256        /* maps auto-resize, just use a sane starting point {{{ */
257        /* url string -> url hash map */
258        um = new map; /* hashing happens internally */
259        um->create(cf.circle_size);
260
261        /* referrer string -> hash map */
262        rm = new map;
263        rm->create(cf.circle_size);
264
265        /* ip string -> ip hash map */
266        im = new map;
267        im->create(cf.circle_size);
268
269        /* host string -> host hash map */
270        hm = new map;
271        hm->create(cf.circle_size);
272        /* }}} */
273
274        memset(&gstats, (char) NULL, sizeof(gstats));
275        gstats.start = time(NULL);
276
277        signal(SIGINT, &catchsig);
278        signal(SIGKILL, &catchsig);
279        signal(SIGWINCH, &catchwinch);
280
281        win = initscr();
282        nonl(); /* no NL->CR/NL on output */
283        cbreak(); /* enable reading chars one at a time */
284        noecho();
285        keypad(win, true);
286
287        nodelay(win, true);
288
289        for( ;; )
290        {
291                if (cf.exit) break;
292
293                now = time(NULL);
294
295                /* periodically check that we're not in a submenu, and we
296                 * haven't been for more than 5 seconds without any more
297                 * keypresses; but if cf.in_submenu_stay is true, we stay in
298                 * the submenu until we receive a keypress (in read_key) */
299                if (cf.in_submenu &&
300                    !cf.in_submenu_stay &&
301                    now - cf.in_submenu_time > 5)
302                {
303                        cf.in_submenu = SUBMENU_NONE;
304                        clear_submenu_banner();
305                }
306
307                /* if we have any input files waiting to be opened, try to */
308                for (x = 0 ; x < MAX_INPUT_FILES ; ++x)
309                {
310                        /* for each entry in input .. */
311                        curfile = &in[x];
312                        if (curfile->inode && curfile->open == false)
313                        {
314                                dprintf("%s needs reopening\n",
315                                   curfile->filename);
316
317                                new_file(curfile->filename, NO_SEEK_TO_END);
318
319                                /* what happens if a new fd is used? the old
320                                 * struct will still have inode > 0 and
321                                 * open == false so we'll keep coming back
322                                 * here */
323                        }
324                }
325
326
327#if HAVE_ADNS_H
328                /* see if adns has got anything for us */
329                collect_dns_responses();
330#endif
331
332                /* see if it's time to update.. */
333                if (display(last_display))
334                {
335                        /* returns true when we updated, so.. */
336                        last_display = now; // remember our last update
337                }
338
339                /* start of for() loop within these folds.. */
340#if (POLLING_METHOD == USING_KQUEUE) /* {{{ */
341                /* every 1/10th of a second */
342                stv.tv_sec = 0; stv.tv_nsec = 10000000;
343                x = kevent(kq, NULL, 0, ev, MAX_INPUT_FILES, &stv);
344//              if (x == 0) continue; /* timeout, nothing happened */
345                if (x < 0) break; /* error */
346
347                for(evptr = ev ; evptr->filter ; evptr++)
348                {
349                        /* udata contains a pointer to the struct input
350                        ** array element for this fd
351                        */
352                        curfile = (struct input *)evptr->udata;
353                        fd = curfile->fd; /* or evptr->ident will work */
354
355                        /* see if we can deduce what happened */
356                        if (((evptr->fflags & NOTE_DELETE) == NOTE_DELETE) ||
357                            ((evptr->fflags & NOTE_RENAME) == NOTE_RENAME))
358                        {
359                                /* file deleted or renamed */
360                                close(fd);
361                                curfile->open = false;
362
363                                /* skip rest of loop, we'll try
364                                 * to reopen at top of for(;;) */
365                                continue;
366                        }
367                        else if ((evptr->fflags & NOTE_WRITE) == NOTE_WRITE)
368                        {
369                                //read_amt = ev.data; /* we don't use this */
370                        }
371/* }}} */
372#elif (POLLING_METHOD == USING_FAM) /* {{{ */
373                FD_ZERO(&readfds);
374                FD_SET(fam_fd, &readfds);
375
376                /* 1/10th of a second */
377                tv.tv_sec = 0;
378                tv.tv_usec = 100000;
379
380                /* perform a select() on the fam filedescriptor */
381                select(fam_fd + 1, &readfds, NULL, NULL, &tv);
382
383                /* if FAM has nothing to report, loop around */
384                if (!(FD_ISSET(fam_fd, &readfds)))
385                        continue;
386
387                while (FAMPending(&fc) == 1 && FAMNextEvent(&fc, &fe))
388                {
389                        /* fe.userdata is struct input * element for this
390                        ** file; see new_file() for how it's set
391                        */
392                        curfile = (struct input *)fe.userdata;
393                        fd = curfile->fd;
394                       
395/* }}} */
396#else /* fallback to stat() {{{ */
397
398                /* 1/10th of a second */
399                tv.tv_sec = 0; tv.tv_usec = 100000;
400                select(NULL, NULL, NULL, NULL, &tv);
401
402                /* check every file */
403                for (x = 0 ; x < MAX_INPUT_FILES ; ++x)
404                {
405                        curfile = &in[x];
406                        fd = curfile->fd;
407
408                        if (curfile->open == false || fd == 0)
409                                continue;
410
411                        if (stat(curfile->filename, &sb) == -1)
412                        {
413                                /* file removed */
414                                close(fd);
415                                curfile->fd = 0;
416                                curfile->open = false;
417                               
418                                /* skip rest of loop, we'll try
419                                 * to reopen at top of for(;;) */
420                                continue;
421                        }
422                        else
423                        {
424                                /* file still there, but lets check the
425                                 * inode hasn't changed (ie, it's been
426                                 * recreated under our feet */
427                                if (sb.st_ino != curfile->inode)
428                                {
429                                        close(fd);
430                                        curfile->open = false;
431
432                                        /* skip rest of loop, we'll try
433                                         * to reopen at top of for(;;) */
434                                        continue;
435                                }
436                        }
437#endif /* }}} */
438
439                        /* read the data */
440                        buflen = read(fd, buf, sizeof(buf));
441
442                        if (buflen == 0) /* no data */
443                        {
444                                /* this should only happen if we've had to
445                                 * fall back to stat() */
446                                continue;
447                        }
448
449                        if (buflen < 0) /* error */
450                        {
451                                DIE("read");
452                        }
453
454                        curfile->lastreq = now;
455
456                        /* pass each line to logsplit; FIXME tidy this up */
457                        nextline = bufcp = buf;
458                        while ((nextline - buf) < buflen && nextline)
459                        {
460                                bufcp = nextline;
461                                /* find the end of this line */
462                                if (!(nextline = strchr(bufcp, '\n')))
463                                {
464                                        /* we can't, ignore it */
465                                        continue;
466                                }
467
468                                *nextline = (char) NULL;
469                                ++nextline;
470
471                                /* which parser? */
472                                #if CHRIS_HAS_WRITTEN_MORE_PARSERS
473                                switch(in->type)
474                                {
475                                        /* only one atm */
476                                        default:
477                                                p = cmmp;
478                                                break;
479                                }
480                                #else
481                                p = cmmp;
482                                #endif
483
484                                if (p->parse(bufcp, &lb) == 0)
485                                {
486                                        /* record which file the log is from */
487                                        lb.fileid = fd;
488                               
489                                        /* insert into circle */
490                                        c->insert(lb);
491
492                                        /* record stats */
493                                        recordstats(lb);
494                                }
495                                // next line..
496
497                        } /* while ((nl - buf) < buflen && nl) */
498                } /* for(evptr = ev ; evptr->filter ; evptr++) */
499                  /* while (FAMPending(&fc) == 1 && FAMNextEvent(&fc, &fe)) */
500                  /* for (i = 0 ; i < MAX_INPUT_FILES ; ++i) */
501
502                /* check for keypresses */
503                if ((ch = wgetch(win)) != ERR)
504                        read_key(ch);
505
506        } /* for( ;; ) */
507
508        endwin();
509
510        return 0;
511}
512
513int recordstats(struct logbits l) /* {{{ */
514{
515        int t;
516
517        /* global stats */
518        if (gstats.alltime.first == 0) gstats.alltime.first = now;
519        gstats.alltime.last = now;
520        gstats.alltime.reqcount++;
521        gstats.alltime.bytecount += l.bytes;
522
523        /* for this return code, increment */
524        t = (int)(l.retcode/100);
525        gstats.r_codes[t].reqcount++;
526        gstats.r_codes[t].bytecount += l.bytes;
527
528        return 0;
529} /* }}} */
530
531int read_key(int ch) /* {{{ */
532{
533#define SUBMENU_SORT_HB_TITLE "sort by.."
534#define SUBMENU_SORT_HB_BANNER "r) REQUESTS  R) REQS/SEC  b) BYTES  B) BYTES/SEC"
535#define SUBMENU_SORT_RC_TITLE "sort by.."
536#define SUBMENU_SORT_RC_BANNER "2) 2xx   3) 3xx   4) 4xx   5) 5xx"
537
538#define SUBMENU_DISP_TITLE "toggle subdisplay.."
539#define SUBMENU_DISP_BANNER "u) URLS  r) REFERRERS  h) HOSTS"
540
541#define SUBMENU_FILT_TITLE "filters.."
542#define SUBMENU_FILT_BANNER "a) add/edit menu  c) clear all  s) show active"
543
544#define SUBMENU_FILT_ADD_TITLE "filters: add.."
545#define SUBMENU_FILT_ADD_BANNER "u) to URLS  r) to REFERRERS  h) to HOSTS"
546
547        /* got a keypress, best process it */
548
549        /* check submenu state; in no submenu we do the master list.. */
550        if (cf.in_submenu == SUBMENU_NONE) /* {{{ */
551            switch(ch)
552        {
553                case 's': /* sort .. submenus */
554                        cf.in_submenu_time = now;
555                        cf.in_submenu_stay = false;
556
557                        switch(cf.numbers_mode)
558                        {
559                                default:
560                                case NUMBERS_HITS_BYTES:
561                                        cf.in_submenu = SUBMENU_SORT_HB;
562                                        /* display banner to aid further
563                                         * presses */
564                                        display_submenu_banner(
565                                            SUBMENU_SORT_HB_TITLE,
566                                            sizeof(SUBMENU_SORT_HB_TITLE),
567                                            SUBMENU_SORT_HB_BANNER);
568                                        break;
569
570                                case NUMBERS_RETCODES:
571                                        cf.in_submenu = SUBMENU_SORT_RC;
572                                        /* display banner to aid further
573                                         * presses */
574                                        display_submenu_banner(
575                                            SUBMENU_SORT_RC_TITLE,
576                                            sizeof(SUBMENU_SORT_RC_TITLE),
577                                            SUBMENU_SORT_RC_BANNER);
578                                        break;
579                        }
580
581                        break;
582
583                case 't': /* toggle displays */
584                        cf.in_submenu = SUBMENU_DISP;
585                        cf.in_submenu_time = now;
586                        cf.in_submenu_stay = false;
587                       
588                        /* display banner to aid further presses */
589                        display_submenu_banner(SUBMENU_DISP_TITLE,
590                            sizeof(SUBMENU_DISP_TITLE), SUBMENU_DISP_BANNER);
591                        break;
592
593                case 'f': /* filter submenu */
594                        cf.in_submenu = SUBMENU_FILT;
595                        cf.in_submenu_time = now;
596                        cf.in_submenu_stay = false;
597
598                        /* display banner to aid further presses */
599                        display_submenu_banner(SUBMENU_FILT_TITLE,
600                            sizeof(SUBMENU_FILT_TITLE), SUBMENU_FILT_BANNER);
601
602                        break;
603
604                case 'h': /* enter help; this is a special submenu */
605                case '?':
606                        cf.in_submenu = SUBMENU_HELP;
607                        cf.in_submenu_time = now;
608
609                        /* stay in here till next keypress */
610                        cf.in_submenu_stay = true;
611
612                        /* don't blat it */
613                        cf.display_paused = true;
614                       
615                        display_help();
616
617                        break;
618
619                case 'p': /* (un)pause display */
620                        cf.display_paused = !cf.display_paused;
621                        if (cf.display_paused)
622                        {
623                                /* tell the user we're paused */
624                                DRAW_PAUSED(0,60); /* macro in display.h */
625                                refresh();
626                        }
627                        else
628                        {
629                                /* turning off pause forces an update */
630                                cf.do_immed_display = true;
631                        }
632                        break;
633       
634                case ' ':
635                        cf.do_immed_display = true;
636                        break;
637
638#if 0
639                case 's': /* new refresh delay */
640#ifndef SOLARIS /* linking against readline is failing on Solaris atm */
641                        endwin();
642                        char *t;
643                        int nd;
644                        t = readline("Seconds to Delay: ");
645                        if ((nd = atoi(t)) != 0)
646                                cf.refresh_delay = nd;
647
648                        refresh();
649#endif
650                        break;
651#endif
652                case 'd': /* display mode; urls or hosts */
653                        switch(cf.display_mode)
654                        {
655                                case DISPLAY_URLS:
656                                        cf.display_mode = DISPLAY_HOSTS;
657                                        break;
658                                case DISPLAY_HOSTS:
659                                        cf.display_mode = DISPLAY_REFS;
660                                        break;
661                                case DISPLAY_REFS:
662                                        cf.display_mode = DISPLAY_URLS;
663                                        break;
664                        }
665                        cf.do_immed_display = true;
666                        break;
667
668                case 'n': /* numbers mode; hits/bytes or returncodes */
669                        switch(cf.numbers_mode)
670                        {
671                                case NUMBERS_HITS_BYTES:
672                                        cf.numbers_mode = NUMBERS_RETCODES;
673                                        break;
674                                case NUMBERS_RETCODES:
675                                        cf.numbers_mode = NUMBERS_HITS_BYTES;
676                                        break;
677                        }
678                        cf.do_immed_display = true;
679                        break;
680
681                case KEY_UP:
682                        if (cf.display_mode != DISPLAY_DETAIL)
683                        {
684                                /* sanity checking for this is done
685                                 * in drawMarker() */
686                                cf.selected_item_screen--;
687                                drawMarker();
688                                translate_screen_to_pos();
689                        }
690                        break;
691#if 0
692                case KEY_END:
693                case KEY_NPAGE:
694                        if (cf.display_mode != DISPLAY_DETAIL)
695                        {
696                                cf.selected_item_screen = 9999;
697                                drawMarker();
698                                translate_screen_to_pos();
699                        }
700                        break;
701#endif
702                case KEY_DOWN:
703                        if (cf.display_mode != DISPLAY_DETAIL)
704                        {
705                                /* sanity checking for this is done
706                                 * in drawMarker() */
707                                cf.selected_item_screen++;
708                                drawMarker();
709                                translate_screen_to_pos();
710                        }
711                        break;
712
713                case KEY_RIGHT:
714                        if (cf.display_mode != DISPLAY_DETAIL)
715                        {
716                                /* enter detailed display mode */
717                                cf.display_mode_detail = cf.display_mode;
718                                cf.display_mode = DISPLAY_DETAIL;
719                                cf.do_immed_display = true;
720                        }
721                        break;
722
723                case KEY_LEFT:
724                        if (cf.display_mode == DISPLAY_DETAIL)
725                        {
726                                /* leave detailed display mode */
727                                cf.display_mode = cf.display_mode_detail;
728                                cf.do_immed_display = true;
729                        }
730                        break;
731               
732                case KEY_REFRESH:
733                        refresh();
734                        break;
735
736                case KEY_BREAK:
737                case 'q':
738                        cf.exit = true;
739                        break;
740        } /* }}} */
741
742        else if (cf.in_submenu == SUBMENU_SORT_HB) /* {{{ */
743        {
744                switch(ch)
745                {
746                        case 'r':
747                                cf.sort = SORT_REQCOUNT;
748                                break;
749
750                        case 'R':
751                                cf.sort = SORT_REQPERSEC;
752                                break;
753
754                        case 'b':
755                                cf.sort = SORT_BYTECOUNT;
756                                break;
757
758                        case 'B':
759                                cf.sort = SORT_BYTESPERSEC;
760                                break;
761
762                }
763                cf.in_submenu = SUBMENU_NONE;
764                clear_submenu_banner();
765                cf.do_immed_display = true;
766        } /* }}} */
767        else if (cf.in_submenu == SUBMENU_SORT_RC) /* {{{ */
768        {
769                switch(ch)
770                {
771                        case '2':
772                                cf.retcodes_sort = SORT_2XX;
773                                break;
774                        case '3':
775                                cf.retcodes_sort = SORT_3XX;
776                                break;
777                        case '4':
778                                cf.retcodes_sort = SORT_4XX;
779                                break;
780                        case '5':
781                                cf.retcodes_sort = SORT_5XX;
782                                break;
783                }
784                cf.in_submenu = SUBMENU_NONE;
785                clear_submenu_banner();
786                cf.do_immed_display = true;
787        } /* }}} */
788
789        else if (cf.in_submenu == SUBMENU_DISP) /* {{{ */
790        {
791                switch(ch)
792                {
793                        case 'u':
794                                cf.detail_display_urls = !cf.detail_display_urls;
795                                break;
796
797                        case 'r':
798                                cf.detail_display_refs = !cf.detail_display_refs;
799                                break;
800
801                        case 'h':
802                        case 'i':
803                                cf.detail_display_hosts = !cf.detail_display_hosts;
804                                break;
805               
806                }
807                cf.in_submenu = SUBMENU_NONE;
808                clear_submenu_banner();
809                cf.do_immed_display = true;
810        } /* }}} */
811
812        else if (cf.in_submenu == SUBMENU_FILT) /* {{{ */
813            switch(ch)
814        {
815                default: /* default action is to back out */
816                        cf.in_submenu = SUBMENU_NONE;
817                        clear_submenu_banner();
818                        cf.do_immed_display = true;
819                        break;
820
821                case 'a': /* add filter.. */
822                        /* clean away existing menu */
823                        clear_submenu_banner();
824
825                        cf.in_submenu = SUBMENU_FILT_ADD;
826                        cf.in_submenu_time = now;
827                        cf.in_submenu_stay = false;
828
829                        /* display banner to aid further presses */
830                        display_submenu_banner(SUBMENU_FILT_ADD_TITLE,
831                            sizeof(SUBMENU_FILT_ADD_TITLE),
832                            SUBMENU_FILT_ADD_BANNER);
833
834                        break;
835
836                case 'c': /* clear all filters */
837                        cf.urlfilter->empty();
838                        cf.hostfilter->empty();
839                        cf.reffilter->empty();
840
841                        cf.in_submenu = SUBMENU_NONE;
842                        clear_submenu_banner();
843                        cf.do_immed_display = true;
844                        break;
845
846                case 's': /* show filter page */
847
848                        cf.in_submenu = SUBMENU_FILT_SHOW;
849                        cf.in_submenu_time = now;
850
851                        /* stay in here till next keypress */
852                        cf.in_submenu_stay = true;
853
854                        /* don't blat it */
855                        cf.display_paused = true;
856                       
857                        /* and render the page; similar to display_help();
858                         * this calls functions in the Filter class to do
859                         * its work */
860                        display_active_filters();
861
862                       
863                        break;
864
865        } /* }}} */
866        else if (cf.in_submenu == SUBMENU_FILT_ADD) /* {{{ */
867        {
868                char *input = NULL;
869
870                /* check the keypress was valid .. */
871                switch(ch)
872                {
873                        case 'u': case 'r': case 'h':
874                                /* it was */
875                                break;
876               
877                        default:
878                                cf.in_submenu = SUBMENU_NONE;
879                                cf.in_submenu_stay = false;
880                                clear_submenu_banner();
881                                cf.do_immed_display = true;
882                                return 0;
883                                break;
884                }
885
886                /* get an expression */
887
888                /* do not leave until readline is done */
889                cf.in_submenu_stay = true;
890                /* do not update the display, because we're endwin()'ed */
891                cf.display_paused = true;
892
893                endwin();
894
895                input = readline("Filter: ");
896
897                /* back into curses mode */
898                refresh();
899                nonl(); /* no NL->CR/NL on output */
900                cbreak(); /* enable reading chars one at a time */
901                noecho();
902
903                /* update again */
904                cf.in_submenu = SUBMENU_NONE;
905                cf.in_submenu_stay = false;
906                cf.display_paused = false;
907                cf.do_immed_display = true;
908
909                if (!(input && *input))
910                {
911                        return 0;
912                }
913
914                add_history(input);
915
916                /* apply to the appropriate filter */
917                switch(ch)
918                {
919                        case 'u':
920                                cf.urlfilter->store(input);
921                                break;
922
923                        case 'h':
924                                cf.hostfilter->store(input);
925                                break;
926
927                        case 'r':
928                                cf.reffilter->store(input);
929                                break;
930                }
931                free(input);
932
933        } /* }}} */
934
935        /* special submenus which take over the entire screen */
936        else if (cf.in_submenu == SUBMENU_HELP ||
937                 cf.in_submenu == SUBMENU_FILT_SHOW)
938        /* {{{ */
939        {
940                /* any key in these special "submenus" exits them */
941                cf.in_submenu = SUBMENU_NONE;
942                cf.in_submenu_stay = false;
943
944                cf.display_paused = false;
945                cf.do_immed_display = true;
946        }
947        /* }}} */
948
949        return 0;
950} /* }}} */
951
952
953int new_file(char *filename, bool do_seek_to_end) /* {{{ */
954/* opens the filename supplied,
955 * and fills in the struct input passed by reference
956*/
957{
958        int i, fd, input_element = -1;
959        struct stat sb;
960        struct input *this_file;
961        char realfile[MAXPATHLEN];
962
963        /* if realpath cannot resolve the file, give up */
964        if ((realpath(filename, realfile) == NULL))
965                return -1;
966
967#if (POLLING_METHOD == USING_KQUEUE)
968        struct kevent kev;
969#endif
970
971        /* stat the item; ensure it's a file and get an inode */
972        if (stat(realfile, &sb) == -1)
973                return -1;
974
975        /* we can't tail anything except a file */
976//      if (sb.st_mode != S_IFREG)
977//              return -1;
978
979        /* choose where to put this input in the struct */
980        /* if it has an existing slot, re-use */
981        for(i = 0 ; i < cf.input_count ; ++i)
982        {
983                if ((strcmp(in[i].filename, filename)) == 0)
984                {
985                        input_element = i;
986                        break;
987                }
988        }
989
990        if (input_element == -1)
991        {
992                /* new open, make sure we have room in our arrays */
993                if (cf.input_count == MAX_INPUT_FILES)
994                {
995                        DIE_N("Only 50 files are supported at the moment");
996                }
997
998                /* add it on at the end */
999                input_element = cf.input_count;
1000        }
1001
1002        if ((fd = open(realfile, O_RDONLY)) == -1)
1003                return -1;
1004
1005        this_file = &in[input_element];
1006        this_file->fd = fd;
1007
1008        if (do_seek_to_end) lseek(fd, 0, SEEK_END);
1009
1010        if (this_file->filename) free(this_file->filename);
1011
1012        this_file->inode = sb.st_ino;
1013        this_file->filename = strdup(realfile);
1014        this_file->lastreq = now;
1015        this_file->type = LOG_COMMON; /* assumption */
1016        this_file->open = true;
1017
1018#if (POLLING_METHOD == USING_KQUEUE)
1019        /* add into kqueue */
1020#ifdef __NetBSD__
1021        EV_SET(&kev, fd, EVFILT_VNODE,
1022            EV_ADD | EV_ENABLE | EV_CLEAR,
1023            NOTE_WRITE | NOTE_DELETE | NOTE_RENAME, 0, (intptr_t)this_file);
1024#else
1025        EV_SET(&kev, fd, EVFILT_VNODE,
1026            EV_ADD | EV_ENABLE | EV_CLEAR,
1027            NOTE_WRITE | NOTE_DELETE | NOTE_RENAME, 0, this_file);
1028#endif
1029        if (kevent(kq, &kev, 1, NULL, 0, NULL) < 0)
1030        {
1031                DIE("cannot create kevent");
1032        }
1033#elif (POLLING_METHOD == USING_FAM)
1034        /* add into FAM */
1035        FAMMonitorFile(&fc, realfile, &this_file->famreq, this_file);
1036#endif
1037
1038        return 0;
1039} /* }}} */
1040
1041void usage(void) /* {{{ */
1042{
1043        fprintf(stderr,
1044            "ApacheTop v%s - Usage:\n"
1045            "File options:\n"
1046            "  -f logfile  open logfile (assumed common/combined) [%s]\n"
1047            "              (repeat option for more than one source)\n"
1048            "\n"
1049            "URL/host/referrer munging options:\n"
1050            "  -q          keep query strings [%s]\n"
1051            "  -l          lowercase all URLs [%s]\n"
1052            "  -s num      keep num path segments of URL [all]\n"
1053            "  -p          preserve protocol at front of referrers [%s]\n"
1054            "  -r          resolve hostnames/IPs into each other [%s]\n"
1055            "\n"
1056            "Stats options:\n"
1057            "  Supply up to one of the following two. default: [-%c %d]\n"
1058            "  -H hits     remember stats for this many hits\n"
1059            "  -T secs     remember stats for this many seconds\n"
1060            "\n"
1061            "  -d secs     refresh delay in seconds [%d]\n"
1062            "\n"
1063            "  -h          this help\n"
1064            "\n"
1065            "Compile Options: %cHAVE_KQUEUE %cHAVE_FAM %cENABLE_PCRE\n"
1066            "Polling Method: %s\n"
1067            ,
1068            PACKAGE_VERSION,
1069            DEFAULT_LOGFILE,
1070            /* cf is set by the time we get to usage, so we can use contents */
1071            cf.keep_querystring ? "yes" : "no",
1072            cf.lowercase_urls ? "yes" : "no",
1073            cf.preserve_ref_protocol ? "yes" : "no",
1074            cf.do_resolving ? "yes" : "no",
1075            cf.circle_mode, cf.circle_size,
1076            cf.refresh_delay,
1077#if HAVE_KQUEUE /* {{{ */
1078            '+',
1079#else
1080            '-',
1081#endif /* }}} */
1082#if HAVE_FAM_H /* {{{ */
1083            '+',
1084#else
1085            '-',
1086#endif /* }}} */
1087#if HAVE_PCRE_H /* {{{ */
1088            '+',
1089#else
1090            '-',
1091#endif /* }}} */
1092            (POLLING_METHOD == USING_KQUEUE ? "kqueue" :
1093                (POLLING_METHOD == USING_FAM ? "fam" :
1094                    "stat"
1095                )
1096            )
1097           
1098            );
1099
1100        return;
1101} /* }}} */
1102
1103int dprintf(const char *fmt, ...) /* {{{ */
1104{
1105        FILE *d;
1106        va_list args;
1107        static char fileName[1024] = {'\0'};
1108
1109        if ( !strlen( fileName ) )
1110        {
1111                strcpy( fileName, "/tmp/atop.XXXXXX" );
1112                mkdtemp( fileName );
1113                strncat( fileName, "/debug", sizeof(fileName ) );
1114        }
1115
1116        if (cf.debug && (d = fopen(fileName, "a")))
1117        {
1118                va_start(args, fmt);
1119                vfprintf(d, fmt, args);
1120                fclose(d);
1121                va_end(args);
1122        }
1123
1124        return 0;
1125} /* }}} */
1126
1127static void catchsig(int s) /* {{{ */
1128{
1129        cf.exit = s;
1130} /* }}} */
1131
1132/* handle a window resize by simply reopening and redrawing our window */
1133static void catchwinch(int s)
1134{
1135        endwin();
1136        refresh();
1137        cf.do_immed_display = true;
1138}
Note: See TracBrowser for help on using the browser.