[comp.sys.encore] sps for UMAX enclosed

madd@bu-cs.BU.EDU (Jim Frost) (08/07/89)

Enclosed is a version of sps which I wrote for the encore running
umax.  Since it is a completely rewritten sps, there are differences
between BSD sps and it, many due to the limited information available
from inq_stats, some due to my limited experience.  Generally it
provides the information most people want from sps; I certainly have
found it useful.

This was originally distributed with a long copyright notice.  I have
since decided to release it to the public domain.  Feel free to
distribute and modify it.  I'd like to see any modifications people
make.

One modification that you might make is to change the process list
that is built to use a hash table instead; this will give a dramatic
speedup.  I never got around to it.  It already operates pretty well,
though.

jim frost
software tool & die
madd@std.com

-- cut here --
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile README build_list.c build_tree.c find_root.c
#   hash.c new_lnode.c new_tnode.c print_proc.c print_tree.c sps.c
#   sps.h sps.l
# Wrapped by madd@bucsf on Sun Aug  6 13:34:02 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(627 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
XFLAGS= -O
X
Xall:		sps
Xsps:		sps.o new_lnode.o new_tnode.o build_list.o find_root.o build_tree.o print_tree.o print_proc.o hash.o
X		cc $(FLAGS) -o sps sps.o new_lnode.o new_tnode.o build_list.o find_root.o build_tree.o print_proc.o print_tree.o hash.o
X
X.c.o:
X		cc -c $(FLAGS) $*.c
Xsps.o:		Makefile sps.h sps.c
Xnew_lnode.o:	Makefile sps.h new_lnode.c
Xnew_tnode.o:	Makefile sps.h new_tnode.c
Xbuild_list.o:	Makefile sps.h build_list.c
Xfind_root.o:	Makefile sps.h find_root.c
Xbuild_tree.o:	Makefile sps.h build_tree.c
Xprint_node.o:	Makefile sps.h print_proc.c
Xprint_tree.o:	Makefile sps.h print_tree.c
Xhash.o:		Makefile sps.h hash.c
END_OF_Makefile
if test 627 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(2406 characters\)
sed "s/^X//" >README <<'END_OF_README'
Xsps - show system processes
X
XThis program is similar to the BSD "sps" program.  It supports many,
Xbut not all, of the same command line options.  Its output is much
Xsimilar as well.
X
X"sps" was developed on an Encore Multimax with 6 32332 processors
Xrunning UMAX 4.2 B3_0.30.  It's possible that changes in the UMAX
Xoperating system affected inq_stats(); if you have any problems,
Xplease contact me at the address below.
X
XOutput from "sps" is done by analyzing the system process tree.  In
Xthe system process tree, every process is a child of another except
Xfor the root process, which is usually init (pid = 1).  On the Encore,
Xhowever, the root process is GOD (pid = 0).  "sps" asks the UMAX
Xoperating system for the process list and then builds a binary tree of
Xprocesses from it.
X
XOne possible problem that "sps" may encounter is an incomplete process
Xlist.  If the Encore's inq_stats() function does not return a complete
Xprocess list, it will be impossible to build a complete process tree
Xfrom the list.  If this happens, "sps" will build as complete a tree
Xas possible and warn the user that the tree is not complete.
X
X"sps" will also warn the user if the two "snapshots" that it takes of
Xthe system conflict.  Sometimes the actual number of processes in the
Xprocess list returned by inq_stats() is not the same as the number of
Xprocesses reported in the system summary returned by inq_stats().
XUsually this is not a real problem, but "sps" reports it to the user
Xanyway.
X
XThe file "sps.c" contains a description of how to invoke "sps", and
Xdetails the differences between 4.x BSD "sps" and this version.  In
Xgeneral, this version of "sps" is not as ornate.  It will not return
Xall of the statistics that 4.x BSD's "sps" does.  A complete list of
Xunsupported switches is contained in the description in "sps.c",
Xincluding the reason(s) why they are not supported.  Reasons go from
Xprogrammer laziness (I just wanted _something_) to inability to find
Xthe information.  Much information available to 4.x BSD's "sps" is not
Xavailable to this version of "sps" because this version does not read
Xthrough the kernel.
X
XWARNING
X
XThe Encore inq_stats() function is subject to change.  Encore changed
Xthe procstats.h file between two versions of our operating system,
Xcausing the program to fail to compile.  As currently written, sps
Xshould run on nearly every version of the Encore.
X
Xjim frost
Xmadd@std.com
END_OF_README
if test 2406 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f build_list.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"build_list.c\"
else
echo shar: Extracting \"build_list.c\" \(992 characters\)
sed "s/^X//" >build_list.c <<'END_OF_build_list.c'
X/* build_list.c:
X *
X * this builds a linked list of PROC_DETAIL structures.  a list
X * is built because the scan time to locate each process's children
X * will diminish as we delete nodes.  if i had thought of it when
X * i wrote this, i would have used a hash table.  feel free.
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include "sps.h"
X
XPROC_LIST *build_list(nprocs,pd)
Xint nprocs;
XPROC_DETAIL *pd;
X{ int a;
X  PROC_LIST *l, *p;
X
X/*
X * if we have a null list, we have real problems.  find_init() will
X * trap this null if we have this problem.
X */
X
X  if ((pd == NULL) || (nprocs == 0))
X    return(NULL);
X
X/*
X * go through table and add an entry for each process
X */
X
X  l= p= new_lnode(pd);           /* first node in list */
X  for (a= 1; a < nprocs; a++) {  /* subsequent nodes */
X    p->next= new_lnode(pd + a);  /* get a new node */
X    p->next->prev= p;            /* link it to this node */
X    p= p->next;                  /* go on */
X  }
X  return(l);
X}
END_OF_build_list.c
if test 992 -ne `wc -c <build_list.c`; then
    echo shar: \"build_list.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f build_tree.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"build_tree.c\"
