blob: 6b1bb514256e35277d4d7cb3890d9e63afa3714a [file] [log] [blame]
/*
* dnode.c - Linux node functions for /proc-based lsof
*/
/*
* Copyright 1997 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 1997 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: dnode.c,v 1.21 2011/08/07 22:53:13 abe Exp $";
#endif
#include "lsof.h"
/*
* Local definitions
*/
#define OFFSET_MAX ((off_t)0x7fffffff) /* this is defined in
* .../src/fs/locks.c and not
* in a header file */
#define PIDBUCKS 64 /* PID hash buckets */
#define HASHPID(pid) (((int)((pid * 31415) >> 3)) & (PIDBUCKS - 1))
/*
* Local structure definitions
*/
struct llock {
int pid;
dev_t dev;
INODETYPE inode;
char type;
struct llock *next;
};
/*
* Local definitions
*/
struct llock **LckH = (struct llock **)NULL; /* PID-hashed locks */
/*
* Local function prototypes
*/
_PROTOTYPE(static void check_lock,(void));
/*
* check_lock() - check lock for file *Lf, process *Lp
*/
static void
check_lock()
{
int h;
struct llock *lp;
h = HASHPID(Lp->pid);
for (lp = LckH[h]; lp; lp = lp->next) {
if (Lp->pid == lp->pid
&& Lf->dev == lp->dev
&& Lf->inode == lp->inode)
{
Lf->lock = lp->type;
return;
}
}
}
/*
* get_fields() - separate a line into fields
*/
int
get_fields(ln, sep, fr, eb, en)
char *ln; /* input line */
char *sep; /* separator list */
char ***fr; /* field pointer return address */
int *eb; /* indexes of fields where blank or an
* entry from the separator list may be
* embedded and are not separators
* (may be NULL) */
int en; /* number of entries in eb[] (may be
* zero) */
{
char *bp, *cp, *sp;
int i, j, n;
MALLOC_S len;
static char **fp = (char **)NULL;
static int nfpa = 0;
for (cp = ln, n = 0; cp && *cp;) {
for (bp = cp; *bp && (*bp == ' ' || *bp == '\t'); bp++);
;
if (!*bp || *bp == '\n')
break;
for (cp = bp; *cp; cp++) {
if (*cp == '\n') {
*cp = '\0';
break;
}
if (*cp == '\t') /* TAB is always a separator */
break;
if (*cp == ' ') {
/*
* See if this field may have an embedded space.
*/
if (!eb || !en)
break;
else {
for (i = j = 0; i < en; i++) {
if (eb[i] == n) {
j = 1;
break;
}
}
if (!j)
break;
}
}
if (sep) {
/*
* See if the character is in the separator list.
*/
for (sp = sep; *sp; sp++) {
if (*sp == *cp)
break;
}
if (*sp) {
/*
* See if this field may have an embedded separator.
*/
if (!eb || !en)
break;
else {
for (i = j = 0; i < en; i++) {
if (eb[i] == n) {
j = 1;
break;
}
}
if (!j)
break;
}
}
}
}
if (*cp)
*cp++ = '\0';
if (n >= nfpa) {
nfpa += 32;
len = (MALLOC_S)(nfpa * sizeof(char *));
if (fp)
fp = (char **)realloc((MALLOC_P *)fp, len);
else
fp = (char **)malloc(len);
if (!fp) {
(void) fprintf(stderr,
"%s: can't allocate %d bytes for field pointers.\n",
Pn, (int)len);
Exit(1);
}
}
fp[n++] = bp;
}
*fr = fp;
return(n);
}
/*
* get_locks() - get lock information from /proc/locks
*/
void
get_locks(p)
char *p; /* /proc lock path */
{
unsigned long bp, ep;
char buf[MAXPATHLEN], *ec, **fp;
dev_t dev;
int ex, i, h, mode, pid;
INODETYPE inode;
struct llock *lp, *np;
FILE *ls;
long maj, min;
char type;
static char *vbuf = (char *)NULL;
static size_t vsz = (size_t)0;
/*
* Destroy previous lock information.
*/
if (LckH) {
for (i = 0; i < PIDBUCKS; i++) {
for (lp = LckH[i]; lp; lp = np) {
np = lp->next;
(void) free((FREE_P *)lp);
}
LckH[i] = (struct llock *)NULL;
}
} else {
/*
* If first time, allocate the lock PID hash buckets.
*/
LckH = (struct llock **)calloc((MALLOC_S)PIDBUCKS,
sizeof(struct llock *));
if (!LckH) {
(void) fprintf(stderr,
"%s: can't allocate %d lock hash bytes\n",
Pn, (int)(sizeof(struct llock *) * PIDBUCKS));
Exit(1);
}
}
/*
* Open the /proc lock file, assign a page size buffer to its stream,
* and read it.
*/
if (!(ls = open_proc_stream(p, "r", &vbuf, &vsz, 0)))
return;
while (fgets(buf, sizeof(buf), ls)) {
if (get_fields(buf, ":", &fp, (int *)NULL, 0) < 10)
continue;
if (!fp[1] || strcmp(fp[1], "->") == 0)
continue;
/*
* Get lock type.
*/
if (!fp[3])
continue;
if (*fp[3] == 'R')
mode = 0;
else if (*fp[3] == 'W')
mode = 1;
else
continue;
/*
* Get PID.
*/
if (!fp[4] || !*fp[4])
continue;
pid = atoi(fp[4]);
/*
* Get device number.
*/
ec = (char *)NULL;
if (!fp[5] || !*fp[5]
|| (maj = strtol(fp[5], &ec, 16)) == LONG_MIN || maj == LONG_MAX
|| !ec || *ec)
continue;
ec = (char *)NULL;
if (!fp[6] || !*fp[6]
|| (min = strtol(fp[6], &ec, 16)) == LONG_MIN || min == LONG_MAX
|| !ec || *ec)
continue;
dev = (dev_t)makedev((int)maj, (int)min);
/*
* Get inode number.
*/
ec = (char *)NULL;
if (!fp[7] || !*fp[7]
|| (inode = strtoull(fp[7], &ec, 0)) == ULONG_MAX
|| !ec || *ec)
continue;
/*
* Get lock extent. Convert it and the lock type to a lock character.
*/
if (!fp[8] || !*fp[8] || !fp[9] || !*fp[9])
continue;
ec = (char *)NULL;
if ((bp = strtoul(fp[8], &ec, 0)) == ULONG_MAX || !ec || *ec)
continue;
if (!strcmp(fp[9], "EOF")) /* for Linux 2.4.x */
ep = OFFSET_MAX;
else {
ec = (char *)NULL;
if ((ep = strtoul(fp[9], &ec, 0)) == ULONG_MAX || !ec || *ec)
continue;
}
ex = ((off_t)bp == (off_t)0 && (off_t)ep == OFFSET_MAX) ? 1 : 0;
if (mode)
type = ex ? 'W' : 'w';
else
type = ex ? 'R' : 'r';
/*
* Look for this lock via the hash buckets.
*/
h = HASHPID(pid);
for (lp = LckH[h]; lp; lp = lp->next) {
if (lp->pid == pid
&& lp->dev == dev
&& lp->inode == inode
&& lp->type == type)
break;
}
if (lp)
continue;
/*
* Allocate a new llock structure and link it to the PID hash bucket.
*/
if (!(lp = (struct llock *)malloc(sizeof(struct llock)))) {
(void) snpf(buf, sizeof(buf), InodeFmt_d, inode);
(void) fprintf(stderr,
"%s: can't allocate llock: PID %d; dev %x; inode %s\n",
Pn, pid, (int)dev, buf);
Exit(1);
}
lp->pid = pid;
lp->dev = dev;
lp->inode = inode;
lp->type = type;
lp->next = LckH[h];
LckH[h] = lp;
}
(void) fclose(ls);
}
/*
* process_proc_node() - process file node
*/
void
process_proc_node(p, s, ss, l, ls)
char *p; /* node's readlink() path */
struct stat *s; /* stat() result for path */
int ss; /* *s status -- i.e., SB_* values */
struct stat *l; /* lstat() result for FD (NULL for
* others) */
int ls; /* *l status -- i.e., SB_* values */
{
mode_t access;
mode_t type = 0;
char *cp;
struct mounts *mp = (struct mounts *)NULL;
size_t sz;
char *tn;
/*
* Set the access mode, if possible.
*/
if (l && (ls & SB_MODE) && ((l->st_mode & S_IFMT) == S_IFLNK)) {
if ((access = l->st_mode & (S_IRUSR | S_IWUSR)) == S_IRUSR)
Lf->access = 'r';
else if (access == S_IWUSR)
Lf->access = 'w';
else
Lf->access = 'u';
}
/*
* Determine node type.
*/
if (ss & SB_MODE) {
type = s->st_mode & S_IFMT;
switch (type) {
case S_IFBLK:
Ntype = N_BLK;
break;
case S_IFCHR:
Ntype = N_CHR;
break;
case S_IFIFO:
Ntype = N_FIFO;
break;
case S_IFSOCK:
process_proc_sock(p, s, ss, l, ls);
return;
}
}
if (Selinet)
return;
/*
* Save the device. If it is an NFS device, change the node type to N_NFS.
*/
if (ss & SB_DEV) {
Lf->dev = s->st_dev;
Lf->dev_def = 1;
}
if ((Ntype == N_CHR || Ntype == N_BLK)) {
if (ss & SB_RDEV) {
Lf->rdev = s->st_rdev;
Lf->rdev_def = 1;
}
}
if (Ntype == N_REGLR && (HasNFS == 2)) {
for (mp = readmnt(); mp; mp = mp->next) {
if ((mp->ty == N_NFS)
&& (mp->ds & SB_DEV) && (Lf->dev == mp->dev)
) {
Ntype = N_NFS;
break;
}
}
}
/*
* Save the inode number.
*/
if (ss & SB_INO) {
Lf->inode = (INODETYPE)s->st_ino;
Lf->inp_ty = 1;
}
/*
* Check for a lock.
*/
if (Lf->dev_def && (Lf->inp_ty == 1))
(void) check_lock();
/*
* Save the file size.
*/
switch (Ntype) {
case N_BLK:
case N_CHR:
case N_FIFO:
if (!Fsize && l && (ls & SB_SIZE) && OffType) {
Lf->off = (SZOFFTYPE)l->st_size;
Lf->off_def = 1;
}
break;
default:
if (Foffset) {
if (l && (ls & SB_SIZE) && OffType) {
Lf->off = (SZOFFTYPE)l->st_size;
Lf->off_def = 1;
}
} else if (!Foffset || Fsize) {
if (ss & SB_SIZE) {
Lf->sz = (SZOFFTYPE)s->st_size;
Lf->sz_def = 1;
}
}
}
/*
* Record the link count.
*/
if (Fnlink && (ss & SB_NLINK)) {
Lf->nlink = (long)s->st_nlink;
Lf->nlink_def = 1;
if (Nlink && (Lf->nlink < Nlink))
Lf->sf |= SELNLINK;
}
/*
* Format the type name.
*/
if (ss & SB_MODE) {
switch (type) {
case S_IFBLK:
tn = "BLK";
break;
case S_IFCHR:
tn = "CHR";
break;
case S_IFDIR:
tn = "DIR";
break;
case S_IFIFO:
tn = "FIFO";
break;
case S_IFREG:
tn = "REG";
break;
case S_IFLNK:
tn = "LINK";
break;
case S_ISVTX:
tn = "VTXT";
break;
default:
(void) snpf(Lf->type, sizeof(Lf->type), "%04o",
((type >> 12) & 0xf));
tn = (char *)NULL;
}
} else
tn = "unknown";
if (tn)
(void) snpf(Lf->type, sizeof(Lf->type), "%s", tn);
Lf->ntype = Ntype;
/*
* Record an NFS file selection.
*/
if (Ntype == N_NFS && Fnfs)
Lf->sf |= SELNFS;
/*
* Test for specified file.
*/
if (Sfile
&& is_file_named((char *)NULL,
((type == S_IFCHR) || (type == S_IFBLK)) ? 1 : 0))
Lf->sf |= SELNM;
/*
* If no NAME information has been stored, store the path.
*
* Store the remote host and mount point for an NFS file.
*/
if (!Namech[0]) {
(void) snpf(Namech, Namechl, "%s", p);
if ((Ntype == N_NFS) && mp && mp->fsname) {
cp = endnm(&sz);
(void) snpf(cp, sz, " (%s)", mp->fsname);
}
}
if (Namech[0])
enter_nm(Namech);
}