[comp.sources.misc] v17i007: space - summarize file system usage, Part01/01

fmc@cnam.cnam.fr (Frederic Chauveau) (02/20/91)

Submitted-by: Frederic Chauveau <fmc@cnam.cnam.fr>
Posting-number: Volume 17, Issue 7
Archive-name: space/part01

space is a tool which create and update data-bases to watch how much and
by whom disk space is used. It maintains a record of the used space on
a per day / per users / per file-system basis.

Frederic Chauveau <fmc@cnam.cnam.fr>

---- Cut Here and unpack ----
#!/bin/sh
# This is space, a shell archive (shar 3.32)
# made 02/13/1991 11:50 UTC by fmc@cnam.cnam.fr
# Source directory /users/labinf/fmc/tools/Space
#
# existing files will NOT be overwritten
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   9367 -rw-r--r-- space.c
#   4960 -rw-r--r-- sysdep.c
#   1649 -rw-r--r-- defs.h
#   3102 -rw-r--r-- Makefile
#   1534 -r--r--r-- space.8
#   4037 -rw-r--r-- INSTALL
#
if touch 2>&1 | fgrep 'amc' > /dev/null
 then TOUCH=touch
 else TOUCH=true
fi
# ============= space.c ==============
if test X"$1" != X"-c" -a -f 'space.c'; then
	echo "File already exists: skipping 'space.c'"
