diff options
Diffstat (limited to 'dialects/freebsd/dproc.c')
-rw-r--r-- | dialects/freebsd/dproc.c | 700 |
1 files changed, 700 insertions, 0 deletions
diff --git a/dialects/freebsd/dproc.c b/dialects/freebsd/dproc.c new file mode 100644 index 0000000..c9eb92c --- /dev/null +++ b/dialects/freebsd/dproc.c @@ -0,0 +1,700 @@ +/* + * dproc.c - FreeBSD process access functions for lsof + */ + + +/* + * Copyright 1994 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 1994 Purdue Research Foundation.\nAll rights reserved.\n"; +static char *rcsid = "$Id: dproc.c,v 1.18 2014/10/13 22:25:07 abe Exp $"; +#endif + +#include "lsof.h" + + +_PROTOTYPE(static void enter_vn_text,(KA_T va, int *n)); +_PROTOTYPE(static void get_kernel_access,(void)); +_PROTOTYPE(static void process_text,(KA_T vm)); + + +/* + * Local static values + */ + +static MALLOC_S Nv = 0; /* allocated Vp[] entries */ +static KA_T *Vp = NULL; /* vnode address cache */ + + +/* + * enter_vn_text() - enter a vnode text reference + */ + +static void +enter_vn_text(va, n) + KA_T va; /* vnode address */ + int *n; /* Vp[] entries in use */ +{ + int i; +/* + * Ignore the request if the vnode has already been entered. + */ + for (i = 0; i < *n; i++) { + if (va == Vp[i]) + return; + } +/* + * Save the text file information. + */ + alloc_lfile(" txt", -1); + Cfp = (struct file *)NULL; + process_node(va); + if (Lf->sf) + link_lfile(); + if (i >= Nv) { + + /* + * Allocate space for remembering the vnode. + */ + Nv += 10; + if (!Vp) + Vp=(KA_T *)malloc((MALLOC_S)(sizeof(struct vnode *)*10)); + else + Vp=(KA_T *)realloc((MALLOC_P *)Vp,(MALLOC_S)(Nv*sizeof(KA_T))); + if (!Vp) { + (void) fprintf(stderr, "%s: no txt ptr space, PID %d\n", + Pn, Lp->pid); + Exit(1); + } + } +/* + * Remember the vnode. + */ + Vp[*n] = va; + (*n)++; +} + + +/* + * gather_proc_info() -- gather process information + */ + +void +gather_proc_info() +{ + short cckreg; /* conditional status of regular file + * checking: + * 0 = unconditionally check + * 1 = conditionally check */ + short ckscko; /* socket file only checking status: + * 0 = none + * 1 = check only socket files, + * including TCP and UDP + * streams with eXPORT data, + * where supported */ + struct filedesc fd; + int i, nf; + MALLOC_S nb; + +#if defined(HAS_FILEDESCENT) + typedef struct filedescent ofb_t; +#else /* !defined(HAS_FILEDESCENT) */ + typedef struct file* ofb_t; +#endif /* defined(HAS_FILEDESCENT) */ + + static ofb_t *ofb = NULL; + static int ofbb = 0; + int pgid, pid; + int ppid = 0; + short pss, sf; + int px; + int tid; /* thread (task) ID */ + uid_t uid; + +#if FREEBSDV<2000 + struct proc *p; + struct pcred pc; + struct pgrp pg; +#else /* FREEBSDV>=2000 */ + struct kinfo_proc *p; +#endif /* FREEBSDV<2000 */ + +#if defined(HASFSTRUCT) && !defined(HAS_FILEDESCENT) + static char *pof = (char *)NULL; + static int pofb = 0; +#endif /* defined(HASFSTRUCT) && !defiled(HAS_FILEDESCENT) */ + +/* + * Define socket and regular file conditional processing flags. + * + * If only socket files have been selected, or socket files have been + * selected, ANDed with other selection options, enable the skipping of + * regular files. + * + * If socket files and some process options have been selected, enable + * conditional skipping of regular file; i.e., regular files will be skipped + * unless they belong to a process selected by one of the specified options. + */ + if (Selflags & SELNW) { + + /* + * Some network files selection options have been specified. + */ + if (Fand || !(Selflags & ~SELNW)) { + + /* + * Selection ANDing or only network file options have been + * specified, so set unconditional skipping of regular files + * and socket file only checking. + */ + cckreg = 0; + ckscko = 1; + } else { + + /* + * If ORed file selection options have been specified, or no + * ORed process selection options have been specified, enable + * unconditional file checking and clear socket file only + * checking. + * + * If only ORed process selection options have been specified, + * enable conditional file skipping and socket file only checking. + */ + if ((Selflags & SELFILE) || !(Selflags & SELPROC)) + cckreg = ckscko = 0; + else + cckreg = ckscko = 1; + } + } else { + + /* + * No network file selection options were specified. Enable + * unconditional file checking and clear socket file only checking. + */ + cckreg = ckscko = 0; + } +/* + * Read the process table. + */ + +#if FREEBSDV<2000 + if ((Np = kvm_getprocs(KINFO_PROC_ALL, 0)) < 0) +#else /* FREEBSDV>=2000 */ + +# if !defined(KERN_PROC_PROC) +#define KERN_PROC_PROC KERN_PROC_ALL +# endif /* !defined(KERN_PROC_PROC) */ + + if ((P = kvm_getprocs(Kd, Ftask ? KERN_PROC_ALL : KERN_PROC_PROC, + 0, &Np)) + == NULL) +#endif /* FREEBSDV<2000 */ + + { + (void) fprintf(stderr, "%s: can't read process table: %s\n", + Pn, + +#if FREEBSDV<2000 + kvm_geterr() +#else /* FREEBSDV>=2000 */ + kvm_geterr(Kd) +#endif /* FREEBSDV<2000 */ + + ); + Exit(1); + } +/* + * Examine proc structures and their associated information. + */ + +#if FREEBSDV<2000 + for (px = 0; px < Np; px++) +#else /* FREEBSDV>=2000 */ + for (p = P, px = 0; px < Np; p++, px++) +#endif /* FREEBSDV<2000 */ + + { + +#if FREEBSDV<2000 + /* + * Read process information, process group structure (if + * necessary), and User ID (if necessary). + */ + if (!(p = kvm_nextproc())) + continue; + if (p->P_STAT == 0 || p->P_STAT == SZOMB) + continue; + pg.pg_id = 0; + if (Fpgid && p->P_PGID) { + if (kread((KA_T)p->P_PGID, (char *)&pg, sizeof(pg))) + continue; + } + pgid = pg.pg_id; + if (!p->p_cred + || kread((KA_T)p->p_cred, (char *)&pc, sizeof(pc))) + continue; + uid = pc.p_ruid; +#else /* FREEBSDV>=2000 */ + if (p->P_STAT == 0 || p->P_STAT == SZOMB) + continue; + pgid = p->P_PGID; +# if FREEBSDV<5000 + uid = p->kp_eproc.e_ucred.cr_uid; +# else /* FREEBSDV>=5000 */ + uid = p->ki_uid; +# endif /* FREEBSDV<5000 */ +#endif /* FREEBSDV<2000 */ + +#if defined(HASPPID) + ppid = p->P_PPID; +#endif /* defined(HASPPID) */ + +#if defined(HASTASKS) + /* + * See if process,including its tasks, is excluded. + */ + tid = Ftask ? (int)p->ki_tid : 0; + if (is_proc_excl(p->P_PID, pgid, (UID_ARG)uid, &pss, &sf, tid)) + continue; +#else /* !defined(HASTASKS) */ + /* + * See if process is excluded. + */ + if (is_proc_excl(p->P_PID, pgid, (UID_ARG)uid, &pss, &sf)) + continue; +#endif /* defined(HASTASKS) */ + + /* + * Read file structure pointers. + */ + if (!p->P_FD + || kread((KA_T)p->P_FD, (char *)&fd, sizeof(fd))) + continue; + if (!fd.fd_refcnt || fd.fd_lastfile > fd.fd_nfiles) + continue; + /* + * Allocate a local process structure. + */ + if (is_cmd_excl(p->P_COMM, &pss, &sf)) + continue; + if (cckreg) { + + /* + * If conditional checking of regular files is enabled, enable + * socket file only checking, based on the process' selection + * status. + */ + ckscko = (sf & SELPROC) ? 0 : 1; + } + alloc_lproc(p->P_PID, pgid, ppid, (UID_ARG)uid, p->P_COMM, + (int)pss, (int)sf); + Plf = (struct lfile *)NULL; + +#if defined(HASTASKS) + /* + * Save the task (thread) ID. + */ + Lp->tid = tid; +#endif /* defined(HASTASKS) */ + +#if defined(P_ADDR) + /* + * Save the kernel proc struct address, if P_ADDR is defined. + */ + Kpa = (KA_T)p->P_ADDR; +#endif /* defined(P_ADDR) */ + + /* + * Save current working directory information. + */ + if (!ckscko && fd.fd_cdir) { + alloc_lfile(CWD, -1); + Cfp = (struct file *)NULL; + process_node((KA_T)fd.fd_cdir); + if (Lf->sf) + link_lfile(); + } + /* + * Save root directory information. + */ + if (!ckscko && fd.fd_rdir) { + alloc_lfile(RTD, -1); + Cfp = (struct file *)NULL; + process_node((KA_T)fd.fd_rdir); + if (Lf->sf) + link_lfile(); + } + +#if FREEBSDV>=5000 + /* + * Save jail directory information. + */ + if (!ckscko && fd.fd_jdir) { + alloc_lfile("jld", -1); + Cfp = (struct file *)NULL; + process_node((KA_T)fd.fd_jdir); + if (Lf->sf) + link_lfile(); + } +#endif /* FREEBSDV>=5000 */ + + /* + * Save information on the text file. + */ + if (!ckscko && p->P_VMSPACE) + process_text((KA_T)p->P_VMSPACE); + /* + * Read open file structure pointers. + */ + if (!fd.fd_ofiles || (nf = fd.fd_nfiles) <= 0) + continue; + nb = (MALLOC_S)(sizeof(ofb_t) * nf); + if (nb > ofbb) { + if (!ofb) + ofb = (ofb_t *)malloc(nb); + else + ofb = (ofb_t *)realloc((MALLOC_P *)ofb, nb); + if (!ofb) { + (void) fprintf(stderr, "%s: PID %d, no file * space\n", + Pn, p->P_PID); + Exit(1); + } + ofbb = nb; + } + if (kread((KA_T)fd.fd_ofiles, (char *)ofb, nb)) + continue; + +#if defined(HASFSTRUCT) && !defined(HAS_FILEDESCENT) + if (Fsv & FSV_FG) { + nb = (MALLOC_S)(sizeof(char) * nf); + if (nb > pofb) { + if (!pof) + pof = (char *)malloc(nb); + else + pof = (char *)realloc((MALLOC_P *)pof, nb); + if (!pof) { + (void) fprintf(stderr, + "%s: PID %d, no file flag space\n", Pn, p->P_PID); + Exit(1); + } + pofb = nb; + } + if (!fd.fd_ofileflags || kread((KA_T)fd.fd_ofileflags, pof, nb)) + zeromem(pof, nb); + } +#endif /* defined(HASFSTRUCT) && !defined(HAS_FILEDESCENT) */ + + /* + * Save information on file descriptors. + */ + for (i = 0; i < nf; i++) { + +#if defined(HAS_FILEDESCENT) + if ((Cfp = ofb[i].fde_file)) +#else /* !defined(HAS_FILEDESCENT) */ + if ((Cfp = ofb[i])) +#endif /* defined(HAS_FILEDESCENT) */ + + { + alloc_lfile(NULL, i); + process_file((KA_T)Cfp); + if (Lf->sf) { + +#if defined(HASFSTRUCT) + if (Fsv & FSV_FG) +# if defined(HAS_FILEDESCENT) + Lf->pof = (long)ofb[i].fde_flags; +# else /* !defined(HAS_FILEDESCENT) */ + Lf->pof = (long)pof[i]; +# endif /* defined(HAS_FILEDESCENT) */ +#endif /* defined(HASFSTRUCT) */ + + link_lfile(); + } + } + } + /* + * Unless threads (tasks) are being processed, examine results. + */ + if (!Ftask) { + if (examine_lproc()) + return; + } + } +} + + +/* + * get_kernel_access() - get access to kernel memory + */ + +static void +get_kernel_access() +{ + +/* + * Check kernel version. + */ + (void) ckkv("FreeBSD", LSOF_VSTR, (char *)NULL, (char *)NULL); +/* + * Set name list file path. + */ + if (!Nmlst) + +#if defined(N_UNIX) + Nmlst = N_UNIX; +#else /* !defined(N_UNIX) */ + { + if (!(Nmlst = get_nlist_path(1))) { + (void) fprintf(stderr, + "%s: can't get kernel name list path\n", Pn); + Exit(1); + } + } +#endif /* defined(N_UNIX) */ + +#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 and the name list files are readable. + */ + if ((Memory && !is_readable(Memory, 1)) + || (Nmlst && !is_readable(Nmlst, 1))) + Exit(1); +#endif /* defined(WILLDROPGID) */ + +/* + * Open kernel memory access. + */ + +#if FREEBSDV<2000 + if (kvm_openfiles(Nmlst, Memory, NULL) == -1) +#else /* FREEBSDV>=2000 */ + if ((Kd = kvm_open(Nmlst, Memory, NULL, O_RDONLY, NULL)) == NULL) +#endif /* FREEBSDV<2000 */ + + { + (void) fprintf(stderr, + "%s: kvm_open%s(execfile=%s, corefile=%s): %s\n", + Pn, + +#if FREEBSDV<2000 + "files", +#else /* FREEBSDV>=2000 */ + "", +#endif /* FREEBSDV<2000 */ + + Nmlst ? Nmlst : "default", + Memory ? Memory : + +#if defined(_PATH_MEM) + _PATH_MEM, +#else /* !defined(_PATH_MEM) */ + "default", +#endif /* defined(_PATH_MEM) */ + + strerror(errno)); + Exit(1); + } + (void) build_Nl(Drive_Nl); + if (kvm_nlist(Kd, Nl) < 0) { + (void) fprintf(stderr, "%s: can't read namelist from %s\n", + Pn, Nmlst); + Exit(1); + } + +#if defined(WILLDROPGID) +/* + * Drop setgid permission, if necessary. + */ + if (!Memory) + (void) dropgid(); +#endif /* defined(WILLDROPGID) */ + +} + + +#if !defined(N_UNIX) +/* + * get_nlist_path() - get kernel name list path + */ + +char * +get_nlist_path(ap) + int ap; /* on success, return an allocated path + * string pointer if 1; return a + * constant character pointer if 0; + * return NULL if failure */ +{ + const char *bf; + static char *bfc; + MALLOC_S bfl; +/* + * Get bootfile name. + */ + if ((bf = getbootfile())) { + if (!ap) + return(""); + bfl = (MALLOC_S)(strlen(bf) + 1); + if (!(bfc = (char *)malloc(bfl))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for boot file path: %s\n", + Pn, (int)bfl, bf); + Exit(1); + } + (void) snpf(bfc, bfl, "%s", bf); + return(bfc); + } + return((char *)NULL); +} +#endif /* !defined(N_UNIX) */ + + +/* + * initialize() - perform all initialization + */ + +void +initialize() +{ + get_kernel_access(); +} + + +/* + * 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 */ +{ + int br; + +#if FREEBSDV<2000 + br = kvm_read((void *)addr, (void *)buf, len); +#else /* FREEBSDV>=2000 */ + br = kvm_read(Kd, (u_long)addr, buf, len); +#endif /* FREEBSDV<2000 */ + + return((br == len) ? 0 : 1); +} + + +/* + * process_text() - process text information + */ +void +process_text(vm) + KA_T vm; /* vm space pointer */ +{ + int i, j; + KA_T ka; + int n = 0; + struct vm_map_entry vmme, *e; + struct vm_object vmo; + struct vmspace vmsp; + +#if FREEBSDV<2020 + struct pager_struct pg; +#endif /* FREEBSDV<2020 */ + +/* + * Read the vmspace structure for the process. + */ + if (kread(vm, (char *)&vmsp, sizeof(vmsp))) + return; +/* + * Read the vm_map structure. Search its vm_map_entry structure list. + */ + for (i = 0; i < vmsp.vm_map.nentries; i++) { + + /* + * Read the next vm_map_entry. + */ + if (i == 0) + e = &vmsp.vm_map.header; + else { + if (!(ka = (KA_T)e->next)) + return; + e = &vmme; + if (kread(ka, (char *)e, sizeof(vmme))) + return; + } + +#if defined(MAP_ENTRY_IS_A_MAP) + if (e->eflags & (MAP_ENTRY_IS_A_MAP|MAP_ENTRY_IS_SUB_MAP)) +#else /* !defined(MAP_ENTRY_IS_A_MAP) */ + if (e->is_a_map || e->is_sub_map) +#endif /* defined(MAP_ENTRY_IS_A_MAP) */ + + continue; + /* + * Read the map entry's object and the object's shadow. + * Look for: a PG_VNODE pager handle (FreeBSD < 2.2); + * an OBJT_VNODE object type (FreeBSD >= 2.2). + */ + for (j = 0, ka = (KA_T)e->object.vm_object; + j < 2 && ka; + j++, + +#if FREEBSDV<2020 + ka = (KA_T)vmo.shadow +#else /* FREEBSDV>=2020 */ + ka = (KA_T)vmo.backing_object +#endif /* FREEBSDV<2020 */ + ) + { + if (kread(ka, (char *)&vmo, sizeof(vmo))) + break; + +#if FREEBSDV<2020 + if ((ka = (KA_T)vmo.pager) == NULL + || kread(ka, (char *)&pg, sizeof(pg))) + continue; + if (pg.pg_handle == NULL || pg.pg_type != PG_VNODE) + continue; + (void) (enter_vn_text((KA_T)pg.pg_handle, &n)); +#else /* FREEBSDV>=2020 */ + if (vmo.type != OBJT_VNODE + || vmo.handle == (void *)NULL) + continue; + (void) (enter_vn_text((KA_T)vmo.handle, &n)); +#endif /* FREEBSDV<2020 */ + + } + } +} |