/* * dproc.c -- pstat-based HP-UX process access functions for lsof */ /* * Copyright 1999 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 1999 Purdue Research Foundation.\nAll rights reserved.\n"; static char *rcsid = "$Id"; #endif #include "lsof.h" /* * Local definitions */ #define FDS_ALLOC_INCR 256 /* fds[] allocation increment */ #define FDS_ALLOC_INIT 64 /* initial fds[] allocation */ #define FINFOINCR 128 /* pst_fileinfo2 table allocation * increment */ #define INCLMEM(s, m) ((size_t)(offsetof(struct s, m) \ + sizeof(((struct s *)0)->m))) /* size of struct s, including * member m */ #define PSTATINCR 512 /* pst_status table allocation * increment */ #define TXTVMINCR 64 /* text and vm info table table * allocation increment */ #define VMREGINCR 64 /* VM region table table allocation * increment */ /* * Local structures */ struct pstatck { size_t moff; /* offset of size member in pst_static * -- from offsetof(...member) */ size_t msz; /* structure's pst_static member * inclusion size -- from INCLMEM(s, m) * macro */ size_t ssz; /* structure size -- from * sizeof(struct) */ char *sn; /* structure name */ } PstatCk[] = { { (size_t)offsetof(struct pst_static, pst_status_size), (size_t)INCLMEM(pst_static, pst_status_size), sizeof(struct pst_status), "pst_status" }, { (size_t)offsetof(struct pst_static, pst_vminfo_size), (size_t)INCLMEM(pst_static, pst_vminfo_size), sizeof(struct pst_vminfo), "pst_vminfo" }, { (size_t)offsetof(struct pst_static, pst_filedetails_size), (size_t)INCLMEM(pst_static, pst_filedetails_size), sizeof(struct pst_filedetails), "pst_filedetails" }, { (size_t)offsetof(struct pst_static, pst_socket_size), (size_t)INCLMEM(pst_static, pst_socket_size), sizeof(struct pst_socket), "pst_socket" }, { (size_t)offsetof(struct pst_static, pst_stream_size), (size_t)INCLMEM(pst_static, pst_stream_size), sizeof(struct pst_stream), "pst_stream" }, { (size_t)offsetof(struct pst_static, pst_mpathnode_size), (size_t)INCLMEM(pst_static, pst_mpathnode_size), sizeof(struct pst_mpathnode), "pst_mpathnode" }, { (size_t)offsetof(struct pst_static, pst_fileinfo2_size), (size_t)INCLMEM(pst_static, pst_fileinfo2_size), sizeof(struct pst_fileinfo2), "pst_fileinfo2" }, }; #define NPSTATCK (sizeof(PstatCk) /sizeof(struct pstatck)) /* * Local static variables */ static int HvRtPsfid = -1; /* "/" psfileid status: * -1: not yet tested; * 0: tested and unknown; * 1: tested and known */ static struct psfileid RtPsfid; /* "/" psfileid */ /* * Local function prototypes */ _PROTOTYPE(static void get_kernel_access,(void)); _PROTOTYPE(static void process_text,(struct pst_status *p)); _PROTOTYPE(static struct pst_fileinfo2 *read_files,(struct pst_status *p, int *n)); _PROTOTYPE(static struct pst_status *read_proc,(int *n)); _PROTOTYPE(static struct pst_vm_status *read_vmreg,(struct pst_status *p, int *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 */ int cwds, fd, *fds, fdsa, i, j, l, nf, np, rtds; struct pst_fileinfo2 *f; long flag; KA_T ka, na; MALLOC_S nb; struct pst_status *p; struct pst_filedetails pd; struct pst_socket *s; short pss, sf; /* * Compute current working and root directory statuses and the statuses of * the first FDS_ALLOC_INIT FDs. */ if (Fand && Fdl) { cwds = (ck_fd_status(CWD, -1) != 2) ? 0 : 1; rtds = (ck_fd_status(RTD, -1) != 2) ? 0 : 1; nb = (MALLOC_S)(sizeof(int) * FDS_ALLOC_INIT); if (!(fds = (int *)malloc(nb))) { (void) fprintf(stderr, "%s: can't allocate %d FD status entries\n", Pn, FDS_ALLOC_INIT); Exit(1); } for (fdsa = 0; fdsa < FDS_ALLOC_INIT; fdsa++) { if (Fand && Fdl) fds[fdsa] = (ck_fd_status(NULL, fdsa) == 2) ? 1 : 0; else fds[fdsa] = 1; } } else { cwds = rtds = 1; fdsa = 0; fds = (int *)NULL; } /* * 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; } /* * Examine proc structures and their associated information. */ for (i = 0, p = read_proc(&np); i < np; i++, p++) { if (!p->pst_stat || p->pst_stat == PS_ZOMBIE) continue; if (is_proc_excl((int)p->pst_pid, (int)p->pst_pgrp, (UID_ARG)p->pst_uid, &pss, &sf)) continue; /* * Make sure the command name is NUL-terminated. */ p->pst_ucomm[PST_UCOMMLEN - 1] = '\0'; if (is_cmd_excl(p->pst_ucomm, &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((int)p->pst_pid, (int)p->pst_pgrp, (int)p->pst_ppid, (UID_ARG)p->pst_uid, p->pst_ucomm, (int)pss, (int)sf); Plf = (struct lfile *)NULL; /* * Save current working directory information. */ if (!ckscko && cwds && IS_PSFILEID(&p->pst_cdir) && (p->pst_cdir.psf_fileid > 0) ) { alloc_lfile(CWD, -1); if ((na = read_det(&p->pst_fid_cdir, p->pst_hi_fileid_cdir, p->pst_lo_fileid_cdir, p->pst_hi_nodeid_cdir, p->pst_lo_nodeid_cdir, &pd))) (void) process_finfo(&pd, &p->pst_fid_cdir, &p->pst_cdir, na); else { (void) snpf(Namech, Namechl, "can't read %s pst_filedetails%s%s", CWD, errno ? ": " : "", errno ? strerror(errno) : ""); enter_nm(Namech); } if (Lf->sf) link_lfile(); } /* * Save root directory information. */ if (!ckscko && rtds && IS_PSFILEID(&p->pst_rdir) && (p->pst_rdir.psf_fileid > 0) ) { if (HvRtPsfid < 0) (void) scanmnttab(); if (!HvRtPsfid || memcmp((void *)&RtPsfid, (void *)&p->pst_rdir, sizeof(RtPsfid))) { alloc_lfile(RTD, -1); if ((na = read_det(&p->pst_fid_rdir, p->pst_hi_fileid_rdir, p->pst_lo_fileid_rdir, p->pst_hi_nodeid_rdir, p->pst_lo_nodeid_rdir, &pd))) (void) process_finfo(&pd, &p->pst_fid_rdir, &p->pst_rdir, na); else { (void) snpf(Namech, Namechl, "can't read %s pst_filedetails%s%s", RTD, errno ? ": " : "", errno ? strerror(errno) : ""); enter_nm(Namech); } if (Lf->sf) link_lfile(); } } /* * Print information on the text files. */ if (!ckscko) (void) process_text(p); /* * Loop through user's files. */ for (j = 0, f = read_files(p, &nf); j < nf; j++, f++) { fd = (int)f->psf_fd; /* * Check FD status and allocate local file space, as required. */ if (Fand && Fdl && fds) { /* * Check and update the FD status array. */ if (fd >= fdsa) { for (l = fdsa; l <= fd; l += FDS_ALLOC_INCR) ; nb = (MALLOC_S)(l * sizeof(int)); if (!(fds = (int *)realloc((MALLOC_P *)fds, nb))) { (void) fprintf(stderr, "%s: can't reallocate %d FD status entries\n", Pn, l); Exit(1); } while (fdsa < l) { fds[fdsa] = (ck_fd_status(NULL, fdsa) == 2) ? 1 : 0; fdsa++; } } if (!fds[fd]) continue; } alloc_lfile(NULL, (int)f->psf_fd); /* * Construct access code. */ if ((flag = (long)(f->psf_flag & ~PS_FEXCLOS)) == (long)PS_FRDONLY) Lf->access = 'r'; else if (flag == (long)PS_FWRONLY) Lf->access = 'w'; else Lf->access = 'u'; #if defined(HASFSTRUCT) /* * Save file structure values. */ if (Fsv & FSV_CT) { Lf->fct = (long)f->psf_count; Lf->fsv |= FSV_CT; } if (Fsv & FSV_FA) { ka = (((KA_T)(f->psf_hi_fileid & 0xffffffff) << 32) | (KA_T)(f->psf_lo_fileid & 0xffffffff)); if ((Lf->fsa = ka)) Lf->fsv |= FSV_FA; } if (Fsv & FSV_FG) { Lf->ffg = flag; Lf->fsv |= FSV_FG; } Lf->pof = (long)(f->psf_flag & PS_FEXCLOS); #endif /* defined(HASFSTRUCT) */ /* * Save file offset. _PSTAT64 should alwaus be defined, but just * to be safe, check for it. */ #if defined(_PSTAT64) Lf->off = (SZOFFTYPE)f->_PSF_OFFSET64; #else /* !defined(_PSTAT64) */ Lf->off = (SZOFFTYPE)f->psf_offset; #endif /* defined(_PSTAT64) */ /* * Process the file by its type. */ switch (f->psf_ftype) { case PS_TYPE_VNODE: if (ckscko || Selinet) break; if ((na = read_det(&f->psf_fid, f->psf_hi_fileid, f->psf_lo_fileid, f->psf_hi_nodeid, f->psf_lo_nodeid, &pd))) (void) process_finfo(&pd, &f->psf_fid, &f->psf_id, na); else { (void) snpf(Namech, Namechl, "can't read pst_filedetails%s%s", errno ? ": " : "", errno ? strerror(errno) : ""); enter_nm(Namech); } break; case PS_TYPE_SOCKET: switch (f->psf_subtype) { case PS_SUBTYPE_SOCK: (void) process_socket(f, (struct pst_socket *)NULL); break; case PS_SUBTYPE_SOCKSTR: if ((s = read_sock(f))) (void) process_socket(f, s); else (void) process_stream(f, (int)ckscko); break; default: (void) snpf(Namech, Namechl, "unknown socket sub-type: %d", (int)f->psf_subtype); enter_nm(Namech); } break; case PS_TYPE_STREAMS: (void) process_stream(f, (int)ckscko); break; case PS_TYPE_UNKNOWN: (void) snpf(Lf->type, sizeof(Lf->type), "UNKN"); (void) enter_nm("no more information"); break; case PS_TYPE_UNSP: (void) snpf(Lf->type, sizeof(Lf->type), "UNSP"); (void) enter_nm("no more information"); break; case PS_TYPE_LLA: (void) snpf(Lf->type, sizeof(Lf->type), "LLA"); (void) enter_nm("no more information"); break; } if (Lf->sf) link_lfile(); } /* * Examine results. */ if (examine_lproc()) return; } } /* * get_kernel_access() -- access the required information in the kernel */ static void get_kernel_access() { int err = 0; int i; struct pst_static pst; _T_LONG_T *szp; /* * Check the kernel version. */ (void) ckkv("HP-UX", LSOF_VSTR, (char *)NULL, (char *)NULL); /* * Check PSTAT support. First make sure we can read pst_static up through * its pst_static_size member. If not, quit. If we can, read the full * pst_static structure. */ if (pstat_getstatic(&pst, (size_t)INCLMEM(pst_static, pst_static_size), 1, 0) != 1) { (void) fprintf(stderr, "%s: FATAL: can't determine PSTAT static size: %s\n", Pn, strerror(errno)); Exit(1); } if (pstat_getstatic(&pst, (size_t)pst.pst_static_size, 1, 0) != 1) { (void) fprintf(stderr, "%s: FATAL: can't read %ld bytes of pst_static\n", Pn, (long)pst.pst_static_size); Exit(1); } /* * Check all the pst_static members defined in PstatCk[]. */ for (i = 0; i < NPSTATCK; i++) { if (pst.pst_static_size < PstatCk[i].msz) { (void) fprintf(stderr, "%s: FATAL: pst_static doesn't contain %s_size\n", Pn, PstatCk[i].sn); err = 1; continue; } szp = (_T_LONG_T *)(((char *)&pst) + PstatCk[i].moff); if (*szp < PstatCk[i].ssz) { (void) fprintf(stderr, "%s: FATAL: %s_size should be: %llu; is %llu\n", Pn, PstatCk[i].sn, (unsigned long long)PstatCk[i].ssz, (unsigned long long)*szp); err = 1; } } /* * Save the clone major device number, if pst_static is big enough to hold it. */ if (pst.pst_static_size >= (size_t)INCLMEM(pst_static, clonemajor)) { CloneMaj = pst.clonemajor; HaveCloneMaj = 1; } if (!err) return; Exit(1); } /* * initialize() -- perform all initialization */ void initialize() { get_kernel_access(); } /* * process_text() -- process text access information */ static void process_text(p) struct pst_status *p; /* pst_status for process */ { int i, j, nr, ntvu; int meme = 0; static int mems = -1; KA_T na; MALLOC_S nb; static int ntva; struct pst_vm_status *rp; static int txts = -1; struct txtvm { char *fd; struct pst_fid opfid; struct psfileid psfid; KA_T na; struct pst_filedetails pd; }; static struct txtvm *tv = (struct txtvm *)NULL; /* * Get and remember "mem" and "txt" FD statuses. */ if (mems < 0) { if (Fand && Fdl) mems = (ck_fd_status("mem", -1) == 2) ? 1 : 0; else mems = 1; } if (txts < 0) { if (Fand && Fdl) txts = (ck_fd_status("txt", -1) == 2) ? 1 : 0; else txts = 1; } if (!mems && !txts) return; /* * Pre-allocate sufficient tv[] space for text file. */ if (!tv) { ntva = TXTVMINCR; nb = (MALLOC_S)(ntva * sizeof(struct txtvm)); if (!(tv = (struct txtvm *)malloc(nb))) { no_txtvm_space: (void) fprintf(stderr, "%s: no memory for text and VM info array; PID: %d\n", Pn, (int)p->pst_pid); Exit(1); } } /* * Enter text file in tv[], if possible. */ if (txts && IS_PSFILEID(&p->pst_text) && (p->pst_text.psf_fileid > 0)) { if ((na = read_det(&p->pst_fid_text, p->pst_hi_fileid_text, p->pst_lo_fileid_text, p->pst_hi_nodeid_text, p->pst_lo_nodeid_text, &tv[0].pd))) { tv[0].fd = "txt"; tv[0].na = na; tv[0].opfid = p->pst_fid_text; tv[0].psfid = p->pst_text; ntvu = 1; } else { alloc_lfile("txt", -1); (void) snpf(Namech, Namechl, "can't read txt pst_filedetails%s%s", errno ? ": " : "", errno ? strerror(errno) : ""); enter_nm(Namech); if (Lf->sf) link_lfile(); ntvu = 0; } } else ntvu = 0; /* * Get unique VM regions. */ if (mems) { for (i = 0, rp = read_vmreg(p, &nr); (i < nr); i++, rp++) { /* * Skip duplicate regions. */ for (j = 0; j < ntvu; j++) { if (memcmp((void *)&rp->pst_id, (void *)&tv[j].psfid, sizeof(struct psfileid)) == 0) break; } if (j < ntvu) continue; /* * Make sure there's tv[] space for this region. */ if (ntvu >= ntva) { ntva += TXTVMINCR; nb = (MALLOC_S)(ntva * sizeof(struct txtvm)); if (!(tv = (struct txtvm *)realloc((MALLOC_P *)tv, nb))) goto no_txtvm_space; } /* * See if we can read the file details for this region. */ if ((na = read_det(&rp->pst_fid, rp->pst_hi_fileid, rp->pst_lo_fileid, rp->pst_hi_nodeid, rp->pst_lo_nodeid, &tv[ntvu].pd))) { tv[ntvu].fd = "mem"; tv[ntvu].na = na; tv[ntvu].opfid = rp->pst_fid; tv[ntvu].psfid = rp->pst_id; ntvu++; } else if (!meme) { alloc_lfile("mem", -1); (void) snpf(Namech, Namechl, "can't read mem pst_filedetails%s%s", errno ? ": " : "", errno ? strerror(errno) : ""); enter_nm(Namech); if (Lf->sf) link_lfile(); meme = 1; } } } /* * Process information for unique regions. */ for (i = 0; i < ntvu; i++) { alloc_lfile(tv[i].fd, -1); (void) process_finfo(&tv[i].pd, &tv[i].opfid, &tv[i].psfid, tv[i].na); if (Lf->sf) link_lfile(); } } /* * read_det() -- read the pst_filedetails structure */ KA_T read_det(ki, hf, lf, hn, ln, pd) struct pst_fid *ki; /* kernel file ID */ uint32_t hf; /* high file ID bits */ uint32_t lf; /* low file ID bits */ uint32_t hn; /* high node ID bits */ uint32_t ln; /* low node ID bits */ struct pst_filedetails *pd; /* details receiver */ { KA_T na; errno = 0; na = (KA_T)(((KA_T)(hn & 0xffffffff) << 32) | (KA_T)(ln & 0xffffffff)); if (pstat_getfiledetails(pd, sizeof(struct pst_filedetails), ki) <= 0 || hf != pd->psfd_hi_fileid || lf != pd->psfd_lo_fileid || hn != pd->psfd_hi_nodeid || ln != pd->psfd_lo_nodeid) return((KA_T)0); return(na); } /* * read_files() -- read the file descriptor information for a process */ static struct pst_fileinfo2 * read_files(p, n) struct pst_status *p; /* pst_status for the process */ int *n; /* returned fi[] entry count */ { size_t ec; static struct pst_fileinfo2 *fi = (struct pst_fileinfo2 *)NULL; MALLOC_S nb; int nf = 0; static int nfa = 0; int rc; static size_t sz = sizeof(struct pst_fileinfo2); /* * Read the pst_fileinfo2 information for all files of the process * into fi[]. */ do { if (nf >= nfa) { /* * Increase the size of fi[]. */ nfa += FINFOINCR; nb = (MALLOC_S)(nfa * sizeof(struct pst_fileinfo2)); if (!fi) fi = (struct pst_fileinfo2 *)malloc(nb); else fi = (struct pst_fileinfo2 *)realloc((MALLOC_P *)fi, nb); if (!fi) { (void) fprintf(stderr, "%s: can't allocate %d bytes for pst_filinfo\n", Pn, nb); Exit(1); } } /* * Read the next block of pst_fileinfo2 structures. */ ec = (size_t)(nfa - nf); if ((rc = pstat_getfile2(fi + nf, sz, ec, nf, p->pst_pid)) > 0) { nf += rc; if (rc < (int)ec) rc = 0; } } while (rc > 0); *n = nf; return(fi); } /* * read_proc() -- read process table status information */ static struct pst_status * read_proc(n) int *n; /* returned ps[] entry count */ { size_t el; int i = 0; MALLOC_S nb; int np = 0; static int npa = 0; static struct pst_status *ps = (struct pst_status *)NULL; int rc; size_t sz = sizeof(struct pst_status); /* * Read the pst_status information for all processes into ps[]. */ do { if (np >= npa) { /* * Increase the size of ps[]. */ npa += PSTATINCR; nb = (MALLOC_S)(npa * sizeof(struct pst_status)); if (!ps) ps = (struct pst_status *)malloc(nb); else ps = (struct pst_status *)realloc((MALLOC_P *)ps, nb); if (!ps) { ps_alloc_error: (void) fprintf(stderr, "%s: can't allocate %d bytes for pst_status table\n", Pn, nb); Exit(1); } } /* * Read the next block of pst_status structures. */ el = (size_t)(npa - np); if ((rc = pstat_getproc(ps + np, sz, el, i)) > 0) { np += rc; i = (ps + np - 1)->pst_idx + 1; if (rc < el) rc = 0; } } while (rc > 0); /* * Reduce ps[] to a minimum, unless repeat mode is in effect. */ if (!RptTm && ps && np && (np < npa)) { nb = (MALLOC_S)(np * sizeof(struct pst_status)); if (!(ps = (struct pst_status *)realloc((MALLOC_P *)ps, nb))) goto ps_alloc_error; } *n = np; return(ps); } /* * read_vmreg() -- read info about the VM regions of a process */ static struct pst_vm_status * read_vmreg(p, n) struct pst_status *p; /* pst_status for process */ int *n; /* returned region count */ { size_t ec = (size_t)p->pst_pid; MALLOC_S nb; int nr, rx; static int nra = 0; struct pst_vm_status *rp; static struct pst_vm_status *reg = (struct pst_vm_status *)NULL; size_t sz = sizeof(struct pst_vm_status); /* * Read all VM region information for the process. */ for (nr = rx = 0;; rx++) { if (nr >= nra) { /* * Increase the region table size. */ nra += VMREGINCR; nb = (MALLOC_S)(nra * sizeof(struct pst_vm_status)); if (!reg) reg = (struct pst_vm_status *)malloc(nb); else reg = (struct pst_vm_status *)realloc((MALLOC_P *)reg, nb); if (!reg) { (void) fprintf(stderr, "%s: can't allocate %d bytes for pst_vm_status\n", Pn, nb); Exit(1); } } /* * Read the pst_vm_status structure for the next region. */ rp = reg + nr; if (pstat_getprocvm(rp, sz, ec, rx) != 1) break; if (IS_PSFILEID(&rp->pst_id) && (rp->pst_id.psf_fileid > 0)) nr++; } *n = nr; return(reg); } /* * scanmnttab() -- scan mount table */ extern void scanmnttab() { struct mounts *mp; /* * Scan the mount table to identify NFS file systems and form the psfileid * for "/". * * This function allows the mount table scan to be deferred until its * information is needed. */ if ((HvRtPsfid >= 0) && (HasNFS >= 0)) return; (void) memset((void *)&RtPsfid, 0, sizeof(RtPsfid)); for (HasNFS = HvRtPsfid = 0, mp = readmnt(); mp; mp = mp->next) { if (mp->MOUNTS_FSTYPE && (strcmp(mp->MOUNTS_FSTYPE, MNTTYPE_NFS) == 0 || strcmp(mp->MOUNTS_FSTYPE, MNTTYPE_NFS3) == 0)) { HasNFS = 1; mp->is_nfs = 1; } else mp->is_nfs = 0; if (!HvRtPsfid && !strcmp(mp->dir, "/")) { HvRtPsfid = 1; RtPsfid.psf_fsid.psfs_id = mp->dev; RtPsfid.psf_fsid.psfs_type = mp->MOUNTS_STAT_FSTYPE; RtPsfid.psf_fileid = mp->inode; } } }