blob: eb32fb93657dba122d12175a1eb0805cb264f3b3 [file] [log] [blame]
/*
* 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.16 2008/10/21 16:16:06 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;
static struct file **ofb = NULL;
static int ofbb = 0;
int pgid, pid;
int ppid = 0;
short pss, sf;
int px;
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)
static char *pof = (char *)NULL;
static int pofb = 0;
#endif /* defined(HASFSTRUCT) */
/*
* 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 ((P = kvm_getprocs(Kd, KERN_PROC_ALL, 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) */
/*
* See if process is excluded.
*
* Read file structure pointers.
*/
if (is_proc_excl(p->P_PID, pgid, (UID_ARG)uid, &pss, &sf))
continue;
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(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(struct file *) * nf);
if (nb > ofbb) {
if (!ofb)
ofb = (struct file **)malloc(nb);
else
ofb = (struct file **)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)
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) */
/*
* Save information on file descriptors.
*/
for (i = 0; i < nf; i++) {
if (ofb[i]) {
alloc_lfile(NULL, i);
process_file((KA_T)(Cfp = ofb[i]));
if (Lf->sf) {
#if defined(HASFSTRUCT)
if (Fsv & FSV_FG)
Lf->pof = (long)pof[i];
#endif /* defined(HASFSTRUCT) */
link_lfile();
}
}
}
/*
* Examine results.
*/
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, 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 */
}
}
}