blob: c95171734614e4c0cbd2ebcf9b1d3a71b6595ed5 [file] [log] [blame]
/*
* 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 <sys/ldr.h>
#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<OPEN_MAX
#undef FDSINFOSIZE /* (We weren't lucky.) */
#define FDSELEMSIZE (sizeof(struct FDSINFO)/OPEN_SHRT_MAX)
#define FDSINFOSIZE (OPEN_MAX * FDSELEMSIZE)
# endif /* OPEN_SHRT_MAX<OPEN_MAX */
#endif /* defined(OPEN_SHRT_MAX) */
#if AIXV>=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 <sys/user.h> 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 <sys/user.h> 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 */