diff options
Diffstat (limited to 'dialects/linux/dnode.c')
-rw-r--r-- | dialects/linux/dnode.c | 549 |
1 files changed, 549 insertions, 0 deletions
diff --git a/dialects/linux/dnode.c b/dialects/linux/dnode.c new file mode 100644 index 0000000..8a9bc73 --- /dev/null +++ b/dialects/linux/dnode.c @@ -0,0 +1,549 @@ +/* + * 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.23 2013/01/02 17:02:36 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, pbr, s, ss, l, ls) + char *p; /* node's readlink() path */ + char *pbr; /* node's path before readlink() */ + 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: + Lf->ntype = Ntype = N_BLK; + break; + case S_IFCHR: + Lf->ntype = Ntype = N_CHR; + break; + case S_IFIFO: + Lf->ntype = Ntype = N_FIFO; + break; + case S_IFSOCK: + /* Lf->ntype = Ntype = N_REGLR; by alloc_lfile() */ + process_proc_sock(p, pbr, s, ss, l, ls); + return; + case 0: + if (!strcmp(p, "anon_inode")) + Lf->ntype = Ntype = N_ANON_INODE; + break; + } + } + 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_def && (Lf->dev == mp->dev) + && (mp->dir && mp->dirl + && !strncmp(mp->dir, p, mp->dirl)) + ) { + Lf->ntype = 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: + if (Ntype == N_ANON_INODE) + tn = "a_inode"; + else { + (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); +/* + * Record an NFS file selection. + */ + if (Ntype == N_NFS && Fnfs) + Lf->sf |= SELNFS; +/* + * Test for specified file. + */ + if (Sfile + && is_file_named(1, p, mp, + ((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); +} |