/* * 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); }