blob: 0a48d2815c75480d5de7f01b201cb74ce3282a58 [file] [log] [blame]
/*
* dproc.c - SCO UnixWare process access functions for lsof
*/
/*
* Copyright 1996 Purdue Research Foundation, West Lafayette, Indiana
* 47907. All rights reserved.
*
* Written by Victor A. Abell
*
* This software is not subject to any license of the American Telephone
* and Telegraph Company or the Regents of the University of California.
*
* Permission is granted to anyone to use this software for any purpose on
* any computer system, and to alter it and redistribute it freely, subject
* to the following restrictions:
*
* 1. Neither the authors nor Purdue University are responsible for any
* consequences of the use of this software.
*
* 2. The origin of this software must not be misrepresented, either by
* explicit claim or by omission. Credit to the authors and Purdue
* University must appear in documentation and sources.
*
* 3. Altered versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 4. This notice may not be removed or altered.
*/
#ifndef lint
static char copyright[] =
"@(#) Copyright 1996 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: dproc.c,v 1.14 2002/10/08 20:18:34 abe Exp $";
#endif
#include "lsof.h"
/*
* Local static values
*/
static int Np; /* occupied P[] count */
static int Npa = 0; /* allocated P[] count */
static struct proc *P = (struct proc *)NULL;
/* proc table */
static KA_T Pract; /* kernel's practive address */
static KA_T Sgdnops; /* kernel's segdev_ops address */
static KA_T Sgvnops; /* kernel's segvn_ops address */
static struct var Var; /* kernel variables */
/*
* Local definitions
*/
#define PROCINCR 32 /* increment for increasing P[] */
/*
* Local function prototypes.
*/
_PROTOTYPE(static int get_clonemaj,(void));
_PROTOTYPE(static void read_proc,(void));
_PROTOTYPE(static void get_kernel_access,(void));
_PROTOTYPE(static void readfsinfo,(void));
_PROTOTYPE(static void process_text,(KA_T pa));
/*
* gather_proc_info() -- gather process information
*/
void
gather_proc_info()
{
struct cred cr;
struct execinfo ex;
static struct fd_entry *fe;
struct fd_entry *f;
KA_T fa;
int i, nf;
MALLOC_S len;
static int nfea = 0;
struct proc *p;
int pgid, pid, px;
struct pid pids;
short pss, sf;
uid_t uid;
#if UNIXWAREV>=70103
struct pollx plx;
#endif /* UNIXWAREV>=70103 */
/*
* Examine proc structures and their associated information.
*/
(void) read_proc();
for (p = P, px = 0; px < Np; p++, px++) {
if ((p->p_flag & P_DESTROY) || (p->p_flag & P_GONE)
|| !p->p_pidp
#if !defined(HAS_P_PGID)
|| !p->p_pgidp
#endif /* !defined(HAS_P_PGID) */
|| !p->p_cred || !p->p_execinfo)
continue;
/*
* Get Process ID, Process group ID, and User ID.
*/
if (!p->p_pidp
|| kread((KA_T)p->p_pidp, (char *)&pids, sizeof(pids)))
continue;
pid = (int)pids.pid_id;
#if defined(HAS_P_PGID)
pgid = (int)p->p_pgid;
#else /* !defined(HAS_P_PGID) */
if (!p->p_pgidp
|| kread((KA_T)p->p_pgidp, (char *)&pids, sizeof(pids)))
continue;
pgid = (int)pids.pid_id;
#endif /* defined(HAS_P_PGID) */
if (!p->p_cred
|| kread((KA_T)p->p_cred, (char *)&cr, sizeof(cr)))
continue;
uid = cr.cr_uid;
if (is_proc_excl(pid, pgid, (UID_ARG)uid, &pss, &sf))
continue;
/*
* Get the execution information -- for the command name.
*/
if (!p->p_execinfo
|| kread((KA_T)p->p_execinfo, (char *)&ex, sizeof(ex)))
continue;
/*
* Allocate a local process structure.
*/
if (is_cmd_excl(ex.ei_comm, &pss, &sf))
continue;
alloc_lproc(pid, pgid, (int)p->p_ppid, (UID_ARG)uid, ex.ei_comm,
(int)pss, (int)sf);
Plf = NULL;
/*
* Save current working directory information.
*/
if (p->p_cdir) {
alloc_lfile(CWD, -1);
process_node((KA_T)p->p_cdir);
if (Lf->sf)
link_lfile();
}
/*
* Save root directory information.
*/
if (p->p_rdir) {
alloc_lfile(RTD, -1);
process_node((KA_T)p->p_rdir);
if (Lf->sf)
link_lfile();
}
/*
* Print information on the text file.
*/
if (Sgvnops && p->p_as)
process_text((KA_T)p->p_as);
/*
* Save information on file descriptors.
*/
if (!p->p_fdtab.fdt_entrytab || (nf = p->p_fdtab.fdt_sizeused) < 1)
continue;
len = (MALLOC_S)(nf * sizeof(struct fd_entry));
if (nf > nfea) {
if (fe)
fe = (struct fd_entry *)realloc((MALLOC_P *)fe, len);
else
fe = (struct fd_entry *)malloc(len);
if (!fe) {
(void) fprintf(stderr,
"%s: PID %d; no space for %d file descriptors\n",
Pn, pid, nf);
Exit(1);
}
nfea = nf;
}
if (kread((KA_T)p->p_fdtab.fdt_entrytab, (char *)fe, len))
continue;
for (f = fe, i = 0; i < nf; f++, i++) {
if ((fa = (KA_T)f->fd_file) && (f->fd_status & FD_INUSE)) {
#if UNIXWAREV>=70103
if (f->fd_flag & FPOLLED) {
if (kread(fa, (char *)&plx, sizeof(plx))
|| !(fa = (KA_T)plx.px_fp))
continue;
}
#endif /* UNIXWAREV>=70103 */
alloc_lfile(NULL, i);
process_file(fa);
if (Lf->sf) {
#if defined(HASFSTRUCT)
if (Fsv & FSV_FG)
Lf->pof = (long)f->fd_flag;
#endif /* defined(HASFSTRUCT) */
link_lfile();
}
}
}
/*
* Examine results.
*/
if (examine_lproc())
return;
}
}
/*
* get_clonemaj() - get clone major device number
*/
static int
get_clonemaj()
{
KA_T v;
#if UNIXWAREV<70000
char buf[32];
struct cdevsw *c, *cd;
int i, sz;
MALLOC_S len;
int rv = 0;
/*
* Read the cdevsw[] size and allocate temporary space for it.
*/
if (get_Nl_value("ncdev", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)&sz, sizeof(sz)) || !sz)
return(rv);
len = (MALLOC_S)(sz * sizeof(struct cdevsw));
if (!(cd = (struct cdevsw *)malloc(len))) {
(void) fprintf(stderr, "%s: can't allocate %d bytes for cdevsw\n",
Pn);
Exit(1);
}
/*
* Read the cdevsw[] from kernel memory.
*/
if (get_Nl_value("cdev", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)cd, (int)len)) {
(void) free((MALLOC_P *)cd);
return(rv);
}
/*
* Scan the cdevsw[], reading it's names, looking for "clone".
* Record its cdevsw[] index (i.e., major device number).
*/
len = sizeof(buf) - 1;
buf[len] = '\0';
for (c = cd, i = 0; i < sz; c++, i++) {
if (!c->d_name
|| kread((KA_T)c->d_name, buf, len)
|| strcmp(buf, "clone") != 0)
continue;
CloneMaj = i;
HaveCloneMaj = rv = 1;
break;
}
(void) free((MALLOC_P *)cd);
return(rv);
#else /* UNIXWAREV>=70000 */
/*
* At UnixWare 7 the clone major device is found in the kernel's
* clonemajor variable.
*/
if (get_Nl_value("cmaj", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)&CloneMaj, sizeof(CloneMaj)))
return(0);
return((HaveCloneMaj = 1));
#endif /* UNIXWAREV<70000 */
}
/*
* get_kernel_access() - get access to kernel memory
*/
static void
get_kernel_access()
{
KA_T v;
/*
* Check kernel version.
*/
(void) ckkv("UW", (char *)NULL, LSOF_VSTR, (char *)NULL);
#if defined(WILLDROPGID)
/*
* If kernel memory isn't coming from KMEM, drop setgid permission
* before attempting to open the (Memory) file.
*/
if (Memory)
(void) dropgid();
#else /* !defined(WILLDROPGID) */
/*
* See if the non-KMEM memory file is readable.
*/
if (Memory && !is_readable(Memory, 1))
Exit(1);
#endif /* defined(WILLDROPGID) */
/*
* Open kernel memory access.
*/
if ((Kd = open(Memory ? Memory : KMEM, O_RDONLY, 0)) < 0) {
(void) fprintf(stderr, "%s: can't open %s: %s\n", Pn,
Memory ? Memory : KMEM, strerror(errno));
Exit(1);
}
#if defined(WILLDROPGID)
/*
* Drop setgid permission, if necessary.
*/
if (!Memory)
(void) dropgid();
#else /* !defined(WILLDROPGID) */
/*
* See if the name list file is readable.
*/
if (Nmlst && !is_readable(Nmlst, 1))
Exit(1);
#endif /* defined(WILLDROPGID) */
/*
* Access kernel symbols and values.
*/
(void) build_Nl(Drive_Nl);
if (nlist(Nmlst ? Nmlst : N_UNIX, Nl) < 0) {
(void) fprintf(stderr, "%s: can't read kernel name list from %s\n",
Pn, Nmlst ? Nmlst : N_UNIX);
Exit(1);
}
if (get_Nl_value("var", Drive_Nl, &v) < 0 || !v
|| kread((KA_T)v, (char *)&Var, sizeof(Var))) {
(void) fprintf(stderr,
"%s: can't read system configuration info\n", Pn);
Exit(1);
}
if (get_Nl_value("proc", Drive_Nl, &Pract) < 0 || !Pract) {
(void) fprintf(stderr,
"%s: can't find active process chain pointer\n", Pn);
Exit(1);
}
if (get_Nl_value("sgdnops", Drive_Nl, &Sgdnops) < 0 || !Sgdnops)
Sgdnops = (unsigned long)0;
if (get_Nl_value("sgvnops", Drive_Nl, &Sgvnops) < 0 || !Sgvnops)
Sgvnops = (unsigned long)0;
/*
* Identify the clone major device number.
*/
if (!get_clonemaj()) {
if (!Fwarn)
(void) fprintf(stderr,
"%s: WARNING; can't identify major clone device number\n",
Pn);
}
}
/*
* initialize() - perform all initialization
*/
void
initialize()
{
get_kernel_access();
readfsinfo();
}
/*
* kread() - read from kernel memory
*/
int
kread(addr, buf, len)
KA_T addr; /* kernel memory address */
char *buf; /* buffer to receive data */
READLEN_T len; /* length to read */
{
READLEN_T br;
#if UNIXWAREV<7000
if (lseek(Kd, (long)addr, L_SET) == (long)-1L)
return(-1);
br = (READLEN_T) read(Kd, buf, len);
#else /* UNIXWAREV>=7000 */
br = (READLEN_T) pread(Kd, buf, len, (off_t)addr);
#endif /* UNIXWAREV<7000 */
return((br == len) ? 0 : 1);
}
/*
* process_text() - process text access information
*/
static void
process_text(pa)
KA_T pa; /* kernel address space description
* pointer */
{
struct as as;
struct segdev_data dv;
char *fd;
int i, j, k, l;
struct seg s;
KA_T v[MAXSEGS];
struct segvn_data vn;
KA_T vp;
/*
* Get address space description.
*/
if (kread(pa, (char *)&as, sizeof(as)))
return;
/*
* Loop through the segments. The loop should stop when the segment
* pointer returns to its starting point, but just in case, it's stopped
* when MAXSEGS have been recorded or 2*MAXSEGS have been examined.
*/
s.s_next = as.a_segs;
for (i = j = k = 0; i < MAXSEGS && j < 2*MAXSEGS; j++) {
if (!s.s_next || kread((KA_T)s.s_next, (char *)&s, sizeof(s)))
break;
fd = (char *)NULL;
vp = (KA_T)NULL;
if (Sgvnops == (KA_T)s.s_ops && s.s_data) {
/*
* Process a virtual node segment.
*/
if (kread((KA_T)s.s_data, (char *)&vn, sizeof(vn)))
break;
if ((vp = (KA_T)vn.svd_vp)) {
if ((vn.svd_flags & SEGVN_PGPROT)
|| (vn.svd_prot & PROT_EXEC))
fd = " txt";
else
fd = " mem";
}
} else if (Sgdnops == (KA_T)s.s_ops && s.s_data) {
/*
* Process a special device segment.
*/
if (kread((KA_T)s.s_data, (char *)&dv, sizeof(dv)))
break;
if ((vp = (KA_T)dv.vp))
fd = "mmap";
}
if (fd && vp) {
/*
* Process the vnode pointer. First make sure it's unique.
*/
for (l = 0; l < k; l++) {
if (v[l] == vp)
break;
}
if (l >= k) {
alloc_lfile(fd, -1);
process_node(vp);
if (Lf->sf) {
link_lfile();
i++;
}
}
v[k++] = vp;
}
/*
* Follow the segment link to the starting point in the address
* space description. (The i and j counters place an absolute
* limit on the loop.)
*/
if (s.s_next == as.a_segs)
break;
}
}
/*
* readfsinfo() - read file system information
*/
static void
readfsinfo()
{
char buf[FSTYPSZ+1];
int i, len;
if ((Fsinfomax = sysfs(GETNFSTYP)) == -1) {
(void) fprintf(stderr, "%s: sysfs(GETNFSTYP) error: %s\n",
Pn, strerror(errno));
Exit(1);
}
if (Fsinfomax == 0)
return;
if (!(Fsinfo = (char **)malloc((MALLOC_S)(Fsinfomax * sizeof(char *)))))
{
(void) fprintf(stderr, "%s: no space for sysfs info\n", Pn);
Exit(1);
}
for (i = 1; i <= Fsinfomax; i++) {
if (sysfs(GETFSTYP, i, buf) == -1) {
(void) fprintf(stderr, "%s: sysfs(GETFSTYP) error: %s\n",
Pn, strerror(errno));
Exit(1);
}
buf[FSTYPSZ] = '\0';
len = strlen(buf) + 1;
if (!(Fsinfo[i-1] = (char *)malloc((MALLOC_S)len))) {
(void) fprintf(stderr,
"%s: no space for file system entry %s\n", Pn, buf);
Exit(1);
}
(void) snpf(Fsinfo[i-1], len, "%s", buf);
}
}
/*
* read_proc() - read the process table
*/
static void
read_proc()
{
MALLOC_S len;
struct proc *p;
KA_T pa;
char tbuf[32];
int try;
if (!P) {
/*
* Allocate initial space for local proc table.
*/
if ((Npa = Var.v_proc) < 1) {
(void) fprintf(stderr, "%s: bad proc table size: %d\n",
Pn, Var.v_proc);
Exit(1);
}
Npa += PROCINCR;
len = (MALLOC_S)(Npa * sizeof(struct proc));
if (!(P = (struct proc *)malloc(len))) {
(void) fprintf(stderr, "%s: no space for %d proc structures\n",
Pn, Npa);
Exit(1);
}
}
/*
* Scan the active process chain.
*/
for (try = 0; try < PROCTRYLM; try++) {
/*
* Read the active process chain head.
*/
pa = (KA_T)NULL;
if (!Pract || kread((KA_T)Pract, (char *)&pa, sizeof(pa)) || !pa) {
if (!Fwarn)
(void) fprintf(stderr,
"%s: active proc chain ptr err; addr=%s, val=%s\n",
Pn, print_kptr(Pract, tbuf, sizeof(tbuf)),
print_kptr(pa, (char *)NULL, 0));
continue;
}
/*
* Follow the active process chain, accumulating proc structures.
*/
for (Np = 0, p = P; pa;) {
if (Np >= Npa) {
/*
* Allocate more proc table space.
*/
Npa += PROCINCR;
len = (MALLOC_S)(Npa * sizeof(struct proc));
if (!(P = (struct proc *)realloc((MALLOC_P *)P, len))) {
(void) fprintf(stderr,
"%s: can't realloc %d proc table entries (%d)\n",
Pn, Npa, len);
Exit(1);
}
p = &P[Np];
}
if (kread(pa, (char *)p, sizeof(struct proc)))
break;
pa = (KA_T)p->p_next;
if ((p->p_flag & P_DESTROY) || (p->p_flag & P_GONE)
|| !p->p_pidp
#if !defined(HAS_P_PGID)
|| !p->p_pgidp
#endif /* !defined(HAS_P_PGID) */
|| !p->p_cred || !p->p_execinfo)
continue;
Np++;
p++;
}
/*
* See if enough processes were accumulated.
*/
if (Np >= PROCMIN)
break;
}
/*
* Quit if not enough proc structures could be collected.
*/
if (try >= PROCTRYLM) {
(void) fprintf(stderr, "%s: can't read proc table\n", Pn);
Exit(1);
}
if (Np < Npa && !RptTm) {
/*
* If not repeating, reduce the local proc table size to a minimum.
*/
len = (MALLOC_S)(Np * sizeof(struct proc));
if (!(P = (struct proc *)realloc((MALLOC_P *)P, len))) {
(void) fprintf(stderr,
"%s: can't reduce proc table to %d entries\n", Pn, Np);
Exit(1);
}
Npa = Np;
}
}