[comp.sources.unix] v20i046: Print current directory of processes

rsalz@uunet.uu.net (Rich Salz) (10/24/89)

Submitted-by: Cliff Spencer <cspencer@spdcc.com>
Posting-number: Volume 20, Issue 46
Archive-name: process-cwd


Here is cwd, it performs a pwd() the hard way.  This is a program that
prints out the current working directory of processes on your BSD based
system. It is probably most useful for amusement purposes. Cwd won't work
on NFS hosts. I welcome all bug reports.
						Cliff Spencer
						spdcc!lemming!cspencer


# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  cd /u(Files
# unpacked will be owned by you and have default permissions.)

echo x - README
sed -e 's/^X//' > "README" << '//E*O*F README//'
XTue Oct 17 21:52:01 EDT 1989
XHere is cwd, a program that prints out the current working directory
Xof processes on your BSD based system. It is probably most useful for 
Xamusement purposes. Cwd won't work on NFS hosts. I welcome all bug 
Xreports.
X						Cliff Spencer
X						spdcc!lemming!cspencer
X
//E*O*F README//

echo x - Makefile
sed -e 's/^X//' > "Makefile" << '//E*O*F Makefile//'
X#Makefile for cwd
X#
X# One of bsd, ultrix, or sun should be defined
X# if you're using gcc or BSD4.3, you'll need to define the
X# appropriate symbol in CFLAGS
XCC=cc
XRM=/bin/rm
X
XCFLAGS=-g 
X
Xcwd: cwd.o 
X	$(CC) $(CFLAGS) -o cwd cwd.o
Xclean:
X	$(RM) -f cwd.o
X
Xclobber: clean
X	$(RM) -f cwd
//E*O*F Makefile//

