From: sac@uvo.dec.com (Stephen Carpenter)
Newsgroups: comp.unix.ultrix
Subject: Version 1.6 of pads
Date: 3 Jan 93 13:04:48 GMT
Reply-To: sac@uvo.dec.com (Stephen Carpenter)
Organization: Digital Equipment Co. Ltd


This is release 1.6 of pads, a utility for listing processes active in
certain directories under ULTRIX.

The major changes in this version of pads are :-

        o Checks for files being executed.
        o Works on VAX ULTRIX (at last!) using the standard C compiler.
        o All search types are now done simultaneously.
        o 100% faster than V1.5.
        o Partition mode won't hang when accessing stale NFS file systems.


Here's an example of pads at work :-

    % pads /etc
    USER     PID   PPID  CMD         TERM    TYPE  MODE PATH
    root     1     0     init        ?       exec       /etc/init
    root     3810  1     elcsd       ?       exec       /etc/elcsd
    root     8     0     idleproc    ?       exec       /etc/startcpu
    root     139   1     ntpd        ?       file  R    /etc/ntp.conf
    root     139   1     ntpd        ?       file  W    /etc/ntp.drift
    root     156   1     mountd      ?       file  RW   /etc/rmtab
    root     24984 325   nntpd       ?       exec       /etc/nntpd
    guest    24117 127   nntpd       ?       exec       /etc/nntpd
    sac      28123 28084 tcsh        ttype   cwd        /etc
    root     3925  325   bootpd      ?       file  R    /etc/bootptab
    root     25042 325   nntpd       ?       exec       /etc/nntpd
    root     19902 1     named       ?       file  R    /etc/services
    root     19902 1     named       ?       file  R    /etc/protocols
                                                                       

The shar archive follows.


Stephen.

   //---------------------------------------------------------------------\\
  //  Stephen Carpenter               "One inode short of a file system"   \\
 //                                                                         \\
 \\  UNIX Support Specialist                   sac@uvo.dec.com              //
  \\  Digital Equipment Corporation                                        //
   \\_____________________________________________________________________// 
           

