uggworek@sunybcs.UUCP (Don Gworek) (06/22/85)
Here is a program which reports unusually large, or unusually old, mailboxes in /usr/spool/mail. When the user file system is under disc quota, the mail system can often be used as "secondary" storage. This program points out users with unusually large amounts of unread mail. The program will report for specific usernames, and sort either alphabetically, by mailbox size, or mailbox age. A quiet option supresses the top banner. # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". # # This archive contains: # cobwebs.1 cobwebs.c echo x - cobwebs.1 sed -e 's/^X//' > "cobwebs.1" << '//E*O*F cobwebs.1//' X.TH COBWEBS 1 "21 June 1985" X.SH NAME Xcobwebs X\- check for unusually large or old mailboxes X.SH SYNOPSIS X.B cobwebs X[ X-agq X] X[ X-d days X] X[ X-s size X] X[ Xusername ... X] X.PP X.SH DESCRIPTION X.I Cobwebs Xreports the size and age of mailboxes in /usr/spool/mail, Xplus the last login of the mailbox owners. The default Xis to report mailboxes which are quite large -- larger than Xthe default size, 20000 bytes. X.PP XWith the -d option, X.I cobwebs Xreports mailboxes that are unusually old -- Xthe default time is 30 days. X.I Cobwebs Xalso reports the mailbox status for usernames (command line Xor piped). And the program makes a general report of all Xmailboxes, with the -g option. X.SH OPTIONS X.PP X-a Sort alphabetically. Use this option with -d or -s. X.PP X-d Sort and/or search by age in days. The default is X30 days, or the number argument is used. X.PP X-g General report \-- status of all mailboxes. By Xdefault, this report is alphabetic. -d or -s will allow Xsort by age or size. X.PP X-q Quiet option \-- no top banner. X.PP X-s Sort and/or search by size in bytes. Default is X20000 bytes, or the number argument is used. X.SH FILES X/usr/spool/mail /etc/passwd /usr/adm/lastlog X.SH AUTHOR XDon Gworek //E*O*F cobwebs.1// ls -l cobwebs.1 echo x - cobwebs.c sed -e 's/^X//' > "cobwebs.c" << '//E*O*F cobwebs.c//' X/* X * cobwebs [-agq] [-d Days] [-s Size] [username ...] X * X * -a alphabetic sort X * -d sort and/or search by age (default DEF_DAYS) X * -g all mailboxes X * -q quiet option -- no top banner X * -s sort and/or search by size (default DEF_SIZE) X * X * Default: cobwebs -s X * X * In case of multiple flags: X * Sort priority -- alpha > days > size X * Search priority -- general > usernames > days > size X * X */ X X#include <sys/param.h> X#include <stdio.h> X#include <sys/dir.h> X#include <sys/stat.h> X#include <lastlog.h> X#include <pwd.h> X X#define DEF_SIZE 20000 /* default mbox size limit */ X#define DEF_DAYS 30 /* default mbox age limit */ X#define MAXSTRING 30 X#define MAXUSERNAMES 400 X#define MAXPACKS 1000 X#define TRUE 1 X#define FALSE 0 X X#define NMAX 10 X Xstruct packet X{ X char name[NMAX]; X off_t size; X time_t filetime; X}; X Xstruct packet *calloc (); Xstruct packet *packs[MAXPACKS]; Xstruct packet **packp; Xstruct stat stbuf; X Xchar *malloc (); Xchar usernames[MAXUSERNAMES][NMAX]; Xint num_unames = 0; /* number of usernames */ Xint now; Xint pack_count = 0; Xint mbox_size = DEF_SIZE; /* default mbox constraint */ Xint mbox_days = DEF_DAYS; /* default mailbox age */ Xlong int mbox_clock = 0; /* convert above to seconds */ Xlong int size_accum = 0; Xlong int age_diff_accum = 0; Xint mbox_total = 0; Xint rep_names = FALSE; /* name search flag */ Xint rep_alpha = FALSE; /* alphabetic sort flag */ Xint rep_gen = FALSE; /* all mailboxes flag */ Xint rep_size = FALSE; /* sort and/or search by size */ Xint rep_days = FALSE; /* sort and/or search by age */ Xint rep_quiet = FALSE; /* print the top banner */ X Xscmp (p, q) Xchar *p, *q; X{ X return (strcmp (p, q)); X} X Xmain (argc, argv) Xint argc; Xchar *argv[]; X{ X int i; X (void) time (&now); X while ((argc-- > 0) && (*(*++argv) == '-')) X switch (*(*argv + 1)) X { X case 's': X if (*(*argv + 2) != '\0') X str_to_int (*argv, 2, &mbox_size); X else X if ((argc>1) && (**(argv+1) >= '0') && (**(argv+1) <= '9')) X { X str_to_int (*++argv, 0, &mbox_size); X argc--; X } X rep_size = TRUE; X break; X case 'd': X if (*(*argv + 2) != '\0') X str_to_int (*argv, 2, &mbox_days); X else X if ((argc>1) && (**(argv+1) >= '0') && (**(argv+1) <= '9')) X { X str_to_int (*++argv, 0, &mbox_days); X argc--; X } X mbox_clock = now - 86400 * mbox_days; X rep_days = TRUE; X break; X default : X i = 1; X do X switch (*(*argv + i)) X { X case 'a' : rep_alpha = TRUE; break; X case 'g' : rep_gen = TRUE; break; X case 'q' : rep_quiet = TRUE; break; X default : usage (); X } X while (*(*argv + (++i)) != '\0'); X break; X } X if ((argc > 0) && !rep_gen) X { X while ((argc-- > 0) && (num_unames < MAXUSERNAMES)) X (void) strcpy (usernames[num_unames++], *argv++); X if (num_unames >= MAXUSERNAMES) X fatal ("Error: MAXUSERNAMES %d reached\n", MAXUSERNAMES); X rep_names = TRUE; X } X if (!isatty (0) && !rep_gen) X { X while (getname (usernames[num_unames++], NMAX) > 0); X if (num_unames >= MAXUSERNAMES) X fatal ("Error: MAXUSERNAMES %d reached\n", MAXUSERNAMES); X rep_names = TRUE; X } X if (rep_names) X { X qsort (usernames, num_unames, sizeof usernames[0], scmp); X if (!rep_size && !rep_days) X rep_alpha = TRUE; X } X else X if (rep_gen && !rep_days && !rep_size) X rep_alpha = TRUE; /* -g default */ X else X if (rep_days) X rep_size = FALSE; /* no conflict */ X else X rep_size = TRUE; /* default */ X check_mailboxes (); X} X Xsize_cmp (p, q) Xstruct packet **p, **q; X{ X if ((*p)->size == (*q)->size) X return (0); X if ((*p)->size > (*q)->size) X return (-1); X return (1); X} X Xalpha_cmp (p, q) Xstruct packet **p, **q; X{ X return (strcmp ((*p)->name, (*q)->name)); X} X Xfiletime_cmp (p, q) Xstruct packet **p, **q; X{ X if ((*p)->filetime == (*q)->filetime) X return (0); X if ((*p)->filetime > (*q)->filetime) X return (1); X return (-1); X} X Xcheck_mailboxes () X{ X DIR * etc; X struct direct *dp; X if (chdir ("/usr/spool/mail") < 0) { X perror ("/usr/spool/mail"); exit (1); X } X if ((etc = opendir (".")) == NULL) { X perror ("/usr/spool/mail"); exit (1); X } X packp = packs; X while (dp = readdir (etc)) X { X if (dp->d_ino == 0) X continue; X if (stat (dp->d_name, &stbuf) < 0) X continue; X if ((stbuf.st_mode & S_IFMT) == S_IFDIR) X continue; X if (pack_count >= MAXPACKS) X fatal ("%d MAXPACKS reached -- report aborted\n", MAXPACKS); X if (rep_names) X { X if (binsearch (dp->d_name)) X { X take_note (dp->d_name); X if (pack_count >= num_unames) X goto sortpacks; /* all names found */ X } X continue; X } X mbox_total++; X age_diff_accum += (stbuf.st_ctime - now); X size_accum += stbuf.st_size; X if ((rep_gen) X || (rep_days && (stbuf.st_ctime < mbox_clock)) X || (rep_size && (stbuf.st_size > mbox_size))) X take_note (dp->d_name); X } Xsortpacks: X if (rep_alpha) X qsort (packs, packp - packs, sizeof packs[0], alpha_cmp); X else X if (rep_days) X qsort (packs, packp - packs, sizeof packs[0], filetime_cmp); X else X qsort (packs, packp - packs, sizeof packs[0], size_cmp); X printout (); X} X Xtake_note (name) Xchar *name; X{ X pack_count++; X *packp = calloc (1, sizeof (struct packet)); X (void) strcpy ((*packp)->name, name); X (*packp)->size = stbuf.st_size; X (*packp)->filetime = stbuf.st_ctime; X packp++; X} X Xchar * Xage_is (period) Xlong int period; X{ X int days, hours; X char *string, temp[MAXSTRING]; X string = malloc (16); X *string = '\0'; X days = period / 86400; X hours = (period % 86400) / 3600; X switch (days) X { X case 0: X (void) sprintf (string, " "); X break; X case 1: X (void) sprintf (string, "%3d Day ", days); X break; X default: X (void) sprintf (string, "%3d Days ", days); X } X switch (hours) X { X case 0: X (void) sprintf (temp, "%2d Mins ", X (((period % 86400) % 3600) / 60)); X break; X case 1: X (void) sprintf (temp, "%2d Hour ", hours); X break; X default: X (void) sprintf (temp, "%2d Hours", hours); X break; X } X (void) strcat (string, temp); X return (string); X} X Xprintout () X{ X register struct packet **p; X int f; X struct passwd *pwd; X struct lastlog ll; X int i; X char *s, host[10]; X if ((f = open ("/usr/adm/lastlog", 0)) < 0) { X perror ("/usr/adm/lastlog"); exit (1); X } X if (!rep_quiet) X { X if (pack_count == 0) X { X printf ("No Report.\n"); X return; X } X for (i = 65; i-- > 0; putchar ('-')); X putchar ('\n'); X (void) gethostname (host, 10); X s = (char *) ctime (&now); X printf ("%10s %.12s ", host, (s + 4)); X if (pack_count == 1) X printf ("%d Mailbox", pack_count); X else X printf ("%d Mailboxes", pack_count); X if (rep_names && (num_unames != pack_count)) X printf (" Found For %d Names", num_unames); X else X if (!rep_gen && !rep_names) X { X if (rep_days) X printf (" Older Than %d Days", mbox_days); X else X if (rep_size) X printf (" Larger Than %d", mbox_size); X } X if (!rep_names) X printf ("\n\nTotal: %d Average Size: %d Average Age:%-16s", X mbox_total, (size_accum/mbox_total), X age_is ((age_diff_accum + now) / mbox_total)); X printf ("\n\n NAME MAILBOX SIZE"); X printf (" MAILBOX AGE LAST LOGIN\n"); X for (i = 65; i-- > 0; putchar ('-')); X putchar ('\n'); X } X for (p = packs; p < packp; p++) X { X printf ("%-8s", (*p)->name); X printf (" %6d ", (*p)->size); X printf ("%-16s", age_is (now - (*p)->filetime)); X if (((pwd = getpwnam ((*p)->name)) != NULL) X && (lseek (f, (long) pwd->pw_uid * sizeof (struct lastlog), 0) >= 0) X && (read (f, (char *) & ll, sizeof ll) == sizeof ll) X && (ll.ll_time <= 0)) X printf (" no login record\n"); X else X { X s = (char *) ctime (&ll.ll_time); X printf (" %.*s,%.*s\n", 24 - 18, (s + 4), 5, (s + 19)); X } X } X (void) close (f); X} X Xbinsearch (target) Xchar *target; X{ X int hi, lo, mid, val; X lo = 0; X hi = num_unames - 1; X while (TRUE) X if (hi < lo) X return (FALSE); X else X if ((val = strcmp (target, usernames[(mid = (lo + hi) / 2)])) == 0) X return (TRUE); X else X if (val > 0) X lo = mid + 1; X else X hi = mid - 1; X} X Xstr_to_int (string, offset, num) Xchar string[]; Xint offset, *num; X{ X int i, place, sum; X for (place = 1, sum = 0, i = strlen (string); i-- > offset; place *= 10) X { X if ((string [i] < '0') || (string [i] > '9')) X usage (); X else X sum = sum + (string [i] - '0') * place; X } X *num = sum; X} X Xgetname (line, lengthlim) Xchar line[]; Xint lengthlim; X{ X int i; X char ch; X for (i=0; i < lengthlim-1 && ((ch = getchar ()) != EOF) X && (ch != '\n') && (ch != ' ') && (ch != '\t');) X line[i++] = ch; X line[i] = '\0'; X return (i); X} X Xusage () X{ X fprintf (stderr, "Usage: cobwebs [-agq] [-d days] "); X fprintf (stderr, "[-s size] [username ...]\n"); X exit (1); X} X Xfatal (format, argument) Xchar *format, *argument; X{ X fprintf (stderr, format, argument); X exit (1); X} //E*O*F cobwebs.c// ls -l cobwebs.c exit 0