echo x - cwd.c
sed -e 's/^X//' > "cwd.c" << '//E*O*F cwd.c//'
X
X/*
X * This program may be freely redistributed but this entire comment 
X * MUST remain intact.
X *
X * Copyright (c) 1989 Clifford Spencer
X *
X * usage: cwd [usernames]
X *
X * for each process on the system print full path of current directory
X * as found in u.u_cdir. Note that this program requires read permission
X * on the raw disks
X * 
X *
X * This program has been compiled successfully on Ultrix 3.0, SunOS 3.5, 
X * and BSD4.3. It won't work for NFS mounted filesystems because of lack 
X * of access to remote devices
X * 
X * author: Cliff Spencer (cspencer@lemming.uucp)
X * Tue Oct 17 21:53:30 EDT 1989
X *
X */
X#if !defined(ultrix) && !defined(bsd) && !defined(sun)
X#define sun
X#endif
X#include <sys/param.h>
X#if !defined(bsd)       /* user.h pulls this in on BSD */
X#include <sys/time.h>
X#endif
X#if defined(sun)
X#include <sys/vnode.h>
X#endif
X#if defined(bsd)
X#include <sys/fs.h>
X#else
X#include <ufs/fs.h>
X#endif
X#include <sys/dir.h>
X#include <sys/file.h>
X#if defined(ultrix) || defined(bsd)
X#include <sys/inode.h>
X#include <sys/mount.h>
X#endif
X#if defined(ultrix)
X#include <sys/gnode.h>
X#endif
X#if defined(sun)
X#include <ufs/mount.h>
X#define KERNEL  /* this is to pick up define for VTOI */
X#include <ufs/inode.h>
X#undef KERNEL
X#endif
X#include <machine/pte.h>
X#include <sys/buf.h>
X#include <sys/user.h>
X#include <sys/proc.h>
X#include <sys/stat.h>
X#include <pwd.h>
X#include <utmp.h>
X#include <stdio.h>
X#include <fstab.h>
X#include <nlist.h>
X#include <errno.h>
X
X/* 
X * handy info to keep around on each mounted filesystem
X */
Xstruct mount_stuff {
X        dev_t   dev;                    /* major/minor of fs */
X        char devname[MAXPATHLEN + 1];   /* name of dev */
X        char path[MAXPATHLEN + 1];      /* name of mount point */
X        int fd;
X        struct fs *fs;
X};
X
X
Xstruct nlist nlst[] = {
X        { "_proc" },
X#define X_PROC          0
X        { "_nproc" },
X#define X_NPROC         1
X#if defined(sun)
X        { "_mounttab" },
X#else
X        { "_mount" },
X#endif
X#define X_MOUNT         2
X#if defined(ultrix)
X        { "_nmount"},
X#define X_NMOUNT        3
X#endif
X        {       0    },
X};
X
Xdaddr_t bmap();
Xchar *emalloc();
Xvoid mread();
Xint kmem;
Xint mem;
Xint swap;
X
X
X#define ANY_UID -1
X#define NOSUCH_USER -2
X
X#if defined(ultrix)
X#define GTOI(g)         (&(g)->g_req)
X#define inode           gnode_req
X#define i_number        gr_number
X#define i_dev           gr_dev
X#endif
X
X
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X        unsigned nproc, i;
X        struct proc *p;
X
X        struct mount_stuff *mount_stuff, *getmountstuff();
X        
X        (void)nlist("/vmunix", nlst);
X        
X        for (i = 0; nlst[i].n_name; i++)
X                if (nlst[i].n_type == 0)
X                        ecmderr(0, "vmunix: can' get symbol <%s>\n", 
X                                nlst[i].n_name);
X        
X        mem = eopen("/dev/mem", O_RDONLY);
X        kmem = eopen("/dev/kmem", O_RDONLY);
X        swap = eopen("/dev/drum", O_RDONLY);
X        
X        
X        mread(kmem,(daddr_t)nlst[X_NPROC].n_value, 
X              (char *)&nproc, sizeof(nproc));
X        mread(kmem, (daddr_t)nlst[X_PROC].n_value, (char *)&p, sizeof(p));
X        mount_stuff = getmountstuff(nlst);
X        
X	printf("PID    USER       COMMAND          PATH\n");
X        if (argc > 1) {
X                while (--argc)
X                        cwd(mount_stuff, p, nproc, name2uid(*++argv));
X        }
X        else
X                cwd(mount_stuff, p, nproc, ANY_UID);
X}
X
Xcwd(mount_stuff, procaddr, nproc, uid)
Xstruct mount_stuff *mount_stuff;
Xstruct proc *procaddr;
Xunsigned nproc;
Xint uid;
X{
X	struct inode *cdir, *getcdir();
X        struct proc *p, proc;
X        struct mount_stuff *m, *devtostuff();
X        struct user *getu();
X        char *path;
X        struct user *u; 
X        char *findpath();
X	int proci;
X        
X	if (uid == NOSUCH_USER)
X		return;
X	for (proci = 0, p = &proc; proci < nproc; proci++) {
X		mread(kmem, (daddr_t)&procaddr[proci], (char *)&proc, 
X			sizeof(struct proc));
X                if (p->p_stat && p->p_stat != SZOMB) {
X                        if (uid != ANY_UID && p->p_uid != uid)
X                                continue;
X                        if (u = getu(p, mem, kmem, swap)) {
X                                cdir = getcdir(kmem,(daddr_t)u->u_cdir);
X				printpid(p->p_pid);
X                                (void)printuser(u->u_uid);
X                                printcomm(u, p->p_pid);
X                                m = devtostuff(cdir->i_dev, mount_stuff);
X                                if (!m)
X                                        printf("<no mount info>");
X                                else {
X                                        path = findpath(m, cdir->i_number,
X                                                        (ino_t)0);
X                                        printf("%s", path);
X                                }
X                                printf("\n");
X                        }
X                }
X        }
X}
X
X/*
X * recursively lookup and return the path to the inode described by "thenum"
X * append path components on return
X * args:
X *      mount_stuff: mount info for the appropriate device
X *      dirinum: i number of the current dir containing the
X *              element being looked up
X *      thenum: i number to look up in this dir (0 means none))
X *
X * returns: pathname corresponding to dirinum
X */
Xchar *findpath(mount_stuff, dirinum, thenum)
Xstruct mount_stuff *mount_stuff;
Xino_t dirinum;
Xino_t thenum;
X{
X        DIR *open_dir(), *dir;
X        struct direct *read_dir(), *dirp;
X        struct dinode *ip, *readinode();
X        ino_t dotdot,dot;
X        int fd = mount_stuff->fd;
X        struct fs *fs = mount_stuff->fs;
X        static char path[MAXPATHLEN+1];
X        
X        
X        /*
X         * hit the root, prepend the mount point and return
X         */
X        if (dirinum == thenum) {
X                strcpy(path, mount_stuff->path);
X                return path;
X        }
X        
X        
X	ip = readinode(fd, fs, dirinum);
X	dir = open_dir(fd, fs, ip);
X	if (dir == NULL)
X		return "<bogus dir>";
X	
X	while (dirp = read_dir(fs, ip, dir)) {
X		if (strcmp(dirp->d_name, ".") == 0) {
X			dot = dirp->d_ino;/* next inode # to lookup */
X			continue;
X		}
X		/* save number of parent for next time */
X		else if (strcmp(dirp->d_name, "..") == 0)
X			dotdot = dirp->d_ino;
X		else if (dirp->d_ino == thenum || thenum == 0) {
X			char name[MAXNAMLEN+1];
X			
X			if (thenum) 
X				sprintf(name, "/%s", dirp->d_name);
X		       
X			(void)findpath(mount_stuff, dotdot, dot);
X			if (thenum) {
X				/* 
X				 * remove redundant leading / for
X				 * things mounted on root
X				 */
X				int len = strlen(path);
X				if (path[len-1] == '/') 
X					len--;
X				strcpy(&path[len],name);
X			}
X			break;
X		}
X	}
X	close_dir(dir);
X        return path;
X}
X
X
X/*
X * read from a block from the disk
X */  
Xbread(fi, bno, buf, cnt)
Xint fi;
Xdaddr_t bno;
Xchar *buf;
Xlong cnt;
X{
X        mread(fi, bno * DEV_BSIZE, buf, cnt);
X}
X
X
X/*
X * map logical to physical block numbers
X */
Xdaddr_t bmap(b, ip, fi, fs)
Xdaddr_t b;
Xstruct dinode *ip;
Xint fi;
Xstruct fs *fs;
X{
X        daddr_t ibuf[MAXBSIZE / sizeof (daddr_t)];
X	daddr_t iblock;
X	int indir;
X	int nblocks;
X        
X        if(b < NDADDR)
X                return(ip->di_db[b]);
X        b -= NDADDR;
X	for (nblocks = 1, indir = 0; indir < NIADDR; indir++) {
X		nblocks *= NINDIR(fs);
X		if (nblocks > b)
X			break;
X		else
X			b -= nblocks;
X	}
X	if (indir == NIADDR)
X                return((daddr_t)0);
X       	iblock = ip->di_ib[indir];
X	for (; indir >= 0; indir--) {
X		bread(fi, fsbtodb(fs, iblock), (char *)ibuf, sizeof(ibuf));
X		nblocks /= NINDIR(fs);
X		iblock = ibuf[(b/nblocks) % NINDIR(fs)];
X	}
X        return iblock;
X}
X
XDIR *open_dir(file, fs, ip)
Xint file;
Xstruct fs *fs;
Xstruct dinode *ip;
X{
X        static DIR dir;
X        
X        if ((ip->di_mode & IFMT) != IFDIR) {
X                fprintf(stderr, "open_dir:  not a directory\n");
X                return NULL;
X        }
X        dir.dd_loc = 0;
X        dir.dd_size = ip->di_size;
X#if !defined(bsd)
X	if (dir.dd_buf)
X		free(dir.dd_buf);
X        dir.dd_buf = (char *)emalloc((unsigned)fs->fs_bsize);
X#endif
X        dir.dd_fd = file;
X        return &dir;
X}
X
Xclose_dir(dirp)
XDIR *dirp;
X{
X#if !defined(bsd)
X        if (dirp->dd_buf) {
X                free(dirp->dd_buf);
X                dirp->dd_buf = NULL;
X        }
X#endif
X}
X
X
X
Xstruct direct *read_dir(fs,ip, dir)
Xstruct fs *fs;
Xstruct dinode *ip;
XDIR *dir;
X{
X        daddr_t bn;
X        static struct direct *dp;
X        
X        if (dir->dd_loc >= dir->dd_size)
X                return NULL;
X        if (blkoff(fs, dir->dd_loc) == 0) {     /* need to read a block */
X                bn = lblkno(fs, dir->dd_loc);
X                bn = bmap(bn, ip, dir->dd_fd, fs);
X                if (bn == 0)
X                        return NULL;
X                bn = fsbtodb(fs, bn);
X                bread(dir->dd_fd, bn, dir->dd_buf, fs->fs_bsize);
X        }
X        dp = (struct direct *)&dir->dd_buf[dir->dd_loc];
X        dir->dd_loc += dp->d_reclen;
X        return dp;
X}
X
X/*
X * error printing 
X * modelled after routines written by Dan Franklin@bbn
X * if e is -1 print system error message from errno
X * if e is 0 don't print error message
X * else use e as errno
X * 
X * note: using _doprnt is less than portable
X */
Xcmderr(e, format, args)
Xint e;
Xchar *format;
Xchar *args;
X{
X        register int nl = 0;
X        register int i = strlen(format) - 1;
X        char fmt[BUFSIZ];
X        extern int sys_nerr;
X        extern char *sys_errlist[];
X        extern int errno;
X        
X        strcpy(fmt, format);
X        if ( e == -1)
X                e = errno;
X        if (fmt[i] == '\n') {
X                fmt[i] = NULL;
X                nl = 1;
X        }
X        _doprnt(fmt, &args, stderr);
X        if (e > sys_nerr)
X                fprintf(stderr," Error %d", e);
X        else if (e)
X                fprintf(stderr, " %s", sys_errlist[e]);
X        if(nl)
X                fprintf(stderr,"\n");
X}
X
X
X/*
X * print error message and exit
X */
Xecmderr(e, fmt, args)
Xint e;
Xchar *fmt;
Xunsigned args;
X{
X        cmderr(e,fmt,args);
X        exit(e);
X}
X
X/*
X * read from the specified address 
X */
Xvoid mread(fd, addr, buf, cnt)
Xint fd;
Xdaddr_t addr;
Xchar *buf;
Xint cnt;
X{
X        int nread;
X        extern daddr_t lseek();
X        if (lseek(fd, addr, L_SET) != addr)
X                ecmderr(-1, "Seek failed\n");
X        if ((nread = read(fd, buf, cnt)) == 0)
X                ecmderr(-1, "Read failed (wanted:%d)\n", cnt);
X        if (nread > cnt) cmderr(-1,"read for %d returned %d\n", cnt, nread);
X}
X
X/* 
X * "safe" open
X */
Xeopen(name, mode)
Xchar *name;
Xunsigned mode;
X{
X        int fd;
X        fd = open(name, mode);
X        if (fd < 0)
X                ecmderr(-1, "cwd: Can't open %s\n", name);
X        return fd;
X}
X
X/*
X * "safe" malloc 
X */
Xchar *emalloc(n)
Xunsigned n;
X{
X        char *p;
X        extern char *malloc();
X        p = malloc(n);
X        if (p == NULL)
X                ecmderr(-1, "Malloc failed\n");
X        return p;
X}
X
X
X/*
X * read kernel mount table and save the stuff we care about
X */
Xstruct mount_stuff *getmountstuff(nlst)
Xstruct nlist nlst[];
X{
X        int  nmount, i;
X        struct mount *mounttable;
X        struct mount_stuff *mount_stuff;
X        struct fstab *fstab, *getfsent();
X        int m;
X        
X#if defined(ultrix)
X        mread(kmem, (daddr_t)nlst[X_NMOUNT].n_value, &nmount, sizeof(nmount));
X#else
X        nmount = NMOUNT;
X#endif
X        
X        mounttable = (struct mount *)emalloc(
X		(unsigned)nmount * sizeof(struct mount));
X        mread(kmem, (daddr_t)nlst[X_MOUNT].n_value, (char *)mounttable, 
X              nmount * sizeof(struct mount));
X        mount_stuff = (struct mount_stuff *)
X                emalloc((nmount + 1)* sizeof(struct mount_stuff));
X        
X        for (m = 0; fstab = getfsent();) {
X                struct stat s;
X                struct buf buf;
X                char *slash;
X                extern char *rindex(), *index();
X                
X                if (*fstab->fs_file == NULL)
X                        continue;
X                if (index(fstab->fs_spec,'@')) {
X                        fprintf(stderr,"%s: Can't handle NFS yet\n",
X                                fstab->fs_spec);
X                        continue;
X                }
X                if (stat(fstab->fs_spec, &s) != 0)
X                        ecmderr(-1, "cwd: Can't stat %s\n", fstab->fs_spec);
X                strcpy(mount_stuff[m].devname, fstab->fs_spec);
X                slash = rindex(mount_stuff[m].devname, '/');
X                if (slash) {
X                        slash++;
X                        bcopy(slash, slash+1, strlen(slash)+1);
X                        *slash = 'r';
X                }
X                strcpy(mount_stuff[m].path, fstab->fs_file);
X                
X                mount_stuff[m].fd = -1;
X                mount_stuff[m].fs = (struct fs *)emalloc(sizeof(struct fs));
X                for (i = 0; i < nmount; i++) {
X                        if (mounttable[i].m_dev != s.st_rdev)
X                                continue;
X                        mount_stuff[m].dev = s.st_rdev;
X                        mread(kmem, (daddr_t)mounttable[i].m_bufp,
X                              (char *)&buf,sizeof(struct buf));
X                        mread(kmem, (daddr_t)buf.b_un.b_fs,(char *)mount_stuff[m].fs, 
X                              sizeof(struct fs));
X                        break;
X                }
X                m++;
X                if (i >= nmount) 
X                        cmderr(0, "cwd: Can't find %s in kernel mount table\n",
X                               fstab->fs_spec);
X        }       
X        mount_stuff[m].dev = -1;
X        free(mounttable);
X        return mount_stuff;
X}
X
X/*
X * return a pointer to some mount information using
X * device as the key
X */
Xstruct mount_stuff *devtostuff(dev, mount_stuff)
Xdev_t dev;
Xstruct mount_stuff mount_stuff[];
X{
X        int i;
X        
X        for (i = 0; mount_stuff[i].dev != -1; i++) {
X                if (mount_stuff[i].dev == dev) {
X                        if (mount_stuff[i].fd == -1)
X                                mount_stuff[i].fd = 
X                                        eopen(mount_stuff[i].devname,
X                                              O_RDONLY);
X                        return &mount_stuff[i];
X                }
X        }
X        return NULL;
X}
X
X/*
X * massage the u_comm field of a user page
X */
Xprintcomm(u, pid)
Xstruct user *u;
Xint pid;
X{
X        char *name;
X        switch (pid) {
X              case 0:
X                name = "swapper";
X                break;
X              case 2:
X                name = "pagedaemon";
X                break;
X              default:
X                name = u->u_comm;
X        }
X        printf("%-*s ", MAXCOMLEN, name );
X}
X
Xprintpid(pid)
Xint pid;
X{
X	printf("%-6d ", pid);
X}
X
X/*
X * print a username based on uid
X */ 
Xprintuser(uid)
Xint uid;
X{
X        struct passwd *pw;
X        pw = getpwuid(uid);
X        if (pw)
X                printf("%-8.8s   ", pw->pw_name);
X        return (pw != NULL);
X}
X
Xstruct dinode *readinode(fd, fs, inum)
Xint fd;
Xstruct fs *fs;
Xino_t inum;
X{
X        static struct dinode itab[MAXIPG];
X        
X        bread(fd, fsbtodb(fs, cgimin(fs, inum/fs->fs_ipg)), (char *)itab,
X              fs->fs_ipg * sizeof (struct dinode));
X        
X        return &itab[inum%fs->fs_ipg];
X}
X
Xname2uid(name)
Xchar *name;
X{
X        struct passwd *pw;
X        pw = getpwnam(name);
X        if (pw)
X                return pw->pw_uid;
X        else {
X                fprintf(stderr,"%s: no such user\n",name);
X                return NOSUCH_USER;
X        }
X}
X
X
X/*
X * return a pointer to a user page
X * this routine was liberally cribbed from the "top" program
X * by William Lefebvre
X */
Xstruct user *getu(p, mem, kmem, swap)
Xstruct proc *p;
X{
X        /* pad reads from swap to a page */
X        static union {
X                struct  user u;
X                char    upages[UPAGES][NBPG];
X        } user;
X#if !defined(bsd)
X        static struct ucred ucred;
X#endif
X        char *up;
X        struct pte *pte, uptes[UPAGES];
X        int n;
X#define min(a,b)        (a < b ? a : b)
X        /* try in core */
X        if (p->p_flag & SLOAD) {
X                mread(kmem, (daddr_t)p->p_addr,(char *)uptes, 
X                      UPAGES * sizeof(struct pte));
X                pte = uptes;
X                for(up = (char *)&user, n = sizeof(struct user); n > 0; 
X                    n -= NBPG) {
X                        mread(mem, (daddr_t)(pte++->pg_pfnum * NBPG),
X                              up,min(n,NBPG));
X                        up += NBPG;
X                }
X        }
X        else 
X                mread(swap, (daddr_t)dtob(p->p_swaddr),
X                      (char *)&user,sizeof(user));
X#if !defined(bsd)       
X        mread(kmem, (daddr_t)user.u.u_cred, &ucred, sizeof(ucred));
X        user.u.u_cred = &ucred;
X#endif
X        return &user.u;
X}
X
X
X/*
X * get the inode corresponding to current working directory
X */
Xstruct inode *getcdir(kmem, addr)
Xint kmem;
Xdaddr_t addr;
X{
X        static struct inode in;
X        struct inode *cdir;
X        
X#if defined(ultrix)
X        struct gnode g;
X        mread(kmem, addr, &g, sizeof(g));
X        cdir = GTOI(&g);
X#endif
X#if defined(bsd)
X        mread(kmem, addr, &in, sizeof(in));
X        cdir = &in;
X#endif
X#if defined(sun)
X        struct vnode v;
X        mread(kmem, addr, &v, sizeof(v));
X        mread(kmem, (daddr_t)VTOI(&v), &in, sizeof(in));
X        cdir = &in;
X#endif
X        return cdir;
X}
//E*O*F cwd.c//

echo x - cwd.1
sed -e 's/^X//' > "cwd.1" << '//E*O*F cwd.1//'
X.TH CWD 1 "Oct 17, 1989"
X.SH NAME
Xcwd \- print current working directory of running processes
X.SH SYNOPSIS
X.B cwd
X[
X.B usernames...
X]
X.br
X.SH DESCRIPTION
X.B Cwd
Xreads the kernel data structures from memory and
Xconstructs a pathname corresponding to the 
X.B u.u_cdir
Xkernel variable associated with each process on the system.
XIf 
X.B usernames 
Xare specified as arguments, only processes belonging to those users
Xwill be considered, the default is to report on all processes
Xon the system. For each eligible process, the user name, process name,
Xprocess id, and current working directory will be displayed. This command 
Xnormally requires root permission unless
Xyour disks are readable (a less than good idea). 
X.B Cwd 
Xcurrently only works on BSD based filesystems.
X
X.PP
X.SH "SEE ALSO"
XNcheck(8)
X.SH BUGS
X.B Cwd
Xcannot report on 
X.I NFS 
Xpathnames.
//E*O*F cwd.1//

exit 0
-- 
Cliff Spencer 
spdcc!lemming!cspencer 			lemming!cspencer@spdcc.com

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.