else
echo shar: Extracting \"build_tree.c\" \(1784 characters\)
sed "s/^X//" >build_tree.c <<'END_OF_build_tree.c'
X/* build_tree.c:
X *
X * this routine builds the process tree
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include "sps.h"
X
Xextern PROC_LIST *l;
X
Xvoid build_tree(t)
XPROC_TREE *t;
X{ PROC_LIST *p;
X  PROC_TREE *u, *v, *w;
X
X  p= l;
X  while (p) {
X    if (p->p->pd_ppid == t->p->pd_pid) { /* add child to tree */
X
X/*
X * link child to its siblings and parent
X */
X
X      u= new_tnode(p->p);
X      u->parent= t;              /* link child to parent */
X      v= t->child;
X      w= NULL;
X      for (;;) {                 /* just loop.  we break out anyway */
X        if (v == NULL) {         /* if current ptr NULL, end of list */
X          if (w)                 /* if w NULL, this is 1st child process */
X            w->sibling= u;       /* otherwise link to end of sibling chain */
X          else
X            t->child= u;
X          break;
X        }
X        else if (u->p->pd_pid < v->p->pd_pid) { /* check pid's for order */
X          u->sibling= v;         /* link to sibling chain */
X          if (w)                 /* if w NULL this is 1st in chain */
X            w->sibling= u;       /* otherwise link to end of chain */
X          else
X            t->child= u;
X          break;
X        }
X        w= v;                    /* proceed down chain */
X        v= v->sibling;
X      }
X
X/*
X * delete this node to speed up subsequent checks
X */
X
X      if (p->next)
X        p->next->prev= p->prev;  
X      if (p->prev)
X          p->prev->next= p->next;
X      else {
X        l= l->next;
X        if (l)
X          l->prev= NULL;
X      }
X    }
X    p= p->next; /* go on down the line */
X  }
X
X/*
X * now we have all children of this process.  next, follow all
X * its children and build their trees.
X */
X
X  u= t->child;
X  while (u) {
X    build_tree(u);
X    u= u->sibling;
X  }
X}
END_OF_build_tree.c
if test 1784 -ne `wc -c <build_tree.c`; then
    echo shar: \"build_tree.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f find_root.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"find_root.c\"
else
echo shar: Extracting \"find_root.c\" \(627 characters\)
sed "s/^X//" >find_root.c <<'END_OF_find_root.c'
X/* find_root.c:
X *
X * finds the root process of the tree.  the pid is ROOT_PROCESS.  on the
X * encore this is zero.
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include "sps.h"
X
Xextern PROC_LIST *l;
X
XPROC_TREE *find_root()
X{ PROC_LIST *p, *q;
X
X  p= l;
X  q= NULL;
X  while (p)
X    if (p->p->pd_pid == ROOT_PROCESS) {
X      if (p->next)
X        p->next->prev= p->prev;
X      if (p->prev)
X        p->prev->next= p->next;
X      else {
X        l= l->next;
X        if (l)
X          l->prev= NULL;
X      }
X      return(new_tnode(p->p));
X    }
X  printf("find_root: root process not found\n");
X  exit(1);
X}
END_OF_find_root.c
if test 627 -ne `wc -c <find_root.c`; then
    echo shar: \"find_root.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f hash.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"hash.c\"
else
echo shar: Extracting \"hash.c\" \(1703 characters\)
sed "s/^X//" >hash.c <<'END_OF_hash.c'
X/* hash.c:
X *
X * this file contains hashing functions to build a uid hash table
X * to make life faster.
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include "sps.h"
X
X/* define hash table */
X
X#define HASHSIZE 100
X
Xtypedef struct hash_entry {
X  int uid;
X  char name[11];
X  struct hash_entry *next;
X} HASH_ENTRY;
X
Xstatic HASH_ENTRY *htab[HASHSIZE];
X
X/*
X * this is our hashing function.  this one is very simple -- it merely
X * overlays uid's based on a modular function.
X */
X
Xstatic int hash_func(n)
Xint n;
X{ return(n % HASHSIZE);
X}
X
X/*
X * this function goes through the /etc/passwd file and builds a hash
X * table of uid's
X */
X
Xvoid build_hash()
X{ int a;
X  struct passwd *pw;
X  HASH_ENTRY *n, *p;
X
X  for (a= 0; a < HASHSIZE; a++) /* initialize hash table */
X    htab[a]= NULL;
X
X  while (pw= getpwent()) {      /* loop for whole file */
X
X/*
X * make new hash entry
X */
X
X    if ((n= (HASH_ENTRY *)malloc(sizeof(HASH_ENTRY))) == NULL) {
X      perror("build_hash: malloc");
X      exit(1);
X    }
X    strncpy(n->name,pw->pw_name,11);
X    n->name[11]= '\0';
X    n->uid= pw->pw_uid;
X    n->next= NULL;
X
X/*
X * now we have an entry for this user so put it in the hash table
X */
X
X    a= hash_func(pw->pw_uid);
X    if (htab[a]) {
X      p= htab[a];
X      while (p->next)
X        p= p->next;
X      p->next= n;
X    }
X    else
X      htab[a]= n;
X  }
X  endpwent();
X}
X
X/*
X * this looks up a uid in the hash table and returns the user's name
X */
X
Xchar *user_name(uid)
Xint uid;
X{ HASH_ENTRY *p;
X  static char *unknown= "???";
X
X  p= htab[hash_func(uid)];
X
X  while (p) {
X    if (p->uid == uid)
X      return(p->name);
X    p= p->next;
X  }
X  return(unknown); /* unknown uid -- something's screwed */
X}
END_OF_hash.c
if test 1703 -ne `wc -c <hash.c`; then
    echo shar: \"hash.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f new_lnode.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"new_lnode.c\"
