[comp.unix.ultrix] sys-admin tool

hubcap@hubcap.clemson.edu (System Janitor) (01/16/90)

Here's a tool I recently implemented that comes in real handy when one
is trying to visualize just what is on each disk, and where, and how much,
and what partitions overlap, etc...

I'd like to share it, and to get some ideas on what needs to be added to
it, and to find out about any bugs I might have missed... for example, I need 
to add some stuff to make it work on a DECstation, right now it only works 
on VAXes running ULTRIX.

Since this program probably won't work on anything but a VAX running ULTRIX
with out some amount of hacking, I decided to post it here instead of
one of the general source groups...

See you at Usenix in DC!

-Mike

#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# \c
-rw-r--r--  1 root         2161 Jan 15 14:52 gdf.8
# \c
-rw-r--r--  1 root        10163 Jan 15 14:03 gdf.c
#
echo 'x - gdf.8'
if test -f gdf.8; then echo 'shar: not overwriting gdf.8'; else
sed 's/^X//' << '________This_Is_The_END________' > gdf.8
X.TH gdf 8 
X.SH NAME
Xgdf \- graphically display disk usage and partition sizes.
X.SH SYNTAX
X.B gdf
X[ \fB\-g\fR \fIgranularity\fR ]
X.I device-name?
X.SH DESCRIPTION
XThe
X.PN gdf
Xcommand is a tool that helps you visualize the relationships between 
Xpartitions, how they overlay, what is in use and what is not.
X.PN gdf
Xis designed to be useful when it comes time to repartition, or perform 
Xvarious other disk maintenance activities.
X.PP
X``device-name?''
Xis the name and drive number of the device to be examined.
XFor example, if you want to examine an RA81 on drive 0,
Xdevice-name? = ra0.
X.PP
XA file system must exist on the \fIa\fR or \fIc\fR partition
Xof the pack.  If you do not have a file system there, create one
Xusing 
X.MS newfs 8 .
X.SH OPTIONS
X.IP \fB\-g\fR 6
XUse the specified granularity, instead of the default.
XBy default each unit in the diagram drawn by gdf represents 10 megabytes.
XIf the argument to -g is in the range of 1 to 256, then
Xit is interpreted as the number of megabytes per unit.
XIf the argument is greater than 256 then it is interpreted as the number
Xof kilobytes per unit.
X.SH EXAMPLES
XThis example shows how to examine the partition table on an RA81
Xdisk pack in drive 0.
X.EX 0
X.ta 2.5i
Xcsh> gdf ra0
X                / a: 
X     PRIMARY SWAP b:S 
X       0   445536 c:******************************************* 
X   65702    61496 d:      ****** 
X  127198    61496 e:            ****** 
X  188695   256841 f:                  ************************* 
X   24662    41040 g:  **** 
X             /usr h:      +++++++++++++++++++++++++++++++...... 
X.EE
X.PP
XThe above example shows that /dev/ra0a is the root partition, /dev/ra0b
Xis the primary swap device, and /usr is mounted on /dev/ra0h. Furthermore,
Xone can see that /usr is more than 3/4th's full.
X.SH RESTRICTIONS
XBe aware of the round off error inherent in the granularity of the ascii based
Xgraphics that
X.PN gdf
Xgenerates. 
XIn other words, the above example makes it looks as if g is available
Xto make a new filesystem on - confirm it with 
X.MS chpt 8 
Xbefore you crank up
X.MS newfs 8 !
XOnly those with read access to raw devices can use this program.
X.SH "SEE ALSO"
Xdf(1), chpt(8)
________This_Is_The_END________
if test `wc -l < gdf.8` -ne 65; then
	echo 'shar: gdf.8 was damaged during transit (should have been 65 bytes)'