else
echo "x - extracting space.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > space.c &&
X/*
X * space	A tool to look out for greedy users
X * $Header: /users/labinf/fmc/tools/Space/RCS/space.c,v 2.4 91/01/24 15:14:18 fmc Exp $
X * $Source: /users/labinf/fmc/tools/Space/RCS/space.c,v $
X * $Locker:  $
X * $State: Exp $
X * $Log:	space.c,v $
X * Revision 2.4  91/01/24  15:14:18  fmc
X * Split System dependencies.
X * Fixed a bug in -tx options.
X * 
X * Revision 2.3  90/10/13  15:20:38  fmc
X * Fixed a bug with the -D option (sort by Delta-percentage)
X * Added a Startday compilation option.
X * Some mode (final?) cleanup.
X * 
X * Revision 2.2  90/09/01  11:06:13  fmc
X * added -d and -D option to sort user by delta or percentage
X * Some more (final) clean-up.
X * 
X * Revision 2.1  90/08/30  19:00:36  fmc
X * Do not log users occupying no space. Added -t option
X * to look through all file Systems.
X * 
X * Revision 2.0  90/08/28  18:57:26  fmc
X * Split the sums by file-systems. Some Clean Up
X * 
X * Revision 1.3  90/08/27  18:48:16  fmc
X * Various Bug Fix
X * 
X * Revision 1.2  90/08/10  16:35:22  fmc
X * Added -Ln option to have a sorted list with the last n days disk occupation.
X * 
X * Revision 1.1  90/08/02  13:47:16  fmc
X * Initial revision
X * 
X * 
X */
X
X#include "defs.h"
X
Xstatic char SortFlag = 0;
Xstatic char DataFile[128];
Xstatic RecP *Sorted;
Xstatic short StartDay = STARTDAY;
X
Xint SortAllRecs(r1, r2)
XSuprecP *r1, *r2; {
X  long v1, v2; 
X
X  switch (SortFlag) 
X    {
X    case TMAX: v1 = (*r1)->Fmax; v2 = (*r2)->Fmax; break;
X    case TMIN: v1 = (*r1)->Fmin; v2 = (*r2)->Fmin; break;
X    case CUR: v1 = (*r1)->Fcur; v2 = (*r2)->Fcur; break;
X    }
X  if (v1 == v2)
X    return 0;
X  return (v2 > v1) ? 1 : -1;
X}
X
Xint SortUsers(r1, r2)
XRecP *r1, *r2; {
X  long v1, v2;
X  v1 = (*r1)->blocks[CurrentDay];
X  v2 = (*r2)->blocks[CurrentDay];
X
X  if (v1 == v2)
X    return 0;
X  return (v2 > v1) ? 1 : -1;
X}
X
Xvoid GetRecords(fn)
Xchar *fn; {
X  unsigned int fd, i, j;
X  Record tmp;
X
X  sprintf(DataFile,"%s/space-%s.dat",DATADIR,fn);
X  fd = open(DataFile,0);
X  if (fd < 0) {
X    perror(DataFile);
X    if (log_file && (log_file != stderr))
X      fclose(log_file);
X    exit(1);
X  }
X
X  for (i = 0; i != MAXUSERS; i++)
X    if (WhatDidTheyDo[i]) {
X      free(WhatDidTheyDo[i]);
X      WhatDidTheyDo[i] = NULL;
X    }
X  while (read(fd, &tmp, sizeof(Record)) == sizeof(Record)) {
X    register unsigned long max = 0L, min = 500000L;
X    if (WhatDidTheyDo[tmp.uid])
X      fprintf(log_file,"Duplicate Record Entry for Uid %d\n",tmp.uid);
X    for (i = 0; i <= CurrentDay; i++) {
X      if (tmp.blocks[i] && (tmp.blocks[i] < min))
X	min = tmp.blocks[i];
X      if (tmp.blocks[i] > max) {
X	max = tmp.blocks[i];
X      }
X    }
X    tmp.dmax = max;
X    tmp.dmin = min;
X    WhatDidTheyDo[tmp.uid] = (RecP) malloc(sizeof(Record));
X    *(WhatDidTheyDo[tmp.uid]) = tmp;
X    MaxUsers++;
X  }
X  close(fd);
X}
X
Xvoid SaveRecords(fn)
Xchar *fn; {
X  unsigned int fd, i;
X
X  sprintf(DataFile,"%s/space-%s.dat",DATADIR,fn);
X  close(creat(DataFile,0777));
X  fd = open(DataFile,1);
X  if (fd < 0) {
X    perror(DataFile);
X    if (log_file && (log_file != stderr))
X      fclose(log_file);
X    exit(1);
X  }
X
X  for (i = 0; i != MAXUSERS; i++)
X    if (WhatDidTheyDo[i] && WhatDidTheyDo[i]->dmax)
X      write(fd, WhatDidTheyDo[i], sizeof(Record));
X  close(fd);
X}
X
Xvoid DispAllFs() {
X  register int i, j;
X
X  printf("  User   ");
X  for (i = 0; i!= MaxFs; i++)
X    printf("%7s",all_fs[i].dev);
X  puts("  Total");
X  for (i = 0; i != MaxAllUsers; i++) {
X    printf("%9s",AllRecs[i]->name);
X    for (j = 0; j != MaxFs; j++) {
X      unsigned long v;
X      v = (SortFlag == CUR) ? AllRecs[i]->current[j] :
X	(SortFlag == TMIN) ? AllRecs[i]->min[j] : AllRecs[i]->max[j];
X      printf("%7lu",v);
X    }
X    printf("%7lu\n",(SortFlag == CUR) ? AllRecs[i]->Fcur :
X	(SortFlag == TMIN) ? AllRecs[i]->Fmin : AllRecs[i]->Fmax);
X  }
X}
X
X
Xvoid LookRoom(fs)
X{
X  char dev[80];
X  register int i;
X  
X  sprintf(dev, "/dev/r%s", fs);
X  fprintf(log_file,"Checking %s\n",dev);
X  check(dev);
X  for (i = 0; i != MAXUSERS; i++) {
X    unsigned long j;
X
X    if (!WhatDidTheyDo[i])
X      continue;
X    j = WhatDidTheyDo[i]->blocks[CurrentDay];
X    WhatDidTheyDo[i]->current = j;
X    if (WhatDidTheyDo[i]->dmin > j)
X      WhatDidTheyDo[i]->dmin = j;
X    else if (WhatDidTheyDo[i]->dmax < j)
X      WhatDidTheyDo[i]->dmax = j;
X  }
X}
X
Xint SortDelta(r1, r2)
XRecP *r1, *r2; {
X  long v1, v2;
X  v1 = (*r1)->ddel;
X  v2 = (*r2)->ddel;
X
X  if (v1 == v2)
X    return 0;
X  return (v2 > v1) ? 1 : -1;
X}
X
Xint SortPerc(r1, r2)
XRecP *r1, *r2; {
X  double v1, v2;
X  v1 = (*r1)->dper;
X  v2 = (*r2)->dper;
X
X  if (v1 == v2)
X    return 0;
X  return (v2 > v1) ? 1 : -1;
X}
X
Xvoid LookDelta(min,mode) {
X  register int i, j;
X
X  if (min > CurrentDay) {
X    fprintf(log_file,"Cannot look more than %d days in the past\n",CurrentDay);
X    min=CurrentDay;
X  }
X  Sorted = (RecP *) malloc(MaxUsers * sizeof(RecP));
X  for (i = 0, j = 0; i != MAXUSERS; i++)
X    if (WhatDidTheyDo[i]) {
X      double f;
X      Sorted[j] = WhatDidTheyDo[i];
X      Sorted[j]->ddel = Sorted[j]->current - Sorted[j]->blocks[CurrentDay-min];
X      f = (double) Sorted[j]->blocks[CurrentDay-min];
X      if (f)
X	Sorted[j]->dper = (double) Sorted[j]->current / f;
X      else
X	Sorted[j]->dper = HUGE;
X      j++;
X    }
X  qsort(Sorted, MaxUsers, sizeof(RecP), (mode) ? SortDelta : SortPerc);
X  for (i = 0; i != MaxUsers; i++) {
X    if (!Sorted[i]->ddel)
X      continue;
X    printf("%8s\t%10lu\t%10lu",Sorted[i]->name,Sorted[i]->current,
X	   Sorted[i]->blocks[CurrentDay-min]);
X    if (mode) 
X      printf("\t%10ld\n",Sorted[i]->ddel);
X    else if (Sorted[i]->dper != HUGE)
X      printf("\t%10.6f%%\n",(Sorted[i]->dper - 1)*100);
X    else
X      printf("\t+ Infini \n");
X  }
X}
X
XInitWDTD() {
X  register short i;
X  FILE *pwd = fopen("/etc/passwd","r");
X  char buf[BUFSIZ], *p, *strchr();
X  int uid;
X  
X  while (fgets(buf,BUFSIZ,pwd)) {
X    if (!*buf || (*buf == '#'))
X      continue;
X    p = strchr(buf,':');
X    if (!p || !*p)
X      continue;
X    *p = '\0';
X    p = strchr(p+1,':');
X    uid = atoi(p+1);
X    if (uid < 0)
X      fprintf(log_file,"Skipping negative uid %d (%s)\n",uid,buf);
X    else if (WhatDidTheyDo[uid])
X      fprintf(log_file,"Duplicate entry uid %d (%s %s)\n",uid,buf,
X	      WhatDidTheyDo[uid]->name);
X    else {
X      WhatDidTheyDo[uid] = (RecP) malloc(sizeof(Record));
X      strncpy(WhatDidTheyDo[uid]->name,buf,8);
X      WhatDidTheyDo[uid]->uid = uid;
X      for (i = 0; i != TOKEEP; i++) {
X	WhatDidTheyDo[uid]->blocks[i] = 0L;
X      }
X      MaxUsers++;
X    }
X  }
X  fclose(pwd);
X}
X
Xvoid ShowUser(username)
Xchar *username; {
X  long first = 0L, min = 0L, max = 0L;
X  register int uid, i, j, k;
X  Record *r;
X
X  uid = usertouid(username);
X  r = WhatDidTheyDo[uid];
X  if (!r) {
X    fprintf(stderr, "No user %s [%d] on the device\n",username,uid);
X    return;
X  }
X  for (i = 0; i <= CurrentDay; i++) {
X    if (!r->blocks[i])
X      continue;
X    if (!first) {
X      first = r->blocks[i];
X      break;
X    }
X  }
X  printf("User %s (%d) is between %lu and %lu blocks (beginning at %lu)\n",
X	 r->name, r->uid, r->dmin, r->dmax, first);
X  if (CurrentDay > 32)
X    k = 32;
X  else
X    k = CurrentDay;
X  printf("Here are the last %d days :",k);
X  for (j = 0, i = CurrentDay - k; i != CurrentDay; i++, j++)
X    if (j & 07)
X      printf("%8lu ",r->blocks[i+1]);
X    else 
X      printf("\n%8lu ",r->blocks[i+1]);
X  puts("");
X}
X
Xint CurDay() {
X  time_t t;
X  struct tm *tt;
X
X  t = time((time_t *) 0);
X  tt = localtime(&t);
X  return (tt->tm_yday - StartDay);
X}
X
XShowList(n) {
X  register int i, j;
X
X  Sorted = (RecP *) malloc(MaxUsers * sizeof(RecP));
X  for (i = 0, j = 0; i != MAXUSERS; i++)
X    if (WhatDidTheyDo[i])
X      Sorted[j++] = WhatDidTheyDo[i];
X  qsort(Sorted, MaxUsers, sizeof(RecP), SortUsers);
X  if (n > CurrentDay)
X    n = CurrentDay;
X  for (i = 0; i != MaxUsers; i++) {
X    printf("%8s",Sorted[i]->name);
X    for (j = 0; j <= n; j++)
X      printf("%8lu",Sorted[i]->blocks[CurrentDay-j]);
X    puts("");
X  }
X}
X
Xvoid Usage() {
X  fprintf(stderr,"Usage: space -iul device\n");
X  fprintf(stderr,"   or  space -Ldays device\n");
X  fprintf(stderr,"   or  space -s device user1 user2 ...\n");
X  fprintf(stderr,"   or  space -t[cm-M]\n");
X  fprintf(stderr,"   or  space -[dD]days device\n");
X  if (log_file && log_file != stderr) {
X    fclose(log_file);
X  }
X  exit(1);
X}
X
Xmain(argc,argv)
Xchar **argv; {
X  char *fs;
X  ++argv; --argc;
X  log_file = fopen(LOGFILE,"a");
X  if (!log_file) {
X    perror(LOGFILE);
X    log_file = stderr;
X    fprintf(stderr,"Loggin informations on stderr\n");
X  }
X  if (argc && (**argv == '-')) {
X    CurrentDay = CurDay();
X    switch(argv[0][1]) {
X      case 'i':
X	InitWDTD(); 
X	LookRoom(argv[1]); 
X	SaveRecords(argv[1]);
X	exit();
X      case 'u':
X	GetRecords(argv[1]); 
X	LookRoom(argv[1]); 
X	SaveRecords(argv[1]);
X	break;
X      case 's':
X	GetRecords(argv[1]);
X	++argv; --argc;
X	while (++argv, --argc)
X	  ShowUser(*argv);
X	break;
X      case 'l':
X	GetRecords(argv[1]);
X	ShowList(0);
X	break;
X      case 'L':
X	GetRecords(argv[1]);
X	ShowList(atoi(*argv+2));
X	break;
X      case 'd':
X      case 'D':
X	GetRecords(argv[1]);
X	LookDelta(atoi(*argv + 2),argv[0][1] == 'd');
X	break;
X      case 't':
X	if (!argv[0][2] || argv[0][2] == 'c')
X	  SortFlag = CUR;
X	else if (argv[0][2] == 'm' || argv[0][2] == '-')
X	  SortFlag = TMIN;
X	else
X	  SortFlag = TMAX;
X	AllFileSystems(CurrentDay);
X	break;
X      default:
X	Usage();
X      }
X  }
X  else
X    Usage();
X  if (log_file && log_file != stderr)
X    fclose(log_file);
X}
SHAR_EOF
$TOUCH -am 0124154791 space.c &&
chmod 0644 space.c ||
echo "restore of space.c failed"
set `wc -c space.c`;Wc_c=$1
if test "$Wc_c" != "9367"; then
	echo original size 9367, current size $Wc_c