else
echo shar: Extracting \"new_lnode.c\" \(391 characters\)
sed "s/^X//" >new_lnode.c <<'END_OF_new_lnode.c'
X/* new_lnode.c:
X *
X * create a new process list node and initialize it
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include "sps.h"
X
XPROC_LIST *new_lnode(p)
XPROC_DETAIL *p;
X{ PROC_LIST *l;
X
X  if ((l= (PROC_LIST *)malloc(sizeof(PROC_LIST))) == NULL) {
X    perror("new_lnode: malloc");
X    exit(1);
X  }
X  l->p= p;
X  l->prev= NULL;
X  l->next= NULL;
X  return(l);
X}
END_OF_new_lnode.c
if test 391 -ne `wc -c <new_lnode.c`; then
    echo shar: \"new_lnode.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f new_tnode.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"new_tnode.c\"
else
echo shar: Extracting \"new_tnode.c\" \(414 characters\)
sed "s/^X//" >new_tnode.c <<'END_OF_new_tnode.c'
X/* new_tnode.c:
X *
X * create a new process tree node and initialize it
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include "sps.h"
X
XPROC_TREE *new_tnode(p)
XPROC_DETAIL *p;
X{ PROC_TREE *t;
X
X  if ((t= (PROC_TREE *)malloc(sizeof(PROC_TREE))) == NULL) {
X    perror("new_tnode: malloc");
X    exit(1);
X  }
X  t->p= p;
X  t->parent= NULL;
X  t->child= NULL;
X  t->sibling= NULL;
X  return(t);
X}
END_OF_new_tnode.c
if test 414 -ne `wc -c <new_tnode.c`; then
    echo shar: \"new_tnode.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f print_proc.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"print_proc.c\"
else
echo shar: Extracting \"print_proc.c\" \(3833 characters\)
sed "s/^X//" >print_proc.c <<'END_OF_print_proc.c'
X/* print_proc.c:
X *
X * prints out process entry
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include <sys/pmdefs.h>
X#include "sps.h"
X
Xextern int maxtab,
X           first_print,
X           print_status,
X           print_nice,
X           print_ppid,
X           print_pgrp,
X           print_size,
X           print_time,
X           list_busy,
X           list_detached,
X           list_foreground,
X           list_stopped,
X           list_waiting,
X           list_zombie;
X
Xstatic char *tty(p)
XPROC_DETAIL *p;
X{ if (!strncmp(p->pd_device,"tty",3))
X    return(&p->pd_device[3]);
X  if (!strcmp(p->pd_device,"console"))
X    return("co");
X  else
X    return("  ");
X}
X
Xstatic char *pstat(p)
XPROC_DETAIL *p;
X{
X  if (p->pd_flag & SZOMBIE)
X    return("ZOMBIE");
X  return(procstates[p->pd_state]); /* encore-provided state array */
X}
X
X/*
X * this takes care of drawing the tree and naming users.
X */
X
Xstatic char *pic(t,tab)
XPROC_TREE *t;
Xint tab;
X{ static char picstr[LONGMAXTAB+1]; /* longest possible area */
X  static int last_uid;
X  char type;
X
X  if (first_print) /* force print of first process */
X    last_uid= -1;
X
X/*
X * if parent and child are in the same process group then use a pipe
X * character to signify this.  otherwise indicate it's a child with a
X * star.
X */
X
X  if (tab > 0)
X    type= (t->parent->p->pd_pgrp == t->p->pd_pgrp ? '|' : '*');
X
X/*
X * if there was a change in uid then indicate the change by printing
X * the name.
X */
X
X  if ((t->p->pd_uid != last_uid) || (tab == 0))
X    if (tab > 0)
X      sprintf(picstr,"%*.c%-*.*s",tab,type,maxtab-tab,
X              maxtab-tab,user_name(t->p->pd_uid));
X    else
X      sprintf(picstr,"%-*.*s",maxtab,maxtab,user_name(t->p->pd_uid));
X  else
X      sprintf(picstr,"%*.c%*.s",tab,type,maxtab-tab,"");
X  last_uid= t->p->pd_uid;
X  return(picstr);
X}
X
Xstatic long proctime(p)
XPROC_DETAIL *p;
X{ return(p->pd_utime.tv_sec+p->pd_stime.tv_sec);
X}
X
X/*
X * print process information here in whatever format we're using.
X */
X
Xvoid print_proc(t,tab)
XPROC_TREE *t;
Xint tab;
X{
X
X/*
X * header
X */
X
X  if (first_print) {
X    printf("Ty %-*.*s ",maxtab,maxtab,"User");
X    if (print_status)
X      printf("Status ");
X    if (print_nice)
X      printf("Nice ");
X    if (print_ppid)
X      printf("PProc ");
X    if (print_pgrp)
X      printf(" PGrp ");
X    if (print_size)
X      printf("Virtual Resident ");
X    if (print_time)
X      printf(" Time ");
X    printf("Proc# Command\n");
X  }
X
X/*
X * print out process line.  this also filters for the process selection
X * flags (eg -Z to list all zombies).
X */
X
X  if ((!(list_busy || list_detached || list_foreground ||
X        list_stopped || list_waiting || list_zombie)) ||
X       (list_busy && (((t->p->pd_state == SRUN) && !(t->p->pd_flag & SZOMBIE)) ||
X                      (t->p->pd_state == SIDL) ||
X                      (t->p->pd_state == SEXEC) ||
X                      (t->p->pd_state == SWAIT))) ||
X       (list_stopped && (t->p->pd_state == SSTOP)) ||
X       (list_waiting && (t->p->pd_state == SWAIT)) ||
X       (list_zombie && (t->p->pd_flag == SZOMBIE))) {
X
X/*
X * these are things i want to search for but haven't figured out
X * how to do it yet.
X *
X *      (list_detached && (
X *      (list_foreground && (
X */
X
X    printf("%s %*.*s ",tty(t->p),maxtab,maxtab,pic(t,tab));
X    if (print_status)
X      printf("%-6.6s ",pstat(t->p));
X    if (print_nice)
X      if (t->p->pd_nice)
X        printf("%3d  ",t->p->pd_nice);
X      else
X        printf("     ");
X    if (print_ppid)
X      printf("%5d ",t->p->pd_ppid);
X    if (print_pgrp)
X      printf("%5d ",t->p->pd_pgrp);
X    if (print_size)
X      printf("%5dk %7dk ",t->p->pd_tsize+t->p->pd_ssize+t->p->pd_dsize,
X             t->p->pd_rssize);
X    if (print_time)
X      printf("%6ld ",proctime(t->p));
X    printf("%5d %s\n",t->p->pd_pid,t->p->pd_command);
X  }
X  first_print= 0;
X}
END_OF_print_proc.c
if test 3833 -ne `wc -c <print_proc.c`; then
    echo shar: \"print_proc.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f print_tree.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"print_tree.c\"
