/* * Copyright (c) 1996-2007, OpenFWTK Development Group * All rights reserved. See LICENSE. */ /* squid-top.c */ /* Copyright 1997-2000 by Eberhard Mattes Donated to the public domain. No warranty. 1997-08-26 Initial version 1999-07-02 Add options -c, -d, and -t; ignore case of request method 2000-07-07 Use instead of */ #include #include #include #include #include #include #include #include "libemfw.h" #include "emio.h" #include "squid-gw.h" #include "tables.h" extern size_t strlcat(char*,const char*,size_t); extern size_t strlcpy(char*,const char*,size_t); #define ISDIGIT(c) ((c) >= '0' && (c) <= '9') struct entry { struct entry *next; /* Hash bucket */ char *name; /* host / content type / client */ long port; long count; long bytes; }; #define HASH_SIZE 997 static struct entry *hash_table[HASH_SIZE]; static long entry_count; static int max_entry_length; static int by_requests; static int by_bytes; static enum { ST_DEST, ST_CLIENT, ST_CTYPE } stat_type; static void usage (void) { puts ("Usage: squid-top [-c|-d|-t] [-b] [-r] -n "); exit (1); } static void init (void) { int i; for (i = 0; i < HASH_SIZE; ++i) hash_table[i] = NULL; } static void add (const char *name, int name_len, long port, long bytes) { struct entry *p; unsigned h; h = hash (name, name_len, HASH_SIZE); for (p = hash_table[h]; p != NULL; p = p->next) if (p->port == port && strncmp (p->name, name, name_len) == 0 && p->name[name_len] == 0) break; if (p == NULL) { int len; p = xmalloc (sizeof (struct entry)); p->next = hash_table[h]; hash_table[h] = p; p->name = xstrndup (name, name_len); p->port = port; p->count = 0; p->bytes = 0; ++entry_count; len = name_len + 1; if (stat_type == ST_DEST) { char tmp[100]; snprintf (tmp, 99, "%ld", port); len += strlen (tmp); /* Don't trust sprintf()'s return value */ } if (len > max_entry_length) max_entry_length = len; } ++p->count; p->bytes += bytes; } static void load (void) { static char buf[65536]; size_t len; char *s, *start, *name; struct url u; int c, nn, name_len; long bytes, port = 0; while (fgets (buf, sizeof (buf), stdin) != NULL) { len = strlen (buf); if (len == 0 || buf[len-1] != '\n') { do { c = getchar (); } while (c != EOF && c != '\n'); if (c == EOF) break; continue; } buf[len-1] = 0; name = NULL; name_len = 0; /* HOST/IP-ADDR */ s = strchr (buf, '\t'); if (s == NULL) continue; if (stat_type == ST_CLIENT) { name = buf; name_len = s - buf; } ++s; /* Request method */ if (strncasecmp (s, "GET ", 4) == 0) s += 4; else if (strncasecmp (s, "POST ", 5) == 0 || strncasecmp (s, "HEAD ", 5) == 0) s += 5; else continue; /* URL */ while (*s == ' ') ++s; start = s; while (*s != 0 && *s != ' ') ++s; if (url_parse (&u, (octet*) start, s - start, 0) != 0) continue; if (u.host_length == 0 || u.port == -1) continue; if (stat_type == ST_DEST) { name = start + u.host_start; name_len = u.host_length; port = u.port; } while (*s == ' ') ++s; if (strncasecmp (s, "HTTP/", 5) != 0) continue; /* HTTP/x.y */ s = strchr (s, '\t'); if (s == NULL) continue; ++s; /* Content-Type */ start = s; s = strchr (s, '\t'); if (s == NULL) continue; if (stat_type == ST_CTYPE) { char *sep; name = start; name_len = s - start; sep = memchr (name, ';', name_len); if (sep != NULL) name_len = sep - start; } ++s; /* Statistics */ nn = 1; while (nn != 0 && (*s == ' ' || ISDIGIT (*s))) { if (*s == ' ') --nn; ++s; } bytes = 0; if (nn == 0 && ISDIGIT (*s)) while (ISDIGIT (*s)) bytes = bytes * 10 + *s++ - '0'; /* Insert into hash table. */ if (name == NULL) abort(); lower_copy (name, name, name_len); add (name, name_len, port, bytes); } } /* Heapsort. */ static void sift (const struct entry **tab, int left, int right, int (*cmp)(const struct entry *, const struct entry *)) { int i, j; const struct entry *p; i = left; j = 2 * i + 1; p = tab[i]; while (j <= right) { if (j < right && cmp (tab[j], tab[j+1]) < 0) ++j; if (cmp (p, tab[j]) >= 0) break; tab[i] = tab[j]; i = j; j = 2 * i + 1; } tab[i] = p; } static void sort_and_report (int (*cmp)(const struct entry *, const struct entry *), long n) { unsigned h; int left, right, i; const struct entry *p; const struct entry **tab; char *name; tab = xmalloc (entry_count * sizeof (struct entry *)); name = xmalloc (max_entry_length + 1); i = 0; for (h = 0; h < HASH_SIZE; ++h) for (p = hash_table[h]; p != NULL; p = p->next) tab[i++] = p; if (i != entry_count) abort (); left = i / 2; right = i - 1; while (left > 0) { --left; sift (tab, left, right, cmp); } while (right >= 0 && n > 0) { p = tab[0]; if (stat_type == ST_DEST) snprintf (name, max_entry_length, "%s:%ld", p->name, p->port); else strlcpy (name, p->name, max_entry_length); /* TODO: don't copy */ if (strlen (name) > max_entry_length) abort (); printf ("%-*s %10ld %10ld\n", max_entry_length, name, p->count, p->bytes); p = tab[left]; tab[left] = tab[right]; tab[right] = p; --right; --n; if (right > 0 && n > 0) sift (tab, left, right, cmp); } free (tab); free (name); } static int cmp_count (const struct entry *h1, const struct entry *h2) { if (h1->count < h2->count) return -1; else if (h1->count > h2->count) return 1; else if (h1->bytes < h2->bytes) return -1; else if (h1->bytes > h2->bytes) return 1; else return 0; } static int cmp_bytes (const struct entry *h1, const struct entry *h2) { if (h1->bytes < h2->bytes) return -1; else if (h1->bytes > h2->bytes) return 1; else if (h1->count < h2->count) return -1; else if (h1->count > h2->count) return 1; else return 0; } static void report (long n) { if (by_bytes) { printf ("Top %ld by number of bytes:\n\n", n); sort_and_report (cmp_bytes, n); } if (by_requests) { if (by_bytes) putchar ('\n'); printf ("Top %ld by number of requests:\n\n", n); sort_and_report (cmp_count, n); } } int main (int argc, char *argv[]) { long n = 0; char *end; int c; by_bytes = by_requests = 0; stat_type = ST_DEST; while ((c = getopt (argc, argv, "bcdn:rt")) != -1) switch (c) { case 'b': by_bytes = 1; break; case 'c': stat_type = ST_CLIENT; break; case 'd': stat_type = ST_DEST; break; case 'n': errno = 0; n = strtol (optarg, &end, 10); if (n < 1 || errno != 0 || *end != 0) usage (); break; case 'r': by_requests = 1; break; case 't': stat_type = ST_CTYPE; break; default: usage (); } if (optind != argc || n < 1) usage (); if (!by_bytes && !by_requests) by_bytes = by_requests = 1; init (); load (); report (n); return 0; }