fi
fi		; : end of overwriting check
echo 'x - gdf.c'
if test -f gdf.c; then echo 'shar: not overwriting gdf.c'; else
sed 's/^X//' << '________This_Is_The_END________' > gdf.c
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/param.h>
X#include <sys/fs.h>
X#include <sys/ioctl.h>
X#include <sys/dkio.h>
X#include <fstab.h>
X#include <sys/fs_types.h>
X#include <errno.h>
X#include <sys/conf.h>
X#include <a.out.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X
X#define TRUE 1
X#define FALSE 0
X#define FOUND  TRUE
X#define NOTFOUND FALSE
X
X/* gdf - graphic disk free. The tools one uses for planning disk management 
X .       (repartitioning, making new filesystems, whatever) are usually
X .       some combination of df(1) and chpt(8) -q. I usually use these
X .       two tools and mentally juggle their outputs to get a visualization
X .       of what's in use and how full it is, what's not in use, and what
X .       overlaps what. Actually, I'm not really all that good at 
X .       visualizing all that, and usually end up drawing it all out on
X .       a scrap of paper. This program does all that for you. If you 
X .       want a summary of /dev/ra2? you just say   gdf ra2    and out pops:
X .
X .               0     7942 a: 
X .                     SWAP b:SSSSSSSSSSS 
X .               0   445536 c:******************************************* 
X .           65702    61496 d:      ****** 
X .                     /var e:            ++++.. 
X .        /var/spool/news-m f:                  +++++++++................ 
X .           24662    41040 g:  **** 
X .           65702   379834 h:      ************************************* 
X . 
X .       So... is that cool or what?
X .
X .       The offsets and lengths are in megabytes.
X .
X . ASSUMPTIONS:
X .  * all raw device files look like    /dev/rXX?{a|b|c|d|e|f|g|h}
X .  * all block device files look like  /dev/XX?{a|b|c|d|e|f|g|h}
X .      XX would be ra for an MSCP disk, rd for a winchester, etc...
X .      ? is the drive number
X .  * all partitions you care about are in /etc/fstab, except for 
X .    the primary swap partition.
X . 
X . RESTRICTIONS: 
X .  * you gotta be root to run this (or you can setuid it) if your raw 
X .    device permissions look like:
X .
X .     crw-------  1 root     system     9,   0 Mar 19  1989 /dev/rra0a
X .
X .  * there is a default granularity of 10 meg, adjustable on the command
X .    line. in other words, each asterisk = 10 meg. this granularity
X .    limitation is responsible for some round off error in the 
X .    pictures drawn by this program, such as can be seen in the ``a''
X .    partition of the above picture.
X .  
X . SYSTEM CALLS USED:
X .   open
X .   lseek
X .   read
X .   chdir
X .   stat
X .   ioctl
X .
X . LIBRARY ROUTINES USED:
X .   getopt
X .   atoi
X .   nlist
X .   sprintf
X .   getwd
X .   opendir
X .   readdir
X .   strcmp
X .   getfsspec
X .
X . BUGS TO:
X .   hubcap@hubcap.clemson.edu
X . 
X . TO DO:
X .  * make this an X application... granularity could be controlled 
X .    acceptably if we were dealing with pixels instead of ascii
X .    characters.
X .  * make a way to specify partitions that we don't think of as empty,
X .    but that aren't in /etc/fstab either.
X */
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{
X  struct pt arg;
X  int fd1, i, j, length, offset, used;
X  static char partition[8] = {'a','b','c','d','e','f','g','h'};
X  char raw[11];
X  char block[10];
X  struct fstab *getfsspec(), *fs_ent;
X  struct fs superblock;
X  int ss = sizeof(superblock);
X  int fd2;
X  float fudge;
X  int fd3,status,num_swapdevs,swaps_found;
X  struct nlist nl[2];
X  struct swdevt sd;
X  struct stat buf;
X  char swapdevs[10][10], majmin[10], swapdev_path[10][MAXPATHLEN];
X  DIR *opendir(), *dirp;
X  struct direct *readdir(), *dp;
X  char here[MAXPATHLEN];
X  int prime_swap, alt_swap_lie, alt_swap;
X  int granularity, gran;
X  extern int optind;
X  extern char *optarg;
X  int opt,dev;
X  char dummy[100];
X
X  if (argc < 2) 
X    error(sprintf(dummy,"usage: gdf [-g granularity] device-name?"));
X
X  dev = 1;
X  granularity = 10240;
X  while ((opt = getopt(argc,argv,"g:")) != EOF) 
X    switch (opt) {
X      case 'g':
X        gran = atoi(optarg);
X        if ((gran > 0) && (gran < 257)) granularity = 1024 * gran;
X        else granularity = gran;
X        dev+=2;
X      break;
X      default:
X        puts("usage: gdf [-g granularity] device-name?");
X        exit(0);
X      break;
X    }
X  /* end while */
X        
X
X  /* dig the _swdevt data structure out of the kernel. _swdevt contains
X   . (among other stuff) the major and minor device numbers of all 
X   . swap devices.
X   */
X  nl[0].n_un.n_name = "_swdevt";
X  nl[1].n_un.n_name = (char *) NULL;
X
X  nlist("/vmunix",nl);
X
X  if ((fd1 = open("/dev/kmem",0)) < 0) 
X    error(sprintf(dummy,"gack: can't open %s\n","/dev/kmem"));
X  
X  if ((status = lseek(fd1, nl[0].n_value, 0)) == -1)
X    error(sprintf(dummy,"gack: can't lseek, status: %d reason %d\n",
X      status, errno));
X  
X  if (read(fd1, (char *)&sd, sizeof(sd)) != sizeof(sd))
X    error(sprintf(dummy,"gack: can't read swap device structure, reason:%d\n",
X      errno));
X
X  num_swapdevs = 0;
X  while (sd.sw_dev != 0) {
X    sprintf(swapdevs[num_swapdevs++],"%x",sd.sw_dev);
X
X    if (read(fd1, (char *)&sd, sizeof(sd)) != sizeof(sd)) 
X      error(sprintf(dummy,"gack: can't read swap device structure, reason:%d\n",
X        errno));
X  }
X  swapdevs[num_swapdevs][0] = NULL;
X
X  if (getwd(here) == 0) error(sprintf(dummy,"gack: I don't know where I am"));
X
X  if (chdir("/dev") == -1) error(sprintf(dummy,"gack: I can't chdir to /dev"));
X
X  if ((dirp = opendir("/dev")) == NULL) 
X    error(sprintf(dummy,"gack: can't opendir %s, reason:%d\n","/dev",errno));
X
X  /* compare all the major and minor device numbers of all the block devices
X   . in /dev to the major and minor device numbers fetched from _swdevt.
X   . remember the full path name of all matches.
X   */
X  while ((dp = readdir(dirp)) != NULL) {
X    if (stat(dp->d_name,&buf) == -1)
X      error(sprintf(dummy,"gack: can't stat %s, reason: %d",dp->d_name,errno));
X
X    sprintf(majmin,"%x%2.2x",major(buf.st_rdev),minor(buf.st_rdev));
X
X    /* mask off all of the mode bits except the ones that tell what kind
X     . of an object we've got here...
X     */
X    buf.st_mode = buf.st_mode & 0770000;
X
X    if (buf.st_mode == S_GFBLK) {
X      for (i=0;i<num_swapdevs;i++)
X        if (!strcmp(majmin,swapdevs[i])) {
X          sprintf(swapdev_path[i],"/dev/%s",dp->d_name);
X          swaps_found++;
X          break;
X        }
X      if (swaps_found == num_swapdevs) break;
X    }
X  }
X
X  if (chdir(here) == -1) 
X    error(sprintf(dummy,"barf: I can't chdir out of /dev."));
X
X  strcpy(block,"/dev/    ");
X  strcpy(raw,"/dev/r   a");
X  raw[6] = block[5] = argv[dev][0];
X  raw[7] = block[6] = argv[dev][1];
X  raw[8] = block[7] = argv[dev][2];
X
X  /* find length and offsets of raw sectors for all partitions. this is
X   . documented in dkio(4).
X   */
X  if ( (fd1 = open(raw,0)) < 0)
X    error(sprintf(dummy,"gack: can't open %s\n",raw));
X
X  if (ioctl(fd1,DIOCGETPT,&arg) < 0) error(sprintf(dummy,"gack: ioctl error"));
X
X  for (i=0;i<=7;i++) {
X    prime_swap = NOTFOUND;
X    alt_swap_lie = FALSE;
X    alt_swap = NOTFOUND;
X    block[8] = partition[i];
X    offset = arg.pt_part[i].pi_blkoff/2;
X    length = arg.pt_part[i].pi_nblocks/2;
X
X    fs_ent = getfsspec(block);
X
X    /* check to see if this partition is a swap device. the primary swap
X     . device shouldn't be listed in /etc/fstab, but all others should.
X     . also, set some flags to see if the swap devices listed in fstab
X     . match reality.
X     */
X    for (j=0;j<num_swapdevs;j++)
X      if (!strcmp(block,swapdev_path[j])) 
X        if (j == 0) prime_swap = FOUND;
X        else {
X          if (fs_ent == 0) alt_swap_lie = TRUE;
X          alt_swap = FOUND;
X        }
X
X    /* draw this partition's piece of the picture. */
X    if (fs_ent == 0) {
X      if (prime_swap)
X        printf("%17.17s %c:","PRIMARY SWAP",partition[i]);
X      if (alt_swap_lie)
X        printf("%17.17s %c:","SWAP bad fstab",partition[i]);
X      if ((!prime_swap) && (!alt_swap_lie))
X        printf("%8d %8d %c:",offset,length,partition[i]);
X    }
X    else
X      if (!strcmp(fs_ent->fs_type,FSTAB_SW)) {
X        if (alt_swap)
X          printf("%17.17s %c:","SWAP",partition[i]);
X        if ((prime_swap) || (!alt_swap))
X          printf("%17.17s %c:","no SWAP fstab lie",partition[i]);
X      }
X      else
X        printf("%17.17s %c:",fs_ent->fs_file,partition[i]);
X
X    while (offset > granularity) {
X      putchar(' ');
X      offset = offset-granularity;
X    }
X    if ((fs_ent ==  NULL) || (!strcmp(fs_ent->fs_type,FSTAB_SW)))
X      while (length > granularity) {
X        if ((fs_ent == NULL) && !prime_swap && !alt_swap_lie) putchar('*');
X        else if ((prime_swap) || (alt_swap)) putchar('S');
X        length = length-granularity;
X      }
X    else {
X      if ((fd2 = open(block,0)) < 0) 
X        error(sprintf(dummy,"gack: can't open %s\n",block));
X
X      lseek(fd2, (long)(SBLOCK * DEV_BSIZE), 0);
X
X      if ((read(fd2, (char *)&superblock, ss)) != ss)
X        error(sprintf(dummy,"gack: can't read super block, reason:%d\n",errno));
X
X      /* in deciding how full each partition is, we are sort of comparing
X       . apples to oranges:
X       .
X       .    apples:  raw sector sizes, fetched from DIOCGETPT ioctl.
X       .    oranges: formatted disk statistics fetched from superblock.
X       .
X       . fudge is used to convert oranges to apples by taking into account
X       . the 10% (or whatever fs_minfree happens to be) of each partition that
X       . is set aside for the berkeley fast file system.
X       .
X       . I also try to account for the extra superblocks sprinkled through
X       . each partition, although this may be a misguided endevour.
X       */
X      fudge =  1.0 + (1.0 / superblock.fs_minfree);
X      used = fudge * (superblock.fs_size - 
X               ((superblock.fs_cstotal.cs_nbfree * superblock.fs_frag +
X                 superblock.fs_cstotal.cs_nffree) + 
X                 (superblock.fs_ncg * (superblock.fs_sbsize/1024))));
X      while (used > granularity) {
X        putchar('+');
X        used = used-granularity;
X        length = length-granularity;
X      }
X      while (length > granularity) {
X        putchar('.');
X        length = length-granularity;
X      }
X    }   
X    puts(" ");
X  }
X}
Xerror(complaint)
Xchar *complaint;
X{
X  puts(complaint);
X  exit(0);
X}
X
________This_Is_The_END________
if test `wc -l < gdf.c` -ne 321; then
	echo 'shar: gdf.c was damaged during transit (should have been 321 bytes)'
fi
fi		; : end of overwriting check
exit 0