| /* |
| * dproc.c - Solaris lsof functions for accessing process information |
| */ |
| |
| |
| /* |
| * 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.36 2010/01/18 19:03:54 abe Exp $"; |
| #endif |
| |
| #include "lsof.h" |
| |
| #if solaris<20500 |
| #include "kernelbase.h" |
| #endif /* solaris<20500 */ |
| |
| #if defined(HAS_CRED_IMPL_H) |
| # if solaris>=110000 |
| #define _KERNEL |
| # endif /* solaris>=110000 */ |
| |
| #include <sys/cred_impl.h> |
| |
| # if solaris>=110000 |
| #undef _KERNEL |
| # endif /* solaris>=110000 */ |
| #endif /* defined(HAS_CRED_IMPL_H) */ |
| |
| |
| /* |
| * Local definitions |
| */ |
| |
| #if defined(__sparc) || defined(__sparcv9) |
| #define ARCH64B "sparcv9" |
| #else /* !defined(__sparc) && !defined(__sparcv9) */ |
| # if defined(__i386) || defined(__amd64) |
| #define ARCH64B "amd64" |
| # endif /* defined(__i386) || defined(__amd64) */ |
| #endif /* defined(__sparc) || defined(__sparcv9) */ |
| |
| #if solaris>=20501 |
| #define KVMHASHBN 8192 /* KVM hash bucket count -- MUST BE |
| * A POWER OF 2!!! */ |
| #define HASHKVM(va) ((int)((va * 31415) >> 3) & (KVMHASHBN-1)) |
| /* virtual address hash function */ |
| |
| # if solaris<70000 |
| #define KAERR (u_longlong_t)-1 /* kvm_physaddr() error return */ |
| #define KBUFT char /* kernel read buffer type */ |
| #define KPHYS u_longlong_t /* kernel physical address type */ |
| #define KVIRT u_int /* kernel virtual address type */ |
| # else /* solaris>=70000 */ |
| #define KAERR (uint64_t)-1 /* kvm_physaddr() error return */ |
| #define KBUFT void /* kernel read buffer type */ |
| #define KPHYS uint64_t /* kernel physical address type */ |
| #define KVIRT uintptr_t /* kernel virtual address type */ |
| # endif /* solaris<70000 */ |
| #endif /* solaris>=20501 */ |
| |
| |
| /* |
| * Local structures |
| */ |
| |
| #if solaris>=20501 |
| typedef struct kvmhash { |
| KVIRT vpa; /* virtual page address */ |
| KPHYS pa; /* physical address */ |
| struct kvmhash *nxt; /* next virtual address */ |
| } kvmhash_t; |
| #endif /* solaris>=20501 */ |
| |
| |
| /* |
| * Local variables |
| */ |
| |
| #if solaris>=20501 |
| static struct as *Kas = (struct as *)NULL; |
| /* pointer to kernel's address space |
| * map in kernel virtual memory */ |
| static kvmhash_t **KVMhb = (kvmhash_t **)NULL; |
| /* KVM hash buckets */ |
| static int PageSz = 0; /* page size */ |
| static int PSMask = 0; /* page size mask */ |
| static int PSShft = 0; /* page size shift */ |
| |
| # if solaris<70000 |
| static struct as Kam; /* kernel's address space map */ |
| static int Kmd = -1; /* memory device file descriptor */ |
| # endif /* solaris<70000 */ |
| #endif /* solaris>=20501 */ |
| |
| #if solaris>=20500 |
| static KA_T Kb = (KA_T)NULL; /* KERNELBASE for Solaris 2.5 */ |
| #endif /* solaris>=20500 */ |
| |
| static int Np; /* number of P[], Pgid[] and Pid[] |
| * entries */ |
| static int Npa = 0; /* number of P[], Pgid[] and Pid[] |
| * entries for which space has been |
| * allocated */ |
| static struct proc *P = NULL; /* local proc structure table */ |
| static int *Pgid = NULL; /* process group IDs for P[] entries */ |
| static int *Pid = NULL; /* PIDs for P[] entries */ |
| static KA_T PrAct = (KA_T)NULL; /* kernel's *practive address */ |
| static gid_t Savedgid; /* saved (effective) GID */ |
| static KA_T Sgvops; /* [_]segvn_ops address */ |
| static int Switchgid = 0; /* must switch GIDs for kvm_open() */ |
| |
| #if defined(HASZONES) |
| static znhash_t **ZoneNm = (znhash_t **)NULL; |
| /* zone names hash buckets */ |
| #endif /* defined(HASZONES) */ |
| |
| |
| /* |
| * Local function prototypes |
| */ |
| |
| _PROTOTYPE(static void get_kernel_access,(void)); |
| _PROTOTYPE(static void process_text,(KA_T pa)); |
| _PROTOTYPE(static void read_proc,(void)); |
| _PROTOTYPE(static void readfsinfo,(void)); |
| |
| #if solaris>=20501 |
| _PROTOTYPE(static void readkam,(KA_T addr)); |
| #endif /* solaris>=20501 */ |
| |
| #if solaris>=20501 && solaris<70000 |
| _PROTOTYPE(extern u_longlong_t kvm_physaddr,(kvm_t *, struct as *, u_int)); |
| #endif /* solaris>=20501 && solaris<70000 */ |
| |
| #if defined(HASZONES) |
| _PROTOTYPE(static int hash_zn,(char *zn)); |
| #endif /* defined(HASZONES) */ |
| |
| |
| |
| /* |
| * close_kvm() - close kernel virtual memory access |
| */ |
| |
| void |
| close_kvm() |
| { |
| if (!Kd) |
| return; |
| if (Kd) { |
| if (kvm_close(Kd) != 0) { |
| (void) fprintf(stderr, "%s: kvm_close failed\n", Pn); |
| Exit(1); |
| } |
| Kd = (kvm_t *)NULL; |
| } |
| |
| #if solaris>=20501 && solaris<70000 |
| if (Kmd >= 0) { |
| (void) close(Kmd); |
| Kmd = -1; |
| } |
| #endif /* solaris>=20501 && solaris<70000 */ |
| |
| } |
| |
| |
| /* |
| * 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 */ |
| static int ft = 1; |
| int i, j; |
| struct proc *p; |
| int pgid, pid, px; |
| long pofv; |
| short pss, sf; |
| struct user *u; |
| uid_t uid; |
| |
| #if solaris>=20400 |
| int k; |
| |
| # if !defined(NFPCHUNK) |
| #define uf_ofile uf_file |
| #define uf_pofile uf_flag |
| #define u_flist u_finfo.fi_list |
| #define u_nofiles u_finfo.fi_nfiles |
| #define NFPREAD 64 |
| # else /* defined(NFPCHUNK) */ |
| #define NFPREAD NFPCHUNK |
| # endif /* !defined(NFPCHUNK) */ |
| uf_entry_t uf[NFPREAD]; |
| #endif /* solaris>=20400 */ |
| #if solaris>=20500 |
| struct cred pc; |
| #endif /* solaris>=20500 */ |
| |
| #if defined(HASZONES) |
| struct zone z; |
| int zh; |
| char zn[ZONENAME_MAX + 1]; |
| znhash_t *zp, *zpn; |
| #endif /* defined(HASZONES) */ |
| |
| if (ft) { |
| /* |
| * Do first-time only operations. |
| */ |
| /* |
| * Get the segment vnodeops address. |
| */ |
| if (get_Nl_value("sgvops", Drive_Nl, &Sgvops) < 0) |
| Sgvops = (KA_T)NULL; |
| ft = 0; |
| } else if (!HasALLKMEM) { |
| |
| /* |
| * If not the first time and the ALLKMEM device isn't available, it is |
| * necessary to close and reopen the KVM device, so that kvm_open() |
| * will acquire a fresh address for the head of the linked list process |
| * table. |
| */ |
| close_kvm(); |
| open_kvm(); |
| |
| #if solaris>=20501 |
| /* |
| * If not the first time and the ALLKMEM device isn't available, |
| * re-read the kernel's address space map. |
| */ |
| readkam((KA_T)NULL); |
| #endif /* solaris>=20501 */ |
| |
| } |
| /* |
| * 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. |
| */ |
| read_proc(); |
| /* |
| * Loop through processes. |
| */ |
| for (p = P, px = 0; px < Np; p++, px++) { |
| |
| /* |
| * Get the process ID. |
| */ |
| |
| if (Fpgid) |
| pgid = Pgid[px]; |
| else |
| pgid = 0; |
| pid = Pid[px]; |
| |
| #if solaris<20500 |
| uid = p->p_uid; |
| #else /* solaris >=20500 */ |
| /* |
| * Read credentials for Solaris 2.5 and above process. |
| */ |
| if (kread((KA_T)p->p_cred, (char *)&pc, sizeof(pc))) |
| continue; |
| uid = pc.cr_uid; |
| #endif /* solaris<20500 */ |
| |
| /* |
| * See if the process is excluded. |
| */ |
| if (is_proc_excl(pid, pgid, (UID_ARG)uid, &pss, &sf)) |
| continue; |
| |
| #if defined(HASZONES) |
| /* |
| * If the -z (zone) option was specified, get the zone name. |
| */ |
| if (Fzone) { |
| zn[0] = zn[sizeof(zn) - 1] = '\0'; |
| if (p->p_zone |
| && !kread((KA_T)p->p_zone, (char *)&z, sizeof(z))) |
| { |
| if (!z.zone_name |
| || kread((KA_T)z.zone_name, (char *)&zn, sizeof(zn) - 1)) |
| zn[0] = '\0'; |
| } |
| } |
| #endif /* defined(HASZONES) */ |
| |
| /* |
| * Get the user area associated with the process. |
| */ |
| u = &p->p_user; |
| /* |
| * Allocate a local process structure and start filling it. |
| */ |
| if (is_cmd_excl(u->u_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(pid, pgid, (int)p->p_ppid, (UID_ARG)uid, u->u_comm, |
| (int)pss, (int)sf); |
| Plf = (struct lfile *)NULL; |
| |
| #if defined(HASZONES) |
| /* |
| * If zone processing is enabled and requested, and if there is a zone |
| * name: |
| * |
| * o Skip processes excluded by zone name. |
| * o Save zone name. |
| */ |
| if (Fzone && zn[0]) { |
| zh = hash_zn(zn); |
| if (ZoneArg) { |
| |
| /* |
| * See if zone name excludes the process. |
| */ |
| for (zp = ZoneArg[zh]; zp; zp = zp->next) { |
| if (!strcmp(zn, zp->zn)) |
| break; |
| } |
| if (!zp) |
| continue; |
| zp->f = 1; |
| Lp->pss |= PS_PRI; |
| Lp->sf |= SELZONE; |
| } |
| /* |
| * Make sure the zone name is cached, then save a pointer to it in |
| * the local proc structure. |
| */ |
| if (!ZoneNm) { |
| if (!(ZoneNm = (znhash_t **)calloc(HASHZONE, |
| sizeof(znhash_t *)))) |
| { |
| (void) fprintf(stderr, |
| "%s: no space for zone name hash\n", Pn); |
| Exit(1); |
| } |
| } |
| for (zp = ZoneNm[zh]; zp; zp = zp->next) { |
| if (!strcmp(zn, zp->zn)) |
| break; |
| } |
| if (!zp) { |
| |
| /* |
| * The zone name isn't cached, so cache it. |
| */ |
| if (!(zp = (znhash_t *)malloc((MALLOC_S)sizeof(znhash_t)))) |
| { |
| (void) fprintf(stderr, |
| "%s: no zone name cache space: %s\n", Pn, zn); |
| Exit(1); |
| } |
| if (!(zp->zn = mkstrcpy(zn, (MALLOC_S *)NULL))) { |
| (void) fprintf(stderr, |
| "%s: no zone name space at PID %d: %s\n", |
| Pn, (int)Lp->pid, zn); |
| Exit(1); |
| } |
| zp->next = ZoneNm[zh]; |
| ZoneNm[zh] = zp; |
| } |
| Lp->zn = zp->zn; |
| } |
| #endif /* defined(HASZONES) */ |
| |
| /* |
| * Save file count. |
| */ |
| Unof = u->u_nofiles; |
| /* |
| * Save current working directory information. |
| */ |
| if (!ckscko && u->u_cdir) { |
| alloc_lfile(CWD, -1); |
| |
| #if defined(FILEPTR) |
| FILEPTR = (struct file *)NULL; |
| #endif /* defined(FILEPTR) */ |
| |
| process_node((KA_T)u->u_cdir); |
| if (Lf->sf) |
| link_lfile(); |
| } |
| /* |
| * Save root directory information. |
| */ |
| if (!ckscko && u->u_rdir) { |
| alloc_lfile(RTD, -1); |
| |
| #if defined(FILEPTR) |
| FILEPTR = (struct file *)NULL; |
| #endif /* defined(FILEPTR) */ |
| |
| process_node((KA_T)u->u_rdir); |
| if (Lf->sf) |
| link_lfile(); |
| } |
| /* |
| * Save information on text files. |
| */ |
| if (!ckscko && p->p_as && Sgvops) { |
| |
| #if defined(FILEPTR) |
| FILEPTR = (struct file *)NULL; |
| #endif /* defined(FILEPTR) */ |
| |
| process_text((KA_T)p->p_as); |
| } |
| /* |
| * Save information on file descriptors. |
| * |
| * Under Solaris the file pointers are stored in dynamically-linked |
| * ufchunk structures, each containing NFPREAD file pointers. The |
| * first ufchunk structure is in the user area. |
| * |
| * Under Solaris 2.4 the file pointers are in a dynamically allocated, |
| * contiguous memory block. |
| */ |
| |
| #if solaris<20400 |
| for (i = 0, j = 0; i < u->u_nofiles; i++) { |
| if (++j > NFPCHUNK) { |
| if (!u->u_flist.uf_next) |
| break; |
| if (kread((KA_T)u->u_flist.uf_next, |
| (char *)&u->u_flist, sizeof(struct ufchunk))) |
| break; |
| j = 1; |
| } |
| if (!u->u_flist.uf_ofile[j-1]) |
| #else /* solaris>=20400 */ |
| for (i = 0, j = NFPREAD; i < u->u_nofiles; i++) { |
| if (++j > NFPREAD) { |
| k = u->u_nofiles - i; |
| if (k > NFPREAD) |
| k = NFPREAD; |
| if (kread((KA_T)((unsigned long)u->u_flist + |
| i * sizeof(uf_entry_t)), |
| (char*)&uf, k * sizeof(uf_entry_t))) |
| { |
| break; |
| } |
| j = 1; |
| } |
| if (!uf[j-1].uf_ofile) |
| #endif /* solaris<20400 */ |
| |
| continue; |
| alloc_lfile((char *)NULL, i); |
| |
| #if solaris<20400 |
| pofv = (long)u->u_flist.uf_pofile[j-1]; |
| process_file((KA_T)u->u_flist.uf_ofile[j-1]); |
| #else /* solaris>=20400 */ |
| pofv = uf[j-1].uf_pofile; |
| process_file((KA_T)uf[j-1].uf_ofile); |
| #endif /* solaris <20400 */ |
| |
| if (Lf->sf) { |
| |
| #if defined(HASFSTRUCT) |
| if (Fsv & FSV_FG) |
| Lf->pof = pofv; |
| #endif /* defined(HASFSTRUCT) */ |
| |
| link_lfile(); |
| } |
| } |
| /* |
| * Examine results. |
| */ |
| if (examine_lproc()) |
| return; |
| } |
| } |
| |
| |
| /* |
| * get_kernel_access() - access the required information in the kernel |
| */ |
| |
| static void |
| get_kernel_access() |
| { |
| int i; |
| struct stat sb; |
| KA_T v; |
| |
| #if defined(HAS_AFS) |
| struct nlist *nl = (struct nlist *)NULL; |
| #endif /* defined(HAS_AFS) */ |
| |
| /* |
| * Check the Solaris or SunOS version number; check the SunOS architecture. |
| */ |
| (void) ckkv("Solaris", LSOF_VSTR, (char *)NULL, (char *)NULL); |
| |
| #if solaris>=70000 |
| /* |
| * Compare the Solaris 7 and above lsof compilation bit size with the kernel |
| * bit size. |
| * |
| * Quit on a mismatch. |
| */ |
| { |
| char *cp, isa[1024]; |
| short kbits = 32; |
| |
| # if defined(_LP64) |
| short xkbits = 64; |
| # else /* !defined(_LP64) */ |
| short xkbits = 32; |
| # endif /* defined(_LP64) */ |
| |
| if (sysinfo(SI_ISALIST, isa, (long)sizeof(isa)) < 0) { |
| (void) fprintf(stderr, "%s: can't get ISA list: %s\n", |
| Pn, strerror(errno)); |
| Exit(1); |
| } |
| for (cp = isa; *cp;) { |
| if (strncmp(cp, ARCH64B, strlen(ARCH64B)) == 0) { |
| kbits = 64; |
| break; |
| } |
| if (!(cp = strchr(cp, ' '))) |
| break; |
| cp++; |
| } |
| if (kbits != xkbits) { |
| (void) fprintf(stderr, |
| "%s: FATAL: lsof was compiled for a %d bit kernel,\n", |
| Pn, (int)xkbits); |
| (void) fprintf(stderr, |
| " but this machine has booted a %d bit kernel.\n", |
| (int)kbits); |
| Exit(1); |
| } |
| } |
| #endif /* solaris>=70000 */ |
| |
| /* |
| * Get kernel symbols. |
| */ |
| if (Nmlst && !is_readable(Nmlst, 1)) |
| Exit(1); |
| (void) build_Nl(Drive_Nl); |
| |
| #if defined(HAS_AFS) |
| if (!Nmlst) { |
| |
| /* |
| * If AFS is defined and we're getting kernel symbol values from |
| * from N_UNIX, make a copy of Nl[] for possible use with the AFS |
| * modload file. |
| */ |
| if (!(nl = (struct nlist *)malloc(Nll))) { |
| (void) fprintf(stderr, "%s: no space (%d) for Nl[] copy\n", |
| Pn, Nll); |
| Exit(1); |
| } |
| (void) memcpy((void *)nl, (void *)Nl, (size_t)Nll); |
| } |
| #endif /* defined(HAS_AFS) */ |
| |
| if (nlist(Nmlst ? Nmlst : N_UNIX, Nl) < 0) { |
| (void) fprintf(stderr, "%s: can't read namelist from %s\n", |
| Pn, Nmlst ? Nmlst : N_UNIX); |
| Exit(1); |
| } |
| |
| #if defined(HAS_AFS) |
| if (nl) { |
| |
| /* |
| * If AFS is defined and we're getting kernel symbol values from |
| * N_UNIX, and if any X_AFS_* symbols isn't there, see if it is in the |
| * the AFS modload file. Make sure that other symbols that appear in |
| * both name list files have the same values. |
| */ |
| if ((get_Nl_value("arFID", Drive_Nl, &v) >= 0 && !v) |
| || (get_Nl_value("avops", Drive_Nl, &v) >= 0 && !v) |
| || (get_Nl_value("avol", Drive_Nl, &v) >= 0 && !v)) |
| (void) ckAFSsym(nl); |
| (void) free((MALLOC_P *)nl); |
| } |
| #endif /* defined(HAS_AFS) */ |
| |
| /* |
| * Determine the availability of the ALLKMEM device. If it is available, the |
| * active processes will be gathered directly from the active process chain. |
| * |
| * If ALLKMEM isn't available, the active processes will be gathered via the |
| * kvm_*proc() functions. |
| */ |
| if (statsafely(ALLKMEM, &sb) == 0) |
| HasALLKMEM = 1; |
| |
| #if defined(HASVXFSUTIL) |
| /* |
| * If the VXFS utility library is being used, attempt to get the VXFS inode |
| * offsets before setgid permission is surrendered. |
| */ |
| if (access_vxfs_ioffsets() && !Fwarn) { |
| |
| /* |
| * Warn that the VxFS offsets are unavailable. |
| */ |
| (void) fprintf(stderr, |
| "%s: WARNING: vxfsu_get_ioffsets() returned an error.\n", Pn); |
| (void) fprintf(stderr, |
| "%s: WARNING: Thus, no vx_inode information is available\n", |
| Pn); |
| (void) fprintf(stderr, |
| "%s: WARNING: for display or selection of VxFS files.\n", Pn); |
| } |
| #endif /* defined(HASVXFSUTIL) */ |
| |
| #if defined(WILLDROPGID) |
| /* |
| * If Solaris kernel memory is coming from KMEM, the process is willing to |
| * surrender GID permission, and the ALLKMEM device is not available, set up |
| * for GID switching after the first call to open_kvm(). |
| */ |
| if (!Memory && !HasALLKMEM) { |
| Savedgid = getegid(); |
| if (Setgid) |
| Switchgid = 1; |
| } |
| /* |
| * 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 access to kernel memory. |
| */ |
| open_kvm(); |
| |
| #if solaris>=20500 |
| /* |
| * Get the kernel's KERNELBASE value for Solaris 2.5 and above. |
| */ |
| v = (KA_T)0; |
| if (get_Nl_value("kbase", Drive_Nl, &v) < 0 || !v |
| || kread((KA_T)v, (char *)&Kb, sizeof(Kb))) { |
| (void) fprintf(stderr, |
| "%s: can't read kernel base address from %s\n", |
| Pn, print_kptr(v, (char *)NULL, 0)); |
| Exit(1); |
| } |
| #endif /* solaris>=20500 */ |
| |
| /* |
| * Get the Solaris clone major device number, if possible. |
| */ |
| v = (KA_T)0; |
| if ((get_Nl_value("clmaj", Drive_Nl, &v) < 0) || !v) { |
| if (get_Nl_value("clmaj_alt", Drive_Nl, &v) < 0) |
| v = (KA_T)0; |
| } |
| if (v && kread((KA_T)v, (char *)&CloneMaj, sizeof(CloneMaj)) == 0) |
| HaveCloneMaj = 1; |
| /* |
| * If the ALLKMEM device is available, check for the address of the kernel's |
| * active process chain. If it's not available, clear the ALLKMEM status. |
| */ |
| if (HasALLKMEM) { |
| if ((get_Nl_value("pract", Drive_Nl, &PrAct) < 0) || !PrAct) |
| HasALLKMEM = 0; |
| } |
| |
| #if solaris>=20501 |
| /* |
| * If the ALLKMEM device isn't available, get the kernel's virtual to physical |
| * map structure for Solaris 2.5.1 and above. |
| */ |
| if (!HasALLKMEM) { |
| if (get_Nl_value("kasp", Drive_Nl, &v) >= 0 && v) { |
| PageSz = getpagesize(); |
| PSMask = PageSz - 1; |
| for (i = 1, PSShft = 0; i < PageSz; i <<= 1, PSShft++) |
| ; |
| (void) readkam(v); |
| } |
| } |
| #endif /* solaris>=20501 */ |
| |
| #if defined(WILLDROPGID) |
| /* |
| * If the ALLKMEM device is available -- i.e., we're not using the kvm_*proc() |
| * functions to read proc structures -- and if we're willing to drop setgid |
| * permission, do so. |
| */ |
| if (HasALLKMEM) |
| (void) dropgid(); |
| #endif /* defined(WILLDROPGID) */ |
| |
| } |
| |
| |
| #if defined(HASZONES) |
| /* |
| * enter_zone_arg() - enter zone name argument |
| */ |
| |
| int |
| enter_zone_arg(zn) |
| char *zn; /* zone name */ |
| { |
| int zh; |
| znhash_t *zp, *zpn; |
| /* |
| * Allocate zone argument hash space, as required. |
| */ |
| if (!ZoneArg) { |
| if (!(ZoneArg = (znhash_t **)calloc(HASHZONE, sizeof(znhash_t *)))) |
| { |
| (void) fprintf(stderr, "%s: no space for zone arg hash\n", Pn); |
| Exit(1); |
| } |
| } |
| /* |
| * Hash the zone name and search the argument hash. |
| */ |
| zh = hash_zn(zn); |
| for (zp = ZoneArg[zh]; zp; zp = zp->next) { |
| if (!strcmp(zp->zn, zn)) |
| break; |
| } |
| if (zp) { |
| |
| /* |
| * Process a duplicate. |
| */ |
| if (!Fwarn) |
| (void) fprintf(stderr, "%s: duplicate zone name: %s\n", Pn, zn); |
| return(1); |
| } |
| /* |
| * Create a new hash entry and link it to its bucket. |
| */ |
| if (!(zpn = (znhash_t *)malloc((MALLOC_S)sizeof(znhash_t)))) { |
| (void) fprintf(stderr, "%s no hash space for zone: %s\n", Pn, zn); |
| Exit(1); |
| } |
| zpn->f = 0; |
| zpn->zn = zn; |
| zpn->next = ZoneArg[zh]; |
| ZoneArg[zh] = zpn; |
| return(0); |
| } |
| |
| |
| /* |
| * hash_zn() - hash zone name |
| */ |
| |
| static int |
| hash_zn(zn) |
| char *zn; /* zone name */ |
| { |
| register int i, h; |
| size_t l; |
| |
| if (!(l = strlen(zn))) |
| return(0); |
| if (l == 1) |
| return((int)*zn & (HASHZONE - 1)); |
| for (i = h = 0; i < (int)(l - 1); i++) { |
| h ^= ((int)zn[i] * (int)zn[i+1]) << ((i*3)%13); |
| } |
| return(h & (HASHZONE - 1)); |
| } |
| #endif /* defined(HASZONES) */ |
| |
| |
| /* |
| * initialize() - perform all initialization |
| */ |
| |
| void |
| initialize() |
| { |
| get_kernel_access(); |
| /* |
| * Read Solaris file system information and construct the clone table. |
| * |
| * The clone table is needed to identify sockets. |
| */ |
| readfsinfo(); |
| |
| #if defined(HASDCACHE) |
| readdev(0); |
| #else /* !defined(HASDCACHE) */ |
| read_clone(); |
| #endif /*defined(HASDCACHE) */ |
| |
| } |
| |
| |
| /* |
| * 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 */ |
| { |
| register int br; |
| /* |
| * Because lsof reads kernel data and follows pointers found there at a |
| * rate considerably slower than the kernel, lsof sometimes acquires |
| * invalid pointers. If the invalid pointers are fed to kvm_[k]read(), |
| * a segmentation violation may result, so legal kernel addresses are |
| * limited by the value of the KERNELBASE symbol (Kb value from the |
| * kernel's _kernelbase variable for Solaris 2.5 and above). |
| */ |
| |
| #if solaris>=20500 |
| #define KVMREAD kvm_kread |
| if (addr < Kb) |
| #else /* solaris<20500 */ |
| #define KVMREAD kvm_read |
| if (addr < (KA_T)KERNELBASE) |
| #endif /* solaris>=20500 */ |
| |
| return(1); |
| |
| #if solaris>=20501 |
| |
| /* |
| * Do extra address checking for Solaris above 2.5 when the ALLKMEM device |
| * isn't available. |
| * |
| * Make sure the virtual address represents real physical memory by testing |
| * it with kvm_physaddr(). |
| * |
| * For Solaris below 7 read the kernel data with llseek() and read(). For |
| * Solaris 7 and above use kvm_pread(). |
| */ |
| if (Kas && !HasALLKMEM) { |
| |
| # if solaris>20501 |
| register int b2r; |
| register char *bp; |
| # endif /* solaris>20501 */ |
| |
| register int h, ip, tb; |
| register kvmhash_t *kp; |
| KPHYS pa; |
| register KVIRT va, vpa; |
| |
| # if solaris<20600 |
| for (tb = 0, va = (KVIRT)addr; |
| tb < len; |
| tb += br, va += (KVIRT)br) |
| # else /* solaris>=20600 */ |
| for (bp = buf, tb = 0, va = (KVIRT)addr; |
| tb < len; |
| bp += br, tb += br, va += (KVIRT)br) |
| # endif /* solaris<20600 */ |
| |
| { |
| vpa = (va & (KVIRT)~PSMask) >> PSShft; |
| ip = (int)(va & (KVIRT)PSMask); |
| h = HASHKVM(vpa); |
| for (kp = KVMhb[h]; kp; kp = kp->nxt) { |
| if (kp->vpa == vpa) { |
| pa = kp->pa; |
| break; |
| } |
| } |
| if (!kp) { |
| if ((pa = kvm_physaddr(Kd, Kas, va)) == KAERR) |
| return(1); |
| if (!(kp = (kvmhash_t *)malloc(sizeof(kvmhash_t)))) { |
| (void) fprintf(stderr, "%s: no kvmhash_t space\n", Pn); |
| Exit(1); |
| } |
| kp->nxt = KVMhb[h]; |
| pa = kp->pa = (pa & ~(KPHYS)PSMask); |
| kp->vpa = vpa; |
| KVMhb[h] = kp; |
| } |
| |
| # if solaris<20600 |
| br = (int)(len - tb); |
| if ((ip + br) > PageSz) |
| br = PageSz - ip; |
| # else /* solaris>=20600 */ |
| b2r = (int)(len - tb); |
| if ((ip + b2r) > PageSz) |
| b2r = PageSz - ip; |
| pa |= (KPHYS)ip; |
| |
| # if solaris<70000 |
| if (llseek(Kmd, (offset_t)pa, SEEK_SET) == (offset_t)-1) |
| return(1); |
| if ((br = (int)read(Kmd, (void *)bp, (size_t)b2r)) <= 0) |
| return(1); |
| # else /* solaris>=70000 */ |
| if ((br = kvm_pread(Kd, pa, (void *)bp, (size_t)b2r)) <= 0) |
| return(1); |
| # endif /* solaris<70000 */ |
| # endif /* solaris<20600 */ |
| |
| } |
| |
| # if solaris>=20600 |
| return(0); |
| # endif /* solaris>=20600 */ |
| |
| } |
| #endif /* solaris>=20501 */ |
| |
| /* |
| * Use kvm_read for Solaris < 2.5; use kvm_kread() Solaris >= 2.5. |
| */ |
| br = KVMREAD(Kd, (u_long)addr, buf, len); |
| return(((READLEN_T)br == len) ? 0 : 1); |
| } |
| |
| |
| /* |
| * open_kvm() - open kernel virtual memory access |
| */ |
| |
| void |
| open_kvm() |
| { |
| if (Kd) |
| return; |
| |
| #if defined(WILLDROPGID) |
| /* |
| * If this Solaris process began with setgid permission and its been |
| * surrendered, regain it. |
| */ |
| (void) restoregid(); |
| #endif /* defined(WILLDROPGID) */ |
| |
| if (!(Kd = kvm_open(Nmlst, Memory, NULL, O_RDONLY, Pn))) { |
| (void) fprintf(stderr, |
| "%s: kvm_open(namelist=%s, corefile=%s): %s\n", |
| Pn, |
| Nmlst ? Nmlst : "default", |
| Memory ? Memory : "default", |
| strerror(errno)); |
| Exit(1); |
| } |
| |
| #if solaris>=20501 && solaris<70000 |
| if ((Kmd = open((Memory ? Memory : KMEM), O_RDONLY)) < 0) { |
| (void) fprintf(stderr, "%s: open(\"/dev/mem\"): %s\n", Pn, |
| strerror(errno)); |
| Exit(1); |
| } |
| #endif /* solaris>=20501 && solaris<70000 */ |
| |
| #if defined(WILLDROPGID) |
| /* |
| * If this process has setgid permission, and is willing to surrender it, |
| * do so. |
| */ |
| (void) dropgid(); |
| /* |
| * If this Solaris process must switch GIDs, enable switching after the |
| * first call to this function. |
| */ |
| if (Switchgid == 1) |
| Switchgid = 2; |
| #endif /* define(WILLDROPGID) */ |
| |
| } |
| |
| |
| /* |
| * process_text() - process text access information |
| */ |
| |
| #if solaris>=90000 |
| #include <sys/avl.h> |
| |
| /* |
| * Avl trees are implemented as follows: types in AVL trees contain an |
| * avl_node_t. These avl_nodes connect to other avl nodes embedded in |
| * objects of the same type. The avl_tree contains knowledge about the |
| * size of the structure and the offset of the AVL node in the object |
| * so we can convert between AVL nodes and (in this case) struct seg. |
| * |
| * This code was provided by Casper Dik <Casper.Dik@holland.sun.com>. |
| */ |
| |
| #define READ_AVL_NODE(n,o,s) \ |
| if (kread((KA_T)AVL_NODE2DATA(n, o), (char*) s, sizeof(*s))) \ |
| return -1 |
| |
| static int |
| get_first_seg(avl_tree_t *av, struct seg *s) |
| { |
| avl_node_t *node = av->avl_root; |
| size_t off = av->avl_offset; |
| int count = 0; |
| |
| while (node != NULL && ++count < MAXSEGS * 2) { |
| READ_AVL_NODE(node, off, s); |
| node = s->s_tree.avl_child[0]; |
| if (node == NULL) |
| return 0; |
| } |
| return -1; |
| } |
| |
| static int |
| get_next_seg(avl_tree_t *av, struct seg *s) |
| { |
| avl_node_t *node = &s->s_tree; |
| size_t off = av->avl_offset; |
| int count = 0; |
| |
| if (node->avl_child[1]) { |
| /* |
| * Has right child, go all the way to the leftmost child of |
| * the right child. |
| */ |
| READ_AVL_NODE(node->avl_child[1], off, s); |
| while (node->avl_child[0] != NULL && ++count < 2 * MAXSEGS) |
| READ_AVL_NODE(node->avl_child[0],off,s); |
| if (count < 2 * MAXSEGS) |
| return 0; |
| } else { |
| /* |
| * No right child, go up until we find a node we're not a right |
| * child of. |
| */ |
| for (;count < 2 * MAXSEGS; count++) { |
| int index = AVL_XCHILD(node); |
| avl_node_t *parent = AVL_XPARENT(node); |
| |
| if (parent == NULL) |
| return -1; |
| |
| READ_AVL_NODE(parent, off, s); |
| |
| if (index == 0) |
| return 0; |
| } |
| } |
| return -1; |
| } |
| |
| static void |
| process_text(pa) |
| KA_T pa; /* address space description pointer */ |
| { |
| struct as as; |
| int i, j, k; |
| struct seg s; |
| struct segvn_data vn; |
| avl_tree_t *avtp; |
| KA_T v[MAXSEGS]; |
| /* |
| * Get address space description. |
| */ |
| if (kread((KA_T)pa, (char *)&as, sizeof(as))) { |
| alloc_lfile(" txt", -1); |
| (void) snpf(Namech, Namechl, "can't read text segment list (%s)", |
| print_kptr(pa, (char *)NULL, 0)); |
| enter_nm(Namech); |
| if (Lf->sf) |
| link_lfile(); |
| 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 unique segments have been recorded or 2*MAXSEGS segments |
| * have been examined. |
| */ |
| for (avtp = &as.a_segtree, i = j = 0; |
| (i < MAXSEGS) && (j < 2*MAXSEGS); |
| j++) |
| { |
| if (j ? get_next_seg(avtp, &s) : get_first_seg(avtp, &s)) |
| break; |
| if ((KA_T)s.s_ops == Sgvops && s.s_data) { |
| if (kread((KA_T)s.s_data, (char *)&vn, sizeof(vn))) |
| break; |
| if (vn.vp) { |
| |
| /* |
| * This is a virtual node segment. |
| * |
| * If its vnode pointer has not been seen already, record the |
| * vnode pointer and process the vnode. |
| */ |
| for (k = 0; k < i; k++) { |
| if (v[k] == (KA_T)vn.vp) |
| break; |
| } |
| if (k >= i) { |
| v[i++] = (KA_T)vn.vp; |
| alloc_lfile(" txt", -1); |
| |
| # if defined(FILEPTR) |
| FILEPTR = (struct file *)NULL; |
| # endif /* defined(FILEPTR) */ |
| |
| process_node((KA_T)vn.vp); |
| if (Lf->sf) |
| link_lfile(); |
| } |
| } |
| } |
| } |
| } |
| |
| #else /* solaris<90000 */ |
| |
| # if solaris>=20400 |
| #define S_NEXT s_next.list |
| # else /* solaris<20400 */ |
| #define S_NEXT s_next |
| # endif /* solaris>=20400 */ |
| |
| static void |
| process_text(pa) |
| KA_T pa; /* address space description pointer */ |
| { |
| struct as as; |
| int i, j, k; |
| struct seg s; |
| struct segvn_data vn; |
| KA_T v[MAXSEGS]; |
| /* |
| * Get address space description. |
| */ |
| if (kread((KA_T)pa, (char *)&as, sizeof(as))) { |
| alloc_lfile(" txt", -1); |
| (void) snpf(Namech, Namechl, "can't read text segment list (%s)", |
| print_kptr(pa, (char *)NULL, 0)); |
| enter_nm(Namech); |
| if (Lf->sf) |
| link_lfile(); |
| 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 unique segments have been recorded or 2*MAXSEGS segments |
| * have been examined. |
| */ |
| for (s.s_next = as.a_segs, i = j = 0; |
| i < MAXSEGS && j < 2*MAXSEGS; |
| j++) |
| { |
| if (!s.S_NEXT |
| || kread((KA_T)s.S_NEXT, (char *)&s, sizeof(s))) |
| break; |
| if ((KA_T)s.s_ops == Sgvops && s.s_data) { |
| if (kread((KA_T)s.s_data, (char *)&vn, sizeof(vn))) |
| break; |
| if (vn.vp) { |
| |
| /* |
| * This is a virtual node segment. |
| * |
| * If its vnode pointer has not been seen already, record the |
| * vnode pointer and process the vnode. |
| */ |
| for (k = 0; k < i; k++) { |
| if (v[k] == (KA_T)vn.vp) |
| break; |
| } |
| if (k >= i) { |
| v[i++] = (KA_T)vn.vp; |
| alloc_lfile(" txt", -1); |
| |
| # if defined(FILEPTR) |
| FILEPTR = (struct file *)NULL; |
| # endif /* defined(FILEPTR) */ |
| |
| process_node((KA_T)vn.vp); |
| if (Lf->sf) |
| link_lfile(); |
| } |
| } |
| } |
| /* |
| * 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 solaris<20400 |
| if (s.s_next == as.a_segs) |
| # else /* solaris>=20400 */ |
| if (s.s_next.list == as.a_segs.list) |
| # endif /* solaris<20400 */ |
| |
| break; |
| } |
| } |
| #endif /* solaris>=90000 */ |
| |
| |
| /* |
| * 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); |
| } |
| if (buf[0] == '\0') { |
| Fsinfo[i-1] = ""; |
| continue; |
| } |
| 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); |
| |
| # if defined(HAS_AFS) |
| if (strcasecmp(buf, "afs") == 0) |
| AFSfstype = i; |
| # endif /* defined(HAS_AFS) */ |
| |
| } |
| } |
| |
| |
| #if solaris>=20501 |
| /* |
| * readkam() - read kernel's address map structure |
| */ |
| |
| static void |
| readkam(addr) |
| KA_T addr; /* kernel virtual address */ |
| { |
| register int i; |
| register kvmhash_t *kp, *kpp; |
| static KA_T kas = (KA_T)NULL; |
| |
| if (addr) |
| kas = addr; |
| Kas = (struct as *)NULL; |
| |
| #if solaris<70000 |
| if (kas && !kread(kas, (char *)&Kam, sizeof(Kam))) |
| Kas = (KA_T)&Kam; |
| #else /* solaris>=70000 */ |
| Kas = (struct as *)kas; |
| #endif /* solaris<70000 */ |
| |
| if (Kas) { |
| if (!KVMhb) { |
| if (!(KVMhb = (kvmhash_t **)calloc(KVMHASHBN, |
| sizeof(kvmhash_t *)))) |
| { |
| (void) fprintf(stderr, |
| "%s: no space (%d) for KVM hash buckets\n", |
| Pn, (int)(KVMHASHBN * sizeof(kvmhash_t *))); |
| Exit(1); |
| } |
| } else if (!addr) { |
| for (i = 0; i < KVMHASHBN; i++) { |
| if ((kp = KVMhb[i])) { |
| while (kp) { |
| kpp = kp->nxt; |
| (void) free((void *)kp); |
| kp = kpp; |
| } |
| KVMhb[i] = (kvmhash_t *)NULL; |
| } |
| } |
| } |
| } |
| } |
| #endif /* solaris>=20501 */ |
| |
| |
| /* |
| * read_proc() - read proc structures |
| * |
| * As a side-effect, Kd is set by a call to kvm_open(). |
| */ |
| |
| static void |
| read_proc() |
| { |
| int ct, ctl, knp, n, try; |
| MALLOC_S len; |
| struct proc *p; |
| KA_T pa, paf, pan; |
| struct pid pg, pids; |
| /* |
| * Try PROCTRYLM times to read a valid proc table. |
| */ |
| for (try = 0; try < PROCTRYLM; try++) { |
| |
| /* |
| * Get a proc structure count estimate. |
| */ |
| if (get_Nl_value("nproc", Drive_Nl, &pa) < 0 || !pa |
| || kread(pa, (char *)&knp, sizeof(knp)) |
| || knp < 1) |
| knp = PROCDFLT; |
| /* |
| * Pre-allocate space, as required. |
| */ |
| n = knp + PROCDFLT/4; |
| if (n > Npa) { |
| |
| /* |
| * Allocate proc structure space. |
| */ |
| len = (n * sizeof(struct proc)); |
| if (P) |
| P = (struct proc *)realloc((MALLOC_P *)P, len); |
| else |
| P = (struct proc *)malloc(len); |
| if (!P) { |
| (void) fprintf(stderr, "%s: no proc table space\n", Pn); |
| Exit(1); |
| } |
| /* |
| * Pre-allocate PGID and PID number space. |
| */ |
| len = (MALLOC_S)(n * sizeof(int)); |
| if (Fpgid) { |
| if (Pgid) |
| Pgid = (int *)realloc((MALLOC_P *)Pgid, len); |
| else |
| Pgid = (int *)malloc(len); |
| if (!Pgid) { |
| (void) fprintf(stderr, "%s: no PGID table space\n", Pn); |
| Exit(1); |
| } |
| } |
| if (Pid) |
| Pid = (int *)realloc((MALLOC_P *)Pid, len); |
| else |
| Pid = (int *)malloc(len); |
| if (!Pid) { |
| (void) fprintf(stderr, "%s: no PID table space\n", Pn); |
| Exit(1); |
| } |
| Npa = n; |
| } |
| if (HasALLKMEM) { |
| |
| /* |
| * Prepare for a proc table scan via direct reading of the active |
| * chain. |
| */ |
| if (!PrAct || kread(PrAct, (char *)&paf, sizeof(pa))) { |
| (void) fprintf(stderr, "%s: can't read practive from %s\n", |
| Pn, print_kptr(PrAct, (char *)NULL, 0)); |
| Exit(1); |
| } |
| ct = 1; |
| ctl = knp << 3; |
| pan = paf; |
| pa = (KA_T)NULL; |
| } else { |
| |
| /* |
| * Prepare for a proc table scan via the kvm_*proc() functions. |
| */ |
| if (kvm_setproc(Kd) != 0) { |
| (void) fprintf(stderr, "%s: kvm_setproc: %s\n", Pn, |
| strerror(errno)); |
| Exit(1); |
| } |
| } |
| /* |
| * Accumulate proc structures. |
| */ |
| Np = 0; |
| for (;;) { |
| if (Np >= Npa) { |
| |
| /* |
| * Expand the local proc table. |
| */ |
| Npa += PROCDFLT/2; |
| len = (MALLOC_S)(Npa * sizeof(struct proc)); |
| if (!(P = (struct proc *)realloc((MALLOC_P *)P, len))) { |
| (void) fprintf(stderr, |
| "%s: no more (%d) proc space\n", Pn, Npa); |
| Exit(1); |
| } |
| /* |
| * Expand the PGID and PID tables. |
| */ |
| len = (MALLOC_S)(Npa * sizeof(int)); |
| if (Fpgid) { |
| if (!(Pgid = (int *)realloc((MALLOC_P *)Pgid, len))) { |
| (void) fprintf(stderr, |
| "%s: no more (%d) PGID space\n", Pn, Npa); |
| Exit(1); |
| } |
| } |
| if (!(Pid = (int *)realloc((MALLOC_P *)Pid, len))) { |
| (void) fprintf(stderr, |
| "%s: no more (%d) PID space\n", Pn, Npa); |
| Exit(1); |
| } |
| } |
| /* |
| * Read the next proc structure. |
| */ |
| if (HasALLKMEM) { |
| |
| /* |
| * If the ALLKMEM device exists, read proc structures directly |
| * from the active chain. |
| */ |
| if (!pa) |
| pa = paf; |
| else { |
| pa = pan; |
| if ((pan == paf) || (++ct > ctl)) |
| break; |
| } |
| if (!pa) |
| break; |
| p = (struct proc *)&P[Np]; |
| if (kread(pa, (char *)p, sizeof(struct proc))) |
| break; |
| pan = (KA_T)p->p_next; |
| } else { |
| |
| /* |
| * If the ALLKMEM device doesn't exist, read proc structures |
| * via kbm_getproc(). |
| */ |
| if (!(p = kvm_nextproc(Kd))) |
| break; |
| } |
| /* |
| * Check process status. |
| */ |
| if (p->p_stat == 0 || p->p_stat == SZOMB) |
| continue; |
| |
| #if solaris >=20500 |
| /* |
| * Check Solaris 2.5 and above p_cred pointer. |
| */ |
| if (!p->p_cred) |
| continue; |
| #endif /* solaris >=20500 */ |
| |
| /* |
| * Read Solaris PGID and PID numbers. |
| */ |
| if (Fpgid) { |
| if (!p->p_pgidp |
| || kread((KA_T)p->p_pgidp, (char *)&pg, sizeof(pg))) |
| continue; |
| } |
| if (!p->p_pidp |
| || kread((KA_T)p->p_pidp, (char *)&pids, sizeof(pids))) |
| continue; |
| /* |
| * Save the PGID and PID numbers in local tables. |
| */ |
| if (Fpgid) |
| Pgid[Np] = (int)pg.pid_id; |
| Pid[Np] = (int)pids.pid_id; |
| /* |
| * If the proc structure came from kvm_getproc(), save it in the |
| * local table. |
| */ |
| if (!HasALLKMEM) |
| P[Np] = *p; |
| Np++; |
| } |
| /* |
| * If not enough processes were saved in the local table, try again. |
| * |
| * If the ALLKMEM device isn't available, it is necessary to close and |
| * reopen the KVM device, so that kvm_open() will acquire a fresh |
| * address for the head of the linked list process table. |
| */ |
| if (Np >= PROCMIN) |
| break; |
| if (!HasALLKMEM) { |
| close_kvm(); |
| open_kvm(); |
| } |
| } |
| /* |
| * Quit if no proc structures were stored in the local table. |
| */ |
| if (try >= PROCTRYLM) { |
| (void) fprintf(stderr, "%s: can't read proc table\n", Pn); |
| Exit(1); |
| } |
| if (Np < Npa && !RptTm) { |
| |
| /* |
| * Reduce the local proc structure table size to its minimum if |
| * not in repeat mode. |
| */ |
| 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\n", |
| Pn, Np); |
| Exit(1); |
| } |
| /* |
| * Reduce the Solaris PGID and PID tables to their minimum if |
| * not in repeat mode. |
| */ |
| len = (MALLOC_S)(Np * sizeof(int)); |
| if (Fpgid) { |
| if (!(Pgid = (int *)realloc((MALLOC_P *)Pgid, len))) { |
| (void) fprintf(stderr, |
| "%s: can't reduce PGID table to %d\n", Pn, Np); |
| Exit(1); |
| } |
| } |
| if (!(Pid = (int *)realloc((MALLOC_P *)Pid, len))) { |
| (void) fprintf(stderr, |
| "%s: can't reduce PID table to %d\n", Pn, Np); |
| Exit(1); |
| } |
| Npa = Np; |
| } |
| } |
| |
| |
| #if defined(WILLDROPGID) |
| /* |
| * restoregid() -- restore setgid permission, as required |
| */ |
| |
| void |
| restoregid() |
| { |
| if (Switchgid == 2 && !Setgid) { |
| if (setgid(Savedgid) != 0) { |
| (void) fprintf(stderr, |
| "%s: can't set effective GID to %d: %s\n", |
| Pn, (int)Savedgid, strerror(errno)); |
| Exit(1); |
| } |
| Setgid = 1; |
| } |
| } |
| #endif /* defined(WILLDROPGID) */ |
| |
| |
| #if defined(HASNCACHE) && solaris>=90000 |
| |
| |
| /* |
| * Local static values |
| */ |
| |
| static int Mhl; /* local name cache hash mask */ |
| static int Nhl = 0; /* size of local name cache hash |
| * pointer table */ |
| struct l_nch { |
| KA_T vp; /* vnode address */ |
| KA_T dp; /* parent vnode address */ |
| struct l_nch *pa; /* parent Ncache address */ |
| char *nm; /* name */ |
| int nl; /* name length */ |
| }; |
| |
| static struct l_nch *Ncache = (struct l_nch *)NULL; |
| /* the local name cache */ |
| static struct l_nch **Nchash = (struct l_nch **)NULL; |
| /* Ncache hash pointers */ |
| static int Ncfirst = 1; /* first-call status */ |
| static KA_T NegVN = (KA_T)NULL; /* negative vnode address */ |
| static int Nla = 0; /* entries allocated to Ncache[] */ |
| static int Nlu = 0; /* entries used in Ncache[] */ |
| |
| _PROTOTYPE(static struct l_nch *ncache_addr,(KA_T v)); |
| |
| #define ncachehash(v) Nchash+((((int)(v)>>2)*31415)&Mhl) |
| |
| _PROTOTYPE(static int ncache_isroot,(KA_T va, char *cp)); |
| |
| #define LNCHINCRSZ 64 /* local size increment */ |
| #define XNC 15 /* extra name characters to read beyond those |
| * in name[] of the ncache_t structure -- this |
| * is an efficiency hint and MUST BE AT LEAST |
| * ONE. */ |
| |
| |
| /* |
| * ncache_addr() - look up a node's local ncache address |
| */ |
| |
| static struct l_nch * |
| |
| ncache_addr(v) |
| KA_T v; /* vnode's address */ |
| { |
| struct l_nch **hp; |
| |
| for (hp = ncachehash(v); *hp; hp++) { |
| if ((*hp)->vp == v) |
| return(*hp); |
| } |
| return((struct l_nch *)NULL); |
| } |
| |
| |
| /* |
| * ncache_isroot() - is head of name cache path a file system root? |
| */ |
| |
| static int |
| ncache_isroot(va, cp) |
| KA_T va; /* kernel vnode address */ |
| char *cp; /* partial path */ |
| { |
| char buf[MAXPATHLEN]; |
| int i; |
| MALLOC_S len; |
| struct mounts *mtp; |
| struct stat sb; |
| struct vnode v; |
| static int vca = 0; |
| static int vcn = 0; |
| static KA_T *vc = (KA_T *)NULL; |
| |
| if (!va) |
| return(0); |
| /* |
| * Search the root vnode cache. |
| */ |
| for (i = 0; i < vcn; i++) { |
| if (va == vc[i]) |
| return(1); |
| } |
| /* |
| * Read the vnode and see if it's a VDIR node with the VROOT flag set. If |
| * it is, then the path is complete. |
| * |
| * If it isn't, and if the file has an inode number, search the mount table |
| * and see if the file system's inode number is known. If it is, form the |
| * possible full path, safely stat() it, and see if it's inode number matches |
| * the one we have for this file. If it does, then the path is complete. |
| */ |
| if (kread((KA_T)va, (char *)&v, sizeof(v)) |
| || v.v_type != VDIR || !(v.v_flag & VROOT)) { |
| |
| /* |
| * The vnode tests failed. Try the inode tests. |
| */ |
| if (Lf->inp_ty != 1 || !Lf->inode |
| || !Lf->fsdir || (len = strlen(Lf->fsdir)) < 1) |
| return(0); |
| if ((len + 1 + strlen(cp) + 1) > sizeof(buf)) |
| return(0); |
| for (mtp = readmnt(); mtp; mtp = mtp->next) { |
| if (!mtp->dir || !mtp->inode) |
| continue; |
| if (strcmp(Lf->fsdir, mtp->dir) == 0) |
| break; |
| } |
| if (!mtp) |
| return(0); |
| (void) strcpy(buf, Lf->fsdir); |
| if (buf[len - 1] != '/') |
| buf[len++] = '/'; |
| (void) strcpy(&buf[len], cp); |
| if (statsafely(buf, &sb) != 0 |
| || (INODETYPE)sb.st_ino != Lf->inode) |
| return(0); |
| } |
| /* |
| * Add the vnode address to the root vnode cache. |
| */ |
| if (vcn >= vca) { |
| vca += 10; |
| len = (MALLOC_S)(vca * sizeof(KA_T)); |
| if (!vc) |
| vc = (KA_T *)malloc(len); |
| else |
| vc = (KA_T *)realloc(vc, len); |
| if (!vc) { |
| (void) fprintf(stderr, "%s: no space for root vnode table\n", |
| Pn); |
| Exit(1); |
| } |
| } |
| vc[vcn++] = va; |
| return(1); |
| } |
| |
| |
| /* |
| * ncache_load() - load the kernel's name cache |
| */ |
| |
| void |
| ncache_load() |
| { |
| char *cp; |
| struct l_nch **hp, *lc; |
| int h, i, len, n, xl; |
| static int iNch = 0; |
| nc_hash_t *kh; |
| static KA_T kha = (KA_T)NULL; |
| static nc_hash_t *khl = (nc_hash_t *)NULL; |
| KA_T kn; |
| static ncache_t *nc = (ncache_t *)NULL; |
| static int Nch = 0; |
| static int nmo = 0; |
| KA_T v; |
| static int xn = 0; |
| |
| if (!Fncache) |
| return; |
| if (Ncfirst) { |
| |
| /* |
| * Do startup (first-time) functions. |
| */ |
| Ncfirst = 0; |
| /* |
| * Establish DNLC hash size. |
| */ |
| v = (KA_T)0; |
| if (get_Nl_value(X_NCSIZE, (struct drive_Nl *)NULL, &v) < 0 |
| || !v |
| || kread((KA_T)v, (char *)&Nch, sizeof(Nch))) |
| { |
| if (!Fwarn) |
| (void) fprintf(stderr, |
| "%s: WARNING: can't read DNLC hash size: %s\n", |
| Pn, print_kptr(v, (char *)NULL, 0)); |
| iNch = Nch = 0; |
| return; |
| } |
| if ((iNch = Nch) < 1) { |
| if (!Fwarn) |
| (void) fprintf(stderr, |
| "%s: WARNING: DNLC hash size: %d\n", Pn, Nch); |
| iNch = Nch = 0; |
| return; |
| } |
| /* |
| * Get negative vnode address. |
| */ |
| if (get_Nl_value(NCACHE_NEGVN, (struct drive_Nl *)NULL, &NegVN) |
| < 0) |
| NegVN = (KA_T)NULL; |
| /* |
| * Establish DNLC hash address. |
| */ |
| v = (KA_T)0; |
| if (get_Nl_value(X_NCACHE,(struct drive_Nl *)NULL,(KA_T *)&v) < 0 |
| || !v |
| || kread(v, (char *)&kha, sizeof(kha)) |
| || !kha |
| ) { |
| if (!Fwarn) |
| (void) fprintf(stderr, |
| "%s: WARNING: no DNLC hash address\n", Pn); |
| iNch = Nch = 0; |
| return; |
| } |
| /* |
| * Allocate space for a local copy of the kernel's hash table. |
| */ |
| len = Nch * sizeof(nc_hash_t); |
| if (!(khl = (nc_hash_t *)malloc((MALLOC_S)len))) { |
| (void) fprintf(stderr, |
| "%s: can't allocate DNLC hash space: %d\n", Pn, len); |
| Exit(1); |
| } |
| /* |
| * Allocate space for a kernel DNLC entry, plus additional name space |
| * for efficiency. |
| */ |
| xn = XNC; |
| if (!(nc = (ncache_t *)malloc((MALLOC_S)(sizeof(ncache_t) + XNC)))) |
| { |
| (void) fprintf(stderr, |
| "%s: can't allocate DNLC ncache_t space\n", Pn); |
| Exit(1); |
| } |
| nmo = offsetof(struct ncache, name); |
| /* |
| * Allocate estimated space for the local cache, based on the |
| * hash table count and the current average hash length. |
| */ |
| v = (KA_T)0; |
| if ((get_Nl_value("hshav", (struct drive_Nl *)NULL, (KA_T *)&v) < 0) |
| || !v |
| || kread(v, (char *)&i, sizeof(i)) |
| || (i < 1) |
| ) { |
| i = 16; |
| if (!Fwarn) { |
| (void) fprintf(stderr, |
| "%s: can't read DNLC average hash bucket size,", Pn); |
| (void) fprintf(stderr, " using %d\n", i); |
| } |
| } |
| Nla = Nch * i; |
| if (!(Ncache = (struct l_nch *)calloc(Nla, sizeof(struct l_nch)))) { |
| |
| no_local_space: |
| |
| (void) fprintf(stderr, |
| "%s: no space for %d byte local name cache\n", Pn, len); |
| Exit(1); |
| } |
| } else { |
| |
| /* |
| * Do setup for repeat calls. |
| */ |
| if (!iNch || !Nla || !Ncache) |
| return; |
| if (Nchash) { |
| (void) free((FREE_P *)Nchash); |
| Nchash = (struct l_nch **)NULL; |
| } |
| if (Ncache && Nlu) { |
| |
| /* |
| * Free space malloc'd to names in local name cache. |
| */ |
| for (i = 0, lc = Ncache; i < Nlu; i++, lc++) { |
| if (lc->nm) { |
| (void) free((FREE_P *)lc->nm); |
| lc->nm = (char *)NULL; |
| } |
| } |
| } |
| Nch = iNch; |
| Nlu = 0; |
| } |
| /* |
| * Read the kernel's DNLC hash. |
| */ |
| if (kread(kha, (char *)khl, (Nch * sizeof(nc_hash_t)))) { |
| if (!Fwarn) |
| (void) fprintf(stderr, |
| "%s: WARNING: can't read DNLC hash: %s\n", |
| Pn, print_kptr(kha, (char *)NULL, 0)); |
| iNch = Nch = 0; |
| return; |
| } |
| /* |
| * Build a local copy of the kernel name cache. |
| */ |
| for (i = n = 0, kh = khl, lc = Ncache; i < Nch; i++, kh++) { |
| |
| /* |
| * Skip empty hash buckets. |
| */ |
| if (!kh->hash_next || ((KA_T)kh->hash_next == kha)) |
| continue; |
| /* |
| * Process a hash bucket. |
| */ |
| for (kn = (KA_T)kh->hash_next, h = 0; |
| kn && (h < Nch) && (!h || (h && kn != (KA_T)kh->hash_next)); |
| kn = (KA_T)nc->hash_next, h++) |
| { |
| if (kread(kn, (char *)nc, sizeof(ncache_t) + XNC)) |
| break; |
| if (!nc->vp || (len = (int)nc->namlen) < 1) |
| continue; |
| if (NegVN && ((KA_T)nc->vp == NegVN)) |
| continue; |
| if ((len < 3) && (nc->name[0] == '.')) { |
| if ((len < 2) || (nc->name[1] == '.')) |
| continue; |
| } |
| /* |
| * If not all the name has been read, read the rest of it, |
| * allocating more space at the end of the ncache structure as |
| * required. |
| */ |
| if (len > (XNC + 1)) { |
| if (len > (xn + 1)) { |
| while (len > (xn + 1)) |
| xn = xn + xn; |
| xn = ((xn + 7) & ~7) - 1; |
| if (!(nc = (ncache_t *)realloc((MALLOC_P *)nc, |
| (sizeof(ncache_t) + xn))) |
| ) { |
| (void) fprintf(stderr, |
| "%s: can't extend DNLC ncache_t buffer\n", Pn); |
| Exit(1); |
| } |
| } |
| cp = &nc->name[XNC + 1]; |
| v = (KA_T)((char *)kn + nmo + XNC + 1); |
| xl = len - XNC - 1; |
| if (kread(v, cp, xl)) |
| continue; |
| } |
| /* |
| * Allocate space for the name in the local name cache entry. |
| */ |
| if (!(cp = (char *)malloc(len + 1))) { |
| (void) fprintf(stderr, |
| "%s: can't allocate %d bytes for name cache name\n", |
| Pn, len + 1); |
| Exit(1); |
| } |
| (void) strncpy(cp, nc->name, len); |
| cp[len] = '\0'; |
| /* |
| * Make sure there is space for another local name cache entry. |
| * If not, allocate twice as many entries. |
| */ |
| if (n >= Nla) { |
| Nla = Nla + Nla; |
| if (!(Ncache = (struct l_nch *)realloc(Ncache, |
| (MALLOC_S)(Nla * sizeof(struct l_nch)))) |
| ) { |
| (void) fprintf(stderr, |
| "%s: can't enlarge local name cache\n", Pn); |
| Exit(1); |
| } |
| lc = &Ncache[n]; |
| } |
| /* |
| * Complete the local cache entry. |
| */ |
| lc->vp = (KA_T)nc->vp; |
| lc->dp = (KA_T)nc->dp; |
| lc->pa = (struct l_nch *)NULL; |
| lc->nm = cp; |
| lc->nl = len; |
| lc++; |
| n++; |
| } |
| } |
| /* |
| * Reduce memory usage, as required. |
| */ |
| if ((Nlu = n) < 1) { |
| |
| /* |
| * No DNLC entries were located, an unexpected result. |
| */ |
| if (!RptTm && Ncache) { |
| |
| /* |
| * If not in repeat mode, free the space that has been malloc'd |
| * to the local name cache. |
| */ |
| (void) free((FREE_P *)Ncache); |
| Ncache = (struct l_nch *)NULL; |
| Nla = Nlu = 0; |
| } |
| /* |
| * Issue a warning and disable furthe DNLC processing. |
| */ |
| if (!Fwarn) |
| (void) fprintf(stderr, |
| "%s: WARNING: unusable local name cache size: %d\n", Pn, n); |
| iNch = Nch = 0; |
| return; |
| } |
| if ((Nlu < Nla) && !RptTm) { |
| len = Nlu * sizeof(struct l_nch); |
| if (!(Ncache = (struct l_nch *)realloc(Ncache, len))) |
| goto no_local_space; |
| Nla = Nlu; |
| } |
| /* |
| * Build a hash table to locate Ncache entries. |
| */ |
| for (Nhl = 1; Nhl < Nlu; Nhl <<= 1) |
| ; |
| Nhl <<= 1; |
| Mhl = Nhl - 1; |
| if (!(Nchash = (struct l_nch **)calloc(Nhl + Nlu, |
| sizeof(struct l_nch *)))) |
| { |
| (void) fprintf(stderr, |
| "%s: no space for %d name cache hash pointers\n", |
| Pn, Nhl + Nlu); |
| Exit(1); |
| } |
| for (i = 0, lc = Ncache; i < Nlu; i++, lc++) { |
| for (hp = ncachehash(lc->vp), h = 1; *hp; hp++) { |
| if ((*hp)->vp == lc->vp && strcmp((*hp)->nm, lc->nm) == 0 |
| && (*hp)->dp == lc->dp |
| ) { |
| h = 0; |
| break; |
| } |
| } |
| if (h) |
| *hp = lc; |
| } |
| /* |
| * Make a final pass through the local cache and convert parent vnode |
| * addresses to local name cache pointers. |
| */ |
| for (i = 0, lc = Ncache; i < Nlu; i++, lc++) { |
| if (!lc->dp) |
| continue; |
| if (NegVN && (lc->dp == NegVN)) { |
| lc->pa = (struct l_nch *)NULL; |
| continue; |
| } |
| lc->pa = ncache_addr(lc->dp); |
| } |
| } |
| |
| |
| /* |
| * ncache_lookup() - look up a node's name in the kernel's name cache |
| */ |
| |
| char * |
| ncache_lookup(buf, blen, fp) |
| char *buf; /* receiving name buffer */ |
| int blen; /* receiving buffer length */ |
| int *fp; /* full path reply */ |
| { |
| char *cp = buf; |
| struct l_nch *lc; |
| struct mounts *mtp; |
| int nl, rlen; |
| |
| *cp = '\0'; |
| *fp = 0; |
| |
| # if defined(HASFSINO) |
| /* |
| * If the entry has an inode number that matches the inode number of the |
| * file system mount point, return an empty path reply. That tells the |
| * caller to print the file system mount point name only. |
| */ |
| if (Lf->inp_ty == 1 && Lf->fs_ino && Lf->inode == Lf->fs_ino) |
| return(cp); |
| # endif /* defined(HASFSINO) */ |
| |
| /* |
| * Look up the name cache entry for the node address. |
| */ |
| if (!Nlu || !(lc = ncache_addr(Lf->na))) { |
| |
| /* |
| * If the node has no cache entry, see if it's the mount |
| * point of a known file system. |
| */ |
| if (!Lf->fsdir || !Lf->dev_def || Lf->inp_ty != 1) |
| return((char *)NULL); |
| for (mtp = readmnt(); mtp; mtp = mtp->next) { |
| if (!mtp->dir || !mtp->inode) |
| continue; |
| if (Lf->dev == mtp->dev |
| && mtp->inode == Lf->inode |
| && strcmp(mtp->dir, Lf->fsdir) == 0) |
| return(cp); |
| } |
| return((char *)NULL); |
| } |
| /* |
| * Begin the path assembly. |
| */ |
| if ((nl = lc->nl) > (blen - 1)) |
| return((char *)NULL); |
| cp = buf + blen - nl - 1; |
| rlen = blen - nl - 1; |
| (void) strcpy(cp, lc->nm); |
| /* |
| * Look up the name cache entries that are parents of the node address. |
| * Quit when: |
| * |
| * there's no parent; |
| * the name is too large to fit in the receiving buffer. |
| */ |
| for (;;) { |
| if (!lc->pa) { |
| if (ncache_isroot(lc->dp, cp)) |
| *fp = 1; |
| break; |
| } |
| lc = lc->pa; |
| if (((nl = lc->nl) + 1) > rlen) |
| break; |
| *(cp - 1) = '/'; |
| cp--; |
| rlen--; |
| (void) strncpy((cp - nl), lc->nm, nl); |
| cp -= nl; |
| rlen -= nl; |
| } |
| return(cp); |
| } |
| #endif /* defined(HASNCACHE) && solaris>=90000 */ |