From 3789080ff46e0341883a5bdf61a834a34b4dbd50 Mon Sep 17 00:00:00 2001 From: Craig Small Date: Wed, 13 Dec 2000 21:22:53 +0000 Subject: [PATCH] imported old files --- psmisc/CHANGES | 197 ++++++++++ psmisc/COPYING | 11 + psmisc/INSTALL | 16 + psmisc/Makefile | 58 +++ psmisc/README | 14 + psmisc/VERSION | 1 + psmisc/comm.h | 18 + psmisc/fuser.1 | 139 +++++++ psmisc/fuser.c | 864 +++++++++++++++++++++++++++++++++++++++++++ psmisc/killall.1 | 80 ++++ psmisc/killall.c | 318 ++++++++++++++++ psmisc/mkdist | 7 + psmisc/pidof.1 | 45 +++ psmisc/psmisc-19.lsm | 14 + psmisc/pstree.1 | 85 +++++ psmisc/pstree.c | 576 +++++++++++++++++++++++++++++ psmisc/signals.c | 53 +++ psmisc/signals.h | 19 + 18 files changed, 2515 insertions(+) create mode 100644 psmisc/CHANGES create mode 100644 psmisc/COPYING create mode 100644 psmisc/INSTALL create mode 100644 psmisc/Makefile create mode 100644 psmisc/README create mode 100644 psmisc/VERSION create mode 100644 psmisc/comm.h create mode 100644 psmisc/fuser.1 create mode 100644 psmisc/fuser.c create mode 100644 psmisc/killall.1 create mode 100644 psmisc/killall.c create mode 100755 psmisc/mkdist create mode 100644 psmisc/pidof.1 create mode 100644 psmisc/psmisc-19.lsm create mode 100644 psmisc/pstree.1 create mode 100644 psmisc/pstree.c create mode 100644 psmisc/signals.c create mode 100644 psmisc/signals.h diff --git a/psmisc/CHANGES b/psmisc/CHANGES new file mode 100644 index 0000000..1d08b7c --- /dev/null +++ b/psmisc/CHANGES @@ -0,0 +1,197 @@ +Changes from version 18 to 19 (25-OCT-1999) +============================= + + - pstree: "static int" was only "static" (fix by Jeremy Buhler) + - now uses cc -E instead of /lib/cpp (suggested by Kristofer Karas) + - fuser: -s conflicted with -k (reported by David Hinds) + - fuser: using -a with -s now yields an error + - added a sanity check for signames.h (suggested by John Summerfield) + - only "signals" < 100 are now added to signames.h, eliminating SIGSTKSZ + + +Changes from version 17 to 18 (1-NOV-1998) +============================= + + - fuser: usage summary listed -s (silent) as -q + - fuser: fuser x/y yielded confusing error message if x/y doesn't exist + (reported by Tigran Aivazian) + - fuser: new option -i for interactive killing (like killall -i) + - killall: tried to print a NULL pointer if full process name was unavailable + and killing failed + - killall: new option -g to kill process group instead of process + - killall: cosmetic PROC_BASE changes (by Florian La Roche) + - pidof: new option -g to show PGID instead of PID (by Florian La Roche) + - pstree: new option -H to highlight process specified by PID + - added -D_GNU_SOURCE to CFLAGS for glibc 2 compatibility + - Makefile should now also work with bash-2 + + +Changes from version 16 to 17 (17-FEB-1998) +============================= + + - fuser: now also handles /proc of recent 2.1 kernels (fix by Andreas Schwab; + other fixed also proposed by Chris Wedgwood and Luca Berra) + - pstree now properly handles init with PID = PPID = 1 + - fuser: no longer changes the name space for relative paths ending with the + name of a name space (e.g. something/tcp) + - fuser: now also reports mount points, swap files, and loop mounts + - updated the fuser man page + - killall: new option -w to wait for the killed processes to die + - killall: didn't handle malloc failure + + +Changes from version 15 to 16 (28-JUL-1997) +============================= + + - killall: now gets the list of all PIDs before killing processes, thereby + avoiding race between readdir and /proc (found by Boris Zentner) + - make install no longer changes BINDIR and MANDIR if they already exist + - changed psmisc..lsm to psmisc-.lsm for consistency + + +Changes from version 14 to 15 (16-JUN-1997) +============================= + + - killall: killall -v didn't print the command name (fixed by Marty Leisner) + - fuser: fuser -a could crash (reported by Helmut Geyer) + - fuser: fuser -m didn't consider UNIX domain sockets (fix based on a patch by + Andries Brouwer) + - fuser: fuser -a /a /a /b no longer merges the first two entries + - changed package name format from psmisc. to psmisc- to + avoid annoyance messages from sunsite's archive scripts + + +Changes from version 13 to 14 (19-APR-1997) +============================= + + - killall: command-line parser didn't accept -signal (fixed by Chris Wedgwood) + - pidof: minor man page correction + - Makefile: pidof is now only installed when running make install-pidof + - added a file with installation instructions (INSTALL) + + +Changes from version 12 to 13 (16-APR-1997) +============================= + + - fuser: didn't check for out of memory condition after malloc (oops !) + - fuser: INET domain sockets can now be specified as + [local_port][,[remote_host][,[remote_port]]] + - fuser: now includes linux/kdev_t.h instead of linux/fs.h. This may break + compilation with some ancient kernels. + - killall: new option -q to suppress error message if no process was found + - killall: man page didn't reset font properly + - killall: now tries harder to handle very long names (> 15 characters) + (proposed by Erik Thiele) + - killall: new option -e to require exact name match + - killall: now lists PIDs if invoked as "pidof" (proposed by Peter Daum) + - minor Makefile change to eliminate need to patch for some versions of Linux + + +Changes from version 11 to 12 (7-APR-1996) +============================= + + - fuser is now able to look up INET and UNIX domain sockets + - pstree: new option -n to sort its output by PID + - pstree: new option -G to use VT100 line drawing characters + + +Changes from version 10 to 11 (20-SEP-1995) +============================= + + - added VERSION file + - size of command name is now defined in comm.h - the old approach of + obtaining values from linux/sched.h:struct task_struct doesn't work anymore) + - signames.h is now generated from cpp output (linux/signals.h no longer + defines them) + - all commands now print the version number when invoked with the -V option + - signames.h added to make clean + - removed use of {,} expansion in mkdist + - various minor documentation fixes + + +Changes from version 9 to 10 (28-MAR-1995) +============================ + + - fuser: now prints header before first path (used to be on same line) + - fuser: fixed line wrapping for long paths (used to wrap too early) + - fuser: fixed a NULL pointer dereference in add_file + - pstree: now outputs strings obtained from termcap with tputs + - some cosmetic changes (to avoid certain warnings if using -Wconversion) + - updated e-mail address in README + +Changes from version 8 to 9 (22-JAN-1995) +=========================== + + - fuser: now works with Plan 9 semantics (i.e. what recent kernels use; + reported by Harald Koenig, Nick Simicich, and others) + - fuser: now also scans /proc/*/maps + - fuser: fixed NULL pointer dereferencing when processes are created while + fuser is running (fix by Pauline Middelink) + - fuser: now resets effective uid to real uid before killing, thereby making + suid installation a smaller security risk + - pstree: fixed process tree truncation (fix by Andreas Schwab) + - pstree: added support for UTF-8 line drawing characters (adapted a patch by + Johan Myreen) + - killall.1 now warns about potential compatibility problems with killall + on other systems (proposed by Christos Ricudis) + - added a copyright notice + - updated e-mail address + +Changes from version 7 to 8 (11-OCT-1994) +=========================== + + - pstree: added -a to display command line arguments + - pstree, fuser and killall: display an error message if /proc has + no process entries (i.e. if it is not mounted) + - killall: more detailed usage output + - killall: added killing by file + - fuser and killall: fixed generation of signal list to include SIGUSR[12] + - fuser: now also accepts signal numbers + - "make install": now also installs man pages; executable permissions changed + from 755 to 555; now installs fuser in /bin, killall and pstree in /usr/bin + - "make spotless": fixed typo + - man pages: minor corrections and improvements + +Changes from version 6 to 7 +=========================== + + - pstree: -h didn't work because of incorrect termcap usage + - pstree: changed branch drawing from --- to -+- + \- `- + - pstree: fixed indentation inside compacted subtrees + - fuser and killall: don't kill themselves + - fuser: suppresses m(map) if file is e(xecuted) + - mkdist now includes itself + - minor updates on all man pages + +Changes from version 5 to 6 +=========================== + + - pstree: unknown display width expressed as zero width (TIOCGWINSZ) is + now correctly handled. + - fuser: added -s for silent operation. + - fuser: added non-zero return code if no processes using any file are + found. + - pstree and fuser: non-printable characters in command names (fuser: also + in file names) are now shown as \nnn (octal). \ is shown as \\. + - added "install" target to Makefile to install binaries in /usr/local/bin + - minor updates on all man pages. + +Changes from version 4 to 5 +=========================== + + - killall: added interactive and verbose modes (options -i and -v) + - fuser: added 0.99pl11 support (changed /proc/*/lib to /proc/*/mmap) + - fuser: mmap'ed files and shared libraries are now marked with "m" + (shlibs were marked with "s" and mmap'ed files weren't marked + at all) + +Changes from version 3 to 4 +=========================== + + - killall and fuser: now accept signal numbers too. + - pstree: added listing of process trees by user name. + - pstree: compaction sometimes generated wrong output. + - fuser: did only recognize the first occurrence of a file when using + the options -u or -v. + - changed Makefile to create stripped impure executables. diff --git a/psmisc/COPYING b/psmisc/COPYING new file mode 100644 index 0000000..4173da3 --- /dev/null +++ b/psmisc/COPYING @@ -0,0 +1,11 @@ +psmisc (fuser, killall and pstree) program code, documentation and +auxiliary programs are +Copyright 1993-1999 Werner Almesberger. +All rights reserved. + +Redistribution and use in source and binary forms of parts of or the +whole original or derived work are permitted provided that the +original work is properly attributed to the author. The name of the +author may not be used to endorse or promote products derived from +this software without specific prior written permission. This work +is provided "as is" and without any express or implied warranties. diff --git a/psmisc/INSTALL b/psmisc/INSTALL new file mode 100644 index 0000000..b95d84f --- /dev/null +++ b/psmisc/INSTALL @@ -0,0 +1,16 @@ +To build all programs in psmisc, simply execute + + make + +To install fuser, killall, pstree, and their man pages, run (as root) + + make install + +fuser is installed in /bin, killall and pstree are installed in /usr/bin + +pidof is not installed by make install because different versions of +pidof exist and a different one may already be installed. If you want to +install the pidof that comes with psmisc (and its man page), simply run +(as root) + + make install-pidof diff --git a/psmisc/Makefile b/psmisc/Makefile new file mode 100644 index 0000000..cae5362 --- /dev/null +++ b/psmisc/Makefile @@ -0,0 +1,58 @@ +CC=cc +CFLAGS=-O -DPSMISC_VERSION=\"`cat VERSION`\" \ + -D_GNU_SOURCE \ + -Wall -Wno-parentheses -Wwrite-strings -Wpointer-arith \ + # -Wcast-align -Wconversion -g +LDFLAGS=#-s -N #-Xlinker -qmagic +REAL_CPP=cc -E +PROGS=killall pstree fuser +EBINDIR=/bin # essential binaries +BINDIR=/usr/bin # not so essential ones +MANDIR=/usr/man/man1 # all man pages + +all: $(PROGS) + +signames.h: /usr/include/signal.h + $(REAL_CPP) -dM - signames.h || \ + { rm -f signames.h; exit 1; } + grep '^{ 1,"HUP" },$$' signames.h >/dev/null || \ + { rm -f signames.h; exit 1; } + +signals.o: signals.h signals.c signames.h Makefile + +fuser.o: fuser.c comm.h signals.h Makefile VERSION +killall.o: killall.c comm.h signals.h Makefile VERSION + +fuser: fuser.o signals.o +killall: killall.o signals.o + +pstree: pstree.c comm.h Makefile VERSION + $(CC) $(CFLAGS) $(LDFLAGS) -o pstree pstree.c -ltermcap + +install: $(PROGS) +# EBINDIR is expected to exist, so we don't try to create it + install -o 0 -g 0 -m 555 fuser $(EBINDIR) + [ -d $(BINDIR) ] || install -d -o 0 -g 0 -m 755 $(BINDIR) + install -o 0 -g 0 -m 555 killall $(BINDIR) + install -o 0 -g 0 -m 555 pstree $(BINDIR) + [ -d $(MANDIR) ] || install -d -o 0 -g 0 -m 755 $(MANDIR) + install -o 0 -g 0 -m 444 fuser.1 $(MANDIR) + install -o 0 -g 0 -m 444 killall.1 $(MANDIR) + install -o 0 -g 0 -m 444 pstree.1 $(MANDIR) + +install-pidof: + @[ -x `echo $(BINDIR)`/killall ] || { \ + echo "Need `echo $(BINDIR)`/killall to install pidof"; \ + exit 1; } + ln -sf killall `echo $(BINDIR)`/pidof + install -o 0 -g 0 -m 444 pidof.1 $(MANDIR) + +clean: + rm -f *.o signames.h + +spotless: clean + rm -f $(PROGS) diff --git a/psmisc/README b/psmisc/README new file mode 100644 index 0000000..34a6f82 --- /dev/null +++ b/psmisc/README @@ -0,0 +1,14 @@ +psmisc, version 19 +================== + +This package contains four little utilities that use the proc FS: + + fuser identifies processes using files or sockets (similar to Sun's + or SGI's fuser) + killall kills processes by name, e.g. killall -HUP named + pidof like killall, buts lists PIDs instead of killing processes + pstree shows the currently running processes as a tree + +They should work with most recent kernels. Man pages are included. + +- Werner Almesberger diff --git a/psmisc/VERSION b/psmisc/VERSION new file mode 100644 index 0000000..d6b2404 --- /dev/null +++ b/psmisc/VERSION @@ -0,0 +1 @@ +19 diff --git a/psmisc/comm.h b/psmisc/comm.h new file mode 100644 index 0000000..7985fe8 --- /dev/null +++ b/psmisc/comm.h @@ -0,0 +1,18 @@ +/* comm.h - command name length definition */ + +/* Copyright 1995 Werner Almesberger. See file COPYING for details. */ + + +#ifndef COMM_H +#define COMM_H + +#if 0 /* broken in 1.3.xx */ +#include +#define COMM_LEN sizeof(dummy.comm) +extern struct task_struct dummy; +#else +#define COMM_LEN 16 /* synchronize with size of comm in struct task_struct in + /usr/include/linux/sched.h */ +#endif + +#endif diff --git a/psmisc/fuser.1 b/psmisc/fuser.1 new file mode 100644 index 0000000..96edf21 --- /dev/null +++ b/psmisc/fuser.1 @@ -0,0 +1,139 @@ +.TH FUSER 1 "October 25, 1999" "Linux" "User Commands" +.SH NAME +fuser \- identify processes using files or sockets +.SH SYNOPSIS +.ad l +.B fuser +.RB [ \-a | \-s ] +.RB [ \-n\ \fIspace ] +.RB [ \-\fIsignal\fB ] +.RB [ \-kimuv ] +.I name ... +.RB [ \- ] +.RB [ \-n\ \fIspace ] +.RB [ \-\fIsignal\fB ] +.RB [ \-kimuv ] +.I name ... +.br +.B fuser +.RB \-l +.br +.B fuser +.RB \-V +.ad b +.SH DESCRIPTION +.B fuser +displays the PIDs of processes using the specified files or file systems. +In the default display mode, each file name is followed by a letter denoting +the type of access: +.RS +.IP \fBc\fP +current directory. +.IP \fBe\fP +executable being run. +.IP \fBf\fP +open file. \fBf\fP is omitted in default display mode. +.IP \fBr\fP +root directory. +.IP \fBm\fP +mmap'ed file or shared library. +.RE +.LP +\fBfuser\fP returns a non-zero return code if none of the specified files +is accessed or in case of a fatal error. If at least one access has been +found, \fBfuser\fP returns zero. +.PP +In order to look up processes using TCP and UDP sockets, the corresponding +name space has to be selected with the \fB-n\fP option. Then the socket(s) can +be specified by the local and remote port, and the remote address. All fields +are optional, but commas in front of missing fields must be present: + +.RB \fB[\fP\fIlcl_port\fP\fB][\fP,\fB[\fP\fIrmt_host\fP\fB][\fP,\fB[\fIrmt_port\fP\fB]]] + +Either symbolic or numeric values can be used for IP addresses and port +numbers. +.SH OPTIONS +.IP \fB\-a\fP +Show all files specified on the command line. By default, only files that are +accessed by at least one process are shown. +.IP \fB\-k\fP +Kill processes accessing the file. Unless changed with \fB-\fP\fIsignal\fP, +SIGKILL is sent. An \fBfuser\fP process never kills itself, but may kill +other \fBfuser\fP processes. The effective user ID of the process executing +\fBfuser\fP is set to its real user ID before attempting to kill. +.IP \fB\-i\fP +Ask the user for confirmation before killing a process. This option is +silently ignored if \fB\-k\fP is not present too. +.IP \fB\-l\fP +List all known signal names. +.IP \fB\-m\fP +\fIname\fP specifies a file on a mounted file system or a block device that +is mounted. All processes accessing files on that file system are listed. +If a directory file is specified, it is automatically changed to +\fIname\fP/. to use any file system that might be mounted on that +directory. +.IP \fB\-n\ \fIspace\fP +Select a different name space. The name spaces \fBfile\fP (file names, the +default), \fBudp\fP (local UDP ports), and \fBtcp\fP (local TCP ports) are +supported. For ports, either the port number or the symbolic name can be +specified. If there is no ambiguity, the shortcut notation +\fIname\fB/\fIspace\fP (e.g. \fIname\fB/\fIproto\fP) can be used. +.IP \fB\-s\fP +Silent operation. \fB\-u\fP and \fB\-v\fP are ignored in this mode. +\fB\-a\fP must not be used with \fB\-s\fP. +.IP \fB\-\fIsignal\fP +Use the specified signal instead of SIGKILL when killing processes. Signals +can be specified either by name (e.g. \fB\-HUP\fP) or by number +(e.g. \fB\-1\fP). +.IP \fB\-u\fP +Append the user name of the process owner to each PID. +.IP \fB\-v\fP +Verbose mode. Processes are shown in a \fBps\fP-like style. The fields PID, +USER and COMMAND are similar to \fBps\fP. ACCESS shows how the process +accesses the file. If the access is by the kernel (e.g. in the case of a +mount point, a swap file, etc.), \fBkernel\fP is shown instead of the PID. +.IP \fB\-V\fP +Display version information. +.IP \fB\-\fP +Reset all options and set the signal back to SIGKILL. +.SH FILES +.nf +/proc location of the proc file system +.fi +.SH EXAMPLES +\fBfuser -km /home\fP kills all processes accessing the file system /home +in any way. +.LP +\fBif fuser -s /dev/ttyS1; then :; else \fIsomething\fP; fi\fR invokes +\fIsomething\fP if no other process is using /dev/ttyS1. +.LP +\fBfuser telnet/tcp\fP shows all processes at the (local) TELNET port. +.SH RESTRICTIONS +Processes accessing the same file or file system several times in the same way +are only shown once. +.PP +If the same object is specified several times on the command line, some of +those entries may be ignored. +.PP +\fBfuser\fP may only be able to gather partial information unless run with +privileges. As a consequence, files opened by processes belonging to other +users may not be listed and executables may be classified as mapped only. +.PP +Installing \fBfuser\fP SUID root will avoid problems associated with +partial information, but may be undesirable for security and privacy +reasons. +.PP +\fBudp\fP and \fBtcp\fP name spaces, and UNIX domain sockets can't be +searched with kernels older than 1.3.78. +.PP +\fBudp\fP and \fBtcp\fP currently only work for IPv4. +.PP +Accesses by the kernel are only shown with the \fB-v\fP option. +.PP +The \fB-k\fP option only works on processes. If the user is the kernel, +\fBfuser\fP will print an advice, but take no action beyond that. +.SH AUTHOR +Werner Almesberger +.SH "SEE ALSO" +kill(1), killall(1), ps(1), kill(2) +.\"{{{}}} diff --git a/psmisc/fuser.c b/psmisc/fuser.c new file mode 100644 index 0000000..3fe2889 --- /dev/null +++ b/psmisc/fuser.c @@ -0,0 +1,864 @@ +/* fuser.c - identify processes using files */ + +/* Copyright 1993-1999 Werner Almesberger. See file COPYING for details. */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for MKDEV */ +#include /* for LOOP_MAJOR */ + +#include "comm.h" +#include "signals.h" + + +#define PROC_BASE "/proc" +#define UID_UNKNOWN -1 +#define NAME_FIELD 20 /* space reserved for file name */ + +#define MAX_LINE 256 /* longest line we may ever find in /proc */ + + +#ifndef LOOP_MAJOR /* don't count on the headers too much ... */ +#define LOOP_MAJOR 7 +#endif + + +#define REF_FILE 1 /* an open file */ +#define REF_ROOT 2 /* current root */ +#define REF_CWD 4 /* current directory */ +#define REF_EXE 8 /* executable */ +#define REF_MMAP 16 /* mmap'ed file or library */ + +#define FLAG_KILL 1 /* kill process */ +#define FLAG_UID 2 /* show uid */ +#define FLAG_VERB 4 /* show verbose output */ +#define FLAG_DEV 8 /* show all processes using this device */ +#define FLAG_ASK 16 /* ask before killing a process */ + + +typedef struct _net_cache { + int lcl_port; + int rmt_port; + unsigned long rmt_addr; + ino_t ino; + struct _net_cache *next; +} NET_CACHE; + +typedef struct _unix_cache { + dev_t fs_dev; + ino_t fs_ino; + ino_t net_ino; + struct _unix_cache *next; +} UNIX_CACHE; + +typedef struct { + const char *name; + NET_CACHE *cache; + int once; +} SPACE_DSC; + +typedef enum { it_proc,it_mount,it_loop,it_swap } ITEM_TYPE; + +typedef struct item_dsc { + ITEM_TYPE type; + union { + struct { + pid_t pid; + int uid; /* must also accept UID_UNKNOWN */ + int ref_set; + } proc; + struct { + const char *path; + } misc; + } u; + struct item_dsc *next; +} ITEM_DSC; + +typedef struct file_dsc { + const char *name; /* NULL if previous entry has name */ + dev_t dev; + ino_t ino; + int flags,sig_num; + SPACE_DSC *name_space; /* or NULL if no indication */ + ITEM_DSC *items; + struct file_dsc *named,*next; +} FILE_DSC; + +static SPACE_DSC name_spaces[] = { + { "file", NULL, 0 }, /* must be first */ + { "tcp", NULL, 0 }, + { "udp", NULL, 0 }, + { NULL, NULL, 0 } +}; + + +static FILE_DSC *files = NULL; +static FILE_DSC *last_named = NULL; +static UNIX_CACHE *unix_cache = NULL; +static pid_t self; +static int all = 0,found_item = 0; + + +static void fill_net_cache(SPACE_DSC *dsc) +{ + FILE *file; + NET_CACHE *new,*last; + char buffer[PATH_MAX+1],line[MAX_LINE+1]; + + if (dsc->once) return; + dsc->once = 1; + sprintf(buffer,PROC_BASE "/net/%s",dsc->name); + if (!(file = fopen(buffer,"r"))) { + perror(buffer); + exit(1); + } + last = NULL; + (void) fgets(line,MAX_LINE,file); + while (fgets(line,MAX_LINE,file)) { + new = malloc(sizeof(NET_CACHE)); + if (!new) { + perror("malloc"); + exit(1); + } + if (sscanf(line,"%*d: %*x:%x %lx:%x %*x %*x:%*x %*x:%*x %*x %*d %*d " + "%ld",&new->lcl_port,&new->rmt_addr,&new->rmt_port,&new->ino) != 4) { + free(new); + continue; + } + if (!new->ino) { + free(new); + continue; + } + new->next = NULL; + if (last) last->next = new; + else dsc->cache = new; + last = new; + } + (void) fclose(file); +} + + +static void fill_unix_cache(void) +{ + static int once; + FILE *file; + UNIX_CACHE *new,*last; + struct stat st; + char path[PATH_MAX+1],line[MAX_LINE+1]; + int ino; + + if (once) return; + once = 1; + if (!(file = fopen(PROC_BASE "/net/unix","r"))) { + perror(PROC_BASE "/net/unix"); + exit(1); + } + last = NULL; + (void) fgets(line,MAX_LINE,file); + while (fgets(line,MAX_LINE,file)) { + if (sscanf(line,"%*x: %*x %*x %*x %*x %*x %d %s",&ino,path) != 2) + continue; + if (stat(path,&st) < 0) continue; + new = malloc(sizeof(UNIX_CACHE)); + new->fs_dev = st.st_dev; + new->fs_ino = st.st_ino; + new->net_ino = ino; + new->next = NULL; + if (last) last->next = new; + else unix_cache = new; + last = new; + } + (void) fclose(file); + +} + + +static unsigned long try_to_find_unix_dev(unsigned long inode) +{ + UNIX_CACHE *walk; + + for (walk = unix_cache; walk; walk = walk->next) + if (walk->net_ino == inode) return walk->fs_dev; + return 0; +} + + +static void add_file(const char *path,unsigned long device,unsigned long inode, + pid_t pid,int ref) +{ + struct stat st; + FILE_DSC *file,*next; + ITEM_DSC **item,*this; + unsigned long mount_dev; + + if (device) mount_dev = device; + else mount_dev = try_to_find_unix_dev(inode); + for (file = files; file; file = next) { + next = file->next; + if (file->flags & FLAG_DEV ? mount_dev && mount_dev == file->dev : + device == file->dev && inode == file->ino) { + if (!file->name) file = file->named; + for (item = &file->items; *item; item = &(*item)->next) + if ((*item)->type == it_proc && (*item)->u.proc.pid >= pid) + break; + if (*item && (*item)->u.proc.pid == pid) this = *item; + else { + if (!(this = malloc(sizeof(ITEM_DSC)))) { + perror("malloc"); + exit(1); + } + this->type = it_proc; + this->u.proc.pid = pid; + this->u.proc.uid = UID_UNKNOWN; + this->u.proc.ref_set = 0; + this->next = *item; + *item = this; + found_item = 1; + } + this->u.proc.ref_set |= ref; + if ((file->flags & (FLAG_UID | FLAG_VERB)) && this->u.proc.uid == + UID_UNKNOWN && lstat(path,&st) >= 0) this->u.proc.uid = st.st_uid; + } + } +} + + +static void add_other(ITEM_TYPE type,unsigned long mount_dev, + unsigned long device,unsigned long inode,const char *path) +{ + FILE_DSC *file,*next; + ITEM_DSC **item,*this; + + for (file = files; file; file = next) { + next = file->next; + if (file->flags & FLAG_DEV ? mount_dev == file->dev : + device == file->dev && inode == file->ino) { + if (!file->name) file = file->named; + for (item = &file->items; *item; item = &(*item)->next); + /* there's no easy way to suppress duplicates, so we don't */ + if (!(this = malloc(sizeof(ITEM_DSC)))) { + perror("malloc"); + exit(1); + } + this->type = type; + if (!(this->u.misc.path = strdup(path))) { + perror("strdup"); + exit(1); + } + this->next = *item; + *item = this; + found_item = 1; + } + } +} + + +static void check_link(const char *path,pid_t pid,int type) +{ + struct stat st; + + if (stat(path,&st) >= 0) + add_file(path,st.st_dev,st.st_ino,pid,type); +} + + +static void check_map(const char *rel,pid_t pid,int type) +{ + FILE *file; + char line[MAX_LINE+1]; + int major,minor; + unsigned long inode; + + if (!(file = fopen(rel,"r"))) return; + while (fgets(line,MAX_LINE,file)) { + if (sscanf(line,"%*s %*s %*s %x:%x %ld",&major,&minor,&inode) != 3) + continue; + if (major || minor || inode) + add_file(rel,MKDEV(major,minor),inode,pid,type); + } + fclose(file); +} + + +static void check_dir(const char *rel,pid_t pid,int type) +{ + DIR *dir; + struct dirent *de; + char path[PATH_MAX+1]; + + if (!(dir = opendir(rel))) return; + while (de = readdir(dir)) + if (strcmp(de->d_name,".") && strcmp(de->d_name,"..")) { + sprintf(path,"%s/%s",rel,de->d_name); + check_link(path,pid,type); + } + (void) closedir(dir); +} + + +static void scan_fd(void) +{ + DIR *dir; + struct dirent *de; + char path[PATH_MAX+1]; + pid_t pid; + int empty; + + if (!(dir = opendir(PROC_BASE))) { + perror(PROC_BASE); + exit(1); + } + empty = 1; + while (de = readdir(dir)) + if (pid = atoi(de->d_name)) { + empty = 0; + sprintf(path,"%s/%d",PROC_BASE,pid); + if (chdir(path) >= 0) { + check_link("root",pid,REF_ROOT); + check_link("cwd",pid,REF_CWD); + check_link("exe",pid,REF_EXE); + check_dir("lib",pid,REF_MMAP); + check_dir("mmap",pid,REF_MMAP); + check_map("maps",pid,REF_MMAP); + check_dir("fd",pid,REF_FILE); + } + } + (void) closedir(dir); + if (empty) { + fprintf(stderr,PROC_BASE " is empty (not mounted ?)\n"); + exit(1); + } +} + + +static void scan_mounts(void) +{ + FILE *file; + struct stat st_dev,st_parent,st_mounted; + char line[MAX_LINE+1],path[PATH_MAX+1],mounted[PATH_MAX+3]; + char tmp[MAX_LINE+1]; + char *end; + + if (!(file = fopen(PROC_BASE "/mounts","r"))) return; /* old kernel */ + while (fgets(line,MAX_LINE,file)) { + if (sscanf(line,"%s %s",path,mounted) != 2) continue; + /* new kernel :-) */ + if (stat(path,&st_dev) < 0) continue; /* might be NFS or such */ + if (S_ISBLK(st_dev.st_mode) && MAJOR(st_dev.st_rdev) == LOOP_MAJOR) { + FILE *pipe; + + sprintf(tmp,"losetup %s",path); + if (!(pipe = popen(tmp,"r"))) + fprintf(stderr,"popen(%s) failed\n",tmp); + else { + int dev,ino; + + if (fscanf(pipe,"%*s [%x]:%d",&dev,&ino) == 2) + add_other(it_loop,dev,dev,ino,path); + (void) fclose(pipe); + } + } + if (stat(mounted,&st_mounted) < 0) { + perror(mounted); + continue; + } + end = strchr(mounted,0); + strcpy(end,"/.."); + if (stat(mounted,&st_parent) >= 0) { + *end = 0; + add_other(it_mount,st_parent.st_dev,st_mounted.st_dev, + st_mounted.st_ino,mounted); + } + } + (void) fclose(file); +} + + +static void scan_swaps(void) +{ + FILE *file; + struct stat st; + char line[MAX_LINE+1],path[PATH_MAX+1],type[MAX_LINE+1]; + + if (!(file = fopen(PROC_BASE "/swaps","r"))) return; /* old kernel */ + (void) fgets(line,MAX_LINE,file); + while (fgets(line,MAX_LINE,file)) { + if (sscanf(line,"%s %s",path,type) != 2) continue; /* new kernel :-) */ + if (strcmp(type,"file")) continue; + if (stat(path,&st) >= 0) + add_other(it_swap,st.st_dev,st.st_dev,st.st_ino,path); + } + (void) fclose(file); +} + + +static int ask(pid_t pid) +{ + int ch,c; + + fflush(stdout); + do { + fprintf(stderr,"Kill process %d ? (y/n) ",pid); + fflush(stderr); + do if ((ch = getchar()) == EOF) exit(0); + while (ch == '\n' || ch == '\t' || ch == ' '); + do if ((c = getchar()) == EOF) exit(0); + while (c != '\n'); + } + while (ch != 'y' && ch != 'n' && ch != 'Y' && ch != 'N'); + return ch == 'y' || ch == 'Y'; +} + + +static void kill_item(const FILE_DSC *file,const ITEM_DSC *item) +{ + char tmp[10]; + + switch (item->type) { + case it_proc: + if (item->u.proc.pid == self) return; + if ((file->flags & FLAG_ASK) && !ask(item->u.proc.pid)) return; + if (kill(item->u.proc.pid,file->sig_num) >= 0) break; + sprintf(tmp,"kill %d",item->u.proc.pid); + perror(tmp); + break; + case it_mount: + fprintf(stderr,"No automatic removal. Please use umount %s\n", + item->u.misc.path); + break; + case it_loop: + fprintf(stderr,"No automatic removal. Please use umount %s\n", + item->u.misc.path); + break; + case it_swap: + fprintf(stderr,"No automatic removal. Please use swapoff %s\n", + file->name); + break; + } +} + + +static void show_files_or_kill(void) +{ + const FILE_DSC *file; + const ITEM_DSC *item; + FILE *f; + const struct passwd *pw; + const char *user,*scan; + char tmp[10],path[PATH_MAX+1],comm[COMM_LEN+1]; + int length,header,first,dummy; + header = 1; + for (file = files; file; file = file->next) + if (file->name && (file->items || all)) { + if (header && (file->flags & FLAG_VERB)) { + printf("\n%*s USER PID ACCESS COMMAND\n",NAME_FIELD,""); + header = 0; + } + length = 0; + for (scan = file->name; *scan; scan++) + if (*scan == '\\') length += printf("\\\\"); + else if (*scan > ' ' && *scan <= '~') { + putchar(*scan); + length++; + } + else length += printf("\\%03o",*scan); + if (file->name_space) + length += printf("/%s",file->name_space->name); + if (!(file->flags & FLAG_VERB)) { + putchar(':'); + length++; + } + while (length < NAME_FIELD) { + putchar(' '); + length++; + } + first = 1; + for (item = file->items; item; item = item->next) { + if (!(file->flags & FLAG_VERB)) { + if (item->type != it_proc) continue; + if (item->u.proc.ref_set & REF_FILE) + printf("%6d",item->u.proc.pid); + if (item->u.proc.ref_set & REF_ROOT) + printf("%6dr",item->u.proc.pid); + if (item->u.proc.ref_set & REF_CWD) + printf("%6dc",item->u.proc.pid); + if (item->u.proc.ref_set & REF_EXE) + printf("%6de",item->u.proc.pid); + else if (item->u.proc.ref_set & REF_MMAP) + printf("%6dm",item->u.proc.pid); + if ((file->flags & FLAG_UID) && item->u.proc.uid != + UID_UNKNOWN) + if (pw = getpwuid(item->u.proc.uid)) + printf("(%s)",pw->pw_name); + else printf("(%d)",item->u.proc.uid); + first = 0; + } + else { + const char *name; + int uid; + + switch (item->type) { + case it_proc: + sprintf(path,PROC_BASE "/%d/stat",item->u.proc.pid); + strcpy(comm,"???"); + if (f = fopen(path,"r")) { + (void) fscanf(f,"%d (%[^)]",&dummy,comm); + (void) fclose(f); + } + name = comm; + uid = item->u.proc.uid; + break; + case it_mount: + case it_loop: + case it_swap: + name = item->u.misc.path; + uid = 0; + break; + default: + fprintf(stderr,"Internal error (type %d)\n", + item->type); + exit(1); + } + if (uid == UID_UNKNOWN) user = "???"; + else if (pw = getpwuid(uid)) user = pw->pw_name; + else { + sprintf(tmp,"%d",uid); + user = tmp; + } + if (!first) printf("%*s",NAME_FIELD,""); + else if (length > NAME_FIELD) + printf("\n%*s",NAME_FIELD,""); + printf(" %-8s ",user); + switch (item->type) { + case it_proc: + printf("%6d %c%c%c%c%c ",item->u.proc.pid, + item->u.proc.ref_set & REF_FILE ? 'f' : '.', + item->u.proc.ref_set & REF_ROOT ? 'r' : '.', + item->u.proc.ref_set & REF_CWD ? 'c' : '.', + item->u.proc.ref_set & REF_EXE ? 'e' : '.', + (item->u.proc.ref_set & REF_MMAP) && + !(item->u.proc.ref_set & REF_EXE) ? 'm' : '.'); + break; + case it_mount: + printf("kernel mount "); + break; + case it_loop: + printf("kernel loop "); + break; + case it_swap: + printf("kernel swap "); + break; + } + if (name) + for (scan = name; *scan; scan++) + if (*scan == '\\') printf("\\\\"); + else if (*scan > ' ' && *scan <= '~') + putchar(*scan); + else printf("\\%03o",(unsigned char) *scan); + putchar('\n'); + } + first = 0; + } + if (!(file->flags & FLAG_VERB) || first) putchar('\n'); + if (first) + fprintf(stderr,"No process references; use -v for the complete" + " list\n"); + if (file->flags & FLAG_KILL) + for (item = file->items; item; item = item->next) + kill_item(file,item); + } +} + + +static void kill_files(void) +{ + const FILE_DSC *file; + const ITEM_DSC *item; + + for (file = files; file; file = file->next) + if (file->flags & FLAG_KILL) + for (item = file->items; item; item = item->next) + kill_item(file,item); +} + + +static void enter_item(const char *name,int flags,int sig_number,dev_t dev, + ino_t ino,SPACE_DSC *name_space) +{ + static FILE_DSC *last = NULL; + FILE_DSC *new; + + if (!(new = malloc(sizeof(FILE_DSC)))) { + perror("malloc"); + exit(1); + } + if (last_named && !strcmp(last_named->name,name) && + last_named->name_space == name_space) new->name = NULL; + else if (!(new->name = strdup(name))) { + perror("strdup"); + exit(1); + } + new->flags = flags; + new->sig_num = sig_number; + new->items = NULL; + new->next = NULL; + new->dev = dev; + new->ino = ino; + new->name_space = name_space; + if (last) last->next = new; + else files = new; + last = new; + new->named = last_named; + if (new->name) last_named = new; +} + + +static int parse_inet(const char *spec,const char *name_space,int *lcl_port, + unsigned long *rmt_addr,int *rmt_port) +{ + char *s,*here,*next,*end; + int port,field; + + if (!(s = strdup(spec))) { + perror("strdup"); + exit(1); + } + *lcl_port = *rmt_port = -1; + *rmt_addr = 0; + field = 0; + for (here = s; here; here = next ? next+1 : NULL) { + next = strchr(here,','); + if (next) *next = 0; + switch (field) { + case 0: + /* fall through */ + case 2: + if (!*here) break; + port = strtoul(here,&end,0); + if (*end) { + struct servent *se; + + if (!(se = getservbyname(here,name_space))) + return 0; + port = ntohs(se->s_port); + } + if (field) *rmt_port = port; + else *lcl_port = port; + break; + case 1: + if (!*here) break; + if ((long) (*rmt_addr = inet_addr(here)) == -1) { + struct hostent *hostent; + + if (!(hostent = gethostbyname(here))) return 0; + if (hostent->h_addrtype != AF_INET) return 0; + memcpy(rmt_addr,hostent->h_addr,hostent->h_length); + } + break; + default: + return 0; + } + field++; + } + return 1; +} + + +static void usage(void) +{ + fprintf(stderr,"usage: fuser [ -a | -s ] [ -n space ] [ -signal ] " + "[ -kimuv ] name ...\n%13s[ - ] [ -n space ] [ -signal ] [ -kimuv ] " + "name ...\n",""); + fprintf(stderr," fuser -l\n"); + fprintf(stderr," fuser -V\n\n"); + fprintf(stderr," -a display unused files too\n"); + fprintf(stderr," -k kill processes accessing that file\n"); + fprintf(stderr," -i ask before killing (ignored without -k)\n"); + fprintf(stderr," -l list signal names\n"); + fprintf(stderr," -m mounted FS\n"); + fprintf(stderr," -n space search in the specified name space (file, " + "udp, or tcp)\n"); + fprintf(stderr," -s silent operation\n"); + fprintf(stderr," -signal send signal instead of SIGKILL\n"); + fprintf(stderr," -u display user ids\n"); + fprintf(stderr," -v verbose output\n"); + fprintf(stderr," -V display version information\n"); + fprintf(stderr," - reset options\n\n"); + fprintf(stderr," udp/tcp names: [local_port][,[rmt_host][,[rmt_port]]]" + "\n\n"); + exit(1); +} + + +int main(int argc,char **argv) +{ + SPACE_DSC *name_space; + char path[PATH_MAX+1]; + int flags,silent,do_kill,sig_number,no_files; + + flags = silent = do_kill = 0; + sig_number = SIGKILL; + name_space = name_spaces; + no_files = 1; + if (argc < 2) usage(); + if (argc == 2 && !strcmp(argv[1],"-l")) { + list_signals(); + return 0; + } + while (--argc) { + argv++; + if (**argv == '-') + if (!argv[0][1]) { + flags = 0; + sig_number = SIGKILL; + } + else while (*++*argv) { + int end; + + end = 0; + switch (**argv) { + case 'a': + all = 1; + break; + case 'k': + flags |= FLAG_KILL; + do_kill = 1; + break; + case 'i': + flags |= FLAG_ASK; + break; + case 'm': + flags |= FLAG_DEV; + break; + case 'n': + if (!--argc) usage(); + argv++; + for (name_space = name_spaces; name_space->name; + name_space++) + if (!strcmp(*argv,name_space->name)) break; + if (!name_space->name) usage(); + end = 1; + break; + case 's': + silent = 1; + break; + case 'u': + flags |= FLAG_UID; + break; + case 'v': + flags |= FLAG_VERB; + break; + case 'V': + fprintf(stderr,"fuser from psmisc version " + PSMISC_VERSION "\n"); + return 0; + default: + if (isupper(**argv) || isdigit(**argv)) { + sig_number = get_signal(*argv,"fuser"); + argv[0][1] = 0; + break; + } + usage(); + } + if (end) break; + } + else { + SPACE_DSC *this_name_space; + struct stat st; + char *here; + + no_files = 0; + last_named = NULL; + this_name_space = name_space; + if (name_space != name_spaces || stat(*argv,&st) < 0) { + here = strchr(*argv,'/'); + if (here && here != *argv) { + for (this_name_space = name_spaces; this_name_space->name; + this_name_space++) + if (!strcmp(here+1,this_name_space->name)) { + *here = 0; + break; + } + if (!this_name_space->name) this_name_space = name_spaces; + } + } + if (this_name_space == name_spaces) { + if (stat(*argv,&st) < 0) { + perror(*argv); + continue; + } + if (flags & FLAG_DEV) + if (S_ISBLK(st.st_mode)) st.st_dev = st.st_rdev; + else if (S_ISDIR(st.st_mode)) { + sprintf(path,"%s/.",*argv); + if (stat(*argv,&st) < 0) { + perror(*argv); + continue; + } + } + if (S_ISSOCK(st.st_mode) || (flags & FLAG_DEV)) + fill_unix_cache(); + if (!S_ISSOCK(st.st_mode) || (flags & FLAG_DEV)) + enter_item(*argv,flags,sig_number,st.st_dev,st.st_ino,NULL); + else { + UNIX_CACHE *walk; + + for (walk = unix_cache; walk; walk = walk->next) + if (walk->fs_dev == st.st_dev && walk->fs_ino == + st.st_ino) + enter_item(*argv,flags,sig_number,0,walk->net_ino, + NULL); + } + } + else { + NET_CACHE *walk; + unsigned long rmt_addr; + int lcl_port,rmt_port; + + if (flags & FLAG_DEV) { + fprintf(stderr,"ignoring -m in name space \"%s\"\n", + this_name_space->name); + flags &= ~FLAG_DEV; + } + fill_net_cache(this_name_space); + if (!parse_inet(*argv,this_name_space->name,&lcl_port, + &rmt_addr,&rmt_port)) { + fprintf(stderr,"%s/%s: invalid specificiation\n",*argv, + this_name_space->name); + continue; + } + for (walk = this_name_space->cache; walk; walk = walk->next) + if ((lcl_port == -1 || walk->lcl_port == lcl_port) && + (!rmt_addr || walk->rmt_addr == rmt_addr) && + (rmt_port == -1 || walk->rmt_port == rmt_port)) + enter_item(*argv,flags,sig_number,0,walk->ino, + this_name_space); + } + } + } + if (no_files || (all && silent)) usage(); + scan_fd(); + scan_mounts(); + scan_swaps(); + if (do_kill && seteuid(getuid()) < 0) { + perror("seteuid"); + return 1; + } + self = getpid(); + if (silent) kill_files(); + else show_files_or_kill(); + return found_item ? 0 : 1; +} diff --git a/psmisc/killall.1 b/psmisc/killall.1 new file mode 100644 index 0000000..edaa2b2 --- /dev/null +++ b/psmisc/killall.1 @@ -0,0 +1,80 @@ +.TH KILLALL 1 "Sep 7, 1999" "Linux" "User Commands" +.SH NAME +killall \- kill processes by name +.SH SYNOPSIS +.ad l +.B killall +.RB [ \-egiqvw ] +.RB [ \-\fIsignal\fB ] +.I name ... +.br +.B killall +.RB \-l +.br +.B killall +.RB \-V +.ad b +.SH DESCRIPTION +.B killall +sends a signal to all processes running any of the specified commands. If no +signal name is specified, SIGTERM is sent. +.PP +Signals can be specified either by name (e.g. \fB\-HUP\fP) or by number +(e.g. \fB\-1\fP). Signal 0 (check if a process exists) can only be specified +by number. +.PP +If the command name contains a slash (\fB/\fP), processes executing that +particular file will be selected for killing, independent of their name. +.PP +\fBkillall\fP returns a non-zero return code if no process has been killed +for any of the listed commands. If at least one process has been killed for +each command, \fBkillall\fP returns zero. +.PP +A \fBkillall\fP process never kills itself (but may kill other \fBkillall\fP +processes). +.SH OPTIONS +.IP \fB\-e\fP +Require an exact match for very long names. If a command name is longer +than 15 characters, the full name may be unavailable (i.e. it is swapped +out). In this case, \fBkillall\fP will kill everything that matches within +the first 15 characters. With \fB\-e\fP, such entries are skipped. +\fBkillall\fP prints a message for each skipped entry +if \fB\-v\fP is specified in addition to \fB\-e\fP, +.IP \fB\-g\fP +Kill the process group to which the process belongs. The kill signal is only +sent once per group, even if multiple processes belonging to the same process +group were found. +.IP \fB\-i\fP +Interactively ask for confirmation before killing. +.IP \fB\-l\fP +List all known signal names. +.IP \fB\-q\fP +Do not complain if no processes were killed. +.IP \fB\-v\fP +Report if the signal was successfully sent. +.IP \fB\-V\fP +Display version information. +.IP \fB\-w\fP +Wait for all killed processes to die. \fBkillall\fP checks once per second if +any of the killed processes still exist and only returns if none are left. +Note that \fBkillall\fP may wait forever if the signal was ignored, had no +effect, or if the process stays in zombie state. +.SH FILES +.nf +/proc location of the proc file system +.fi +.SH "KNOWN BUGS" +Killing by file only works for executables that are kept open during +execution, i.e. impure executables can't be killed this way. +.PP +Be warned that typing \fBkillall\fP \fIname\fP may not have the desired +effect on non-Linux systems, especially when done by a privileged +user. +.PP +\fBkillall \-w\fP doesn't detect if a process disappears and is replaced by +a new process with the same PID between scans. +.SH AUTHOR +Werner Almesberger +.SH "SEE ALSO" +kill(1), fuser(1), pidof(1), ps(1), kill(2) +.\"{{{}}} diff --git a/psmisc/killall.c b/psmisc/killall.c new file mode 100644 index 0000000..962fe5f --- /dev/null +++ b/psmisc/killall.c @@ -0,0 +1,318 @@ +/* killall.c - kill processes by name or list PIDs */ + +/* Copyright 1993-1998 Werner Almesberger. See file COPYING for details. */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comm.h" +#include "signals.h" + + +#define PROC_BASE "/proc" +#define MAX_NAMES (sizeof(unsigned long)*8) + + +static int verbose = 0,exact = 0,interactive = 0,quiet = 0,wait_until_dead = 0, + process_group = 0,pidof; + + +static int ask(char *name,pid_t pid) +{ + int ch,c; + + do { + printf("Kill %s(%s%d) ? (y/n) ",name,process_group ? "pgid " : "",pid); + fflush(stdout); + do if ((ch = getchar()) == EOF) exit(0); + while (ch == '\n' || ch == '\t' || ch == ' '); + do if ((c = getchar()) == EOF) exit(0); + while (c != '\n'); + } + while (ch != 'y' && ch != 'n' && ch != 'Y' && ch != 'N'); + return ch == 'y' || ch == 'Y'; +} + + +static int kill_all(int signal,int names,char **namelist) +{ + DIR *dir; + struct dirent *de; + FILE *file; + struct stat st,sts[MAX_NAMES]; + int *name_len; + char path[PATH_MAX+1],comm[COMM_LEN]; + char command_buf[PATH_MAX+1]; + char *command; + pid_t *pid_table,pid,self,*pid_killed; + pid_t *pgids; + int empty,i,j,okay,length,got_long,error; + int pids,max_pids,pids_killed; + unsigned long found; + + if (!(name_len = malloc(sizeof(int)*names))) { + perror("malloc"); + exit(1); + } + for (i = 0; i < names; i++) + if (!strchr(namelist[i],'/')) { + sts[i].st_dev = 0; + name_len[i] = strlen(namelist[i]); + } + else if (stat(namelist[i],&sts[i]) < 0) { + perror(namelist[i]); + exit(1); + } + self = getpid(); + found = 0; + if (!(dir = opendir(PROC_BASE))) { + perror(PROC_BASE); + exit(1); + } + max_pids = 256; + pid_table = malloc(max_pids*sizeof(pid_t)); + if (!pid_table) { + perror("malloc"); + exit(1); + } + pids = 0; + while (de = readdir(dir)) { + if (!(pid = atoi(de->d_name)) || pid == self) continue; + if (pids == max_pids) { + if (!(pid_table = realloc(pid_table,2*pids*sizeof(pid_t)))) { + perror("realloc"); + exit(1); + } + max_pids *= 2; + } + pid_table[pids++] = pid; + } + (void) closedir(dir); + empty = 1; + pids_killed = 0; + pid_killed = malloc(max_pids*sizeof(pid_t)); + if (!pid_killed) { + perror("malloc"); + exit(1); + } + if (!process_group) pgids = NULL; /* silence gcc */ + else { + pgids = malloc(pids*sizeof(pid_t)); + if (!pgids) { + perror("malloc"); + exit(1); + } + } + for (i = 0; i < pids; i++) { + sprintf(path,PROC_BASE "/%d/stat",pid_table[i]); + if (!(file = fopen(path,"r"))) continue; + empty = 0; + okay = fscanf(file,"%*d (%[^)]",comm) == 1; + (void) fclose(file); + if (!okay) continue; + got_long = 0; + command = NULL; /* make gcc happy */ + length = strlen(comm); + if (length == COMM_LEN-1) { + sprintf(path,PROC_BASE "/%d/cmdline",pid_table[i]); + if (!(file = fopen(path,"r"))) continue; + okay = fscanf(file,"%s",command_buf) == 1; + (void) fclose(file); + if (exact && !okay) { + if (verbose) + fprintf(stderr,"skipping partial match %s(%d)\n",comm, + pid_table[i]); + continue; + } + got_long = okay; + if (okay) { + command = strrchr(command_buf,'/'); + if (command) command++; + else command = command_buf; + } + } + for (j = 0; j < names; j++) { + pid_t id; + + if (!sts[j].st_dev) { + if (length != COMM_LEN-1 || name_len[j] < COMM_LEN-1) { + if (strcmp(namelist[j],comm)) continue; + } + else if (got_long ? strcmp(namelist[j],command) : + strncmp(namelist[j],comm,COMM_LEN-1)) continue; + } + else { + sprintf(path,PROC_BASE "/%d/exe",pid_table[i]); + if (stat(path,&st) < 0) continue; + if (sts[j].st_dev != st.st_dev || sts[j].st_ino != st.st_ino) + continue; + } + if (!process_group) id = pid_table[i]; + else { + int j; + + id = getpgid(pid_table[i]); + pgids[i] = id; + if (id < 0) { + fprintf(stderr,"getpgid(%d): %s\n",pid_table[i], + strerror(errno)); + } + for (j = 0; j < i; j++) + if (pgids[j] == id) break; + if (j < i) continue; + } + if (interactive && !ask(comm,id)) continue; + if (pidof) { + if (found) putchar(' '); + printf("%d",id); + found |= 1 << j; + } + else if (kill(process_group ? -id : id,signal) >= 0) { + if (verbose) + fprintf(stderr,"Killed %s(%s%d)\n",got_long ? command : + comm,process_group ? "pgid " : "",id); + found |= 1 << j; + pid_killed[pids_killed++] = id; + } + else if (errno != ESRCH || interactive) + fprintf(stderr,"%s(%d): %s\n",got_long ? command : + comm,id,strerror(errno)); + } + } + if (empty) { + fprintf(stderr,PROC_BASE " is empty (not mounted ?)\n"); + exit(1); + } + if (!quiet && !pidof) + for (i = 0; i < names; i++) + if (!(found & (1 << i))) + fprintf(stderr,"%s: no process killed\n",namelist[i]); + if (pidof) putchar('\n'); + error = found == ((1 << (names-1)) | ((1 << (names-1))-1)) ? 0 : 1; + /* + * We scan all (supposedly) killed processes every second to detect dead + * processes as soon as possible in order to limit problems of race with + * PID re-use. + */ + while (pids_killed && wait_until_dead) { + for (i = 0; i < pids_killed;) { + if (kill(process_group ? -pid_killed[i] : pid_killed[i],0) < 0 && + errno == ESRCH) { + pid_killed[i] = pid_killed[--pids_killed]; + continue; + } + i++; + } + sleep(1); /* wait a bit longer */ + } + return error; +} + + +static void usage_pidof(void) +{ + fprintf(stderr,"usage: pidof [ -eg ] name ...\n"); + fprintf(stderr," pidof -V\n\n"); + fprintf(stderr," -e require exact match for very long names;\n"); + fprintf(stderr," skip if the command line is unavailable\n"); + fprintf(stderr," -g show process group ID instead of process ID\n"); + fprintf(stderr," -V display version information\n\n"); +} + + +static void usage_killall(void) +{ + fprintf(stderr,"usage: killall [ -egiqvw ] [ -signal ] name ...\n"); + fprintf(stderr," killall -l\n"); + fprintf(stderr," killall -V\n\n"); + fprintf(stderr," -e require exact match for very long names;\n"); + fprintf(stderr," skip if the command line is unavailable\n"); + fprintf(stderr," -g kill process group instead of process\n"); + fprintf(stderr," -i ask for confirmation before killing\n"); + fprintf(stderr," -l list all known signal names\n"); + fprintf(stderr," -q quiet; don't print complaints\n"); + fprintf(stderr," -signal send signal instead of SIGTERM\n"); + fprintf(stderr," -v report if the signal was successfully sent\n"); + fprintf(stderr," -V display version information\n"); + fprintf(stderr," -w wait for processes to die\n\n"); +} + + +static void usage(void) +{ + if (pidof) usage_pidof(); + else usage_killall(); + exit(1); +} + + +int main(int argc,char **argv) +{ + char *name,*walk; + int sig_num; + + name = strrchr(*argv,'/'); + if (name) name++; + else name = *argv; + pidof = strcmp(name,"killall"); + if (argc == 2 && !strcmp(argv[1],"-l")) { + if (pidof) usage(); + list_signals(); + return 0; + } + if (argc == 2 && !strcmp(argv[1],"-V")) { + fprintf(stderr,"%s from psmisc version " PSMISC_VERSION "\n", + pidof ? "pidof" : "killall"); + return 0; + } + sig_num = SIGTERM; + while (argc > 1 && *argv[1] == '-') { + argc--; + argv++; + if (**argv == '-') { + for (walk = *argv+1; *walk && strchr("eigqvw",*walk); walk++) { + switch (*walk) { + case 'e': + exact = 1; + break; + case 'i': + if (pidof) usage(); + interactive = 1; + break; + case 'g': + process_group = 1; + break; + case 'q': + if (pidof) usage(); + quiet = 1; + break; + case 'v': + if (pidof) usage(); + verbose = 1; + break; + case 'w': + if (pidof) usage(); + wait_until_dead = 1; + break; + } + } + if (*walk) + if (walk != *argv+1 || pidof) usage(); + else sig_num = get_signal(*argv+1,"killall"); + } + } + if (argc < 2) usage(); + if (argc > MAX_NAMES+1) { + fprintf(stderr,"Maximum number of names is %d\n",MAX_NAMES); + exit(1); + } + return kill_all(sig_num,argc-1,argv+1); +} diff --git a/psmisc/mkdist b/psmisc/mkdist new file mode 100755 index 0000000..2472e6b --- /dev/null +++ b/psmisc/mkdist @@ -0,0 +1,7 @@ +#!/bin/sh +VERSION=`cat ./VERSION` +cd .. +tar cvf - psmisc/VERSION psmisc/README psmisc/INSTALL psmisc/CHANGES \ + psmisc/COPYING psmisc/psmisc-$VERSION.lsm \ + psmisc/Makefile psmisc/comm.h psmisc/signals.h \ + psmisc/*.[c1] psmisc/mkdist | gzip -9 >psmisc-$VERSION.tar.gz diff --git a/psmisc/pidof.1 b/psmisc/pidof.1 new file mode 100644 index 0000000..c9820b2 --- /dev/null +++ b/psmisc/pidof.1 @@ -0,0 +1,45 @@ +.TH PIDOF 1 "May 6, 1998" "Linux" "User Commands" +.SH NAME +pidof \- finds processes by name and lists their PIDs +.SH SYNOPSIS +.ad l +.B pidif +.RB [ \-eg ] +.I name ... +.br +.B pidof +.RB \-V +.ad b +.SH DESCRIPTION +.B pidof +lists the PIDs of all processes running any of the specified commands. +.PP +If the command name contains a slash (\fB/\fP), processes executing that +particular file will be selected, independent of their name. +.PP +\fBpidof\fP returns a non-zero return code if no process has been found +for any of the listed commands. If at least one process has been found for +each command, \fBpidof\fP returns zero. +.SH OPTIONS +.IP \fB\-e\fP +Require an exact match for very long names. If a command name is longer +than 15 characters, the full name may be unavailable (i.e. it is swapped +out). In this case, \fBpidof\fP will list everything that matches within +the first 15 characters. With \fB\-e\fP, such entries are silently skipped. +.IP \fB\-g\fP +Show process group IDs instead of process IDs. If multiple processes with +the same process group ID were found, only the first process is shown. +.IP \fB\-V\fP +Display version information. +.SH FILES +.nf +/proc location of the proc file system +.fi +.SH "KNOWN BUGS" +Selection by file only works for executables that are kept open during +execution, i.e. impure executables can't be selected this way. +.SH AUTHOR +Werner Almesberger +.SH "SEE ALSO" +killall(1), fuser(1), ps(1) +.\"{{{}}} diff --git a/psmisc/psmisc-19.lsm b/psmisc/psmisc-19.lsm new file mode 100644 index 0000000..82006a1 --- /dev/null +++ b/psmisc/psmisc-19.lsm @@ -0,0 +1,14 @@ +Begin3 +Title: psmisc +Version: 19 +Entered-date: 25OCT99 +Description: miscellaneous proc FS tools: fuser, killall, pidof, and pstree +Keywords: psmisc, fuser, killall, pidof, pstree +Author: Werner Almesberger +Maintained-by: Werner Almesberger +Primary-site: lrcftp.epfl.ch /pub/linux/local/psmisc + 21 kB psmisc-19.tar.gz +Alternate-site: sunsite.unc.edu /pub/Linux/system/status/ps +Platforms: Should work with most kernels. Requires /proc +Copying-policy: BSD-like +End diff --git a/psmisc/pstree.1 b/psmisc/pstree.1 new file mode 100644 index 0000000..7d5367c --- /dev/null +++ b/psmisc/pstree.1 @@ -0,0 +1,85 @@ +.TH PSTREE 1 "May 6, 1998" "Linux" "User Commands" +.SH NAME +pstree \- display a tree of processes +.SH SYNOPSIS +.ad l +.B pstree +.RB [ \-a ] +.RB [ \-c ] +.RB [ \-h | \-H \fIpid\fB ] +.RB [ \-l ] +.RB [ \-n ] +.RB [ \-p ] +.RB [ \-u ] +.RB [ \-G | \-U ] +.RB [ \fIpid\fB | \fIuser\fB] +.br +.B pstree +.RB \-V +.ad b +.SH DESCRIPTION +.B pstree +shows running processes as a tree. The tree is rooted at either +\fIpid\fP or \fBinit\fP if \fIpid\fP is omitted. If a user name is specified, +all process trees rooted at processes owned by that user are shown. +.PP +\fBpstree\fP visually merges identical branches by putting them in square +brackets and prefixing them with the repetition count, e.g. +.nf +.sp + init\-+\-getty + |\-getty + |\-getty + `-getty +.sp +.fi +becomes +.nf +.sp + init\-\-\-4*[getty] +.sp +.fi +.SH OPTIONS +.IP \fB\-a\fP +Show command line arguments. If the command line of a process is swapped out, +that process is shown in parentheses. \fB\-a\fP implicitly disables compaction. +.IP \fB\-c\fP +Disable compaction of identical subtrees. By default, subtrees are compacted +whenever possible. +.IP \fB\-G\fP +Use VT100 line drawing characters. +.IP \fB\-h\fP +Highlight the current process and its ancestors. This is a no-op if the +terminal doesn't support highlighting or if neither the current process +nor any of its ancestors are in the subtree being shown. +.IP \fB\-H\fP +Like \fB\-h\fP, but highlight the specified process instead. Unlike with +\fB\-h\fP, \fBpstree\fP fails when using \fB\-H\fP if highlighting is not +available. +.IP \fB\-l\fP +Display long lines. By default, lines are truncated to the display width or +132 if output is sent to a non-tty or if the display width is unknown. +.IP \fB\-n\fP +Sort processes with the same ancestor by PID instead of by name. (Numeric +sort.) +.IP \fB\-p\fP +Show PIDs. PIDs are shown as decimal numbers in parentheses after each +process name. \fB\-p\fP implicitly disables compaction. +.IP \fB\-u\fP +Show uid transitions. Whenever the uid of a process differs from the uid of +its parent, the new uid is shown in parentheses after the process name. +.IP \fB\-U\fP +Use UTF-8 (Unicode) line drawing characters. Under Linux 1.1-54 and above, +UTF-8 mode is entered on the console with \fBecho -e '\\033%8'\fP and left +with \fBecho -e '\\033%@'\fP +.IP \fB\-V\fP +Display version information. +.SH FILES +.nf +/proc location of the proc file system +.fi +.SH AUTHOR +Werner Almesberger +.SH "SEE ALSO" +ps(1), top(1) +.\"{{{}}} diff --git a/psmisc/pstree.c b/psmisc/pstree.c new file mode 100644 index 0000000..bd6f335 --- /dev/null +++ b/psmisc/pstree.c @@ -0,0 +1,576 @@ +/* pstree.c - display process tree */ + +/* Copyright 1993-1999 Werner Almesberger. See file COPYING for details. */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "comm.h" + + +#ifndef MAX_DEPTH +#define MAX_DEPTH 100 +#endif +#define PROC_BASE "/proc" + +/* UTF-8 defines by Johan Myreen */ +#define UTF_V "\342\224\202\277" /* Vertical line drawing char */ +#define UTF_VR "\342\224\234\277" /* Vertical and right */ +#define UTF_H "\342\224\200\277" /* Horizontal */ +#define UTF_UR "\342\224\224\277" /* Up and right */ +#define UTF_HD "\342\224\254\277" /* Horizontal and down */ + +#define VT_BEG "\033(0\017" /* use graphic chars */ +#define VT_END "\033(B" /* back to normal char set */ +#define VT_V "x" /* see UTF definitions above */ +#define VT_VR "t" +#define VT_H "q" +#define VT_UR "m" +#define VT_HD "w" + +typedef struct _proc { + char comm[COMM_LEN+1]; + char **argv; /* only used : argv[0] is 1st arg; undef if argc < 1 */ + int argc; /* with -a : number of arguments, -1 if swapped */ + pid_t pid; + uid_t uid; + int highlight; + struct _child *children; + struct _proc *parent; + struct _proc *next; +} PROC; + +typedef struct _child { + PROC *child; + struct _child *next; +} CHILD; + +static struct { + const char *empty_2; /* */ + const char *branch_2; /* |- */ + const char *vert_2; /* | */ + const char *last_2; /* `- */ + const char *single_3; /* --- */ + const char *first_3; /* -+- */ +} sym_ascii = { " ", + "|-", + "| ", + "`-", + "---", + "-+-" }, + sym_utf = { " ", + UTF_VR UTF_H, + UTF_V " ", + UTF_UR UTF_H, + UTF_H UTF_H UTF_H, + UTF_H UTF_HD UTF_H }, + sym_vt100 = { " ", + VT_BEG VT_VR VT_H VT_END, + VT_BEG VT_V VT_END " ", + VT_BEG VT_UR VT_H VT_END, + VT_BEG VT_H VT_H VT_H VT_END, + VT_BEG VT_H VT_HD VT_H VT_END }, + *sym = &sym_ascii; + +static PROC *list = NULL; +static int width[MAX_DEPTH],more[MAX_DEPTH]; +static int print_args = 0,compact = 1,user_change = 0,pids = 0,by_pid = 0, + trunc = 1; +static int output_width = 132; +static int cur_x = 1; +static char last_char = 0; +static int dumped = 0; /* used by dump_by_user */ + + +static void out_char(char c) +{ + cur_x += (c & 0xc0) != 0x80; /* only count first UTF-8 char */ + if (cur_x <= output_width || !trunc) putchar(c); + if (cur_x == output_width+1 && trunc) + if (last_char || (c & 0x80)) putchar('+'); + else { + last_char = c; + cur_x--; + return; + } +} + + +static void out_string(const char *str) +{ + while (*str) out_char(*str++); +} + + +static int out_int(int x) /* non-negative integers only */ +{ + int digits,div; + + digits = 0; + for (div = 1; x/div; div *= 10) digits++; + if (!digits) digits = 1; + for (div /= 10; div; div /= 10) out_char('0'+(x/div) % 10); + return digits; +} + + +static void out_newline(void) +{ + if (last_char && cur_x == output_width) putchar(last_char); + last_char = 0; + putchar('\n'); + cur_x = 1; +} + + +static PROC *find_proc(pid_t pid) +{ + PROC *walk; + + for (walk = list; walk; walk = walk->next) + if (walk->pid == pid) break; + return walk; +} + + +static PROC *new_proc(const char *comm,pid_t pid,uid_t uid) +{ + PROC *new; + + if (!(new = malloc(sizeof(PROC)))) { + perror("malloc"); + exit(1); + } + strcpy(new->comm,comm); + new->pid = pid; + new->uid = uid; + new->highlight = 0; + new->children = NULL; + new->parent = NULL; + new->next = list; + return list = new; +} + + +static void add_child(PROC *parent,PROC *child) +{ + CHILD *new,**walk; + int cmp; + + if (!(new = malloc(sizeof(CHILD)))) { + perror("malloc"); + exit(1); + } + new->child = child; + for (walk = &parent->children; *walk; walk = &(*walk)->next) + if (by_pid) { + if ((*walk)->child->pid > child->pid) break; + } + else if ((cmp = strcmp((*walk)->child->comm,child->comm)) > 0) break; + else if (!cmp && (*walk)->child->uid > child->uid) break; + new->next = *walk; + *walk = new; +} + + +static void set_args(PROC *this,const char *args,int size) +{ + char *start; + int i; + + if (!size) { + this->argc = -1; + return; + } + this->argc = 0; + for (i = 0; i < size-1; i++) + if (!args[i]) this->argc++; + if (!this->argc) return; + if (!(this->argv = malloc(sizeof(char *)*this->argc))) { + perror("malloc"); + exit(1); + } + start = strchr(args,0)+1; + size -= start-args; + if (!(this->argv[0] = malloc((size_t) size))) { + perror("malloc"); + exit(1); + } + start = memcpy(this->argv[0],start,(size_t) size); + for (i = 1; i < this->argc; i++) this->argv[i] = start = strchr(start,0)+1; +} + + +static void add_proc(const char *comm,pid_t pid,pid_t ppid,uid_t uid, + const char *args,int size) +{ + PROC *this,*parent; + + if (!(this = find_proc(pid))) this = new_proc(comm,pid,uid); + else { + strcpy(this->comm,comm); + this->uid = uid; + } + if (args) set_args(this,args,size); + if (pid == ppid) ppid = 0; + if (!(parent = find_proc(ppid))) parent = new_proc("?",ppid,0); + add_child(parent,this); + this->parent = parent; +} + + +static int tree_equal(const PROC *a,const PROC *b) +{ + const CHILD *walk_a,*walk_b; + + if (strcmp(a->comm,b->comm)) return 0; + if (user_change && a->uid != b->uid) return 0; + for (walk_a = a->children, walk_b = b->children; walk_a && walk_b; + walk_a = walk_a->next, walk_b = walk_b->next) + if (!tree_equal(walk_a->child,walk_b->child)) return 0; + return !(walk_a || walk_b); +} + + +static void dump_tree(PROC *current,int level,int rep,int leaf,int last, + uid_t prev_uid,int closing) +{ + CHILD *walk,*next,**scan; + const struct passwd *pw; + int lvl,i,add,offset,len,swapped,info,count,comm_len,first; + const char *tmp,*here; + char comm_tmp[5]; + + if (!current) return; + if (level >= MAX_DEPTH-1) { + fprintf(stderr,"MAX_DEPTH not big enough.\n"); + exit(1); + } + if (!leaf) + for (lvl = 0; lvl < level; lvl++) { + for (i = width[lvl]+1; i; i--) out_char(' '); + out_string(lvl == level-1 ? last ? sym->last_2 : sym->branch_2 : + more[lvl+1] ? sym->vert_2 : sym->empty_2); + } + if (rep < 2) add = 0; + else { + add = out_int(rep)+2; + out_string("*["); + } + if (current->highlight && (tmp = tgetstr("md",NULL))) tputs(tmp,1,putchar); + if (swapped = print_args && current->argc < 0) out_char('('); + comm_len = 0; + for (here = current->comm; *here; here++) + if (*here == '\\') { + out_string("\\\\"); + comm_len += 2; + } + else if (*here > ' ' && *here <= '~') { + out_char(*here); + comm_len++; + } + else { + sprintf(comm_tmp,"\\%03o",(unsigned char) *here); + out_string(comm_tmp); + comm_len += 4; + } + offset = cur_x; + info = pids || (user_change && prev_uid != current->uid); + if (info) out_char(swapped ? ',' : '('); + if (pids) (void) out_int(current->pid); + if (user_change && prev_uid != current->uid) { + if (pids) out_char(','); + if ((pw = getpwuid(current->uid))) out_string(pw->pw_name); + else (void) out_int(current->uid); + } + if (info || swapped) out_char(')'); + if (current->highlight && (tmp = tgetstr("me",NULL))) tputs(tmp,1,putchar); + if (print_args) { + for (i = 0; i < current->argc; i++) { + out_char(' '); + len = 0; + for (here = current->argv[i]; *here; here++) + len += *here > ' ' && *here <= '~' ? 1 : 4; + if (cur_x+len <= output_width-(i == current->argc-1 ? 0 : 4)) + for (here = current->argv[i]; *here; here++) + if (*here > ' ' && *here <= '~') out_char(*here); + else { + sprintf(comm_tmp,"\\%03o",(unsigned char) *here); + out_string(comm_tmp); + } + else { + out_string("..."); + break; + } + } + } + if (print_args || !current->children) { + while (closing--) out_char(']'); + out_newline(); + if (print_args) { + more[level] = !last; + width[level] = swapped+(comm_len > 1 ? 0 : -1); + for (walk = current->children; walk; walk = walk->next) + dump_tree(walk->child,level+1,1,0,!walk->next,current->uid,0); + } + } + else { + more[level] = !last; + width[level] = comm_len+cur_x-offset+add; + if (cur_x >= output_width && trunc) { + out_string(sym->first_3); + out_string("+"); + out_newline(); + } + else { + first = 1; + for (walk = current->children; walk; walk = next) { + count = 0; + next = walk->next; + if (compact) { + scan = &walk->next; + while (*scan) + if (!tree_equal(walk->child,(*scan)->child)) + scan = &(*scan)->next; + else { + if (next == *scan) next = (*scan)->next; + count++; + *scan = (*scan)->next; + } + } + if (first) { + out_string(next ? sym->first_3 : sym->single_3); + first = 0; + } + dump_tree(walk->child,level+1,count+1,walk == current->children, + !next,current->uid,closing+(count ? 1 : 0)); + } + } + } +} + + +static void dump_by_user(PROC *current,uid_t uid) +{ + const CHILD *walk; + + if (current->uid == uid) { + if (dumped) putchar('\n'); + dump_tree(current,0,1,1,1,uid,0); + dumped = 1; + return; + } + for (walk = current->children; walk; walk = walk->next) + dump_by_user(walk->child,uid); +} + + +static void read_proc(void) +{ + DIR *dir; + struct dirent *de; + FILE *file; + struct stat st; + char path[PATH_MAX+1],comm[COMM_LEN+1]; + char *buffer; + pid_t pid,ppid; + int fd,size; + int empty,dummy; + + if (!print_args) buffer = NULL; + else if (!(buffer = malloc((size_t) (output_width+1)))) { + perror("malloc"); + exit(1); + } + if (!(dir = opendir(PROC_BASE))) { + perror(PROC_BASE); + exit(1); + } + empty = 1; + while (de = readdir(dir)) + if (pid = atoi(de->d_name)) { + sprintf(path,"%s/%d/stat",PROC_BASE,pid); + if (file = fopen(path,"r")) { + empty = 0; + if (fstat(fileno(file),&st) < 0) { + perror(path); + exit(1); + } + if (fscanf(file,"%d (%[^)]) %c %d",&dummy,comm,(char *) &dummy, + &ppid) == 4) { + if (!print_args) add_proc(comm,pid,ppid,st.st_uid,NULL,0); + else { + sprintf(path,"%s/%d/cmdline",PROC_BASE,pid); + if ((fd = open(path,O_RDONLY)) < 0) { + perror(path); + exit(1); + } + if ((size = read(fd,buffer,(size_t) output_width)) < 0) + { + perror(path); + exit(1); + } + (void) close(fd); + if (size) buffer[size++] = 0; + add_proc(comm,pid,ppid,st.st_uid,buffer,size); + } + } + (void) fclose(file); + } + } + (void) closedir(dir); + if (print_args) free(buffer); + if (empty) { + fprintf(stderr,PROC_BASE " is empty (not mounted ?)\n"); + exit(1); + } +} + + +#if 0 + +/* Could use output of ps achlx | awk '{ print $3,$4,$2,$13 }' */ + +static void read_stdin(void) +{ + char comm[PATH_MAX+1]; + char *cmd; + int pid,ppid,uid; + + while (scanf("%d %d %d %s\n",&pid,&ppid,&uid,comm) == 4) { + if (cmd = strrchr(comm,'/')) cmd++; + else cmd = comm; + if (*cmd == '-') cmd++; + add_proc(cmd,pid,ppid,uid,NULL,0); + } +} + +#endif + + +static void usage(void) +{ + fprintf(stderr,"usage: pstree [ -a ] [ -c ] [ -h | -H pid ] [ -l ] [ -n ] " + "[ -p ] [ -u ]\n%14s[ -G | -U ] [ pid | user]\n",""); + fprintf(stderr," pstree -V\n\n"); + fprintf(stderr," -a show command line arguments\n"); + fprintf(stderr," -c don't compact identical subtrees\n"); + fprintf(stderr," -h highlight current process and its ancestors\n"); + fprintf(stderr," -H pid highlight process \"pid\" and its ancestors\n"); + fprintf(stderr," -G use VT100 line drawing characters\n"); + fprintf(stderr," -l don't truncate long lines\n"); + fprintf(stderr," -n sort output by PID\n"); + fprintf(stderr," -p show PIDs; implies -c\n"); + fprintf(stderr," -u show uid transitions\n"); + fprintf(stderr," -U use UTF-8 (Unicode) line drawing characters\n"); + fprintf(stderr," -V display version information\n"); + fprintf(stderr," pid start at pid, default 1 (init)\n"); + fprintf(stderr," user show only trees rooted at processes of that " + "user\n\n"); + exit(1); +} + + +int main(int argc,char **argv) +{ + PROC *current; + struct winsize winsz; + const struct passwd *pw; + pid_t pid,highlight; + char termcap_area[1024]; + int c; + + if (ioctl(1,TIOCGWINSZ,&winsz) >= 0) + if (winsz.ws_col) output_width = winsz.ws_col; + pid = 1; + highlight = 0; + pw = NULL; + while ((c = getopt(argc,argv,"acGhH:npluUV")) != EOF) + switch (c) { + case 'a': + print_args = 1; + break; + case 'c': + compact = 0; + break; + case 'G': + if (sym != &sym_ascii) usage(); + sym = &sym_vt100; + break; + case 'h': + if (highlight) usage(); + if (getenv("TERM") && tgetent(termcap_area,getenv("TERM")) > 0) + highlight = getpid(); + break; + case 'H': + if (highlight) usage(); + if (!getenv("TERM")) { + fprintf(stderr,"TERM is not set\n"); + return 1; + } + if (tgetent(termcap_area,getenv("TERM")) <= 0) { + fprintf(stderr,"can't get terminal capabilities\n"); + return 1; + } + if (!(highlight = atoi(optarg))) usage(); + break; + case 'l': + trunc = 0; + break; + case 'n': + by_pid = 1; + break; + case 'p': + pids = 1; + compact = 0; + break; + case 'u': + user_change = 1; + break; + case 'U': + if (sym != &sym_ascii) usage(); + sym = &sym_utf; + break; + case 'V': + fprintf(stderr,"pstree from psmisc version " PSMISC_VERSION + "\n"); + return 0; + default: + usage(); + } + if (optind == argc-1) + if (isdigit(*argv[optind])) { + if (!(pid = atoi(argv[optind++]))) usage(); + } + else if (!(pw = getpwnam(argv[optind++]))) { + fprintf(stderr,"No such user name: %s\n",argv[optind-1]); + return 1; + } + if (optind != argc) usage(); + read_proc(); + for (current = find_proc(highlight); current; current = current->parent) + current->highlight = 1; + if (!pw) + dump_tree(find_proc(pid),0,1,1,1,0,0); + else { + dump_by_user(find_proc(1),pw->pw_uid); + if (!dumped) { + fprintf(stderr,"No processes found.\n"); + return 1; + } + } + return 0; +} diff --git a/psmisc/signals.c b/psmisc/signals.c new file mode 100644 index 0000000..454ab9e --- /dev/null +++ b/psmisc/signals.c @@ -0,0 +1,53 @@ +/* signals.c - signal name handling */ + +/* Copyright 1993-1995 Werner Almesberger. See file COPYING for details. */ + + +#include +#include +#include +#include +#include "signals.h" + + +typedef struct { + int number; + const char *name; +} SIGNAME; + + +static SIGNAME signals[] = { +#include "signames.h" + { 0,NULL }}; + + +void list_signals(void) +{ + SIGNAME *walk; + int col; + + col = 0; + for (walk = signals; walk->name; walk++) { + if (col+strlen(walk->name)+1 > 80) { + putchar('\n'); + col = 0; + } + printf("%s%s",col ? " " : "",walk->name); + col += strlen(walk->name)+1; + } + putchar('\n'); +} + + +int get_signal(char *name,const char *cmd) +{ + SIGNAME *walk; + + if (isdigit(*name)) + return atoi(name); + for (walk = signals; walk->name; walk++) + if (!strcmp(walk->name,name)) break; + if (walk->name) return walk->number; + fprintf(stderr,"%s: unknown signal; %s -l lists signals.\n",name,cmd); + exit(1); +} diff --git a/psmisc/signals.h b/psmisc/signals.h new file mode 100644 index 0000000..1ba75a4 --- /dev/null +++ b/psmisc/signals.h @@ -0,0 +1,19 @@ +/* signals.h - signal name handling */ + +/* Copyright 1993-1995 Werner Almesberger. See file COPYING for details. */ + + +#ifndef SIGNALS_H +#define SIGNALS_H + +void list_signals(void); + +/* Lists all known signal names on standard output. */ + +int get_signal(char *name,const char *cmd); + +/* Returns the signal number of NAME. If no such signal exists, an error + message is displayed and the program is terminated. CMD is the name of the + application. */ + +#endif -- 2.40.0