| /* |
| * 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; |
| } |
| } |