else
echo shar: Extracting \"print_tree.c\" \(740 characters\)
sed "s/^X//" >print_tree.c <<'END_OF_print_tree.c'
X/* print_tree.c:
X *
X * prints out the process tree.
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include "sps.h"
X
Xextern int maxtab;
X
Xvoid print_tree(t,uid,tab)
XPROC_TREE *t;
Xint uid, tab;
X{ PROC_TREE *u;
X
X/*
X * if uid is not zero then force tab back to zero.  this makes the
X * user's tree print from first column.
X */
X
X  if (uid)
X    tab= 0;
X
X/*
X * if we matched a user's uid, then print all subprocesses even if the
X * uid changes.  easiest way to do this is force uid= 0 and always
X * print things when uid is zero.
X */
X
X  if (t->p->pd_uid == uid)
X    uid= 0;
X  if (uid == 0)
X    print_proc(t,tab);
X
X  u= t->child;
X  while (u) {
X    print_tree(u,uid,(tab < maxtab ? tab+1 : tab));
X    u= u->sibling;
X  }
X}
END_OF_print_tree.c
if test 740 -ne `wc -c <print_tree.c`; then
    echo shar: \"print_tree.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sps.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sps.c\"
else
echo shar: Extracting \"sps.c\" \(14993 characters\)
sed "s/^X//" >sps.c <<'END_OF_sps.c'
X/* sps.c:  show process status
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X *
X * this program constructs a process tree from a process list and
X * prints it out in a nice format.  very similar to sps in standard
X * berkeley distribution, although not quite as fancy.  obtains its
X * information by asking the system, not by poking through memory.
X *
X * options currently accepted on command line:
X *   -A    use complete process tree
X *   -B    list busy processes
X *   -N    print summary only (no processes)
X *   -S    list stopped processes
X *   -U    list processes belonging to user who's name follows -U
X *   -W    list waiting processes
X *   -Z    list zombie processes
X *   -f    include parent's uid in printout
X *   -g    include process group in printout
X *   -l    print out in long (verbose) format
X *   -r    repeat output indefinitely.  may be followed by numeric arg
X *         which will repeat indefinitely with pause for that many
X *         seconds between each run.
X *   -s    print out in short format (included so you can change default)
X *   -v    print out in long (verbose) format
X *   -w    print out in wide format.  prints out EVERYTHING even if it
X *         extends beyond current line.
X *
X * conflicting switches will be cancelled with last switch having
X * effect.
X *
X * switches which are not supported (and why):
X *   -D    list detached processes.  haven't figured out a good way to
X *         do this yet.
X *   -F    list foreground processes.  haven't figured out how to do this
X *         yet.
X *   -P    list only specified processes.  haven't gotten around to doing
X *         this one.  probably will only allow 1 process number anyway.
X *   -T    list processes assosciated with specific terminal.  same reason
X *         as -P.
X *   -d    list output reflecting how each process affects the disc and
X *         paging activity of the system.  informaion not available.
X *   -e    list environment passed to each process.  information not
X *         available.
X *   -i    initialize sps.  this version needs no initialization.
X *   -j    use different information file.  this version needs no information
X *         file.
X *   -k    use specific disc file rather than physical memory.  we don't use
X *         physical memory.
X *   -o    avoid looking at /dev/drum (swap device).  we ask the machine
X *         for our information so we don't have to look at this.
X *   -s    name of file to use in place of /vmunix.  we don't use /vmunix.
X *
X * history:
X *   9.23.87    original version
X *   9.27.87    correct zombie checking implemented.  fix bug with -B
X *              flag that caused it to print zombies.  fix bug with -r
X *              flag that caused the user name not to print on successive
X *              executions.  fix bug in find_root() lnode deletion and
X *              add error checking for process list consistency.
X *   9.28.87    add check to print owner of process whenever tab == 0.
X *   10.13.87   fix bug with -N flag.  process configuration wasn't
X *              called in order to determine the size of the process
X *              list; this caused a negative number of running processes
X *              to print.
X *   11.2.87    fix bug in error message printing.  forgot to include
X *              the constant PROG in a printf statement.
X */
X
X#include "sps.h"
X
X/*
X * due to an apparent bug in the way inq_stat() checks pointers, these are
X * global vars.  if you don't use them as globals, sometimes (!) inq_stat()
X * says you passed it an invalid pointer.  this takes care of it.
X */
X
XSTAT_DESCR sd,sd2;
XPROC_CONFIG pc;
XPROC_SUMMARY ps;
X
X/*
X * we need to play with the value of the process list pointer all over the
X * place so this is global.
X */
X
XPROC_LIST *l;
X
X/*
X * values and switches used in various places.  they could be passed but
X * that would be a pain.
X */
X
Xint maxtab,
X    first_print,
X    print_status,
X    print_nice,
X    print_ppid,
X    print_pgrp,
X    print_size,
X    print_time,
X    list_busy,
X    list_detached,
X    list_foreground,
X    list_stopped,
X    list_waiting,
X    list_zombie,
X    no_processes; /* this could be local but it's easy to find here */
X
Xmain(argc,argv)
Xint argc;
Xchar *argv[];
X{ PROC_TREE *t;        /* process tree */
X  PROC_DETAIL *pd;     /* pointer to array of PROC_DETAIL structs */
X  int a,               /* counter var */
X      err= 0,          /* parameter error */
X      uid,             /* uid of requested tree */
X      repeat;          /* sleep time between repeats */
X  char *s,             /* string pointer needed in places */
X       *uname[11];     /* user name from -U flag */
X  struct passwd *pw;   /* password file pointer used for -U */
X
X/*
X * set up default values
X */
X
X  uid= getuid();
X  print_status= 0;
X  print_nice= 0;
X  print_ppid= 0;
X  print_pgrp= 0;
X  print_size= 0;
X  print_time= 0;
X  list_busy= 0;
X  list_detached= 0;
X  list_foreground= 0;
X  list_stopped= 0;
X  list_waiting= 0;
X  list_zombie= 0;
X  no_processes= 0;
X  first_print= 1;
X  repeat= -1;
X
X/*
X * go through arguments (if any) and set up changes to defaults.  this
X * could be coded smaller but this works and is easy enough to modify.
X */
X
X  for (a= 1; a < argc; a++) {
X    s= argv[a];
X    if (*(s++) == '-')       /* look for switches */
X      while (*s) {
X        switch (*s) {
X          case ('A') :       /* -A -- use full process list */
X            uid= 0;
X            ++s;
X            break;
X          case ('B') :       /* -B -- list busy processes */
X            list_busy= 1;
X            ++s;
X            break;
X          case ('D') :       /* -D -- list detached processes */
X            printf("-D option not supported (ignored)\n");
X/*           list_detached= 1;
X *           ++s; */
X            break;
X          case ('F') :       /* -F -- list foreground processes */
X            printf("-F option not supported (ignored)\n");
X/*            list_foreground= 1;
X *           ++s; */
X            break;
X          case ('N') :       /* -N -- don't list processes */
X            no_processes= 1;
X            ++s;
X            break;
X          case ('P') :       /* -P -- list specific processes */
X            printf("-P option not supported (ignored)\n");
X            ++s;
X            break;
X          case ('S') :       /* -S -- list stopped processes */
X            list_stopped= 1;
X            ++s;
X            break;
X          case ('T') :       /* -T -- list processes attached to terminal */
X            printf("-T option not supported (ignored)\n");
X            ++s;
X            break;
X          case ('U') :       /* -U switch -- use user tree */
X            if (*(++s)) /* next char not null use rest of parm */
X              strncpy(uname,s,11);
X            else {      /* use next parm */
X              if (argc <= ++a)
X                err= 1;
X              else if (*argv[a] == '\0')
X                err= 1;
X              else
X                strncpy(uname,argv[a],11);
X            }
X            if ((pw= getpwnam(uname)) == NULL) {
X              printf("%s: unknown user\n",uname);
X              exit(1);
X            }
X            uid= pw->pw_uid;
X            while (*s)
X              s++;
X            break;
X          case ('W') :       /* -W -- list waiting processes */
X            list_waiting= 1;
X            ++s;
X            break;
X          case ('Z') :       /* -Z -- list zombie processes */
X            list_zombie= 1;
X            ++s;
X            break;
X          case ('d') :       /* -d -- disc and paging */
X            printf("-d option not supported (ignored)\n");
X            ++s;
X            break;
X          case ('e') :       /* -e -- show process environment */
X            printf("-e option not supported (ignored)\n");
X            ++s;
X            break;
X          case ('f') :       /* -f -- show parent pid */
X            print_ppid= 1;
X            ++s;
X            break;
X          case ('g') :       /* -g -- show process group numbers */
X            print_pgrp= 1;
X            ++s;
X            break;
X          case ('i') :       /* -i -- initialize sps */
X            printf("-i unnecessary for this sps\n");
X            ++s;
X            break;
X          case ('j') :       /* -j -- specify spsinfo file */
X            printf("-j unnecessary for this sps\n");
X            ++s;
X            break;
X          case ('k') :       /* -k -- use file other than /dev/mem */
X            printf("-k option unsupported (ignored)\n");
X            ++s;
X            break;
X          case ('l') :       /* -l -- use long format */
X          case ('v') :       /* -v synonymous */
X            print_status=1,
X            print_nice=1;
X            print_size=1;
X            print_time=1;
X            ++s;
X            break;
X          case ('o') :       /* -o -- omit /dev/drum checks */
X            printf("-o switch unnecessary (ignored)\n");
X            ++s;
X            break;
X          case ('q') :       /* -q -- list only user times */
X            printf("-q switch unsupported (ignored)\n");
X            ++s;
X            break;
X          case ('r') :       /* -r -- repeat output */
X            if (*(++s))      /* chars follow the switch */
X              if ((*s >= '0') && (*s <= '9'))
X                repeat= atoi(s); /* it's a number */
X              else {
X                repeat= 0;
X                break;       /* not number so assume more switches */
X              }
X            else if (++a < argc) /* if no following chars try next parm */
X              if (*argv[a] != '-') /* not switch so use it */
X                repeat= atoi(argv[a]);
X              else {         /* backup and use next arg as switches */
X                a--;
X                break;
X              }
X            else             /* no numeric arg so use 0 */
X              repeat= 0;
X            while (*s)
X              s++;
X            break;
X          case ('s') :       /* -s -- use specific information file (/vmunix) */
X            printf("-s unnecessary for this sps\n");
X            ++s;
X            break;
X          case ('w') :
X            print_status= 1; /* -w -- toggles everything on */
X            print_nice= 1;
X            print_ppid= 1;
X            print_pgrp= 1;
X            print_size= 1;
X            print_time= 1;
X            ++s;
X            break;
X          case ('y') :       /* -y -- show status of each terminal line */
X            printf("-y flag not supported (ignored)\n");
X            ++s;
X            break;
X 	  default :           /* user is braindamaged */
X            ++s;
X            err= 1;
X	}
X      }
X    else
X      err= 1;
X  }
X
X/*
X * these options are handled within print_proc and are used on the
X * entire list.  force uid to zero.
X */
X
X  if (list_busy || list_detached || list_foreground ||
X      list_stopped || list_waiting || list_zombie)
X    uid= 0;
X
X  if (uid)
X    maxtab= SHORTMAXTAB;
X  else
X    maxtab= LONGMAXTAB;
X
X  if (err) {
X    printf("Usage: %s [-fglvwABNSW] [-r[secs]] [-Uuser]\n",PROG);
X    exit(1);
X  }
X
X
X/*
X * build hash file of user names to make everything fast
X */
X
X  if (!no_processes)
X    build_hash();
X
X/*
X * allocate space for process list.  ask system how many process
X * structures it has (maximum) and allocate that much space.  this
X * is also called when using -N flag in order to get max processes.
X */
X
X  sd.sd_subsys= SUBSYS_PROC;
X  sd.sd_type= PROCTYPE_CONFIG;
X  sd.sd_options= 0;
X  sd.sd_objid= 0;
X  sd.sd_addr= (char *)&pc;
X  sd.sd_size= sizeof(PROC_CONFIG);
X  if ((inq_stats(1,&sd) <0) || (sd.sd_status != 0)) {
X    perror("proc_config");
X    exit(1);
X  }
X
X/*
X * allocate memory to get process table in.
X */
X
X  if (!no_processes)
X    if ((pd= (PROC_DETAIL *)malloc(pc.pc_nprocs * sizeof(PROC_DETAIL))) == NULL) {
X      perror("proc_config: malloc"); /* not enough memory to store table */
X      exit(1);
X    }
X
X  for (;;) { /* repeat loop */
X
X/*
X * take snapshot of processes and process summary.  note that no documentation
X * existed for how to get this snapshot and i had to try every combination
X * of the PROCTYPE_DETAIL options before I found the one that actually
X * returned the full list of processes.  option PROC_DETAIL_ALL does NOT
X * return the full list as you would expect it to.  just to make sure that
X * a full process list was returned, an error check is done where we
X * calculate the actual number of processes returned by the PROCTYPE_DETAIL
X * request and compare this to the number of processes currently on the
X * system process list.  sometimes there is a conflict, although it's seldom.
X * we report inconsistencies to the user.
X */
X
X    if (!no_processes) {
X      sd.sd_subsys= SUBSYS_PROC;
X      sd.sd_type= PROCTYPE_DETAIL;
X      sd.sd_options= PROC_DETAIL_ALL|PROC_DETAIL_ALLPROC;
X      sd.sd_objid= 0;
X      sd.sd_addr= (char *)pd;
X      sd.sd_size= (pc.pc_nprocs * sizeof(PROC_DETAIL));
X    }
X
X    sd2.sd_subsys= SUBSYS_PROC;
X    sd2.sd_type= PROCTYPE_SUMMARY;
X    sd2.sd_options= 0;
X    sd2.sd_objid= 0;
X    sd2.sd_addr= (char *)&ps;
X    sd2.sd_size= sizeof(PROC_SUMMARY);
X
X/*
X * error check the call
X */
X
X    if (!no_processes) {
X      if (inq_stats(2,&sd,&sd2) < 0) {
X        perror("inq_stats");
X        exit(1);
X      }
X      if (sd.sd_status != 0) {
X        perror("proc_detail");
X        exit(1);
X      }
X    }
X    else
X      if (inq_stats(1,&sd2) < 0) {
X        perror("inq_stats");
X        exit(1);
X      }
X    if (sd2.sd_status != 0) {
X      perror("proc_summary");
X      exit(1);
X    }
X
X    if (!no_processes) {
X      if (sd.sd_sizeused/sizeof(PROC_DETAIL) != pc.pc_nprocs-ps.ps_nfree)
X        printf("proc_detail: warning -- system process count conflicts with process list\n");
X
X/*
X * build list of processes.  this is more efficient for large lists
X * because the scan for children eliminates processes that have been
X * added to the tree.  note that the number of processes is determined
X * by the actual number returned by inq_stat(), not the actual number
X * that may be running.  usually this isn't a problem but occasionally
X * the two conflict.  this way you will get a list even if the list is
X * incomplete.
X */
X
X      l= build_list(sd.sd_sizeused/sizeof(PROC_DETAIL),pd);
X
X/*
X * build the process tree and print it out.
X */
X
X      t= find_root();
X      build_tree(t);
X
X/*
X * make sure we got everything.  if not then the process tree is
X * inconsistent and we should warn the user.
X */
X
X      if (l)
X        printf("build_tree: process tree inconsistent --  processes missing\n");
X      print_tree(t,uid,0);
X    }
X
X    printf("%d processes, %d running, %d waiting, %d stopped, %d zombied\n",
X           pc.pc_nprocs-ps.ps_nfree, /* total processes */
X           ps.ps_nrunnable,          /* runnable processes */
X           ps.ps_nwaiting,           /* waiting processes */
X           ps.ps_nstopped,           /* stopped processes */
X           ps.ps_nzombies);          /* zombie processes */
X    if (repeat < 0)
X      exit(0);
X    if (repeat)
X      sleep(repeat); /* sleep for a time */
X    first_print= 1;  /* print out title again */
X    printf("\n");    /* put blank line between execs */
X  }
X}
END_OF_sps.c
if test 14993 -ne `wc -c <sps.c`; then
    echo shar: \"sps.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sps.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sps.h\"
