/* * dproc.c - AIX 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.26 2008/10/21 16:14:18 abe Exp $"; #endif #include "lsof.h" _PROTOTYPE(static void get_kernel_access,(void)); #if AIXA<2 _PROTOTYPE(static struct le *getle,(KA_T a, KA_T sid, char **err)); #endif /* AIXA<2 */ #if AIXV>=4110 _PROTOTYPE(static void getlenm,(struct le *le, KA_T sid)); #endif /* AIXV>=4110 */ _PROTOTYPE(static int kreadx,(KA_T addr, char *buf, int len, KA_T sid)); #if AIXA<2 _PROTOTYPE(static void process_text,(KA_T sid)); #else /* AIXA>=2 */ _PROTOTYPE(static void getsoinfo,(void)); _PROTOTYPE(static void process_text,(pid_t pid)); #endif /* AIXA<2 */ #if defined(SIGDANGER) # if defined(HASINTSIGNAL) _PROTOTYPE(static int lowpgsp,(int sig)); # else /* !defined(HASINTSIGNAL) */ _PROTOTYPE(static void lowpgsp,(int sig)); # endif /* defined(HASINTSIGNAL) */ #endif /* defined(SIGDANGER) */ /* * Local definitions */ #if AIXV<4300 #define PROCINFO procinfo #else /* AIXV>=4300 */ #define PROCINFO_INCR 256 # if AIXA<1 #define FDSINFO fdsinfo #define GETPROCS getprocs #define PROCINFO procsinfo # else /* AIXA>=1 */ #define FDSINFO fdsinfo64 #define GETPROCS getprocs64 #define PROCINFO procentry64 # if AIXA>1 /* * AIX 5 and greater ia64 loader definitions */ #include #define SOHASHBUCKS 128 /* SoHash[] bucket count * MUST BE A POWER OF 2!!! */ #define SOHASH(d, n) ((((int)(((GET_MIN_DEV(d) & 0x7fffffff) * SOHASHBUCKS) \ + n) * 31415) >> 7) & (SOHASHBUCKS - 1)) typedef struct so_hash { dev_t dev; /* device (st_dev) */ int nlink; /* link count (st_nlink) */ char *nm; /* name (mi_name) */ INODETYPE node; /* node number (st_ino) */ struct so_hash *next; /* next entry in hash bucket */ SZOFFTYPE sz; /* size (st_size) */ } so_hash_t; so_hash_t **SoHash = (so_hash_t **)NULL; # endif /* AIXA>1 */ # endif /* AIXA<1 */ #endif /* AIXV<4300 */ #define PROCSIZE sizeof(struct PROCINFO) /* * Create the FDSINFOSIZE definition for allocating FDSINFO space. (This * isn't as straightforward as it might seem, because someone made a bad * decision to change the struct fdsinfo* family at AIX 5.2.) */ #define FDSINFOSIZE sizeof(struct FDSINFO) /* (If we're lucky.) */ #if defined(OPEN_SHRT_MAX) # if OPEN_SHRT_MAX=4110 /* * Loader access definitions for AIX 4.1.1 and above. */ #define LIBNMLN 40 /* maximum library table name * length */ #define LIBMASK 0xf0000000 /* library table mask */ #define LIBNMCOMP 0xd0000000 /* library table name has * multiple components */ # if AIXA<1 #define RDXMASK 0x0fffffff /* kreadx() address mask */ # else /* AIXA>=1 */ #define RDXMASK 0x0fffffffffffffff /* kreadx() address mask */ #define URDXMASK 0x0fffffff00000000 /* upper part of RDXMASK */ # endif /* AIXA<1 */ #endif /* AIXV>=4110 */ /* * Loader structure definitions. (AIX doesn't supply ld_data.h.) */ struct le { /* loader entry */ struct le *next; /* next entry pointer */ #if AIXV<4300 ushort dummy1; ushort dummy2; uint dummy3; struct file *fp; /* file table entry pointer */ # if AIXV>=4110 int ft; /* file type indicator */ unsigned dummy4; char *dummy5; unsigned dummy6; char *dummy7[3]; char *nm; /* name */ # endif /* AIXV>=4110 */ #else /* AIXV>=4300 */ # if AIXA<2 uint flags; struct file *fp; /* file table entry pointer */ char *nm; /* name */ # else /* AIXA>=2 */ KA_T d1[2]; KA_T nm; /* name */ KA_T d2[10]; struct file *fp; /* file table entry pointer */ # endif /* AIXA<2 */ #endif /* AIXV<4300 */ }; #if AIXV>=4300 /* * The elements of interest from the AIX >= 4.3 loader anchor structure. */ struct la { /* loader anchor */ # if AIXA<2 struct le *list; struct le *exec; # else /* AIXA>=2 */ KA_T exec; KA_T list; # endif /* AIXA<2 */ }; #endif /* AIXV>=4300 */ /* * Local static values */ static int Np = 0; /* number of processes */ static struct PROCINFO *P = (struct PROCINFO *)NULL; /* the process table */ static struct user *Up; /* user structure */ #if AIXV>=4110 # if AIXA<2 static KA_T Soff; /* shared library VM offset */ int Soff_stat = 0; /* Soff-available status */ # endif /* AIXA<2 */ static KA_T Uo; /* user area VM offset */ #endif /* AIXV>=4110 */ /* * ckkv() - check kernel version */ void ckkv(d, er, ev, ea) char *d; /* dialect */ char *er; /* expected release */ char *ev; /* expected version */ char *ea; /* expected architecture */ { #if defined(HASKERNIDCK) # if AIXV<5000 /* * Use oslevel below AIX 5. */ int br, p[2], pid; char buf[128], *cp; struct stat sb; if (Fwarn) return; /* * Make sure we can execute OSLEVEL. If OSLEVEL doesn't exist and the AIX * version is below 4.1, return quietly. */ #define OSLEVEL "oslevel" #define OSLEVELPATH "/usr/bin/oslevel" if (stat(OSLEVELPATH, &sb)) { # if AIXV<4100 if (errno == ENOENT) return; # endif /* AIXV<4100 */ (void) fprintf(stderr, "%s: can't execute %s: %s\n", Pn, OSLEVELPATH, strerror(errno)); Exit(1); } if ((sb.st_mode & (S_IROTH | S_IXOTH)) != (S_IROTH | S_IXOTH)) { (void) fprintf(stderr, "%s: can't execute %s, modes: %o\n", Pn, OSLEVELPATH, sb.st_mode); Exit(1); } /* * Open a pipe for receiving the version number from OSLEVEL. Fork a * child to run OSLEVEL. Retrieve the OSLEVEL output. */ if (pipe(p)) { (void) fprintf(stderr, "%s: can't create pipe to: %s\n", Pn, OSLEVELPATH); Exit(1); } if ((pid = fork()) == 0) { (void) close(1); (void) close(2); (void) close(p[0]); dup2(p[1], 1); dup2(p[1], 2); (void) close(p[1]); execl(OSLEVELPATH, OSLEVEL, NULL); _exit(0); } if (pid < 0) { (void) fprintf(stderr, "%s: can't fork a child for %s: %s\n", Pn, OSLEVELPATH, strerror(errno)); Exit(1); } (void) close(p[1]); br = read(p[0], buf, sizeof(buf) - 1); (void) close(p[0]); (void) wait(NULL); /* * Warn if the actual and expected versions don't match. */ if (br > 0) { buf[br] = '\0'; if ((cp = strrchr(buf, '\n'))) *cp = '\0'; } else (void) snpf(buf, sizeof(buf), "UNKNOWN"); # else /* AIXV>=5000 */ /* * Use uname() for AIX 5 and above. */ char buf[64]; struct utsname u; (void) memset((void *)&u, 0, sizeof(u)); (void) uname(&u); (void) snpf(buf, sizeof(buf) - 1, "%s.%s.0.0", u.version, u.release); buf[sizeof(buf) - 1] = '\0'; # endif /* AIXV<5000 */ if (!ev || strcmp(buf, ev)) (void) fprintf(stderr, "%s: WARNING: compiled for %s version %s; this is %s.\n", Pn, d, ev ? ev : "UNKNOWN", buf); #endif /* defined(HASKERNIDCK) */ } /* * 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 */ KA_T cdir, fp, pdir, rdir; char *cmd; int hl, i, nf, np; struct PROCINFO *p; short pss, sf; struct user us; #if AIXV>=4300 static struct FDSINFO *fds = (struct FDSINFO *)NULL; MALLOC_S msz; # if AIXA==1 pid32_t pid; /* Since we're operating with types defined * under _KERNEL (see machine.), but * getprocs64() expects application types * (where pid_t is 32 bits), the pid variable * must be cast in an application-compatible * manner. */ # else /* AIXA!=1 */ pid_t pid; # endif /* AIXA==1 */ # if AIXV==4330 static int trx = 0; unsigned int mxof; static int uo = 0; # endif /* AIXV==4330 */ #endif /* AIXV>=4300 */ /* * 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 AIXV<4300 if (!P) { if (!(P = (struct PROCINFO *)malloc((MALLOC_S)PROCSIZE))) { (void) fprintf(stderr, "%s: can't allocate space for 1 proc\n", Pn); Exit(1); } Np = 1; } while (((np = getproc(P, Np, PROCSIZE)) == -1) && errno == ENOSPC) { Np = P->p_pid + 10; if (!(P = (struct PROCINFO *)realloc((MALLOC_P *)P, (size_t)(Np * PROCSIZE)))) { (void) fprintf(stderr, "%s: no space for %d procinfo's\n", Pn, Np); Exit(1); } } #else /* AIXV>=4300 */ if (!P) { msz = (MALLOC_S)(PROCSIZE * PROCINFO_INCR); if (!(P = (struct PROCINFO *)malloc(msz))) { (void) fprintf(stderr, "%s: can't allocate space for %d procs\n", Pn, PROCINFO_INCR); Exit(1); } Np = PROCINFO_INCR; } np = pid = 0; p = P; while ((i = GETPROCS(p, PROCSIZE, (struct FDSINFO *)NULL, 0, &pid, PROCINFO_INCR)) == PROCINFO_INCR) { np += PROCINFO_INCR; if (np >= Np) { msz = (MALLOC_S)(PROCSIZE * (Np + PROCINFO_INCR)); if (!(P = (struct PROCINFO *)realloc((MALLOC_P *)P, msz))) { (void) fprintf(stderr, "%s: no more space for proc storage\n", Pn); Exit(1); } Np += PROCINFO_INCR; } p = (struct PROCINFO *)((char *)P + (np * PROCSIZE)); } if (i > 0) np += i; #endif /* AIXV<4300 */ /* * Loop through processes. */ for (p = P, Up = &us; np > 0; np--, p++) { if (p->p_stat == 0 || p->p_stat == SZOMB) continue; if (is_proc_excl(p->p_pid, (int)p->p_pgid, (UID_ARG)p->p_uid, &pss, &sf)) continue; #if AIXV<4300 /* * Get user structure for AIX < 4.3. * * If AIX version is below 4.1.1, use getuser(). * * If AIX version is 4.1.1 or above: if readx() is disabled (no -X * option, use getuser(); if readx() is enabled (-X), use readx(). */ # if AIXV>=4110 if (Fxopt && kreadx(Uo, (char *)Up, U_SIZE, (KA_T)p->pi_adspace) == 0) i = 1; else i = 0; if (i == 0) { if (getuser(p, PROCSIZE, Up, U_SIZE) != 0) continue; } hl = i; # else /* AIXV<4110 */ if (getuser(p, PROCSIZE, Up, U_SIZE) != 0) continue; hl = 1; # endif /* AIXV>=4110 */ /* * Save directory vnode addresses, command name address, and open file * count from user structure. * * Skip processes excluded by the user structure command name. */ cdir = (KA_T)Up->u_cdir; # if AIXV<4100 pdir = (KA_T)Up->u_pdir; # endif /* AIXV<4100 */ rdir = (KA_T)Up->u_rdir; cmd = Up->u_comm; nf = Up->u_maxofile; if (is_cmd_excl(cmd, &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; } #else /* AIXV>=4300 */ /* * For AIX 4.3 and above, skip processes excluded by the procsinfo * command name. Use getprocs() to get the file descriptors for * included processes. * * If readx is enabled (-X), use it to get the loader_anchor structure. */ if (is_cmd_excl(p->pi_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; } if (!fds) { if (!(fds = (struct FDSINFO *)malloc((MALLOC_S)FDSINFOSIZE))) { (void) fprintf(stderr, "%s: can't allocate fdsinfo struct for PID %d\n", Pn, pid); Exit(1); } } pid = p->p_pid; if (GETPROCS((struct PROCINFO *)NULL, PROCSIZE, fds, FDSINFOSIZE, &pid, 1) != 1) continue; hl = 0; # if AIXV==4330 /* * Handle readx() for AIX 4.3.3 specially, because 4.3.3 was released * with two different user struct definitions in and * their form affects using readx() to get the loader table pointers * from U_loader of the user structure (when -X is specified). */ if (Fxopt) { for (;;) { /* * Read the AIX 4.3.3 U_loader pointers. */ if (kreadx((KA_T)((char *)Uo + offsetof(struct user, U_loader) + uo), (char *)&Up->U_loader, sizeof(struct la), (KA_T)p->pi_adspace)) break; if (trx) { hl = 1; break; } /* * Until the correct size of the U_loader offset in lo has been * established, read U_maxofile and match it to pi_maxofile * from the PROCINFO structure. Try the offsets 0, 48, and * -48. Note: these offsets are heuristic attempts to adjust * to differences in the user struct as observed on two systems * whose header files differed. U_maxofile * follows U_loader by the same number of elements in both * user structs, so the U_loader offset should be the same as * the U_maxofile offset. */ if (!kreadx((KA_T)((char *)Uo + offsetof(struct user,U_maxofile) + uo), (char *)&mxof, sizeof(mxof), (KA_T)p->pi_adspace) && (mxof == p->pi_maxofile)) { hl = trx = 1; break; } if (uo == 0) uo = 48; else if (uo == 48) uo = -48; else { Fxopt = hl = 0; trx = 1; if (!Fwarn) { (void) fprintf(stderr, "%s: WARNING: user struct mismatch;", Pn); (void) fprintf(stderr, " -X option disabled.\n"); } break; } } } # else /* AIXV!=4330 */ if (Fxopt && kreadx((KA_T)((char *)Uo + offsetof(struct user, U_loader)), (char *)&Up->U_loader, sizeof(struct la), (KA_T)p->pi_adspace) == 0) hl = 1; # endif /* AIXV==4330 */ /* * Save directory vnode addresses, command name, and open file count * from procinfo structure. */ cdir = (KA_T)p->pi_cdir; pdir = (KA_T)NULL; rdir = (KA_T)p->pi_rdir; cmd = p->pi_comm; nf = p->pi_maxofile; #endif /* AIXV<4300 */ /* * Allocate a local process structure and start filling it. */ alloc_lproc(p->p_pid, (int)p->p_pgid, (int)p->p_ppid, (UID_ARG)p->p_uid, cmd, (int)pss, (int)sf); Plf = (struct lfile *)NULL; /* * Save current working directory information. */ if (!ckscko && cdir) { alloc_lfile(CWD, -1); process_node(cdir); if (Lf->sf) link_lfile(); } /* * Save root directory information. */ if (!ckscko && rdir) { alloc_lfile(RTD, -1); process_node(rdir); if (Lf->sf) link_lfile(); } #if AIXV<4100 /* * Save parent directory information. */ if (!ckscko && pdir) { alloc_lfile(" pd", -1); process_node(pdir); if (Lf->sf) link_lfile(); } #endif /* AIXV<4100 */ /* * Save information on text files. */ if (!ckscko && hl) { #if AIXA<2 # if AIXA<1 process_text((KA_T)p->pi_adspace); # else /* AIXA==1 */ { int ck = 1; KA_T sid = (KA_T)p->pi_adspace; if ((Up->U_loader[0] & URDXMASK) || (Up->U_loader[1] & URDXMASK)) { /* * If the upper part of either loader map address is * non-zero and this is not the lsof process, skip the * processing of text files. If this is the lsof process, * set the segment address to zero, forcing text file * information to come from kmem rather than mem. */ if (Mypid == p->p_pid) sid = (KA_T)0; else ck = 0; } if (ck) process_text(sid); } # endif /* AIXA<1 */ #else /* AIXA>=2 */ process_text(p->p_pid); #endif /* AIXA<2 */ } /* * Save information on file descriptors. */ for (i = 0; i < nf; i++) { #if AIXV<4300 fp = (KA_T)Up->u_ufd[i].fp; #else /* AIXV>=4300 */ fp = (KA_T)fds->pi_ufd[i].fp; #endif /* AIXV<4300 */ if (fp) { alloc_lfile((char *)NULL, i); process_file(fp); if (Lf->sf) { #if defined(HASFSTRUCT) if (Fsv & FSV_FG) # if AIXV<4300 Lf->pof = (long)(Up->u_ufd[i].flags & 0x7f); #else /* AIXV>=4300 */ Lf->pof = (long)(fds->pi_ufd[i].flags & 0x7f); #endif /* AIXV<4300 */ #endif /* defined(HASFSTRUCT) */ link_lfile(); } } } /* * Examine results. */ if (examine_lproc()) return; } } /* * get_kernel_access() - get access to kernel memory */ static void get_kernel_access() { int oe = 0; #if defined(AIX_KERNBITS) int kb; char *kbb, *kbr; /* * Check the kernel bit size against the size for which this executable was * configured. */ if (__KERNEL_32()) { kb = 32; kbr = "32"; } else if (__KERNEL_64()) { kb = 64; kbr = "64"; } else { kb = 0; kbr = "unknown"; } if ((AIX_KERNBITS == 0) || !kb || (kb != AIX_KERNBITS)) { if (AIX_KERNBITS == 32) kbb = "32"; else if (AIX_KERNBITS == 64) kbb = "64"; else kbb = "unknown"; (void) fprintf(stderr, "%s: FATAL: compiled for a kernel of %s bit size.\n", Pn, kbb); (void) fprintf(stderr, " The bit size of this kernel is %s.\n", kbr); Exit(1); } #endif /* defined(AIX_KERNBITS) */ /* * Access /dev/mem. */ if ((Km = open("/dev/mem", O_RDONLY, 0)) < 0) { (void) fprintf(stderr, "%s: can't open /dev/mem: %s\n", Pn, strerror(errno)); oe++; } #if defined(WILLDROPGID) /* * If kernel memory isn't coming from KMEM, drop setgid permission * before attempting to open the (Memory) file. */ if (Memory) (void) dropgid(); #else /* !defined(WILLDROPGID) */ /* * See if the non-KMEM memory file is readable. */ if (Memory && !is_readable(Memory, 1)) Exit(1); #endif /* defined(WILLDROPGID) */ /* * Access kernel memory file. */ if ((Kd = open(Memory ? Memory : KMEM, O_RDONLY, 0)) < 0) { (void) fprintf(stderr, "%s: can't open %s: %s\n", Pn, Memory ? Memory : KMEM, strerror(errno)); oe++; } if (oe) Exit(1); #if defined(WILLDROPGID) /* * Drop setgid permission, if necessary. */ if (!Memory) (void) dropgid(); #endif /* defined(WILLDROPGID) */ /* * Get kernel symbols. */ if (knlist(Nl, X_NL_NUM, sizeof(struct nlist)) || !Nl[X_UADDR].n_value) { (void) fprintf(stderr, "%s: can't get kernel's %s address\n", Pn, Nl[X_UADDR].n_name); Exit(1); } #if defined(HAS_AFS) (void) knlist(AFSnl, X_AFSNL_NUM, sizeof(struct nlist)); #endif /* defined(HAS_AFS) */ #if AIXV>=4110 /* * Get user area and shared library VM offsets for AIX 4.1.1 and above. */ if (Fxopt) { struct ublock *ub; # if AIXA<2 struct nlist ll[] = { { "library_anchor" }, # if AIXV>=4330 { "library_le_handle" }, # else /* AIXV<4330 */ { "library_data_handle" }, # endif /* AIXV>=4330 */ { (char *)NULL } }; if (nlist(N_UNIX, ll) == 0 && ll[0].n_value != (long)0 && ll[1].n_value != (long)0 && kreadx((KA_T)(ll[1].n_value & RDXMASK), (char *)&Soff, sizeof(Soff), (KA_T)0) == 0) Soff_stat++; # endif /* AIXA<2 */ ub = (struct ublock *)Nl[X_UADDR].n_value; Uo = (KA_T)((KA_T)&ub->ub_user & RDXMASK); } #endif /* AIXV>=4110 */ /* * Check the kernel version number. */ (void) ckkv("AIX", (char *)NULL, LSOF_VSTR, (char *)NULL); #if defined(SIGDANGER) /* * If SIGDANGER is defined, enable its handler. */ (void) signal(SIGDANGER, lowpgsp); #endif /* defined(SIGDANGER) */ } #if AIXA<2 /* * getle() - get loader entry structure */ static struct le * getle(a, sid, err) KA_T a; /* loader entry kernel address */ KA_T sid; /* user structure segment ID */ char **err; /* error message (if return is NULL) */ { static struct le le; #if AIXV<4110 if (a < Nl[X_UADDR].n_value) { *err = "address too small"; return((struct le *)NULL); } if (((char *)a + sizeof(le)) <= ((char *)Nl[X_UADDR].n_value + U_SIZE)) return((struct le *)((char *)Up + a - Nl[X_UADDR].n_value)); #endif /* AIXV<4110 */ if (!Fxopt) { *err = "readx() disabled for Stale Segment ID bug (see -X)"; return((struct le *)NULL); } #if AIXV>=4110 if (!sid) { if (!kread(a, (char *)&le, sizeof(le))) return(&le); } else { if (!kreadx((KA_T)(a & RDXMASK),(char *)&le,sizeof(le),(KA_T)sid)) return(&le); } #else /* AIXV<4110 */ if (!kreadx((KA_T)a, (char *)&le, sizeof(le), (KA_T)sid)) return(&le); #endif /* AIXV>=4110 */ getle_err: *err = "can't readx()"; return((struct le *)NULL); } #endif /* AIXA<2 */ #if AIXV>=4110 /* * getlenm() - get loader entry file name for AIX >= 4.1.1 */ static void getlenm(le, sid) struct le *le; /* loader entry structure */ KA_T sid; /* segment ID */ { char buf[LIBNMLN]; int i; # if AIXV<4300 if ((le->ft & LIBMASK) != LIBNMCOMP) return; #else /* AIXV>=4300 */ # if AIXA<2 if (!sid) { if (kread((KA_T)le->nm, buf, LIBNMLN)) return; } else { if (!Soff_stat || !le->nm || kreadx((KA_T)le->nm & (KA_T)RDXMASK, buf, LIBNMLN, (KA_T)Soff)) return; } buf[LIBNMLN - 1] = '\0'; i = strlen(buf); if (i < (LIBNMLN - 3) && buf[i+1]) enter_nm(&buf[i+1]); else if (buf[0]) enter_nm(buf); # else /* AIXA>=2 */ if (!le->nm || kread(le->nm, buf, sizeof(buf))) return; buf[LIBNMLN - 1] = '\0'; if (!strlen(buf)) return; enter_nm(buf); # endif /* AIXA<2 */ #endif /* AIXV<4300 */ } #endif /* AIXV>=4110 */ #if AIXA>1 /* * getsoinfo() - get *.so information for ia64 AIX >= 5 */ static void getsoinfo() { char buf[65536]; uint bufsz = (uint) sizeof(buf); int ct, h; char *ln = (char *)NULL; char *rn = (char *)NULL; LDR_Mod_info_t *lp; struct stat sb; so_hash_t *sp; /* * See if loader information is needed. Warn if this process has insufficient * permission to acquire it from all processes. */ if (!Fxopt) return; if ((Myuid != 0) && !Setuidroot && !Fwarn) { (void) fprintf(stderr, "%s: WARNING: insufficient permission to access all", Pn); (void) fprintf(stderr, " /%s/object sub-\n", HASPROCFS); (void) fprintf(stderr, " directories; some loader information may", Pn); (void) fprintf(stderr, " be unavailable.\n"); } /* * Get the loader module table. Allocate hash space for it. */ if ((ct = ldr_get_modules(SOL_GLOBAL, (void *)buf, &bufsz)) < 1) return; if (!(SoHash = (so_hash_t **)calloc((MALLOC_S)SOHASHBUCKS, sizeof(so_hash_t *)))) { (void) fprintf(stderr, "%s: no space for *.so hash buckets\n", Pn); Exit(1); } /* * Cache the loader module information, complete with stat(2) results. */ for (lp = (LDR_Mod_info_t *)buf; ct; ct--, lp++) { /* * Release previous name space allocations. */ if (ln) { (void) free((MALLOC_P *)ln); ln = (char *)NULL; } if (rn) { (void) free((MALLOC_P *)rn); rn = (char *)NULL; } /* * Make a copy of the loader module name. */ if (!(rn = mkstrcpy(lp->mi_name, (MALLOC_S *)NULL))) { (void) fprintf(stderr, "%s: no space for name: %s\n", Pn, lp->mi_name); Exit(1); } /* * Resolve symbolic links. */ ln = Readlink(rn); if (ln == rn) rn = (char *)NULL; /* * Get stat(2) information. */ if (statsafely(ln, &sb)) { if (!Fwarn) (void) fprintf(stderr, "%s: WARNING: can't stat: %s\n", Pn, ln); continue; } /* * Allocate and fill a loader information hash structure. */ if (!(sp = (so_hash_t *)malloc((MALLOC_S)sizeof(so_hash_t)))) { (void) fprintf(stderr, "%s: no space for *.so hash entry: %s\n", Pn, ln); Exit(1); } sp->dev = sb.st_dev; sp->nlink = (int)sb.st_nlink; sp->nm = ln; ln = (char *)NULL; sp->node = (INODETYPE)sb.st_ino; sp->sz = (SZOFFTYPE)sb.st_size; /* * Link the structure to the appropriate hash bucket. */ h = SOHASH(sb.st_dev, (INODETYPE)sb.st_ino); if (SoHash[h]) sp->next = SoHash[h]; else sp->next = (so_hash_t *)NULL; SoHash[h] = sp; } /* * Free any unused name space that was allocated. */ if (ln) (void) free((MALLOC_P *)ln); if (rn) (void) free((MALLOC_P *)rn); } #endif /* AIXA>1 */ /* * initialize() - perform all initialization */ void initialize() { get_kernel_access(); #if AIXA>1 (void) getsoinfo(); #endif /* AIXA>1 */ } /* * 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 AIXV<4200 if (lseek(Kd, (off_t)addr, L_SET) == (off_t)-1) #else /* AIXV>=4200 */ if (lseek64(Kd, (off64_t)addr, L_SET) == (off64_t)-1) #endif /* AIXV<4200 */ return(1); br = read(Kd, buf, len); return((br == len) ? 0 : 1); } /* * kreadx() - read kernel segmented memory */ int kreadx(addr, buf, len, sid) KA_T addr; /* kernel address */ char *buf; /* destination buffer */ int len; /* length */ KA_T sid; /* segment ID */ { int br; #if AIXV<4200 if (lseek(Km, addr, L_SET) == (off_t)-1) #else /* AIXV>=4200 */ if (lseek64(Km, (off64_t)addr, L_SET) == (off64_t)-1) #endif /* AIXV<4200 */ return(1); br = readx(Km, buf, len, sid); return (br == len ? 0 : 1); } #if defined(SIGDANGER) /* * lowpgsp() - hangle a SIGDANGER signal about low paging space */ # if defined(HASINTSIGNAL) static int # else /* !defined(HASINTSIGNAL) */ static void # endif /* defined(HASINTSIGNAL) */ lowpgsp(sig) int sig; { (void) fprintf(stderr, "%s: FATAL: system paging space is low.\n", Pn); Exit(1); } #endif /* defined(SIGDANGER) */ #if AIXA<2 /* * process_text() - process text file information for non-ia64 AIX */ static void process_text(sid) KA_T sid; /* user area segment ID */ { char *err, fd[8]; static struct file **f = (struct file **)NULL; int i, j, n; struct le *le; KA_T ll; MALLOC_S msz; static MALLOC_S nf = 0; struct file *xf = (struct file *)NULL; #if AIXV>=4300 struct la *la = (struct la *)&Up->U_loader; #endif /* AIXV>=4300 */ /* * Display information on the exec'd entry. */ #if AIXV<4300 if ((ll = (KA_T)Up->u_loader[1])) #else /* AIXV>=4300 */ if ((ll = (KA_T)la->exec)) #endif /* AIXV<4300 */ { alloc_lfile(" txt", -1); if ((le = getle(ll, sid, &err))) { if ((xf = le->fp)) { process_file((KA_T)xf); if (Lf->sf) { #if AIXV>=4110 && AIXV<4300 if (!Lf->nm || !Lf->nm[0]) getlenm(le, sid); #endif /* AIXV>=4110 && AIXV<4300 */ link_lfile(); } } } else { (void) snpf(Namech, Namechl, "text entry at %s: %s", print_kptr((KA_T)ll, (char *)NULL, 0), err); enter_nm(Namech); if (Lf->sf) link_lfile(); } } /* * Display the loader list. */ for (i = n = 0, #if AIXV<4300 ll = (KA_T)Up->u_loader[0]; #else /* AIXV>=4300 */ ll = (KA_T)la->list; #endif /* AIXV<4300 */ ll; ll = (KA_T)le->next) { (void) snpf(fd, sizeof(fd), " L%02d", i); alloc_lfile(fd, -1); if (!(le = getle(ll, sid, &err))) { (void) snpf(Namech, Namechl, "loader entry at %s: %s", print_kptr((KA_T)ll, (char *)NULL, 0), err); enter_nm(Namech); if (Lf->sf) link_lfile(); return; } /* * Skip entries with no file pointers, the exec'd file, and entries * that have already been processed. */ if (!le->fp || (le->fp == xf)) continue; for (j = 0; j < n; j++) { if (f[j] == le->fp) break; } if (j < n) continue; if (n >= nf) { /* * Allocate file structure address cache space. */ nf += 10; msz = (MALLOC_S)(nf * sizeof(struct file *)); if (f) f = (struct file **)realloc((MALLOC_P *)f, msz); else f = (struct file **)malloc(msz); if (!f) { (void) fprintf(stderr, "%s: no space for text file pointers\n", Pn); Exit(1); } } f[n++] = le->fp; /* * Save the loader entry. */ process_file((KA_T)le->fp); if (Lf->sf) { #if AIXV>=4110 if (!Lf->nm || !Lf->nm[0]) getlenm(le, sid); #endif /* AIXV>=4110 */ link_lfile(); i++; } } } #else /* AIXA>=2 */ /* * process_text() - process text file information for ia64 AIX >= 5 */ static void process_text(pid) pid_t pid; /* process PID */ { char buf[MAXPATHLEN+1], fd[8], *nm, *pp; size_t bufl = sizeof(buf); DIR *dfp; struct dirent *dp; int i; struct la *la = (struct la *)&Up->U_loader; struct le le; struct lfile *lf; struct stat sb; so_hash_t *sp; size_t sz; dev_t xdev; INODETYPE xnode; int xs = 0; /* * Display information on the exec'd entry. */ if (la->exec && !kread((KA_T)la->exec, (char *)&le, sizeof(le)) && le.fp) { alloc_lfile(" txt", -1); process_file((KA_T)le.fp); if (Lf->dev_def && (Lf->inp_ty == 1)) { xdev = Lf->dev; xnode = Lf->inode; xs = 1; } if (Lf->sf) { if (!Lf->nm || !Lf->nm[0]) getlenm(&le, (KA_T)0); link_lfile(); } } /* * Collect devices and names for the entries in /HASPROCFS/PID/object -- the * AIX 5 loader list equivalent. When things fail in this processing -- most * likely for insufficient permissions -- be silent; a warning was issued by * getsoinfo(). */ (void) snpf(buf, bufl, "/%s/%ld/object", HASPROCFS, (long)pid); if (!(dfp = opendir(buf))) return; if ((sz = strlen(buf)) >= bufl) return; buf[sz++] = '/'; pp = &buf[sz]; sz = bufl - sz; /* * Read the entries in the /HASPROCFS/PID/object subdirectory. */ for (dp = readdir(dfp), i = 0; dp; dp = readdir(dfp)) { /* * Skip '.', "..", entries with no node number, and entries whose * names are too long. */ if (!dp->d_ino || (dp->d_name[0] == '.')) continue; if ((dp->d_namlen + 1) >= sz) continue; (void) strncpy(pp, dp->d_name, dp->d_namlen); pp[dp->d_namlen] = '\0'; /* * Get stat(2) information. */ if (statsafely(buf, &sb)) continue; /* * Ignore the exec'd and non-regular files. */ if (xs && (xdev == sb.st_dev) && (xnode == (INODETYPE)sb.st_ino)) continue; if (!S_ISREG(sb.st_mode)) continue; /* * Allocate space for a file entry. Set its basic characteristics. */ (void) snpf(fd, sizeof(fd), "L%02d", i++); alloc_lfile(fd, -1); Lf->dev_def = Lf->inp_ty = Lf->nlink_def = Lf->sz_def = 1; Lf->dev = sb.st_dev; Lf->inode = (INODETYPE)sb.st_ino; (void) snpf(Lf->type, sizeof(Lf->type), "VREG"); /* * Look for a match on device and node numbers in the *.so cache. */ for (sp = SoHash[SOHASH(sb.st_dev, (INODETYPE)sb.st_ino)]; sp; sp = sp->next) { if ((sp->dev == sb.st_dev) && (sp->node == (INODETYPE)sb.st_ino)) { /* * A match was found; use its name, link count, and size. */ nm = sp->nm; Lf->nlink = sp->nlink; Lf->sz = sp->sz; break; } } if (!sp) { /* * No match was found; use the /HASPROCFS/object name, its link * count, and its size. */ nm = pp; Lf->nlink_def = sb.st_nlink; Lf->sz = sb.st_size; } /* * Do selection tests: NFS; link count; file name; and file system. */ # if defined(HAS_NFS) if (Fnfs && (GET_MIN_DEV(Lf->dev_def) & SDEV_REMOTE)) Lf->sf |= SELNFS; # endif /* defined(HAS_NFS) */ if (Nlink && (Lf->nlink < Nlink)) Lf->sf |= SELNLINK; if (Sfile && is_file_named(NULL, VREG, 0, 0)) Lf->sf |= SELNM; if (Lf->sf) { /* * If the file was selected, enter its name and link it to the * other files of the process. */ enter_nm(nm); link_lfile(); } } (void) closedir(dfp); } #endif /* AIXA<2 */