fi
fi
# ============= sysdep.c ==============
if test X"$1" != X"-c" -a -f 'sysdep.c'; then
	echo "File already exists: skipping 'sysdep.c'"
else
echo "x - extracting sysdep.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > sysdep.c &&
X/*
X * space	A tool to look out for greedy users
X * $Header: /users/labinf/fmc/tools/Space/RCS/sysdep.c,v 1.2 91/01/24 15:30:13 fmc Exp $
X * $Source: /users/labinf/fmc/tools/Space/RCS/sysdep.c,v $
X * $Locker:  $
X * $State: Exp $
X * $Log:	sysdep.c,v $
X * Revision 1.2  91/01/24  15:30:13  fmc
X * Added FSNAME define to check for non-local file-systems.
X * 
X * Revision 1.1  91/01/24  15:17:50  fmc
X * Initial revision
X * 
X */
X
X#include "defs.h"
X
XRecP WhatDidTheyDo[MAXUSERS];
XSuprecP TempRecs[MAXUSERS];
XSuprecP *AllRecs;
Xint MaxUsers = 0, MaxAllUsers = 0;
XFsName all_fs[MAXFS];
Xint MaxFs = 0;
X
XFILE *log_file;
Xshort CurrentDay;
X
X/*
X * User name to UID.
X */
X
Xint usertouid(user)
Xchar *user;
X{
X  register struct passwd *pw;
X  struct passwd *getpwent();
X  
X  setpwent();
X  while (pw = getpwent()) 
X    {
X      if (!strcmp(user,pw->pw_name)) {
X	endpwent();      
X	return (pw->pw_uid);
X      }
X    }
X  endpwent();
X  return -1;
X}
X
X/*
X * UID to user name
X */
X
Xchar *uidtouser(uid)
Xshort uid;
X{
X  register struct passwd *pw;
X  struct passwd *getpwent();
X  
X  setpwent();
X  while (pw = getpwent()) 
X    {
X      if (uid == pw->pw_uid) {
X	endpwent();      
X	return (pw->pw_name);
X      }
X    }
X  endpwent();
X  return NULL;
X}
X
X/*
X * Take inode ino/ip into account. ino (the current inode number) is used only
X * to log inodes which belongs to no known users.
X */
X
Xvoid acct(ip,ino)
Xregister struct dinode *ip;
Xint ino;
X{
X  int uid = ip->di_uid;
X
X  if ((ip->di_mode & IFMT) == 0)
X    return;
X  if ((uid >= 0) && (uid < MAXUSERS)) {
X    if (!WhatDidTheyDo[uid]) {
X      register int i;
X      char *n;
X      WhatDidTheyDo[uid] = (RecP) malloc(sizeof(Record));
X      WhatDidTheyDo[uid]->uid = uid;
X      n = uidtouser(uid);
X      if (n)
X	strcpy(WhatDidTheyDo[uid]->name,n);
X      else
X	sprintf(WhatDidTheyDo[uid]->name,"#%d",uid);
X      for (i = 0; i != CurrentDay; i++)
X	WhatDidTheyDo[uid]->blocks[i] = 0L;
X      MaxUsers++;
X    }
X    WhatDidTheyDo[uid]->blocks[CurrentDay] += ip->di_blocks / 2;
X    if (WhatDidTheyDo[uid]->name[0] == '#')
X      fprintf(log_file,"inode %d of %d blocks owned by unknown uid %d\n",
X	      ino,ip->di_blocks,uid);
X  }
X}
X
X/*
X * Block Read. Read block bno of size cnt from file fd into buf.
X */
X
Xvoid bread(fd, bno, buf, cnt)
Xunsigned bno;
Xchar *buf;
X{
X  
X  lseek(fd, (long)bno * DEV_BSIZE, L_SET);
X  if (read(fd, buf, cnt) != cnt) {
X    fprintf(log_file, "\tread error at block %u\n", bno);
X    if (log_file && log_file != stderr)
X      fclose(log_file);
X    exit(1);
X  }
X}
X
Xunion {
X  struct fs u_sblock;
X  char dummy[SBSIZE];
X} sb_un;
X#define sblock sb_un.u_sblock
X
Xstruct dinode itab[MAXBSIZE/sizeof(struct dinode)];
Xunsigned	ino;
X
X/*
X * Browse the file file_system. file should be the name of a block device
X * corresponding to a disk partition.
X */
X
Xvoid check(file)
Xchar *file;
X{
X  register int i, j, nfiles;
X  daddr_t iblk;
X  int c, fd;
X  
X  fd = open(file, O_RDONLY);
X  if (fd < 0) {
X    fprintf(log_file, "\tCan't access %s\n",file);
X    return;
X  }
X  sync();
X  bread(fd, SBLOCK, (char *)&sblock, SBSIZE);
X  nfiles = sblock.fs_ipg * sblock.fs_ncg;
X  for (ino = 0; ino < nfiles; ) {
X    iblk = fsbtodb(&sblock, itod(&sblock, ino));
X    bread(fd, iblk, (char *)itab, sblock.fs_bsize);
X    for (j = 0; j < INOPB(&sblock) && ino < nfiles; j++, ino++) {
X      if (ino < ROOTINO)
X	continue;
X      acct(&itab[j],ino);
X    }
X  }
X  close(fd);
X}
X
X/*
X * Browse though all file-systems (using getfsent()) and check all
X * valid file system (file system of type ufs_name). 
X */
X
Xvoid AllFileSystems(day)
Xshort day;
X{
X  register struct fstab *fs;
X  register char *cp;
X  char dev[80], *rindex();
X  FsName *fsp;
X  register int i, j;
X  
X  if (setfsent() == 0) {
X    fprintf(log_file, "space: no %s file\n", FSTAB);
X    exit(1);
X  }
X  while (fs = getfsent()) {
X
X    /*
X     * Make sure we can read the file system.
X     */
X
X#ifdef FSNAME
X    if (!strcmp(fs->fs_name,FSNAME))
X      continue;
X#endif
X    if (strcmp(fs->fs_type, FSTAB_RO) &&
X	strcmp(fs->fs_type, FSTAB_RW) &&
X	strcmp(fs->fs_type, FSTAB_RQ))
X      continue;
X    cp = rindex(fs->fs_spec, '/');
X    if (cp == 0)
X      continue;
X    fsp = all_fs + MaxFs;
X    strcpy(fsp->dev,++cp);
X    fsp->mount_point = (char *) malloc(strlen(fs->fs_file)+1);
X    strcpy(fsp->mount_point,fs->fs_file);
X    GetRecords(cp);
X    for (i = 0; i != MAXUSERS; i++) {
X      RecP rp = WhatDidTheyDo[i];
X      SuprecP sp = TempRecs[i];
X
X      if (!rp) 
X	continue;
X      if (!sp) {
X	sp = TempRecs[i] = (SuprecP) malloc(sizeof(Suprec));
X	sp->uid = rp->uid;
X	strcpy(sp->name,rp->name);
X	MaxAllUsers++;
X      }
X      sp->Fcur += sp->current[MaxFs] = rp->current;
X      sp->Fmin += sp->min[MaxFs] = rp->dmin;
X      sp->Fmax += sp->max[MaxFs] = rp->dmax;
X    }
X    MaxFs++;
X  }
X  endfsent();
X  AllRecs = (SuprecP *) malloc(MaxAllUsers * sizeof(SuprecP));
X  for (i = 0, j = 0; i != MAXUSERS; i++)
X    if (TempRecs[i])
X      AllRecs[j++] = TempRecs[i];
X  qsort(AllRecs,MaxAllUsers,sizeof(SuprecP),SortAllRecs);
X  DispAllFs();
X}
X
SHAR_EOF
$TOUCH -am 0124154791 sysdep.c &&
chmod 0644 sysdep.c ||
echo "restore of sysdep.c failed"
set `wc -c sysdep.c`;Wc_c=$1
if test "$Wc_c" != "4960"; then
	echo original size 4960, current size $Wc_c