else
echo shar: Extracting \"sps.h\" \(2613 characters\)
sed "s/^X//" >sps.h <<'END_OF_sps.h'
X/* sps.h:
X *
X * definition file for sps.c.
X *
X * jim frost 09.23.87
X *
X * this program is in the public domain.
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/statistics.h>
X#include <sys/procstats.h>
X
X/*
X * encore went and changed the way they do structure definitions
X * in procstats.h, which broke my program.  this fixes it.
X */
X
X#define STAT_DESCR   struct stat_descr
X#define PROC_CONFIG  struct proc_config
X#define PROC_SUMMARY struct proc_summary
X#define PROC_DETAIL  struct proc_detail
X
X/*
X * these are the system flags used by sps.  i really should include
X * <sys/proc.h> but this file needs some other include that i never
X * did manage to find and our proc.h file is out of date anyway.
X */
X
X/*
X * status codes (pd_state)
X */
X
X#define SWAIT  2
X#define SRUN   3
X#define SIDL   4
X#define SSTOP  5
X#define SEXEC  6
X#define SEVENT 7
X
X/*
X * flag bits (pd_flag).  only included the one i needed.
X */
X
X#define SZOMBIE 0x0008
X
X/*
X * end of stuff that should be in proc.h
X */
X
X#define PROG         "sps" /* name of program */
X#define SHORTMAXTAB  8     /* maximum we can tab on short output */
X#define LONGMAXTAB  12     /* maximum we can tab on long output */
X#define SHORT        0     /* short format flag */
X#define LONG         1     /* long format flag */
X#define ROOT_PROCESS 0     /* root process number on the process tree */
X
X/*
X * this structure is used when constructing a process tree.  first
X * all processes are placed in this list then they are deleted one
X * at a time until the tree is as complete as possible.  this is
X * much faster than just scanning the process list.
X */
X
Xtypedef struct proc_list {
X  PROC_DETAIL *p;         /* pointer to process definition structure */
X  struct proc_list *prev, /* pointer to previous process in list */
X                   *next; /* pointer to next process in list */
X} PROC_LIST;
X
X/*
X * this is the process tree structure.  it's a backward-linked binary
X * tree, basically.  sibling pointer points to other children of the
X * process' parent.
X */
X
Xtypedef struct proc_tree {
X  PROC_DETAIL *p;            /* pointer to process definition structure */
X  struct proc_tree *parent,  /* pointer to parent process */
X                   *child,   /* pointer to child process list (if any) */
X                   *sibling; /* pointer to sibling process list (if any) */
X} PROC_TREE;
X
X/*
X * function definitions
X */
X
XPROC_LIST *new_lnode();
XPROC_LIST *build_list();
XPROC_TREE *new_tnode();
XPROC_TREE *find_root();
Xvoid build_tree();
Xvoid print_tree();
Xvoid print_node();
Xvoid build_hash();
Xchar *user_name();
END_OF_sps.h
if test 2613 -ne `wc -c <sps.h`; then
    echo shar: \"sps.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sps.l -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sps.l\"
