[comp.os.minix] New version of 'df'

dal@syntel.UUCP (Dale Schumacher) (06/25/89)

This version of 'df' uses the '/etc/mtab' file (as Minix-PC v1.4?) to keep
track of what is mounted, and assumes '/etc/mount' and '/etc/umount' will
update that file properly.  In addition, something like the following
should be in your '/etc/rc' file.

	echo "/dev/hd3 is mounted on /" > /etc/mtab
	chmod 644 /etc/mtab

This allows 'df' to display stats on all mounted devices if it is run with
no parameters, all of which is no surprise to Minix-PC users.  What is new
in this version of 'df' is that it radically alters the output format to
start with a single header line (describing the fields) and adds two new
fields.  One showing percent of disk blocks used, and the other showing
the mount point for each device.  Here is some sample output:

device   inodes: used   free   blocks: used   free %use  mount point
/dev/hd3          106    222            354    606  36%  /
/dev/hd4         2392   5749          14909   9491  61%  /usr
/dev/ram            6     35             93      7  93%  /rbin
/dev/dd0           37    211            708     12  98%  /user

The context diffs were almost as big as the whole source, so I'm posting
the new source directly.

PS.  I'll be incommunicado from 6/26 to 6/30, but I'll answer mail as
soon after that as possible.

----------------------------------------------------------------------------
/* df - disk free block printout	Author: Andy Tanenbaum */

#include <minix/const.h>
#include <minix/type.h>
#include <fs/const.h>
#include <fs/type.h>
#include <fs/super.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

extern char *itoa();

char *mtab = "/etc/mtab";

  
main(argc, argv)
int argc;
char *argv[];
{
  register int i;

  sync();			/* have to make sure disk is up-to-date */
  prints(
  "device   inodes: used   free   blocks: used   free %use  mount point\n");
  defaults((argc > 1) ? argv[1] : ((char *) 0));
  exit(0);			/* redundant, defaults() doesn't return */
}


df(name, mountpoint)
char *name, *mountpoint;
{
  register int fd;
  int i_count, z_count, totblocks, busyblocks, i;
  long freepercent;
  char buf[BLOCK_SIZE];
  struct super_block super, *sp;
  struct stat statbuf;

  if ( (fd = open(name,0)) < 0) {
	perror(name);
	return;
  }

  /* Is it a block special file? */
  if (fstat(fd, &statbuf) < 0) {
	stderr2(name, ": Cannot stat\n");
	return;
  }
  if ( (statbuf.st_mode & S_IFMT) != S_IFBLK) {
	stderr2(name, ": not a block special file\n");
	return;
  }

  lseek(fd, (long)BLOCK_SIZE, 0);	/* skip boot block */
  if (read(fd, &super, SUPER_SIZE) != SUPER_SIZE) {
	stderr2(name, ": Can't read super block\n");
	close(fd);
	return;
  }


  lseek(fd, (long) BLOCK_SIZE * 2L, 0);	/* skip rest of super block */
  sp = &super;
  if (sp->s_magic != SUPER_MAGIC) {
	stderr2(name, ": Not a valid file system\n");
	close(fd);
	return;
  }

  i_count = bit_count(sp->s_imap_blocks, sp->s_ninodes+1, fd);
  if (i_count < 0) {
	stderr2(name, ": can't find bit maps\n");
	close(fd);
	return;
  }

  z_count = bit_count(sp->s_zmap_blocks, sp->s_nzones, fd);
  if (z_count < 0) {
	stderr2(name, ": can't find bit maps\n");
	close(fd);
	return;
  }
  totblocks = sp->s_nzones << sp->s_log_zone_size;
  busyblocks = z_count << sp->s_log_zone_size;

  /* Print results. */
  sfield(name, -14);
  sfield(itoa(i_count - 1), 7);
  sfield(itoa(sp->s_ninodes + 1 - i_count), 7);
  prints("        ");
  sfield(itoa(busyblocks), 7);
  sfield(itoa(totblocks - busyblocks), 7);
  freepercent = ((long) busyblocks) * 100L;
  freepercent /= (long) totblocks;
  sfield(itoa((int) freepercent), 4);
  prints("%  ");
  prints(mountpoint);
  prints("\n");

  close(fd);
}

bit_count(blocks, bits, fd)
int blocks;
int bits;
int fd;
{
  register int i, b;
  int busy, count, w;
  int *wptr, *wlim;
  int buf[BLOCK_SIZE/sizeof(int)];

  /* Loop on blocks, reading one at a time and counting bits. */
  busy = 0;
  count = 0;
  for (i = 0; i < blocks; i++) {
	if (read(fd, buf, BLOCK_SIZE) != BLOCK_SIZE) return(-1);

	wptr = &buf[0];
	wlim = &buf[BLOCK_SIZE/sizeof(int)];

	/* Loop on the words of a block */
	while (wptr != wlim) {
		w = *wptr++;

		/* Loop on the bits of a word. */
		for (b = 0; b < 8*sizeof(int); b++) {
			if ( (w>>b) & 1) busy++;
			if (++count == bits) return(busy);
		}
	}
  }
  return(0);
}


stderr2(s1, s2)
char *s1, *s2;
{
  std_err(s1);
  std_err(s2);
}

sfield(s, n)
char *s;
int n;
{
  char pad[128], *p;
  int m, x;

  m = strlen(s);		/* compute and create padding string */
  for(p=pad, x=((n<0)?-n:n); (m < x); ++m)
	*p++ = ' ';
  *p = '\0';
  if(n > 0)			/* right justification */
	prints(pad);
  prints(s);
  if(n < 0)			/* left justification */
	prints(pad);
  return(m);			/* characters printed */
}

defaults(device)
char *device;
{
/* Use the root file system and all mounted file systems. */
/* If a device is specified, only display that device. */

  char mdev[256], *mdir, *getname();

  close(0);
  if (open(mtab, 0) < 0) {
	std_err("df: cannot open ");
	std_err(mtab);
	std_err("\n");
	exit(1);
  }

  /* Read /etc/mtab and iterate on the lines. */
  while (1) {
	mdir = getname(mdev);		/* getname exits upon hitting EOF */
	if(!device || (strcmp(device, mdev) == 0))
		df(mdev, mdir);
  }

}

char *getname(p)
char *p;
{
  int c;

  while (1) {
	c = getchar();
	if (c == EOF)
		exit(0);
	if (c == ' ')
		c = 0;
	if (c == '\n') {
		*p = '\0';
		while(*--p)
			;
		return(++p);
	}
	*p++ = (char)c;
  }
}
----------------------------------------------------------------------------

--
      Dale Schumacher                         399 Beacon Ave.
      (alias: Dalnefre')                      St. Paul, MN  55104-3527
      ...bungia!midgard.mn.org!syntel!dal     United States of America
             "I may be competitive, but I'm never ruthless"