fi
fi
# ============= defs.h ==============
if test X"$1" != X"-c" -a -f 'defs.h'; then
	echo "File already exists: skipping 'defs.h'"
else
echo "x - extracting defs.h (Text)"
sed 's/^X//' << 'SHAR_EOF' > defs.h &&
X/*
X * defs.h
X * $Header:
X * $Source:
X * $Locker:  $
X * $State: Exp $
X * $Log:	
X * 
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <signal.h>
X#include <ctype.h>
X#include <pwd.h>
X#include <fstab.h>
X#include <math.h>
X#include <sys/time.h>
X#include <sys/stat.h>
X#include <sys/param.h>
X#include <sys/file.h>
X#include <ufs/fs.h>
X#ifdef ULTRIX
X#include <sys/inode.h>
X#else
X#include <sys/vnode.h>
X#include <ufs/inode.h>
X#endif
X
X#ifndef TOKEEP
X#define TOKEEP 365
X#endif
X
X#define MAXUSERS 9999
X#define MAXFS 10
X
X#define CUR	0
X#define TMIN	1
X#define TMAX	2
X#define FCUR	3
X#define FMIN	4
X#define FMAX	5
X
Xtypedef unsigned long Blocks[TOKEEP];
X
Xtypedef struct {
X  char dev[5];
X  char *mount_point;
X} FsName;  
X
Xtypedef struct {
X  short uid;
X  char name[9];
X  unsigned long current[MAXFS], min[MAXFS], max[MAXFS];
X  unsigned long Fcur, Fmin, Fmax;
X} Suprec, *SuprecP;
X
Xtypedef struct {
X  short uid;
X  char name[9];
X  unsigned long current;
X  union {
X    unsigned long min;
X    long delta;
X  } delt;
X#define dmin delt.min
X#define ddel delt.delta
X  union {
X    unsigned long max;
X    double perc;
X  } perc;
X#define dmax perc.max
X#define dper perc.perc
X  Blocks blocks;
X} Record, *RecP;
X
X
X#ifndef DATADIR
X#define DATADIR "/usr/tmp"
X#endif
X
X#ifndef LOGFILE
X#define LOGFILE "/usr/tmp/space.log"
X#endif
X
X#ifndef STARTDAY
X#define STARTDAY 0
X#endif
X
Xextern FILE *log_file;
Xextern short CurrentDay;
Xextern short StartDay;
Xextern char SortFlag;
Xextern RecP WhatDidTheyDo[MAXUSERS];
Xextern RecP *Sorted;
Xextern SuprecP TempRecs[MAXUSERS];
Xextern SuprecP *AllRecs;
Xextern int MaxUsers, MaxAllUsers;
Xextern FsName all_fs[MAXFS];
Xextern int MaxFs;
X
Xint SortAllRecs();
X
SHAR_EOF
$TOUCH -am 0124154791 defs.h &&
chmod 0644 defs.h ||
echo "restore of defs.h failed"
set `wc -c defs.h`;Wc_c=$1
if test "$Wc_c" != "1649"; then
	echo original size 1649, current size $Wc_c
fi
fi
# ============= Makefile ==============
if test X"$1" != X"-c" -a -f 'Makefile'; then
	echo "File already exists: skipping 'Makefile'"
else
echo "x - extracting Makefile (Text)"
sed 's/^X//' << 'SHAR_EOF' > Makefile &&
X# $Header:
X# $Source:
X# $Locker:  $
X# $State: Exp $
X# $Log:	Makefile,v $
X# Revision 1.9  91/01/24  15:48:42  fmc
X# More fixes.
X# 
X# Revision 1.7  91/01/24  15:28:46  fmc
X# Added check for make depend.
X# 
X# Revision 1.6  91/01/24  15:16:42  fmc
X# moved the system dependent stuff in sysdep.c
X# 
X# Revision 1.5  90/09/01  11:05:52  fmc
X# Added install and clean target.
X# 
X# Revision 1.4  90/08/31  16:26:24  fmc
X# Added makedepend
X# 
X# Revision 1.3  90/08/31  16:19:10  fmc
X# More clean up
X# 
X# Revision 1.2  90/08/30  19:01:25  fmc
X# Some cleanup
X# 
X# Revision 1.1  90/08/08  17:53:12  fmc
X# Initial revision
X#
X
XDESTDIR=/users/labinf/fmc/bin
XCC=gcc
XDEBUGFLAGS= -g
XOPTFLAGS= -O -fwritable-strings -finline-functions
XCPPFLAGS=
X#
X# For some system you have to define FSNAME as a string 
X# with the type of local file-system. 
X# On Ultrix it should be :
X# SYSFLAGS= -DFSNAME=\"ufs\" -DULTRIX
XSYSFLAGS= -DFSNAME=\"ufs\" -DULTRIX -DDATADIR=\"/usr/src\"
XCFLAGS= $(DEBUGFLAGS) $(CPPFLAGS) $(OPTFLAGS) $(SYSFLAGS)
XLDFLAGS= $(DEBUGFLAGS)
XMISC= Makefile space.8 INSTALL
XSRCS= space.c sysdep.c defs.h
XOBJS= space.o sysdep.o
XMAKEDEPEND= makedepend
X
Xall:	space
X
Xspace: $(OBJS)
X	$(CC) $(CFLAGS) $(LDFLAGS) -o space $(OBJS) -lm
X
Xinstall: all
X	install space $(DESTDIR)
X
Xclean:
X	rm -f *~ #* *.o core space .depend
X
Xdepend: $(SRCS)
X	$(MAKEDEPEND) -- $(CPPFLAGS) $(SYSFLAGS) -- $(SRCS)
X
Xshar:	$(SRCS) $(MISC)
X	shar -n space -a -x -c -o space.shar $(SRCS) $(MISC)
X	
X
X# DO NOT DELETE THIS LINE -- make depend depends on it.
X
Xspace.o: defs.h /usr/include/stdio.h /usr/include/sys/types.h
Xspace.o: /usr/include/signal.h /usr/include/ctype.h /usr/include/pwd.h
Xspace.o: /usr/include/fstab.h /usr/local/lib/gcc-include/math.h
Xspace.o: /usr/include/sys/time.h /usr/include/sys/stat.h
Xspace.o: /usr/include/sys/param.h /usr/include/machine/param.h
Xspace.o: /usr/include/sys/smp_lock.h /usr/include/sys/file.h
Xspace.o: /usr/include/ufs/fs.h /usr/include/sys/inode.h
Xspace.o: /usr/include/sys/gnode_common.h /usr/include/ufs/ufs_inode.h
Xspace.o: /usr/include/sys/gnode.h
Xsysdep.o: defs.h /usr/include/stdio.h /usr/include/sys/types.h
Xsysdep.o: /usr/include/signal.h /usr/include/ctype.h /usr/include/pwd.h
Xsysdep.o: /usr/include/fstab.h /usr/local/lib/gcc-include/math.h
Xsysdep.o: /usr/include/sys/time.h /usr/include/sys/stat.h
Xsysdep.o: /usr/include/sys/param.h /usr/include/machine/param.h
Xsysdep.o: /usr/include/sys/smp_lock.h /usr/include/sys/file.h
Xsysdep.o: /usr/include/ufs/fs.h /usr/include/sys/inode.h
Xsysdep.o: /usr/include/sys/gnode_common.h /usr/include/ufs/ufs_inode.h
Xsysdep.o: /usr/include/sys/gnode.h
Xdefs.o: /usr/include/stdio.h /usr/include/sys/types.h /usr/include/signal.h
Xdefs.o: /usr/include/ctype.h /usr/include/pwd.h /usr/include/fstab.h
Xdefs.o: /usr/local/lib/gcc-include/math.h /usr/include/sys/time.h
Xdefs.o: /usr/include/sys/stat.h /usr/include/sys/param.h
Xdefs.o: /usr/include/machine/param.h /usr/include/sys/smp_lock.h
Xdefs.o: /usr/include/sys/file.h /usr/include/ufs/fs.h
Xdefs.o: /usr/include/sys/inode.h /usr/include/sys/gnode_common.h
Xdefs.o: /usr/include/ufs/ufs_inode.h /usr/include/sys/gnode.h
SHAR_EOF
$TOUCH -am 0213114991 Makefile &&
chmod 0644 Makefile ||
echo "restore of Makefile failed"
set `wc -c Makefile`;Wc_c=$1
if test "$Wc_c" != "3102"; then
	echo original size 3102, current size $Wc_c
fi
fi
# ============= space.8 ==============
if test X"$1" != X"-c" -a -f 'space.8'; then
	echo "File already exists: skipping 'space.8'"
else
echo "x - extracting space.8 (Text)"
sed 's/^X//' << 'SHAR_EOF' > space.8 &&
X.\" RCSID: @(#)space.8	2.3	11/10/90
X.TH space 8 
X.SH Name
Xspace \- summarize file system usage
X.SH Syntax
X.B /usr/local/bin/space
X[ option ] ...
X[ filesystem ]
X.SH Description
X.NXR "space command"
X.NXR "file system" "disk usage per user"
XThe
X.PN space
Xcommand maintains the disk usage for a file system on a user basis.
X.SH Options
X.TP
X.B \-i filesystem
XInitialize the data-base associated with the given file system. Only the super user can give this command.
X.TP
X.B \-u filesystem
XUpdate the database for the given file system. Onlu the super user can give this command.
X.TP
X.B \-s filesystem user1 user2 ... usern
XList the disk usage on the given filesystem for each users in arguments.
X.TP
X.B \-l filesystem
XList the current disk usage for the given file system, sorted by most greedy users.
X.TP
X.B \-Ln filesystem
XSame as option \-l but 
X.I n
Xdays in the past.
X.TP
X.B \-dn filesystem
XList the difference in blocks between current usage and usage 
X.I n
Xdays in the past. 
X.TP
X.B \-Dn filesystem
XList the difference in percentage between current usage and usage 
X.I n
Xdays in the past. 
X.TP
X.B \-tx
XSummarize the disk usage on the whole system, depending on the 
X.I x
Xletter. 
X.I c
Xgives the current usage.
X.I m
Xgives the minimum usage.
X.I M
Xgives the maximum usage.
X.SH Files
XFile system names varies with system.
X.br
X/etc/passwd			to get user names
X.br
X/usr/src/space-xxx.data		for accumulated data for filesystem xxx
X.SH See Also
Xdu(1), ls(1), quot(8)
X.SH Bugs
XCertainly a lot. If you find one, send a mail to fmc@cnam.cnam.fr
X
SHAR_EOF
$TOUCH -am 0213114791 space.8 &&
chmod 0444 space.8 ||
echo "restore of space.8 failed"
set `wc -c space.8`;Wc_c=$1
if test "$Wc_c" != "1534"; then
	echo original size 1534, current size $Wc_c
fi
fi
# ============= INSTALL ==============
if test X"$1" != X"-c" -a -f 'INSTALL'; then
	echo "File already exists: skipping 'INSTALL'"
else
echo "x - extracting INSTALL (Text)"
sed 's/^X//' << 'SHAR_EOF' > INSTALL &&
Xspace Version 1.9		Bugs, Gripes, etc... to fmc@cnam.cnam.fr
X				Last Modification:	12 Feb 91
X
Xspace is a maintenance tool used to watch disk-space usage on a per
Xuser per day basis. Be warned that you need :
X
X1/ root privilege to update the data-base (not to read it).
X
X2/ 1488 bytes per user per file-system, for each user UID possessing a
X   file on a file-system. Here we have around 300 users, 6
X   file-systems are checked, giving us 496 user/fs entries or
X   738048 bytes.
X
X3/ Right now, this usage is ... on a year by year basis. It means that
X     1/ Each year, you have to re-initialize the DB
X     2/ The starting point of the record keeping is hard-wired in the
X	code.
X	In fact, the rationale behind STARTDAY definition is not quite
X	clear. It will be used in the futur, but right now, it's only
X	use is to save some space (If you begin checking on the 1st Feb, 
X	#define STARTDAY 31 and #define TOKEEP 334).
X   In a (hopefully) near future, space will read a configuration file
X   to determine such parameters.
X
X4/ Finally, space haws only be tested on :
X
X    1/ A Vax running Ultrix (3.1 and now 4.0) using gcc
X    2/ A Sony MIPS running sony-os (3.3 and now 4.0) with mips-cc.
X
X   I think the code in sysdep.c is rather BSD-dependent, but I've no
X   SysV box to test it.
X
XHow to Compile.
X
XIn the best case do a make depend, followed by a make all and a make install.
X
XI think everything system dependent resides in sysdep.c and it's there
Xyou should check for trouble.
X
XCOMPILATION options.
X
XThe following #defines resides in defs.h change them if the default
Xvalue doesn't correspond to your needs/environment :
X
Xname		default value	meaning
XTOKEEP		365		How many days are stored.
XMAXUSERS	9999		Maximum number of users (should be enough:-)
XMAXFS		10		Maximum number of fs checked.
XDATADIR		"/usr/tmp"	Where to keep the DBs
XLOGFILE		"/usr/tmp/space.log"	The log file name.
XSTARTDAY	0		First Day in the record (from 1st Jan.)
XFSNAME		not defined	To check the fs type for local fs only.
XDESTDIR		/usr/local/bin	In the Makefile for make install.
X
XHow to Install.
X
Xmake install (hopefully). You then must :
X    1/ Initialize the DBs for each fs checked by :
X	  for fs in fs1 fs2 ... fsn ; do space -i $fs ; done
X       space -i fs1 will look for the /dev/rfs1 file.
X
X    2/ Make sure the DBs are updated once a day (there's no check
X       to see if the DBs were already updated or not.) by doing
X	  for fs in fs1 fs2 ... fsn ; do space -u $f ; done
X       The simplest way is to run it from cron, at night :-)
X
XHow to Use.
X
XThe most useful option are :
X
X -- space -l fs1
X    which will list, by decreasing order, all users with the disk space 
X    they are currently using on /dev/rfs1.
X
X -- space -dn fs1
X    which will list, by decreasing order, the variation of the disk space
X    used between 'n' days ago and today.
X
XOther options include :
X
X -- space -s fs1 user1 user2 ... userN
X    last 32 days space usage on /dev/rfs1 for each user name mentionned
X
X -- space -Ln fs1
X    last 'n' days space usage on dev/rfs1 for each users.
X
X -- space -Dn fs1
X    same as -dn but display percentage instead of difference.
X
X -- space -tx
X    display disk usage for everybody on every local fs in /etc/fstab.
X    the 'x' has the following meaning :
X	- space -tc or space -t		List Current Usage.
X	- space -tm or space -t-	List Minimum Usage.
X	- space -tM or space -t+	List Maximum Usage.
X    Really useful if all file-systems are checked.
X
X
XFutur modifications.
X
XThe main modification will be to introduce some sort of config. file 
Xcontaining all parameters wich are now #define'd.
X
XI also plane to introduce some sort of "time granularity" to enable
Xthe check to be done on a 1 hour basis or every 2 days, and so on.
X
XI also intend to add code to get ride of old users and extend the
Xlife time of the DBs for more than a year.
X
XAnd, of course, I'll try to fix bugs as they are reported. Space is 
Xrunning on our site for more than a 4 months now without much trouble,
Xbut it as only be tested on the configuration mentionned above.
X
SHAR_EOF
$TOUCH -am 0213114691 INSTALL &&
chmod 0644 INSTALL ||
echo "restore of INSTALL failed"
set `wc -c INSTALL`;Wc_c=$1
if test "$Wc_c" != "4037"; then
	echo original size 4037, current size $Wc_c
fi
fi
exit 0

							[fmc]

-------------------------------------------------------------------------------
Frederic Chauveau		     Conservatoire National des Arts et Metiers
fmc@cnam.cnam.fr
-------------------------------------------------------------------------------
Paradise is exactly like where you are right now, only much, much better.
							   William S. Burroughs
-------------------------------------------------------------------------------

exit 0 # Just in case...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.