else
echo shar: Extracting \"sps.l\" \(5466 characters\)
sed "s/^X//" >sps.l <<'END_OF_sps.l'
X.if n .pl 66
X.TH SPS LOCAL
X.SH NAME
Xsps \ \ \-\ \ \ show process status
X.SH SYNOPSIS
X\fBsps\ \fP \ [ \fB-fgijkrslvwABNSUWZ\fP ]\ \ [ \fIuser\fP ]
X.SH DESCRIPTION
X\fISps\fP reports information concerning system processes.
XIt shows the current state of any process by
Xlisting information such as ownership, CPU time usage, and memory usage.
X.PP
X\fISps\fP should be used in preference to \fIps\fP(1)
Xas it is faster and the output is easier to understand.
X.SH OPTIONS
XBy default, \fIsps\fP prints basic information about one's own processes.
XThe various options described below select other processes or make
X\fIsps\fP more verbose.
X.PP
XUpper case options select processes to be described.
XLower case options specify the format of the output.
XFor instance, the options \fBBv\fP specify that \fIsps\fP
Xshould list "busy" processes in a verbose format.
X.PP
XThe following options specify the format of the listed output -
X.TP 8
X\fB-f\fP
XInclude the process-id of the parent of each process.
X.TP
X\fB-g\fP
XInclude the process group of each process.
X.TP
X\fB-r\fP
XRepeat the output indefinitely.
XIf the next argument is numeric, \fIsps\fP repeats the output with that
Xmany seconds delay between each repetition.
XOtherwise the output is repeated with no delay.
X.TP
X\fB-l\fP
X.br
X.ns
X.TP
X\fB-v\fP
XList additional information in a verbose format. See below.
X.TP
X\fB-w\fP
XList output in a wide format.  This option forces \fIsps\fP
Xto print all the command arguments, even if doing so extends the output
Xbeyond one line.
X.PP
XThe following options specify which processes are to be described -
X.TP 8
X\fB-A\fP
XList all processes.
X.TP
X\fB-B\fP
XList busy processes.  A process is considered to be busy
Xif it is immediately runnable or awaiting a fast event such as disc I/O.
X.TP
X\fB-N\fP
XShow no processes at all. Only the summary line is printed.
X.TP
X\fB-S\fP
XList stopped processes.
X.TP
X\fB-U\fP
XList only processes belonging to the following specified user.
X.TP
X\fB-W\fP
XList waiting processes.
X.TP
X\fB-Z\fP
XList zombie (exiting) processes.
X.SH OUTPUT
X\fISps\fP produces output in the following fields -
X.TP 8
X\fITy\fP
XThe terminal identifier to which the process is attached.
X.TP
X\fIUser\fP
XThe symbolic name of the process' effective user-id (see \fIexec\fP(2)
Xand \fIsetuid\fP(2)).
XThis name is defined by the system password file (/etc/passwd).
XOtherwise, an asterisk (*) or vertical bar (|) appearing in this
Xcolumn denotes that the process is an immediate relative of the
Xpreceding process.
XA bar is listed, rather than an asterisk, if both processes belong
Xto the same process group.
XIn this case, a user name is listed only if the effective user-id
Xdiffers from that of the preceding process.
X.TP
X\fIProc#\fP
XThe unique process identifier.
X.TP
X\fIPpid#\fP
XThe process-id of the process' parent.
X.TP
X\fIPgrp#\fP
XThe process group to which the process belongs.
X.TP
X\fICommand\fP
XThe command name of the process.
X.PP
XThe following additional fields are listed when \fIsps\fP
Xis invoked with one of the \fB-l\fP or \fB-v\fP options -
X.TP 8
X\fIStatus\fP
XThe process' current state.
X.RE
X.TP 8
X\fINice\fP
XThe "niceness" of the process (see \fInice\fP(2)).
X.TP
X\fIVirtual\fP
XThe virtual memory size of the process in kilobytes.
X.TP
X\fIResident\fP
XThe resident memory size of the process in kilobytes, representing
Xthe real memory devoted to the process.
X.TP
X\fITime\fP
XThe total CPU time accumulated by this process.
X(This is the sum of the system plus user times.)
X.TP
X\fIPgrp\fP
XThe process group associated with the terminal.
X.PP
XAfter listing the requested output, \fIsps\fP prints a summary line.
XThis indicates the number of processes and how many are running,
Xstopped, or zombied.
X.SH DIAGNOSTICS
X\fISps\fP reports a self-explanatory message if it is given an
Xinvalid argument list.
X.PP
XThis version of \fIsps\fP complains when an option supported by the
XBSD \fIsps\fP but not supported by the Encore version is supplied by
Xthe user.  In general the option is just ignored.
X.PP
XIf the system summary and the detailed process information table that
Xare returned by inq_stats (see \fIinq_stats\fP(2)) conflict, \fIsps\fP
Xcomplains.
X.PP
XIf inq_stats returns an incomplete process table, \fIsps\fP complains
Xand then builds the process tree as best it can.
X.SH EXAMPLES
X\fBsps -vB\fP
X.PP
X\fISps\fP describes all busy processes in a verbose manner.
X.PP
X\fBsps -wU robert -r 2\fP
X.PP
X\fISps\fP reports processes belonging to the specified user.
XIt lists the command name and displays information in a wide format.
XThe output is produced indefinitely, with a delay of two seconds between
Xeach listing.
X.SH FILES
X.ta 2.5i
X.nf
X/dev/console	Console
X/dev/tty??	Terminal and pty devices
X/etc/passwd	Password file
X.fi
X.ta
X.SH SEE ALSO
X\fIkill\fP(1), \fIps\fP(1),
X\fIvmstat\fP(1), \fIexec\fP(2), \fIinq_stats\fP(2)
X\fInice\fP(2), \fIpause\fP(2), \fIsetuid\fP(2),
X\fIwait\fP(2),
X\fItty\fP(4).
X.SH AUTHOR
X.nf
XJim Frost
Xmadd@std.com
X.fi
X.SH BUGS
XBecause the system is continually changing, the information reported by
X\fIsps\fP is only an approximation to reality.  This version of
X\fIsps\fP uses the UMAX inq_stats() call to get information about
Xprocesses.
X.PP
X\fISps\fP does not list all the detailed information shown by \fIps\fP(1).
XNor are all the options supported by \fIps\fP(1) available from \fIsps\fP.
X.PP
XThe number of options to \fIsps\fP is ridiculous.
X.PP
XThe Encore version of \fIsps\fP is not completely compatible with the
XBSD version and does not list as much information.
END_OF_sps.l
if test 5466 -ne `wc -c <sps.l`; then
    echo shar: \"sps.l\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0