---------------------------------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:  README Makefile pads.1 pads.c
# Wrapped by sac@decuk.uvo.dec.com on Sun Jan  3 12:40:57 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2675 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X   Copyright (c) 1991, 1992, 1993 by Digital Equipment Corporation
X
X   Permission to use, copy, modify, and distribute this software for any
X   purpose and without fee is hereby granted, provided that the above
X   copyright notice and this permission notice appear in all copies, and that
X   the name of Digital Equipment Corporation not be used in advertising or
X   publicity pertaining to distribution of the document or software without
X   specific, written prior permission.
X
X   Digital Equipment Corporation makes no representations about the suitability
X   of the software described herein for any purpose.  It is provided "as is"
X   without express or implied warranty.
X
X
X			###########################
X
X
This is release 1.6 of pads, a utility for listing processes active in
certain directories under ULTRIX.
X
X
The major changes in this version of pads are :-
X
X	o Checks for files being executed.
X	o Works on VAX ULTRIX (at last!) using the standard C compiler.
X	o All search types are now done simultaneously.
X	o 100% faster than V1.5.
X	o Partition mode won't hang when accessing stale NFS file systems.
X
X
Here's an example of pads at work :-
X
X    % pads /etc
X    USER     PID   PPID  CMD         TERM    TYPE  MODE PATH
X    root     1     0     init        ?       exec       /etc/init
X    root     3810  1     elcsd       ?       exec       /etc/elcsd
X    root     8     0     idleproc    ?       exec       /etc/startcpu
X    root     139   1     ntpd        ?       file  R    /etc/ntp.conf
X    root     139   1     ntpd        ?       file  W    /etc/ntp.drift
X    root     156   1     mountd      ?       file  RW   /etc/rmtab
X    root     24984 325   nntpd       ?       exec       /etc/nntpd
X    guest    24117 127   nntpd       ?       exec       /etc/nntpd
X    sac      28123 28084 tcsh        ttype   cwd        /etc
X    root     3925  325   bootpd      ?       file  R    /etc/bootptab
X    root     25042 325   nntpd       ?       exec       /etc/nntpd
X    root     19902 1     named       ?       file  R    /etc/services
X    root     19902 1     named       ?       file  R    /etc/protocols
X
X
History
X~~~~~~~
X
I wrote pads after a colleague complained that he couldn't dismount a
file system because someone had a process active in it and if he had
a command like SHOW DEVICE /FILES from VMS he could track down the
offending process.
X
Pads is completely original work. You may find that its functionality
overlaps some programs like ofiles but I believe that it is unique
in the way it can search whole directory structures or partitions.
X
X
Author
X~~~~~~
X
X     Stephen Carpenter, UK UNIX Support, Digital Equipment Corp.
X
X     E-mail: sac@uvo.dec.com
END_OF_FILE
if test 2675 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(1408 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X###############################################################################
X#
X#  (c) Copyright 1991, 1992, 1993, Digital Equipment Corporation
X#
X#   Permission to use, copy, modify, and distribute this software for any
X#   purpose and without fee is hereby granted, provided that the above
X#   copyright notice and this permission notice appear in all copies, and that
X#   the name of Digital Equipment Corporation not be used in advertising or
X#   publicity pertaining to distribution of the document or software without
X#   specific, written prior permission.
X#
X#   Digital Equipment Corporation makes no representations about the
X#   suitability of the software described herein for any purpose.  It is
X#   provided "as is" without express or implied warranty.
X#
X# DEC is a registered trademark of Digital Equipment Corporation
X# DIGITAL is a registered trademark of Digital Equipment Corporation
X#
X###############################################################################
X
X
X#
X# Makefile for pads program
X#
X
CC = cc
CFLAGS = -O
X
X#
X# The installation directories
X#
XEXE_DIR = /usr/local/bin
MAN_DIR = /usr/man/man1
X
all: pads
X
pads: pads.c
X	$(CC) $(CFLAGS) pads.c -o pads
X
X$(EXE_DIR)/pads: pads
X	install -c -m 2755 -o root -g kmem -s pads $(EXE_DIR)
X
X$(MAN_DIR)/pads.1: pads.1
X	install -c -m 0444 -o root -g system pads.1 $(MAN_DIR)
X
install: $(EXE_DIR)/pads $(MAN_DIR)/pads.1
X
clean:
X	rm -f pads
END_OF_FILE
if test 1408 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'pads.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pads.1'\"
else
echo shar: Extracting \"'pads.1'\" \(3976 characters\)
sed "s/^X//" >'pads.1' <<'END_OF_FILE'
X.\"
X.\" ***************************************************************************
X.\" *
X.\" *  (c) Copyright 1991, 1992, 1993, Digital Equipment Corporation
X.\" *
X.\" *  Permission to use, copy, modify, and distribute this software for any
X.\" *  purpose and without fee is hereby granted, provided that the above
X.\" *  copyright notice and this permission notice appear in all copies, and
X.\" *  that the name of Digital Equipment Corporation not be used in
X.\" *  advertising or publicity pertaining to distribution of the document or
X.\" *  software without specific, written prior permission.
X.\" *
X.\" *  Digital Equipment Corporation makes no representations about the
X.\" *  suitability of the software described herein for any purpose.  It is
X.\" *  provided "as is" without express or implied warranty.
X.\" *
X.\" * DEC is a registered trademark of Digital Equipment Corporation
X.\" * DIGITAL is a registered trademark of Digital Equipment Corporation
X.\" *
X.\" ***************************************************************************
X.\"
X.TH pads 1
X.SH Name
pads \- process active directory search
X.SH Syntax
X.B pads
X.B [\-r | \-p] [\-v]
X.I directory...
X.SH Description
X
The
X.IR pads
command searches for processes which have either current
working directories matching, or open files in,
the directories specified in the argument list.
X
XFor
X.IR directory ,
you can specify either a full or partial path.  You can specify
multiple directories, separated by spaces.
X
X.SH Options
X.IP \-r
Recursively search any directories subordinate to
X.PN directory .
X
X.IP \-p
Partition mode. Search for matches based on the device numbers
of the directories specified.
X
X.IP \-v
Verbose mode. Display warning messages.
X
X.SH Output Fields
X
XFor each matched process the output fields are:
X.IP USER 10
Name of the owner of the process.
X.IP PID 10
The process identification (PID) number.
X.IP PPID 10
The process identification (PID) number of the parent process.
X.IP CMD 10
The command the process is executing.
X.IP TERM 10
The controlling terminal of the process.
X.IP TYPE 10
The file type of the process match:
X.RS
X.IP cwd 8
The PATH field is the current working directory of the process.
X.IP file 8
The PATH field is a file the process currently has open.
X.IP exec 8
The PATH field is the executable being run in the process.
X.RE
X.IP MODE 10
The open mode of the file encoded thus:
X.RS
X.IP R 4
Open for reading
X.IP W 4
Open for writing
X.IP A 4
Open for appending
X.IP S 4
Shared lock
X.IP X 4
XExclusive use
X.IP I 4
Asychronous I/O notification
X.IP B 4
Block if inuse bit is set (shared line semaphore)
X.RE
X
X.PP
Additional fields in normal and recursive modes:
X.IP PATH 10
The path name of the process's current working directory or open file.
X
X.PP
Additional fields in partition mode:
X.IP DEVICE 10
The major and minor device numbers of the partition
the process is active in.
X.IP GNODE 10
The gnode number of the process's current working directory
or open file.
X.SH Example
X
Unable to unmount a filesystem:
X.EX
X% umount /mnt
umount /dev/ra10h: Mount device busy
X.EE
One or more processes are therefore either active in
or have open files within this filesystem.
Using
X.PN pads
you can find the processes that are preventing the unmount:
X.EX
X% pads -p /mnt
USER     PID   PPID  CMD         TERM    TYPE  MODE DEVICE     GNODE
root     1079  421   csh         ttyp2   cwd        23/34      1852
root     1281  1079  cat         ttyp2   file  R    23/34      1913
X.EE
It is now possible to kill the offending processes and unmount the
filesystem:
X.EX
X% kill -9 1079 1281
X% umount /mnt
X.EE
X
X.SH Files
X.TP 1.2i
X.PN /vmunix
System namelist.
X.TP 1.2i
X.PN /dev
Searched to find terminal names.
X.TP 1.2i
X.PN /dev/kmem
Kernel memory.
X.TP 1.2i
X.PN /dev/mem
User process information.
X.TP 1.2i
X.PN /dev/drum
Swap device.
X
X.SH Note
This command is not part of ULTRIX and is NOT supported!
X
X.SH Author
X
Stephen Carpenter, UK UNIX Support, Digital Equipment Corp.
X
XE-mail: sac@uvo.dec.com
X.EE
END_OF_FILE
if test 3976 -ne `wc -c <'pads.1'`; then
    echo shar: \"'pads.1'\" unpacked with wrong size!
fi
# end of 'pads.1'
fi
if test -f 'pads.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'pads.c'\"
else
echo shar: Extracting \"'pads.c'\" \(29702 characters\)
sed "s/^X//" >'pads.c' <<'END_OF_FILE'
X/******************************************************************************
X*******************************************************************************
X*
X*  (c) Copyright 1991, 1992, 1993, Digital Equipment Corporation
X*  
X*   Permission to use, copy, modify, and distribute this software for any
X*   purpose and without fee is hereby granted, provided that the above
X*   copyright notice and this permission notice appear in all copies, and that
X*   the name of Digital Equipment Corporation not be used in advertising or
X*   publicity pertaining to distribution of the document or software without
X*   specific, written prior permission.
X*
X*   Digital Equipment Corporation makes no representations about the
X*   suitability of the software described herein for any purpose.  It is
X*   provided "as is" without express or implied warranty.
X*  
X* DEC is a registered trademark of Digital Equipment Corporation
X* DIGITAL is a registered trademark of Digital Equipment Corporation
X*
X*******************************************************************************
X******************************************************************************/
X
X
X/*
X * PROGRAM: pads (Process Active Directory Search)
X *
X *   Search for processes active in specified directories.
X *
X *
X * NOTE:
X *   This tool is NOT part of ULTRIX and is NOT a supported product.
X *
X * AUTHOR:
X *   Stephen Carpenter, UK UNIX Support, Digital Equipment Co. Ltd.
X *   E-Mail: sac@uvo.dec.com
X */
X
X
static char *version = "Version 1.6  -  3rd January 1993";
X
X
X#include <stdlib.h>
X#include <stdio.h>
X#include <string.h>
X#include <unistd.h>
X#include <nlist.h>
X#include <pwd.h>
X
X#include <sys/types.h>
X#include <sys/dir.h>
X#include <sys/gnode.h>
X#include <sys/stat.h>
X
X#include <machine/pte.h>
X#include <sys/param.h>
X#include <sys/mount.h>
X#include <sys/user.h>
X#include <sys/proc.h>
X#include <sys/text.h>
X#include <sys/tty.h>
X
X#define KERNEL
X#include <sys/file.h>
X#undef KERNEL
X
X
X/*
X * Figure out if this a V4.2 or above system with the capability of
X * extending the number of open files per process above 64 (to a max of 4096)
X */
X#ifdef NOFILE_IN_U
X#  define OFILE_EXTEND
X#else
X#  define NOFILE_IN_U NOFILE
X#endif
X
X/*
X * Define a generic NULL pointer
X */
X#define NIL_PTR(type) (type *) 0x0
X
X/*
X * Define a macro for the general error return of a syscall
X */
X#define ERROR -1
X
X/*
X * Define a macro to make a copy of a string in new storage
X */
X#define strdup(x) strcpy((char *) malloc (1 + strlen (x)), (x))
X
X/*
X * The maximum number of characters we're going to print on a line
X */
X#define MAX_LINE_WIDTH 132
X
X/*
X * Define a boolean data type
X */
typedef enum Boolean {False, True} Boolean;
X
X/*
X * Signify the type of match
X */
typedef enum Match {Cwd, File, Exec} Match;
X
X/*
X * Define a record containing the gnode info we require
X */
struct entry
X{
X  dev_t device;
X  gno_t number;
X};
X
X/*
X * Define a directory list record
X */
struct entries
X{
X  struct entry *dir_entry;
X  char *entry_name;
X  Boolean is_directory;
X} *dir_entries;
X
int number_of_entries;
X
X/*
X * Define the device list record
X */
struct devent
X{
X  dev_t device;
X  char *dev_name;
X} *dev_table;
X
int number_of_devents;
X
X/*
X * File descriptors for the various type of system memory
X */
int kmem, mem, swap;
X
X/*
X * Define the name lists were are interested in
X */
X#define N_PROC 0
X#define N_NPROC 1
struct nlist name_list[] = {
X  { "_proc" },
X  { "_nproc" },
X  { 0 }
X};
X
X/*
X * Name by which the program was invoked
X */
char *program;
X
X/*
X * Flags for the command line switches
X */
Boolean recursive_mode = False;
Boolean partition_mode = False;
Boolean verbose_mode = False;
X
X
X/*
X * FUNCTION: Lseek
X *   Like lseek but with error handling
X *
X * ARGUMENTS:
X *   file      - The file descriptor to seek
X *   offset    - The offset from the beginning of the file to seek
X *   file_name - The name of the file being seeked
X *
X * RETURN VALUE:
X *   None.
X */
X
void
Lseek (file, offset, file_name)
X  int file;
X  long offset;
X  char *file_name;
X{
X  if (lseek (file, (off_t) offset, 0) == ERROR)
X  {
X    fprintf (stderr, "%s: Failed to seek to offset %d in %s\n",
X             program, offset, file_name);
X    exit (1);
X  }
X}
X
X
X/*
X * FUNCTION: read_from
X *   Combine seeking and reading into one operation.
X *
X * ARGUMENTS:
X *   file      - The file descriptor to read from.
X *   offset    - The offset from the beginning of the file to seek.
X *   variable  - The variable to read information into.
X *   size      - The number of bytes to read.
X *   file_name - The name of the file being read.
X *
X * RETUTN VALUE:
X *   None.
X */
X
void
read_from (file, offset, variable, size, file_name)
X  int file;
X  off_t offset;
X  void *variable;
X  size_t size;
X  char *file_name;
X{
X  if (lseek (file, offset, 0) == ERROR)
X  {
X    fprintf (stderr, "%s: Failed to seek to offset %d in %s\n",
X             program, offset, file_name);
X    exit (1);
X  }
X
X  if (read (file, (char *) variable, (int) size) != size)
X  {
X    fprintf (stderr, "%s: Failed to read %d bytes at 0x%x from %s\n",
X             program, size, variable, file_name);
X    exit (1);
X  }
X}
X
X
X/*
X * FUNCTION: full_name
X *   Concatenate a file name to a directory name
X *
X * ARGUMENTS:
X *   dir  - The name of the diretory where the file resides
X *   file - The name of the file.
X *
X * RETURN VALUE:
X *   A pointer to a string containing the path name of the file.
X */
X
char *
full_name (dir, file)
X  char *dir;
X  char *file;
X{
X  char *path = (char *) malloc (MAXPATHLEN * sizeof (char));
X
X  path = strcpy (path, dir);
X  if (dir[strlen (dir) - 1] != '/')
X    path = strcat (path, "/");
X  path = strcat (path, file);
X
X  return (path);
X}
X
X
X/*
X * FUNCTION: expand_name
X *   Expand a file name to a full path name
X *
X * ARGUMENTS:
X *   file - The name of the file.
X *
X * RETURN VALUE:
X *   A string containing the full path name of the file.
X */
X
char *
expand_file (file)
X  char *file;
X{
X  char *path = (char *) malloc (MAXPATHLEN * sizeof (char));
X
X  if (*file == '/')
X    return (strcpy (path, file));
X
X  /*
X   * Check if the file is the process cwd
X   */
X  if (!strcmp (file, "."))
X  {
X    path = (char *) getwd (path);
X
X    return (path);
X  }
X
X  if (!strncmp (file, "./", 2))
X    path = strcat ((char *) getwd (path), ++file);
X  else {
X    path = strcat ((char *) getwd (path), "/");
X    path = strcat (path, file);
X  }
X
X  return (path);
X}
X
X
X/*
X * FUNCTION: get_tty_name
X *   Match a tty structure against the device list
X *
X * ARGUMENTS:
X *   ttyp - A pointer to the tty structure of the controlling terminal.
X *
X * RETURN VALUE:
X *   A string containing the name of the tty device.
X */
X
char *
get_tty_name (tty_ptr)
X  struct tty *tty_ptr;
X{
X  int index;
X  struct tty control_tty;
X
X  if (tty_ptr != NIL_PTR (struct tty))
X  {
X    /*
X     * Read the tty structure out of kernel memory
X     */
X    read_from (kmem, (off_t) tty_ptr, &control_tty,
X               sizeof (struct tty), "/dev/kmem");
X
X    /*
X     * Search the device list for this tty
X     */
X    for (index = 0; index < number_of_devents; index++)
X      if ((dev_table + index)->device == control_tty.t_dev)
X        return ((dev_table + index)->dev_name);
X  }
X
X  /*
X   * The process is not associated with a terminal
X   */
X  return ("?");
X}
X
X
X/*
X * FUNCTION: create_device_table
X *   Create a list of the character devices found in /dev
X *
X * ARGUMENTS:
X *   None.
X *
X * RETURN VALUE:
X *   None.
X */
X
void
create_device_table ()       
X{
X  struct direct *dir_entry;
X  DIR *dir_ptr;
X  struct stat stat_info;
X
X  dev_table = NIL_PTR (struct devent);
X  number_of_devents = 0;
X
X  if ((dir_ptr = opendir ("/dev")) == NULL)
X  {
X    fprintf (stderr, "%s: ", program);
X    perror ("Can't open directory /dev");
X    return;
X  }
X
X  /*
X   * Check each device
X   */
X  while ((dir_entry = readdir(dir_ptr)) != NULL)
X  {
X    /*
X     * Find out the type of device
X     */
X    if (lstat (full_name ("/dev", dir_entry->d_name), &stat_info) == ERROR)
X    {
X      fprintf (stderr, "%s: Can't stat ", program);
X      perror (full_name ("/dev", dir_entry->d_name));
X      continue;
X    }
X
X    /*
X     * Add any character devices to the list
X     */
X    if ((stat_info.st_mode & S_IFMT) == S_IFCHR)
X    {
X      number_of_devents++;
X      dev_table =
X         (struct devent *) realloc (dev_table,
X                                    number_of_devents * sizeof (struct devent));
X
X      (dev_table + number_of_devents - 1)->dev_name = strdup (dir_entry->d_name);
X      (dev_table + number_of_devents - 1)->device = stat_info.st_rdev;
X    }
X  }
X  closedir(dir_ptr);
X}
X
X
X/*
X * FUNCTION: add_dir_entry
X *   Add an entry to the directory list
X *
X * ARGUMENTS:
X *   entry_name - A string containing the name of the direcory to add
X *
X * RETURN VALUE:
X *   None.
X */
X
void
add_dir_entry (entry_name, is_directory)
X  char *entry_name;
X  Boolean is_directory;
X{
X  struct entry *the_entry = (struct entry *) malloc (sizeof (struct entry));
X  struct stat stat_info;
X  struct fs_data mount_info;
X
X  if (partition_mode)
X  {
X    /*
X     * In partition mode we use getmnt() rather than stat() to avoid
X     * hangs due to NFS server problems
X     */
X    if (getmnt (0, &mount_info, 0, NOSTAT_ONE, entry_name) == ERROR)
X    {
X      fprintf (stderr, "%s: Can't get mount info for ", program);
X      perror (entry_name);
X      return;
X    }
X    the_entry->device = mount_info.fd_req.dev;
X
X  } else {
X    /*
X     * Get the gnode info
X     */
X    if (lstat (entry_name, &stat_info) == ERROR)
X    {
X      fprintf (stderr, "%s: Can't stat ", program);
X      perror (entry_name);
X      return;
X    }
X
X    the_entry->device = stat_info.st_dev;
X    the_entry->number = stat_info.st_ino;
X  }
X
X  number_of_entries++;
X  dir_entries = (struct entries *)
X                    realloc (dir_entries,
X                             number_of_entries * sizeof (struct entries));
X
X  /*
X   * Store the gnode info
X   */
X  (dir_entries + number_of_entries - 1)->dir_entry = the_entry;
X
X  /*
X   * Store the directory name
X   */
X  (dir_entries + number_of_entries - 1)->entry_name = strdup (entry_name);
X
X  (dir_entries + number_of_entries - 1)->is_directory = is_directory;
X}
X
X
X/*
X * FUNCTION: recursive_dir_add
X *   Recursively search the direcory structure for directory entries
X *
X * ARGUMENTS:
X *   entry_name   - A string containing the name of the direcory search
X *   do_recursion - A flag to determine if actual recursion is required
X *
X * RETURN VALUE:
X *   None.
X */
X
void
recursive_dir_add (entry_name, do_recursion)
X  char *entry_name;
X  Boolean do_recursion;
X{
X  DIR *dir_ptr;
X  struct direct *dir_entry;
X  struct stat stat_info;
X  char *file;
X  char *error_string;
X
X  /*
X   * Open the directory for reading
X   */
X  if ((dir_ptr = opendir (entry_name)) == NULL)
X  {
X    error_string = (char *) malloc (sizeof (char) * (strlen (entry_name) + 18));
X    sprintf (error_string, "%s: Error opening %s", program, entry_name);
X    perror (error_string);
X    return;
X  }
X
X  /*
X   * Skip '.' & '..' directories
X   */
X  dir_entry = readdir (dir_ptr);
X  dir_entry = readdir (dir_ptr);
X
X  for (dir_entry = readdir (dir_ptr); dir_entry != NULL;
X       dir_entry = readdir (dir_ptr))
X  {
X    /*
X     * Get the directory info
X     */
X    file = full_name (entry_name, dir_entry->d_name);
X
X    if (lstat (file, &stat_info) == ERROR)
X    {
X      fprintf (stderr, "%s: Can't stat ", program);
X      perror (file);
X      continue;
X    }
X
X    if ((stat_info.st_mode & S_IFMT) == S_IFDIR)
X    {
X      /*
X       * It's a directory so add the entry to the directory list
X       * and search this directory for sub-directories.
X       */
X      add_dir_entry (file, True);
X
X      if (do_recursion)
X        recursive_dir_add (file, True);
X    } else
X      add_dir_entry (file, False);
X
X    /*
X     * Free up redundant space
X     */
X    free (file);
X  }
X  closedir (dir_ptr);
X}
X
X
X/*
X * FUNCTION: create_dir_table
X *   Initialise and start off the directory list
X *
X * ARGUMENTS:
X *   argc    - Argument count
X *   argv    - Argument vector
X *
X * RETURN VALUE:
X *   None.
X */
X
void
create_dir_table (dir_argc, dir_argv)
X  int dir_argc;
X  char *dir_argv[];
X{
X  struct stat stat_info;
X  int index;
X  char *path;
X
X  /*
X   * Initialise the directory list
X   */
X  number_of_entries = 0;
X  dir_entries = NIL_PTR (struct entries);
X
X  /*
X   * Add the directories in the arguments to the directory list
X   */
X  for (index = 0; index < dir_argc; index++)
X  {
X    path = expand_file (dir_argv[index]);
X    if (partition_mode)
X    {
X        add_dir_entry (path, True);
X    } else 
X      if (recursive_mode)
X      {
X        add_dir_entry (path, True);
X        recursive_dir_add (path, True);
X      } else {
X        if (lstat (path, &stat_info) == ERROR)
X        {
X          fprintf (stderr, "%s: Can't stat ", program);
X          perror (path);
X          exit (1);
X        }
X
X        if ((stat_info.st_mode & S_IFMT) == S_IFDIR)
X        {
X          add_dir_entry (path, True);
X          recursive_dir_add (path, False);
X        } else
X          add_dir_entry (path, False);
X      }
X    free (path);
X  }
X}
X
X
X/*
X * FUNCTION: get_user_area
X *   Read the user area for a given process from memory or swap
X *
X * ARGUMENTS:
X *   the_proc - The process to get the user area for
X *
X * RETURN VALUE:
X *   A pointer to copy of the user area of the process
X */
X
struct user *
get_user_area (the_proc)
X  struct proc *the_proc;
X{
X  struct dmap stack_dmap;            /* Stack segment disk map                */
X  struct pte proc_pte[UPAGES];       /* Page table entries for process        */
X  char *user_area;                   /* The user area of the process          */
X  int user_block;                    /* File system block number of user area */
X  int clusters;                      /* Number of page clusters               */
X  int pagenum;                       /* Page number                           */
X  long pageaddr;                     /* Page address                          */
X
X  if ((the_proc->p_sched & SLOAD) == 0)
X  {
X    /*
X     * The process is currently swapped out
X     * We therefore need to pull the info out of the swap device
X     */
X
X    /*
X     * Read disk map for stack segment
X     */
X    read_from (kmem, (off_t) the_proc->p_smap,
X               &stack_dmap, sizeof (struct dmap), "/dev/kmem");
X
X    /*
X     * Read address of disk address of user page table
X     */
X    read_from (kmem, (off_t) stack_dmap.dm_ptdaddr,
X               &user_block, sizeof (int), "/dev/kmem");
X
X    /*
X     * Grab memory for the user area variable
X     */
X    user_area = (char *) malloc (sizeof (struct user));
X
X    /*
X     * Now read in the user area from swap
X     */
X    Lseek (swap, (long) dtob (user_block), "/dev/drum");
X    if (read (swap, user_area, sizeof (struct user)) != sizeof (struct user))
X    {
X      fprintf (stderr,
X               "%s: Can't read user area of process %d from /dev/drum\n",
X               program, the_proc->p_pid);
X
X      free (user_area);
X      return (NIL_PTR (struct user));
X    } else
X      return ((struct user *) user_area);
X
X  } else {
X
X    /*
X     * The process is in memory. Read the process pte from virtual memory.
X     */
X    Lseek (kmem, (long) the_proc->p_addr, "/dev/kmem");
X    if (read (kmem, (char *) &proc_pte[0], sizeof (proc_pte))
X        !=  sizeof (proc_pte))
X    {
X      fprintf (stderr,
X               "%s: Can't read pte for user page of process %d from /dev/kmem\n",
X               program, the_proc->p_pid);
X              
X      return (NIL_PTR (struct user));
X    }
X
X    /*
X     * Grab memory to hold the user pages
X     */
X    user_area = (char *) malloc (UPAGES * NBPG);
X
X    /*
X     * Read in the user area pages from memory
X     */
X    clusters = (sizeof (struct user) + NBPG*CLSIZE - 1) / (NBPG*CLSIZE);
X    while (--clusters >= 0)
X    {
X      pagenum = clusters * CLSIZE;
X      pageaddr = ctob (proc_pte[pagenum].pg_pfnum);
X      Lseek (mem, pageaddr, "/dev/mem");
X      if (read (mem, user_area + (NBPG * pagenum), CLSIZE*NBPG) != CLSIZE*NBPG)
X      {
X        fprintf (stderr,
X                 "%s: Can't read page 0x%x of process %d from /dev/mem\n",
X                 program, (int) pageaddr, the_proc->p_pid);
X
X        free (user_area);
X        return (NIL_PTR (struct user));
X      }
X    }
X    return ((struct user *) user_area);
X  }
X}
X
X
X/*
X * FUNCTION: file_open_mode
X *   Get the open mode of a file.
X *
X * ARGUMENTS:
X *   flags - The file mode flags.
X *
X * RETURN VALUE:
X *   A string containing the mode acronym.
X */
X
char *
file_open_mode (flags)
X  int flags;
X{
X  char *mode = (char *) malloc (7 * sizeof (char));
X  int pos = 0;
X
X  if (flags & FREAD)
X    mode[pos++] = 'R';
X
X  if (flags & FWRITE)
X    if (flags & FAPPEND)
X      mode[pos++] = 'A';
X    else
X      mode[pos++] = 'W';
X
X  if (flags & FSHLOCK)
X    mode[pos++] = 'S';
X
X  if (flags & FEXLOCK)
X    mode[pos++] = 'X';
X
X  if (flags & FASYNC)
X    mode[pos++] = 'I';
X
X  if (flags & FBLKINUSE)
X    mode[pos++] = 'B';
X
X  mode[pos] = '\0';
X
X  return (mode);
X}
X
X
X/*
X * FUNCTION: print_info
X *   Print a line of process information
X *
X * ARGUMENTS:
X *   user  - A string containing the user name of the process
X *   pid   - The process ID of the process
X *   cmd   - A string containing the command being executed by the process
X *   node  - The gnode of the CWD or open file
X *   type  - What sort of match is this?
X *   ttyp  - A pointer to the controlling terminal of the process
X *   flags - The flags the gnode was opened with
X *   file  - A string containing the name of the file or directory
X *
X * RETURN VALUE:
X *   None.
X */
X
void
print_info (user, pid, ppid, cmd, node, type, ttyp, flags, file)
X  char *user;
X  int pid;
X  int ppid;
X  char *cmd;
X  struct gnode node;
X  Match type;
X  struct tty *ttyp;
X  int flags;
X  char *file;
X{
X  char line[MAX_LINE_WIDTH];
X  char pid_string[10];
X  char *mode_string;
X  char *tty;
X
X  tty = get_tty_name (ttyp);
X
X  /*
X   * Build up the info line for each process
X   */
X  memset (line, ' ', sizeof (line));
X
X  strncpy (&line[0], user, strlen (user));
X  sprintf (&pid_string[0], "%d", pid);
X  strncpy (&line[9],  pid_string, strlen (pid_string));
X  sprintf (&pid_string[0], "%d", ppid);
X  strncpy (&line[15],  pid_string, strlen (pid_string));
X  strncpy (&line[21], cmd, strlen (cmd));
X  strncpy (&line[33], tty, strlen (tty));
X
X  switch (type)
X  {
X    case Cwd:
X      strncpy (&line[41], "cwd", 3);
X      break;
X    case File:
X      strncpy (&line[41], "file", 4);
X      break;
X    case Exec:
X      strncpy (&line[41], "exec", 4);
X  }
X
X  if (type == File)
X  {
X    mode_string = file_open_mode (flags);
X    if (partition_mode)
X    {
X      strncpy (&line[47], mode_string, strlen (mode_string));
X      sprintf (&line[52], "%d/%d  \t%d",
X               major(node.g_req.gr_dev), minor(node.g_req.gr_dev),
X               (int) node.g_req.gr_number);
X    } else {
X      strncpy (&line[47], mode_string, strlen (mode_string));
X      sprintf (&line[52], "%.*s", (MAX_LINE_WIDTH - 29), file);
X    }
X  } else
X    if (partition_mode)
X      sprintf (&line[52], "%d/%d\t%d",
X               major(node.g_req.gr_dev), minor(node.g_req.gr_dev),
X               (int) node.g_req.gr_number);
X    else
X      sprintf (&line[52], "%.*s", (MAX_LINE_WIDTH - 29), file);
X
X  /*
X   * Output the line info
X   */
X  puts (line);
X}
X
X
X/*
X * FUNCTION: gnode_check
X *   Check if the process CWD or open file gnode is in the
X *   direcory entries list.
X *
X * ARGUMENTS:
X *   user  - A string containing the user name of the process
X *   pid   - The process ID of the process
X *   cmd   - A string containing the command being executed by the process
X *   node  - The gnode of the CWD or open file
X *   flags - The flags the gnode was opened with
X *   ttyp  - A pointer to the controlling terminal of the process
X *   type  - What sort of match is this?
X *
X * RETURN VALUE:
X *   None.
X */
X
void
gnode_check (user, pid, ppid, cmd, node, flags, ttyp, type)
X  char *user;
X  int pid;
X  int ppid;
X  char *cmd;
X  struct gnode node;
X  int flags;
X  struct tty *ttyp;
X  Match type;
X{
X  int index;
X
X  /*
X   * Check if this gnode is in the directory list
X   */
X  for (index = 0; index < number_of_entries; index++)
X  {
X    /*
X     * Do we have the right device?
X     */
X    if (node.g_req.gr_dev == (dir_entries + index)->dir_entry->device)
X      if (partition_mode)
X        /*
X         * We're in partition mode and therefore we have a match
X         */
X        print_info (user, pid, ppid, cmd, node, type, ttyp, flags, "");
X      else
X        if (type == Cwd && !(dir_entries + index)->is_directory)
X          /*
X           * The cwd was actually a file! 
X           * Is this an OS bug??
X           */
X          continue;
X        else
X          /*
X           * Does the gnode check out?
X           */
X          if (node.g_req.gr_number == (dir_entries + index)->dir_entry->number)
X            print_info (user, pid, ppid, cmd, node, type, ttyp, flags,
X                        (dir_entries + index)->entry_name);
X  }
X}
X
X
X/*
X * FUNCTION: check_procs
X *   Search the process table for processes with current working directories
X *   matching the directory list entries
X *
X * ARGUMENTS:
X *   procbase - The address of the start of the process table
X *   nproc    - The size of the process table
X *
X * RETURN VALUE:
X *   None.
X */
X
void
check_procs (procbase, nproc)
X  long procbase;
X  int nproc;
X{
X  struct user *proc_uarea = NIL_PTR (struct user);
X  struct proc the_proc;
X  struct file **ofile_table_extension;
X  struct file open_file;
X  struct text text;
X  struct gnode node;
X  struct passwd *passwd_entry;
X  int index, index1;
X
X  /*
X   * Print title bar
X   */
X  if (partition_mode)
X    printf ("USER     PID   PPID  CMD         TERM    TYPE  MODE DEVICE\tGNODE\n");
X  else
X    printf ("USER     PID   PPID  CMD         TERM    TYPE  MODE PATH\n");
X
X#ifdef OFILE_EXTEND
X  /*
X   * Reserve space for the extended open file table of a process
X   */
X  ofile_table_extension =
X     (struct file **) malloc ((getdtablesize () - NOFILE_IN_U)
X                              * sizeof (struct file *));
X#endif
X
X  /*
X   * Walk the process table
X   */
X  for (index = 0; index < nproc; index++)
X  {
X    /*
X     * Read in the process structure
X     */
X    read_from (kmem, (off_t) (procbase + (long)(index * sizeof (struct proc))),
X               &the_proc, sizeof (struct proc), "/dev/kmem");
X
X    /*
X     * Skip over empty proc slots
X     */
X    if (the_proc.p_stat == 0 || the_proc.p_pid == 0)
X      continue;
X
X    /*
X     * Ignore zombies
X     */
X    if (the_proc.p_stat == SZOMB)
X    {
X      if (verbose_mode)
X        printf ("%s: Process %d is a zombie!\n", program, the_proc.p_pid);
X      continue;
X    }
X
X    /*
X     * Free up any malloced user area memory
X     */
X    if (proc_uarea != NIL_PTR (struct user))
X      free (proc_uarea);
X
X    /*
X     * Check for 'soft' errors in reading the user area
X     */
X    if ((proc_uarea = get_user_area (&the_proc)) == 0)
X      continue;
X
X    /*
X     * Get password info (we really want the user name)
X     */
X    passwd_entry = getpwuid (the_proc.p_uid);
X
X
X    /***
X     ** First check: Open Files
X     ***/
X
X#ifdef OFILE_EXTEND
X    /*
X     * If we have more than NOFILE_IN_U open files
X     * then read in the extended open file table.
X     */
X    if (proc_uarea->u_lastfile >= NOFILE_IN_U)
X    {
X      read_from (kmem, (off_t) proc_uarea->u_ofile_of, ofile_table_extension,
X                 proc_uarea->u_of_count * sizeof (struct file *), "/dev/kmem");
X    }
X#endif
X    for (index1 = 0; index1 <= proc_uarea->u_lastfile; index1++)
X    {
X      if (index1 < NOFILE_IN_U)
X      {
X        if (proc_uarea->u_ofile[index1] == NIL_PTR (struct file))
X          continue;
X
X        read_from (kmem, (off_t) proc_uarea->u_ofile[index1],
X                   &open_file, sizeof (open_file), "/dev/kmem");
X      } else {
X#ifdef OFILE_EXTEND
X        /*
X         * The open file is in the extended table
X         */
X        if (*(ofile_table_extension + index1 - NOFILE_IN_U)
X            == NIL_PTR (struct file))
X          continue;
X
X        read_from (kmem,
X                   (off_t) *(ofile_table_extension + index1 - NOFILE_IN_U),
X                   &open_file, sizeof (open_file), "/dev/kmem");
X#endif
X      }
X
X      /*
X       * The reference count should be at least one
X       */
X      if (open_file.f_count == 0)
X        continue;
X
X      /*
X       * Make sure that this file has a gnode
X       */
X      if (open_file.f_data == NIL_PTR (char))
X        continue;
X
X      /*
X       * Read in the gnode associated with this file
X       */
X      Lseek (kmem, (long) open_file.f_data, "/dev/kmem");
X      if (read (kmem, &node, sizeof (node)) !=  sizeof (node))
X      {
X        if (verbose_mode)
X          printf ("%s: Unable to read open file gnode 0x%x of process %d\n",
X                  program, open_file.f_data, the_proc.p_pid);
X      } else
X
X        /*
X         * Check if this gnode is in the directory list
X         */
X        gnode_check (passwd_entry->pw_name, the_proc.p_pid, the_proc.p_ppid,
X                     proc_uarea->u_comm, node, open_file.f_flag,
X                     the_proc.p_ttyp, File);
X    }
X
X
X    /***
X     ** Second check: Process Current Working Directory
X     ***/
X
X    /*
X     * Read in the current working directory gnode
X     */
X    Lseek (kmem, (long) proc_uarea->u_cdir, "/dev/kmem");
X    if (read (kmem, &node, sizeof (node)) !=  sizeof (node))
X    {
X      fprintf (stderr,
X               "%s: Error reading current directory gnode 0x%x of process %d\n",
X               program, proc_uarea->u_cdir, the_proc.p_pid);
X    } else
X
X      /*
X       * Check if this gnode is in the directory list
X       */
X      gnode_check (passwd_entry->pw_name, the_proc.p_pid, the_proc.p_ppid,
X                   proc_uarea->u_comm, node, 0, the_proc.p_ttyp, Cwd);
X
X
X    /***
X     ** Third check: Executables
X     ***/
X
X    /*
X     * Make sure there is a valid gnode (Wot no exec?)
X     */
X    if (the_proc.p_textp == NIL_PTR (struct text))
X      continue;
X
X    /*
X     * Read info about the text segment of this process
X     */
X    read_from (kmem, (off_t) the_proc.p_textp, &text,
X               sizeof (struct text), "/dev/kmem");
X
X    /*
X     * Read in the executable gnode
X     */
X    Lseek (kmem, (long) text.x_gptr, "/dev/kmem");
X    if (read (kmem, &node, sizeof (node)) !=  sizeof (node))
X    {
X      fprintf (stderr,
X               "%s: Error reading executable gnode 0x%x of process %d\n",
X               program, text.x_gptr, the_proc.p_pid);
X      continue;
X    }
X
X    /*
X     * Check if this gnode is in the directory list
X     */
X    gnode_check (passwd_entry->pw_name, the_proc.p_pid, the_proc.p_ppid,
X                 proc_uarea->u_comm, node, 0, the_proc.p_ttyp, Exec);
X  }
X}
X
X
X/*
X * FUNCTION: main
X *   Parse arguments and open device files
X *
X * ARGUMENTS:
X *   argc - The argument count
X *   argv - The argument vector
X *
X * RETURN VALUE:
X *   None.
X */
X
void
main (argc, argv)
X  int argc;
X  char *argv[];
X{
X  long procbase;        /* Start address of the process table          */
X  int nproc;            /* Number of processes in the process table    */
X
X  int option;
X  Boolean error_flag = False;
X
X  extern int optind, opterr;  /* Global variables used by getopt */
X  extern char *optarg;
X
X  /*
X   * Remember the program name
X   */
X  program = *argv;
X
X  /*
X   * Check the arguments
X   */
X  if (argc < 2)
X    error_flag = True;
X  else
X    while ((option = getopt (argc, argv, "qQrRpPvV")) != EOF)
X      switch (option)
X      {
X        case 'R':
X        case 'r':
X          if (partition_mode)
X            error_flag = True;
X          else
X            recursive_mode = True;
X          break;
X
X        case 'Q':
X        case 'q':
X        case 'P':
X        case 'p':
X          if (recursive_mode)
X            error_flag = True;
X          else
X            partition_mode = True;
X          break;
X
X        case 'V':
X        case 'v':
X          verbose_mode = True;
X          break;
X
X        case '?':
X          error_flag = True;
X      }
X
X  if (verbose_mode)
X    printf ("%s: %s\n", program, version);
X
X  if (optind == argc)
X    error_flag = True;
X
X  if (error_flag)
X  {
X    fprintf (stderr, "%s: incorrect syntax\n", program);
X    fprintf (stderr,
X             "%s: correct syntax: %s [-r | -p] [-v] directory...\n",
X             program, program);
X    exit (0);
X  }
X
X  /*
X   * Open the memory devices
X   */
X  if ((mem = open ("/dev/mem", 0)) < 0)
X  {
X    fprintf (stderr, "%s: ", program);
X    perror ("Can't open /dev/mem");
X    exit (1);
X  }
X
X  if ((kmem = open ("/dev/kmem", 0)) < 0)
X  {
X    fprintf (stderr, "%s: ", program);
X    perror ("Can't open /dev/kmem");
X    exit (1);
X  }
X
X  if ((swap = open ("/dev/drum", 0)) < 0)
X  {
X    fprintf (stderr, "%s: ", program);
X    perror ("Can't open /dev/drum");
X    exit (1);
X  }
X
X  /*
X   * Get the kernel name list
X   */
X  if (nlist ("/vmunix", name_list) == ERROR)
X  {
X    fprintf (stderr, "%s: Couldn't read name list from /vmunix\n", program);
X    exit (1);
X  }
X
X  if (name_list[N_PROC].n_type == 0)
X  {
X    fprintf (stderr,
X             "%s: Couldn't find symbol \"_proc\" in /vmunix\n", program);
X    exit (1);
X  }
X
X  if (name_list[N_NPROC].n_type == 0)
X  {
X    fprintf (stderr,
X             "%s: Couldn't find symbol \"_nproc\" in /vmunix\n", program);
X    exit (1);
X  }
X
X  /*
X   * Find the start of the process table
X   */
X  read_from (kmem, (off_t) name_list[N_PROC].n_value,
X             &procbase, sizeof (procbase), "/dev/kmem");
X
X  /*
X   * Find the size of the process table
X   */
X  read_from (kmem, (off_t) name_list[N_NPROC].n_value,
X             &nproc, sizeof (nproc), "/dev/kmem");
X 
X  /*
X   * Create the terminal list
X   */
X  create_device_table ();
X
X  /*
X   * Create the list of directories
X   */
X  create_dir_table ((argc - optind), (argv + optind));
X
X  /*
X   * Check for processes active in those directories
X   */
X  if (number_of_entries > 0)
X    check_procs (procbase, nproc);
X
X  exit (0);
X}
END_OF_FILE
if test 29702 -ne `wc -c <'pads.c'`; then
    echo shar: \"'pads.c'\" unpacked with wrong size!
fi
# end of 'pads.c'
fi
echo shar: End of shell archive.
exit 0
Newsgroups: comp.unix.ultrix