diff options
author | Patrick McCarty <patrick.mccarty@linux.intel.com> | 2013-02-08 13:26:27 -0800 |
---|---|---|
committer | Patrick McCarty <patrick.mccarty@linux.intel.com> | 2013-02-08 13:26:27 -0800 |
commit | 9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7 (patch) | |
tree | 881eebfa461e4f8aa6b6f44b96ac0decd3bc887a /dialects | |
download | lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.tar.gz lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.tar.bz2 lsof-9bb81f8a90ecc8b70c955bff72ec59dd3d9e5ae7.zip |
Imported Upstream version 4.87upstream/4.87
Diffstat (limited to 'dialects')
-rw-r--r-- | dialects/linux/Makefile | 157 | ||||
-rwxr-xr-x | dialects/linux/Mksrc | 25 | ||||
-rw-r--r-- | dialects/linux/dfile.c | 387 | ||||
-rw-r--r-- | dialects/linux/dlsof.h | 179 | ||||
-rw-r--r-- | dialects/linux/dmnt.c | 698 | ||||
-rw-r--r-- | dialects/linux/dnode.c | 549 | ||||
-rw-r--r-- | dialects/linux/dproc.c | 1563 | ||||
-rw-r--r-- | dialects/linux/dproto.h | 51 | ||||
-rw-r--r-- | dialects/linux/dsock.c | 3946 | ||||
-rw-r--r-- | dialects/linux/dstore.c | 114 | ||||
-rw-r--r-- | dialects/linux/machine.h | 644 |
11 files changed, 8313 insertions, 0 deletions
diff --git a/dialects/linux/Makefile b/dialects/linux/Makefile new file mode 100644 index 0000000..2bea108 --- /dev/null +++ b/dialects/linux/Makefile @@ -0,0 +1,157 @@ + +# Linux /proc-based Makefile +# +# $Id: Makefile,v 1.11 2008/04/15 13:30:01 abe Exp $ + +PROG= lsof + +BIN= ${DESTDIR} + +DOC= ${DESTDIR} + +I=/usr/include +S=/usr/include/sys +L=/usr/include/local +P= + +CDEF= +CDEFS= ${CDEF} ${CFGF} +DEP= ${CFGD} ${CFGDN} +INCL= ${DINC} +CFLAGS= ${CDEFS} ${INCL} ${DEP} ${DEBUG} + +GRP= + +HDR= lsof.h lsof_fields.h dlsof.h machine.h proto.h dproto.h + +SRC= dfile.c dmnt.c dnode.c dproc.c dsock.c dstore.c \ + arg.c main.c misc.c node.c print.c proc.c store.c usage.c \ + util.c + +OBJ= dfile.o dmnt.o dnode.o dproc.o dsock.o dstore.o \ + arg.o main.o misc.o node.o print.o proc.o store.o usage.o \ + util.o + +MAN= lsof.8 + +OTHER= + +SHELL= /bin/sh + +SOURCE= Makefile ${OTHER} ${MAN} ${HDR} ${SRC} + +all: ${PROG} + +${PROG}: ${P} ${LIB} ${OBJ} + ${CC} -o $@ ${OBJ} ${CFGL} + +clean: FRC + rm -f Makefile.bak ${PROG} a.out core errs lint.out tags *.o version.h ${CFGDN} + rm -f machine.h.old new_machine.h + (cd lib; ${MAKE} -f Makefile.skel clean) + +install: all FRC + @echo '' + @echo 'Please write your own install rule. Lsof should be installed' + @echo 'setuid to root if you wish any lsof user to be able to examine' + @echo 'all open files. Your install rule actions might look something' + @echo 'like this:' + @echo '' + @echo ' install -m 4xxx -o root -g $${GRP} $${PROG} $${BIN}' + @echo ' install -m 444 $${MAN} $${DOC}' + @echo '' + @echo 'You will have to complete the 4xxx modes, the GRP value, and' + @echo 'the skeletons for the BIN and DOC strings, given at the' + @echo 'beginning of this Makefile, e.g.,' + @echo '' + @echo ' BIN= $${DESTDIR}/usr/local/etc' + @echo ' DOC= $${DESTDIR}/usr/man/man8' + @echo ' GRP= sys' + @echo '' + +${LIB}: FRC + (cd lib; ${MAKE} DEBUG="${DEBUG}" CFGF="${CFGF}") + +version.h: FRC + @echo Constructing version.h + @rm -f version.h + @echo '#define LSOF_BLDCMT "${LSOF_BLDCMT}"' > version.h; + @echo '#define LSOF_CC "${CC}"' >> version.h + @echo '#define LSOF_CCV "${CCV}"' >> version.h + @echo '#define LSOF_CCDATE "'`date`'"' >> version.h + @echo '#define LSOF_CCFLAGS "'`echo ${CFLAGS} | sed 's/\\\\(/\\(/g' | sed 's/\\\\)/\\)/g' | sed 's/"/\\\\"/g'`'"' >> version.h + @echo '#define LSOF_CINFO "${CINFO}"' >> version.h + @if [ "X${LSOF_HOST}" = "X" ]; then \ + echo '#define LSOF_HOST "'`uname -n`'"' >> version.h; \ + else \ + if [ "${LSOF_HOST}" = "none" ]; then \ + echo '#define LSOF_HOST ""' >> version.h; \ + else \ + echo '#define LSOF_HOST "${LSOF_HOST}"' >> version.h; \ + fi \ + fi + @echo '#define LSOF_LDFLAGS "${CFGL}"' >> version.h + @if [ "X${LSOF_LOGNAME}" = "X" ]; then \ + echo '#define LSOF_LOGNAME "${LOGNAME}"' >> version.h; \ + else \ + if [ "${LSOF_LOGNAME}" = "none" ]; then \ + echo '#define LSOF_LOGNAME ""' >> version.h; \ + else \ + echo '#define LSOF_LOGNAME "${LSOF_LOGNAME}"' >> version.h; \ + fi; \ + fi + @if [ "X${LSOF_SYSINFO}" = "X" ]; then \ + echo '#define LSOF_SYSINFO "'`uname -a`'"' >> version.h; \ + else \ + if [ "${LSOF_SYSINFO}" = "none" ]; then \ + echo '#define LSOF_SYSINFO ""' >> version.h; \ + else \ + echo '#define LSOF_SYSINFO "${LSOF_SYSINFO}"' >> version.h; \ + fi \ + fi + @if [ "X${LSOF_USER}" = "X" ]; then \ + echo '#define LSOF_USER "${USER}"' >> version.h; \ + else \ + if [ "${LSOF_USER}" = "none" ]; then \ + echo '#define LSOF_USER ""' >> version.h; \ + else \ + echo '#define LSOF_USER "${LSOF_USER}"' >> version.h; \ + fi \ + fi + @sed '/VN/s/.ds VN \(.*\)/#define LSOF_VERSION "\1"/' < version >> version.h + +FRC: + +# DO NOT DELETE THIS LINE - make depend DEPENDS ON IT + +dfile.o: ${HDR} dfile.c + +dmnt.o: ${HDR} dmnt.c + +dnode.o: ${HDR} dnode.c + +dproc.o: ${HDR} dproc.c + +dsock.o: ${HDR} dsock.c + +dstore.o: ${HDR} dstore.c + +arg.o: ${HDR} arg.c + +main.o: ${HDR} main.c + +misc.o: ${HDR} misc.c + +node.o: ${HDR} node.c + +print.o: ${HDR} print.c + +proc.o: ${HDR} proc.c + +store.o: ${HDR} store.c + +usage.o: ${HDR} version.h usage.c + +util.o: ${HDR} util.c + +# *** Do not add anything here - It will go away. *** diff --git a/dialects/linux/Mksrc b/dialects/linux/Mksrc new file mode 100755 index 0000000..8a1ffa1 --- /dev/null +++ b/dialects/linux/Mksrc @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Mksrc - make Linux source files for /proc-based lsof +# +# WARNING: This script assumes it is running from the main directory +# of the lsof, version 4 distribution. +# +# One environment variable applies: +# +# LSOF_MKC is the method for creating the source files. +# It defaults to "ln -s". A common alternative is "cp". +# +# $Id: Mksrc,v 1.2 2000/12/04 14:31:02 abe Exp $ + + +D=dialects/linux +L="dfile.c dlsof.h dmnt.c dnode.c dproc.c dproto.h dsock.c dstore.c machine.h" + +for i in $L +do + rm -f $i + $LSOF_MKC $D/$i $i + echo "$LSOF_MKC $D/$i $i" +done + diff --git a/dialects/linux/dfile.c b/dialects/linux/dfile.c new file mode 100644 index 0000000..4a30737 --- /dev/null +++ b/dialects/linux/dfile.c @@ -0,0 +1,387 @@ +/* + * dfile.c - Linux file processing 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: dfile.c,v 1.8 2012/04/10 16:39:50 abe Exp $"; +#endif + + +#include "lsof.h" + + +/* + * Local structures + */ + +struct hsfile { + struct sfile *s; /* the Sfile table address */ + struct hsfile *next; /* the next hash bucket entry */ +}; + +/* + * Local static variables + */ + +static struct hsfile *HbyFdi = /* hash by file (dev,ino) buckets */ + (struct hsfile *)NULL; +static int HbyFdiCt = 0; /* HbyFdi entry count */ +static struct hsfile *HbyFrd = /* hash by file raw device buckets */ + (struct hsfile *)NULL; +static int HbyFrdCt = 0; /* HbyFrd entry count */ +static struct hsfile *HbyFsd = /* hash by file system buckets */ + (struct hsfile *)NULL; +static int HbyFsdCt = 0; /* HbyFsd entry count */ +static struct hsfile *HbyNm = /* hash by name buckets */ + (struct hsfile *)NULL; +static int HbyNmCt = 0; /* HbyNm entry count */ + + +/* + * Local definitions + */ + +#define SFDIHASH 4094 /* Sfile hash by (device,inode) number + * pair bucket count (power of 2!) */ +#define SFFSHASH 1024 /* Sfile hash by file system device + * number bucket count (power of 2!) */ +#define SFHASHDEVINO(maj, min, ino, mod) ((int)(((int)((((int)(maj+1))*((int)((min+1))))+ino)*31415)&(mod-1))) + /* hash for Sfile by major device, + * minor device, and inode, modulo mod + * (mod must be a power of 2) */ +#define SFRDHASH 1024 /* Sfile hash by raw device number + * bucket count (power of 2!) */ +#define SFHASHRDEVI(maj, min, rmaj, rmin, ino, mod) ((int)(((int)((((int)(maj+1))*((int)((min+1))))+((int)(rmaj+1)*(int)(rmin+1))+ino)*31415)&(mod-1))) + /* hash for Sfile by major device, + * minor device, major raw device, + * minor raw device, and inode, modulo + * mod (mod must be a power of 2) */ +#define SFNMHASH 4096 /* Sfile hash by name bucket count + * (must be a power of 2!) */ + + +/* + * hashSfile() - hash Sfile entries for use in is_file_named() searches + */ + +void +hashSfile() +{ + static int hs = 0; + int i; + struct sfile *s; + struct hsfile *sh, *sn; +/* + * Do nothing if there are no file search arguments cached or if the + * hashes have already been constructed. + */ + if (!Sfile || hs) + return; +/* + * Allocate hash buckets by (device,inode), file system device, and file name. + */ + if (!(HbyFdi = (struct hsfile *)calloc((MALLOC_S)SFDIHASH, + sizeof(struct hsfile)))) + { + (void) fprintf(stderr, + "%s: can't allocate space for %d (dev,ino) hash buckets\n", + Pn, SFDIHASH); + Exit(1); + } + if (!(HbyFrd = (struct hsfile *)calloc((MALLOC_S)SFRDHASH, + sizeof(struct hsfile)))) + { + (void) fprintf(stderr, + "%s: can't allocate space for %d rdev hash buckets\n", + Pn, SFRDHASH); + Exit(1); + } + if (!(HbyFsd = (struct hsfile *)calloc((MALLOC_S)SFFSHASH, + sizeof(struct hsfile)))) + { + (void) fprintf(stderr, + "%s: can't allocate space for %d file sys hash buckets\n", + Pn, SFFSHASH); + Exit(1); + } + if (!(HbyNm = (struct hsfile *)calloc((MALLOC_S)SFNMHASH, + sizeof(struct hsfile)))) + { + (void) fprintf(stderr, + "%s: can't allocate space for %d name hash buckets\n", + Pn, SFNMHASH); + Exit(1); + } + hs++; +/* + * Scan the Sfile chain, building file, file system, raw device, and file + * name hash bucket chains. + */ + for (s = Sfile; s; s = s->next) { + for (i = 0; i < 3; i++) { + switch (i) { + case 0: /* hash by name */ + if (!s->aname) + continue; + sh = &HbyNm[hashbyname(s->aname, SFNMHASH)]; + HbyNmCt++; + break; + case 1: /* hash by device and inode, or file + * system device */ + if (s->type) { + sh = &HbyFdi[SFHASHDEVINO(GET_MAJ_DEV(s->dev), + GET_MIN_DEV(s->dev), s->i, + SFDIHASH)]; + HbyFdiCt++; + } else { + sh = &HbyFsd[SFHASHDEVINO(GET_MAJ_DEV(s->dev), + GET_MIN_DEV(s->dev), + 0, + SFFSHASH)]; + HbyFsdCt++; + } + break; + case 2: /* hash by file's raw device */ + if ((s->mode == S_IFCHR) || (s->mode == S_IFBLK)) { + sh = &HbyFrd[SFHASHRDEVI(GET_MAJ_DEV(s->dev), + GET_MIN_DEV(s->dev), + GET_MAJ_DEV(s->rdev), + GET_MIN_DEV(s->rdev), + s->i, + SFRDHASH)]; + HbyFrdCt++; + } else + continue; + } + /* + * Add hash to the bucket's chain, allocating new entries for + * all after the first. + */ + if (!sh->s) { + sh->s = s; + sh->next = (struct hsfile *)NULL; + continue; + } else { + if (!(sn = (struct hsfile *)malloc( + (MALLOC_S)sizeof(struct hsfile)))) + { + (void) fprintf(stderr, + "%s: can't allocate hsfile bucket for: %s\n", + Pn, s->aname); + Exit(1); + } + sn->s = s; + sn->next = sh->next; + sh->next = sn; + } + } + } +} + + +/* + * is_file_named() - is this file named? + */ + +int +is_file_named(ty, p, mp, cd) + int ty; /* search type: 0 = only by device + * and inode + * 1 = by device and + * inode, or by file + * system device and + * path for NFS file + * systems + * 2 = only by path + */ + char *p; /* path name (device and inode are + * identified via *Lf) */ + struct mounts *mp; /* NFS file system (NULL if not) */ + int cd; /* character or block type file -- + * VCHR or VBLK vnode, or S_IFCHR + * or S_IFBLK inode */ +{ + char *ep; + int f = 0; + struct mounts *smp; + struct sfile *s = (struct sfile *)NULL; + struct hsfile *sh; + size_t sz; +/* + * Check for a path name match, as requested. + */ + if ((ty == 2) && p && HbyNmCt) { + for (sh = &HbyNm[hashbyname(p, SFNMHASH)]; sh; sh = sh->next) { + if ((s = sh->s) && strcmp(p, s->aname) == 0) { + f = 2; + break; + } + } + } +/* + * Check for a regular file by device and inode number. + */ + if (!f && (ty < 2) && HbyFdiCt && Lf->dev_def + && (Lf->inp_ty == 1 || Lf->inp_ty == 3)) + { + for (sh = &HbyFdi[SFHASHDEVINO(GET_MAJ_DEV(Lf->dev), + GET_MIN_DEV(Lf->dev), + Lf->inode, + SFDIHASH)]; + sh; + sh = sh->next) + { + if ((s = sh->s) && (Lf->dev == s->dev) + && (Lf->inode == s->i)) { + f = 1; + break; + } + } + } +/* + * Check for a file system match. + */ + if (!f && (ty == 1) && HbyFsdCt && Lf->dev_def) { + for (sh = &HbyFsd[SFHASHDEVINO(GET_MAJ_DEV(Lf->dev), + GET_MIN_DEV(Lf->dev), 0, + SFFSHASH)]; + sh; + sh = sh->next) + { + if ((s = sh->s) && (s->dev == Lf->dev)) { + if (Lf->ntype != N_NFS) { + + /* + * A non-NFS file matches to a non-NFS file system by + * device. + */ + if (!(smp = s->mp) || (smp->ty != N_NFS)) { + f = 1; + break; + } + } else { + + /* + * An NFS file must also match to a file system by the + * the path name of the file system -- i.e., the first + * part of the file's path. This terrible, non-UNIX + * hack is forced on lsof by an egregious error in + * Linux NFS that can assign the same device number + * to two different NFS mounts. + */ + if (p && mp && mp->dirl && mp->dir && s->name + && !strncmp(mp->dir, s->name, mp->dirl)) + { + f = 1; + break; + } + } + } + } + } +/* + * Check for a character or block device match. + */ + if (!f && !ty && HbyFrdCt && cd + && Lf->dev_def && (Lf->dev == DevDev) + && Lf->rdev_def + && (Lf->inp_ty == 1 || Lf->inp_ty == 3)) + { + for (sh = &HbyFrd[SFHASHRDEVI(GET_MAJ_DEV(Lf->dev), + GET_MIN_DEV(Lf->dev), + GET_MAJ_DEV(Lf->rdev), + GET_MIN_DEV(Lf->rdev), + Lf->inode, SFRDHASH)]; + sh; + sh = sh->next) + { + if ((s = sh->s) && (s->dev == Lf->dev) + && (s->rdev == Lf->rdev) && (s->i == Lf->inode)) + { + f = 1; + break; + } + } + } +/* + * Convert the name if a match occurred. + */ + switch (f) { + case 0: + return(0); + case 1: + if (s->type) { + + /* + * If the search argument isn't a file system, propagate it + * to Namech[]; otherwise, let printname() compose the name. + */ + (void) snpf(Namech, Namechl, "%s", s->name); + if (s->devnm) { + ep = endnm(&sz); + (void) snpf(ep, sz, " (%s)", s->devnm); + } + } + break; + case 2: + (void) strcpy(Namech, p); + break; + } + if (s) + s->f = 1; + return(1); +} + + +/* + * printdevname() - print character device name + * + * Note: this function should not be needed in /proc-based lsof, but + * since it is called by printname() in print.c, an ersatz one + * is provided here. + */ + +int +printdevname(dev, rdev, f, nty) + dev_t *dev; /* device */ + dev_t *rdev; /* raw device */ + int f; /* 1 = follow with '\n' */ + int nty; /* node type: N_BLK or N_chr */ +{ + char buf[128]; + + (void) snpf(buf, sizeof(buf), "%s device: %d,%d", + (nty == N_BLK) ? "BLK" : "CHR", + (int)GET_MAJ_DEV(*rdev), (int)GET_MIN_DEV(*rdev)); + safestrprt(buf, stdout, f); + return(1); +} diff --git a/dialects/linux/dlsof.h b/dialects/linux/dlsof.h new file mode 100644 index 0000000..8cca3c5 --- /dev/null +++ b/dialects/linux/dlsof.h @@ -0,0 +1,179 @@ +/* + * dlsof.h - Linux header file 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. + */ + + +/* + * $Id: dlsof.h,v 1.22 2012/04/10 16:39:50 abe Exp $ + */ + + +#if !defined(LINUX_LSOF_H) +#define LINUX_LSOF_H 1 + +#include <dirent.h> +#define DIRTYPE dirent /* for arg.c's enter_dir() */ +#define __USE_GNU /* to get all O_* symbols in fcntl.h */ +#include <fcntl.h> +#include <malloc.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <setjmp.h> +#include <unistd.h> +#include <netinet/in.h> + +# if defined(GLIBCV) || defined(__UCLIBC__) +#include <netinet/tcp.h> +# else /* !defined(GLIBCV) && !defined(__UCLIBC__) */ +#include <linux/tcp.h> +# endif /* defined(GLIBCV) || defined(__UCLIBC__) */ + +# if !defined(HASNORPC_H) +#include <rpc/rpc.h> +#include <rpc/pmap_prot.h> +# endif /* !defined(HASNORPC_H) */ + +#if defined(HASSELINUX) +#include <selinux/selinux.h> +#endif /* defined(HASSELINUX) */ + +#include <sys/sysmacros.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <linux/if_ether.h> +#include <linux/netlink.h> + + +/* + * This definition is needed for the common function prototype definitions + * in "proto.h", but isn't used in /proc-based lsof. + */ + +typedef unsigned long KA_T; + + +/* + * Local definitions + */ + +#define COMP_P const void +#define DEVINCR 1024 /* device table malloc() increment */ +#define FSNAMEL 4 +#define MALLOC_P void +#define FREE_P MALLOC_P +#define MALLOC_S size_t +#define MAXSYSCMDL 15 /* max system command name length + * This value should be obtained from a + * header file #define, but no consistent one + * exists. Some versions of the Linux kernel + * have a hard-coded "char comm[16]" command + * name member of the task structured + * definition in <linux/sched.h>, while others + * have a "char comm[TASK_COMM_LEN]" member + * with TASK_COMM_LEN #define'd to be 16. + * Hence, a universal, local definition of + * 16 is #define'd here. */ +#define PROCFS "/proc" +#define QSORT_P void +#define READLEN_T size_t + +/* + * Definitions that indicate what values are present in a stat(2) or lstat(2) + * buffer. + */ + +#define SB_DEV 0x01 /* st_dev */ +#define SB_INO 0x02 /* st_ino */ +#define SB_MODE 0x04 /* st_mode */ +#define SB_NLINK 0x08 /* st_nlink */ +#define SB_RDEV 0x10 /* st_rdev */ +#define SB_SIZE 0x20 /* st_size */ +#define SB_ALL (SB_DEV | SB_INO | SB_MODE | SB_NLINK | SB_RDEV | \ + SB_SIZE) /* all values */ + +#define STRNCPY_L size_t +#define STRNML 32 + +# if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS==64 +#define SZOFFTYPE unsigned long long + /* size and offset internal storage + * type */ +#define SZOFFPSPEC "ll" /* SZOFFTYPE print specification + * modifier */ +# endif /* defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS==64 */ + +#define XDR_PMAPLIST (xdrproc_t)xdr_pmaplist +#define XDR_VOID (xdrproc_t)xdr_void + + +/* + * Global storage definitions (including their structure definitions) + */ + +struct mounts { + char *dir; /* directory name (mounted on) */ + char *fsname; /* file system + * (symbolic links unresolved) */ + char *fsnmres; /* file system + * (symbolic links resolved) */ + size_t dirl; /* length of directory name */ + dev_t dev; /* directory st_dev */ + dev_t rdev; /* directory st_rdev */ + INODETYPE inode; /* directory st_ino */ + mode_t mode; /* directory st_mode */ + int ds; /* directory status -- i.e., SB_* + * values */ + mode_t fs_mode; /* file system st_mode */ + int ty; /* node type -- e.g., N_REGLR, N_NFS */ + struct mounts *next; /* forward link */ +}; + +struct sfile { + char *aname; /* argument file name */ + char *name; /* file name (after readlink()) */ + char *devnm; /* device name (optional) */ + dev_t dev; /* device */ + dev_t rdev; /* raw device */ + mode_t mode; /* S_IFMT mode bits from stat() */ + int type; /* file type: 0 = file system + * 1 = regular file */ + INODETYPE i; /* inode number */ + int f; /* file found flag */ + struct mounts *mp; /* mount structure pointer for file + * system type entries */ +#define SAVE_MP_IN_SFILE 1 /* for ck_file_arg() im arg.c */ + struct sfile *next; /* forward link */ +}; + +extern int HasNFS; +extern int OffType; + +#endif /* LINUX_LSOF_H */ diff --git a/dialects/linux/dmnt.c b/dialects/linux/dmnt.c new file mode 100644 index 0000000..ab57bbb --- /dev/null +++ b/dialects/linux/dmnt.c @@ -0,0 +1,698 @@ +/* + * dmnt.c -- Linux mount support 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: dmnt.c,v 1.19 2012/04/10 16:39:50 abe Exp $"; +#endif + + +#include "lsof.h" + + +/* + * Local definitions + */ + +#if defined(HASMNTSUP) +#define HASHMNT 128 /* mount supplement hash bucket count + * !!!MUST BE A POWER OF 2!!! */ +#endif /* defined(HASMNTSUP) */ + + +/* + * Local function prototypes + */ + +_PROTOTYPE(static char *cvtoe,(char *os)); + +#if defined(HASMNTSUP) +_PROTOTYPE(static int getmntdev,(char *dn, size_t dnl, struct stat *s, int *ss)); +_PROTOTYPE(static int hash_mnt,(char *dn)); +#endif /* defined(HASMNTSUP) */ + + +/* + * Local structure definitions. + */ + +#if defined(HASMNTSUP) +typedef struct mntsup { + char *dn; /* mounted directory name */ + size_t dnl; /* strlen(dn) */ + dev_t dev; /* device number */ + int ln; /* line on which defined */ + struct mntsup *next; /* next entry */ +} mntsup_t; +#endif /* defined(HASMNTSUP) */ + + +/* + * Local static definitions + */ + +static struct mounts *Lmi = (struct mounts *)NULL; /* local mount info */ +static int Lmist = 0; /* Lmi status */ +static mntsup_t **MSHash = (mntsup_t **)NULL; /* mount supplement + * hash buckets */ + + +/* + * cvtoe() -- convert octal-escaped characters in string + */ + +static char * +cvtoe(os) + char *os; /* original string */ +{ + int c, cl, cx, ol, ox, tx; + char *cs; + int tc; +/* + * Allocate space for a copy of the string in which octal-escaped characters + * can be replaced by the octal value -- e.g., \040 with ' '. Leave room for + * a '\0' terminator. + */ + if (!(ol = (int)strlen(os))) + return((char *)NULL); + if (!(cs = (char *)malloc(ol + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for octal-escaping.\n", + Pn, ol + 1); + Exit(1); + } +/* + * Copy the string, replacing octal-escaped characters as they are found. + */ + for (cx = ox = 0, cl = ol; ox < ol; ox++) { + if (((c = (int)os[ox]) == (int)'\\') && ((ox + 3) < ol)) { + + /* + * The beginning of an octal-escaped character has been found. + * + * Convert the octal value to a character value. + */ + for (tc = 0, tx = 1; os[ox + tx] && (tx < 4); tx++) { + if (((int)os[ox + tx] < (int)'0') + || ((int)os[ox + tx] > (int)'7')) + { + + /* + * The escape isn't followed by octets, so ignore the + * escape and just copy it. + */ + break; + } + tc <<= 3; + tc += (int)(os[ox + tx] - '0'); + } + if (tx == 4) { + + /* + * If three octets (plus the escape) were assembled, use their + * character-forming result. + * + * Otherwise copy the escape and what follows it until another + * escape is found. + */ + ox += 3; + c = (tc & 0xff); + } + } + if (cx >= cl) { + + /* + * Expand the copy string, as required. Leave room for a '\0' + * terminator. + */ + cl += 64; /* (Make an arbitrary increase.) */ + if (!(cs = (char *)realloc(cs, cl + 1))) { + (void) fprintf(stderr, + "%s: can't realloc %d bytes for octal-escaping.\n", + Pn, cl + 1); + Exit(1); + } + } + /* + * Copy the character. + */ + cs[cx++] = (char)c; + } +/* + * Terminate the copy and return its pointer. + */ + cs[cx] = '\0'; + return(cs); +} + + +#if defined(HASMNTSUP) +/* + * getmntdev() - get mount device from mount supplement + */ + +static int +getmntdev(dn, dnl, s, ss) + char *dn; /* mounted directory name */ + size_t dnl; /* strlen(dn) */ + struct stat *s; /* stat(2) buffer receptor */ + int *ss; /* stat(2) status result -- i.e., SB_* + * values */ +{ + static int err = 0; + int h; + mntsup_t *mp, *mpn; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + + if (err) + return(0); + if (!MSHash) { + + /* + * No mount supplement hash buckets have been allocated, so read the + * mount supplement file and create hash buckets for its entries. + */ + char buf[(MAXPATHLEN*2) + 1], *dp, path[(MAXPATHLEN*2) + 1]; + dev_t dev; + FILE *fs; + int ln = 0; + size_t sz; + + if ((MntSup != 2) || !MntSupP) + return(0); + if (!is_readable(MntSupP, 1)) { + + /* + * The mount supplement file isn't readable. + */ + err = 1; + return(0); + } + if (!(fs = open_proc_stream(MntSupP, "r", &vbuf, &vsz, 0))) { + + /* + * The mount supplement file can't be opened for reading. + */ + if (!Fwarn) + (void) fprintf(stderr, "%s: can't open(%s): %s\n", + Pn, MntSupP, strerror(errno)); + err = 1; + return(0); + } + buf[sizeof(buf) - 1] = '\0'; + /* + * Read the mount supplement file. + */ + while (fgets(buf, sizeof(buf) - 1, fs)) { + ln++; + if ((dp = strchr(buf, '\n'))) + *dp = '\0'; + if (buf[0] != '/') { + + /* + * The mount supplement line doesn't begin with the absolute + * path character '/'. + */ + if (!Fwarn) + (void) fprintf(stderr, + "%s: %s line %d: no path: \"%s\"\n", + Pn, MntSupP, ln, buf); + err = 1; + continue; + } + if (!(dp = strchr(buf, ' ')) || strncmp(dp + 1, "0x", 2)) { + + /* + * The path on the mount supplement line isn't followed by + * " 0x". + */ + if (!Fwarn) + (void) fprintf(stderr, + "%s: %s line %d: no device: \"%s\"\n", + Pn, MntSupP, ln, buf); + err = 1; + continue; + } + sz = (size_t)(dp - buf); + (void) strncpy(path, buf, sz); + path[sz] = '\0'; + /* + * Assemble the hexadecimal device number of the mount supplement + * line. + */ + for (dev = 0, dp += 3; *dp; dp++) { + if (!isxdigit((int)*dp)) + break; + if (isdigit((int)*dp)) + dev = (dev << 4) + (int)*dp - (int)'0'; + else + dev = (dev << 4) + (int)tolower(*dp) - (int)'a' + 10; + } + if (*dp) { + + /* + * The device number couldn't be assembled. + */ + if (!Fwarn) + (void) fprintf(stderr, + "%s: %s line %d: illegal device: \"%s\"\n", + Pn, MntSupP, ln, buf); + err = 1; + continue; + } + /* + * Search the mount supplement hash buckets. (Allocate them as + * required.) + */ + if (!MSHash) { + if (!(MSHash = (mntsup_t **)calloc(HASHMNT, + sizeof(mntsup_t *))) + ) { + (void) fprintf(stderr, + "%s: no space for mount supplement hash buckets\n", + Pn); + Exit(1); + } + } + h = hash_mnt(path); + for (mp = MSHash[h]; mp; mp = mp->next) { + if ((mp->dnl == dnl) && !strcmp(mp->dn, path)) + break; + } + if (mp) { + + /* + * A path match was located. If the device number is the + * same, skip this mount supplement line. Otherwise, issue + * a warning. + */ + if (mp->dev != dev) { + (void) fprintf(stderr, + "%s: %s line %d path duplicate of %d: \"%s\"\n", + Pn, MntSupP, ln, mp->ln, buf); + err = 1; + } + continue; + } + /* + * Allocate and fill a new mount supplement hash entry. + */ + if (!(mpn = (mntsup_t *)malloc(sizeof(mntsup_t)))) { + (void) fprintf(stderr, + "%s: no space for mount supplement entry: %d \"%s\"\n", + Pn, ln, buf); + Exit(1); + } + if (!(mpn->dn = (char *)malloc(sz + 1))) { + (void) fprintf(stderr, + "%s: no space for mount supplement path: %d \"%s\"\n", + Pn, ln, buf); + Exit(1); + } + (void) strcpy(mpn->dn, path); + mpn->dnl = sz; + mpn->dev = dev; + mpn->ln = ln; + mpn->next = MSHash[h]; + MSHash[h] = mpn; + } + if (ferror(fs)) { + if (!Fwarn) + (void) fprintf(stderr, "%s: error reading %s\n", + Pn, MntSupP); + err = 1; + } + (void) fclose(fs); + if (err) { + if (MSHash) { + for (h = 0; h < HASHMNT; h++) { + for (mp = MSHash[h]; mp; mp = mpn) { + mpn = mp->next; + if (mp->dn) + (void) free((MALLOC_P *)mp->dn); + (void) free((MALLOC_P *)mp); + } + } + (void) free((MALLOC_P *)MSHash); + MSHash = (mntsup_t **)NULL; + } + return(0); + } + } +/* + * If no errors have been detected reading the mount supplement file, search + * its hash buckets for the supplied directory path. + */ + if (err) + return(0); + h = hash_mnt(dn); + for (mp = MSHash[h]; mp; mp = mp->next) { + if ((dnl == mp->dnl) && !strcmp(dn, mp->dn)) { + memset((void *)s, 0, sizeof(struct stat)); + s->st_dev = mp->dev; + *ss |= SB_DEV; + return(1); + } + } + return(0); +} + + +/* + * hash_mnt() - hash mount point + */ + +static int +hash_mnt(dn) + char *dn; /* mount point directory name */ +{ + register int i, h; + size_t l; + + if (!(l = strlen(dn))) + return(0); + if (l == 1) + return((int)*dn & (HASHMNT - 1)); + for (i = h = 0; i < (int)(l - 1); i++) { + h ^= ((int)dn[i] * (int)dn[i+1]) << ((i*3)%13); + } + return(h & (HASHMNT - 1)); +} +#endif /* defined(HASMNTSUP) */ + + +/* + * readmnt() - read mount table + */ + +struct mounts * +readmnt() +{ + char buf[MAXPATHLEN], *cp, **fp; + char *dn = (char *)NULL; + size_t dnl; + int ds, ne; + char *fp0 = (char *)NULL; + char *fp1 = (char *)NULL; + int fr, ignrdl, ignstat; + char *ln; + struct mounts *mp; + FILE *ms; + int nfs; + struct stat sb; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + + if (Lmi || Lmist) + return(Lmi); +/* + * Open access to /proc/mounts, assigning a page size buffer to its stream. + */ + (void) snpf(buf, sizeof(buf), "%s/mounts", PROCFS); + ms = open_proc_stream(buf, "r", &vbuf, &vsz, 1); +/* + * Read mount table entries. + */ + while (fgets(buf, sizeof(buf), ms)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 3 + || !fp[0] || !fp[1] || !fp[2]) + continue; + /* + * Convert octal-escaped characters in the device name and mounted-on + * path name. + */ + if (fp0) { + (void) free((FREE_P *)fp0); + fp0 = (char *)NULL; + } + if (fp1) { + (void) free((FREE_P *)fp1); + fp1 = (char *)NULL; + } + if (!(fp0 = cvtoe(fp[0])) || !(fp1 = cvtoe(fp[1]))) + continue; + /* + * Locate any colon (':') in the device name. + * + * If the colon is followed by * "(pid*" -- it's probably an + * automounter entry. + * + * Ignore autofs, pipefs, and sockfs entries. + */ + cp = strchr(fp0, ':'); + if (cp && !strncasecmp(++cp, "(pid", 4)) + continue; + if (!strcasecmp(fp[2], "autofs") || !strcasecmp(fp[2], "pipefs") + || !strcasecmp(fp[2], "sockfs")) + continue; + /* + * Interpolate a possible symbolic mounted directory link. + */ + if (dn) + (void) free((FREE_P *)dn); + dn = fp1; + fp1 = (char *)NULL; + +#if defined(HASEOPT) + if (Efsysl) { + + /* + * If there is an -e file system list, check it to decide if a stat() + * and Readlink() on this one should be performed. + */ + efsys_list_t *ep; + + for (ignrdl = ignstat = 0, ep = Efsysl; ep; ep = ep->next) { + if (!strcmp(dn, ep->path)) { + ignrdl = ep->rdlnk; + ignstat = 1; + break; + } + } + } else + +#endif /* defined(HASEOPT */ + + ignrdl = ignstat = 0; + + /* + * Avoid Readlink() when requested. + */ + if (!ignrdl) { + if (!(ln = Readlink(dn))) { + if (!Fwarn) { + (void) fprintf(stderr, + " Output information may be incomplete.\n"); + } + continue; + } + if (ln != dn) { + (void) free((FREE_P *)dn); + dn = ln; + } + } + if (*dn != '/') + continue; + dnl = strlen(dn); + /* + * Test for duplicate and NFS directories. + */ + for (mp = Lmi; mp; mp = mp->next) { + if ((dnl == mp->dirl) && !strcmp(dn, mp->dir)) + break; + } + if ((nfs = strcasecmp(fp[2], "nfs"))) { + if ((nfs = strcasecmp(fp[2], "nfs3"))) + nfs = strcasecmp(fp[2], "nfs4"); + } + if (mp) { + + /* + * If this duplicate directory is not root, ignore it. If the + * already remembered entry is NFS-mounted, ignore this one. If + * this one is NFS-mounted, ignore the already remembered entry. + */ + if (strcmp(dn, "/")) + continue; + if (mp->ty == N_NFS) + continue; + if (nfs) + continue; + } + /* + * Stat() the directory. + */ + if (ignstat) + fr = 1; + else { + if ((fr = statsafely(dn, &sb))) { + if (!Fwarn) { + (void) fprintf(stderr, "%s: WARNING: can't stat() ", + Pn); + safestrprt(fp[2], stderr, 0); + (void) fprintf(stderr, " file system "); + safestrprt(dn, stderr, 1); + (void) fprintf(stderr, + " Output information may be incomplete.\n"); + } + } else + ds = SB_ALL; + } + +#if defined(HASMNTSUP) + if (fr) { + + /* + * If the stat() failed or wasn't called, check the mount + * supplement table, if possible. + */ + if ((MntSup == 2) && MntSupP) { + ds = 0; + if (getmntdev(dn, dnl, &sb, &ds) || !(ds & SB_DEV)) { + (void) fprintf(stderr, + "%s: assuming dev=%#lx for %s from %s\n", + Pn, (long)sb.st_dev, dn, MntSupP); + } + } else { + if (!ignstat) + continue; + ds = 0; /* No stat() was allowed. */ + } + } +#else /* !defined(HASMNTSUP) */ + if (fr) { + if (!ignstat) + continue; + ds = 0; /* No stat() was allowed. */ + } +#endif /* defined(HASMNTSUP) */ + + /* + * Fill a local mount structure or reuse a previous entry when + * indicated. + */ + if (mp) { + ne = 0; + if (mp->dir) { + (void) free((FREE_P *)mp->dir); + mp->dir = (char *)NULL; + } + if (mp->fsname) { + (void) free((FREE_P *)mp->fsname); + mp->fsname = (char *)NULL; + } + } else { + ne = 1; + if (!(mp = (struct mounts *)malloc(sizeof(struct mounts)))) { + (void) fprintf(stderr, + "%s: can't allocate mounts struct for: ", Pn); + safestrprt(dn, stderr, 1); + Exit(1); + } + } + mp->dir = dn; + dn = (char *)NULL; + mp->dirl = dnl; + if (ne) + mp->next = Lmi; + mp->dev = ((mp->ds = ds) & SB_DEV) ? sb.st_dev : 0; + mp->rdev = (ds & SB_RDEV) ? sb.st_rdev : 0; + mp->inode = (INODETYPE)((ds & SB_INO) ? sb.st_ino : 0); + mp->mode = (ds & SB_MODE) ? sb.st_mode : 0; + if (!nfs) { + mp->ty = N_NFS; + if (HasNFS < 2) + HasNFS = 2; + } else + mp->ty = N_REGLR; + +#if defined(HASMNTSUP) + /* + * If support for the mount supplement file is defined and if the + * +m option was supplied, print mount supplement information. + */ + if (MntSup == 1) { + if (mp->dev) + (void) printf("%s %#lx\n", mp->dir, (long)mp->dev); + else + (void) printf("%s 0x0\n", mp->dir); + } +#endif /* defined(HASMNTSUP) */ + + /* + * Save mounted-on device or directory name. + */ + dn = fp0; + fp0 = (char *)NULL; + mp->fsname = dn; + /* + * Interpolate a possible file system (mounted-on) device name or + * directory name link. + * + * Avoid Readlink() when requested. + */ + if (ignrdl || (*dn != '/')) { + if (!(ln = mkstrcpy(dn, (MALLOC_S *)NULL))) { + (void) fprintf(stderr, + "%s: can't allocate space for: ", Pn); + safestrprt(dn, stderr, 1); + Exit(1); + } + ignstat = 1; + } else + ln = Readlink(dn); + dn = (char *)NULL; + /* + * Stat() the file system (mounted-on) name and add file system + * information to the local mount table entry. + */ + if (ignstat || !ln || statsafely(ln, &sb)) + sb.st_mode = 0; + mp->fsnmres = ln; + mp->fs_mode = sb.st_mode; + if (ne) + Lmi = mp; + } +/* + * Clean up and return the local mount info table address. + */ + (void) fclose(ms); + if (dn) + (void) free((FREE_P *)dn); + if (fp0) + (void) free((FREE_P *)fp0); + if (fp1) + (void) free((FREE_P *)fp1); + Lmist = 1; + return(Lmi); +} 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); +} diff --git a/dialects/linux/dproc.c b/dialects/linux/dproc.c new file mode 100644 index 0000000..c3045a2 --- /dev/null +++ b/dialects/linux/dproc.c @@ -0,0 +1,1563 @@ +/* + * dproc.c - Linux process access 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: dproc.c,v 1.27 2013/01/02 17:02:36 abe Exp $"; +#endif + +#include "lsof.h" + + +/* + * Local definitions + */ + +#define FDINFO_FLAGS 1 /* fdinfo flags available */ +#define FDINFO_POS 2 /* fdinfo position available */ +#define FDINFO_ALL (FDINFO_FLAGS | FDINFO_POS) +#define LSTAT_TEST_FILE "/" +#define LSTAT_TEST_SEEK 1 + +#if !defined(ULLONG_MAX) +#define ULLONG_MAX 18446744073709551615ULL +#endif /* !defined(ULLONG_MAX) */ + + +/* + * Local structures + */ + +struct l_fdinfo { + int flags; /* flags: line value */ + off_t pos; /* pos: line value */ +}; + + +/* + * Local variables + */ + +static short Cckreg; /* conditional status of regular file + * checking: + * 0 = unconditionally check + * 1 = conditionally check */ +static short Ckscko; /* socket file only checking status: + * 0 = none + * 1 = check only socket files */ + + +/* + * Local function prototypes + */ + +_PROTOTYPE(static int get_fdinfo,(char *p, struct l_fdinfo *fi)); +_PROTOTYPE(static int getlinksrc,(char *ln, char *src, int srcl, char **rest)); +_PROTOTYPE(static int isefsys,(char *path, char *type, int l, + efsys_list_t **rep, struct lfile **lfr)); +_PROTOTYPE(static int nm2id,(char *nm, int *id, int *idl)); +_PROTOTYPE(static int read_id_stat,(int ty, char *p, int id, char **cmd, + int *ppid, int *pgid)); +_PROTOTYPE(static void process_proc_map,(char *p, struct stat *s, int ss)); +_PROTOTYPE(static int process_id,(char *idp, int idpl, char *cmd, UID_ARG uid, + int pid, int ppid, int pgid, int tid)); +_PROTOTYPE(static int statEx,(char *p, struct stat *s, int *ss)); + + +#if defined(HASSELINUX) +_PROTOTYPE(static int cmp_cntx_eq,(char *pcntx, char *ucntx)); + + +#include <fnmatch.h> + + +/* + * cmp_cntx_eq -- compare program and user security contexts + */ + +static int +cmp_cntx_eq(pcntx, ucntx) + char *pcntx; /* program context */ + char *ucntx; /* user supplied context */ +{ + return !fnmatch(ucntx, pcntx, 0); +} + + +/* + * enter_cntx_arg() - enter name ecurity context argument + */ + +int +enter_cntx_arg(cntx) + char *cntx; /* context */ +{ + cntxlist_t *cntxp; +/* + * Search the argument list for a duplicate. + */ + for (cntxp = CntxArg; cntxp; cntxp = cntxp->next) { + if (!strcmp(cntxp->cntx, cntx)) { + if (!Fwarn) { + (void) fprintf(stderr, "%s: duplicate context: %s\n", + Pn, cntx); + } + return(1); + } + } +/* + * Create and link a new context argument list entry. + */ + if (!(cntxp = (cntxlist_t *)malloc((MALLOC_S)sizeof(cntxlist_t)))) { + (void) fprintf(stderr, "%s: no space for context: %s\n", Pn, cntx); + Exit(1); + } + cntxp->f = 0; + cntxp->cntx = cntx; + cntxp->next = CntxArg; + CntxArg = cntxp; + return(0); +} +#endif /* defined(HASSELINUX) */ + + +/* + * gather_proc_info() -- gather process information + */ + +void +gather_proc_info() +{ + char *cmd, *tcmd; + struct dirent *dp; + unsigned char ht, pidts; + int n, nl, pgid, pid, ppid, rv, tid, tpgid, tppid, tx; + static char *path = (char *)NULL; + static int pathl = 0; + static char *pidpath = (char *)NULL; + static MALLOC_S pidpathl = 0; + static MALLOC_S pidx = 0; + static DIR *ps = (DIR *)NULL; + struct stat sb; + static char *taskpath = (char *)NULL; + static int taskpathl = 0; + static char *tidpath = (char *)NULL; + static int tidpathl = 0; + DIR *ts; + UID_ARG uid; + +/* + * Do one-time setup. + */ + if (!pidpath) { + pidx = strlen(PROCFS) + 1; + pidpathl = pidx + 64 + 1; /* 64 is growth room */ + if (!(pidpath = (char *)malloc(pidpathl))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for \"%s/\"<pid>\n", + Pn, (int)pidpathl, PROCFS); + Exit(1); + } + (void) snpf(pidpath, pidpathl, "%s/", PROCFS); + } +/* + * Get lock and net information. + */ + (void) make_proc_path(pidpath, pidx, &path, &pathl, "locks"); + (void) get_locks(path); + (void) make_proc_path(pidpath, pidx, &path, &pathl, "net/"); + (void) set_net_paths(path, strlen(path)); +/* + * 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 /proc, looking for PID directories. Open each one and + * gather its process and file information. + */ + if (!ps) { + if (!(ps = opendir(PROCFS))) { + (void) fprintf(stderr, "%s: can't open %s\n", Pn, PROCFS); + Exit(1); + } + } else + (void) rewinddir(ps); + while ((dp = readdir(ps))) { + if (nm2id(dp->d_name, &pid, &n)) + continue; + /* + * Build path to PID's directory. + */ + if ((pidx + n + 1 + 1) > pidpathl) { + pidpathl = pidx + n + 1 + 1 + 64; + if (!(pidpath = (char *)realloc((MALLOC_P *)pidpath, pidpathl))) + { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for \"%s/%s/\"\n", + Pn, (int)pidpathl, PROCFS, dp->d_name); + Exit(1); + } + } + (void) snpf(pidpath + pidx, pidpathl - pidx, "%s/", dp->d_name); + n += (pidx + 1); + /* + * Process the PID's stat info. + */ + if (stat(pidpath, &sb)) + continue; + uid = (UID_ARG)sb.st_uid; + ht = pidts = 0; + +#if defined(HASTASKS) + /* + * If task reporting is selected, check the tasks of the process first, + * so that the "-p<PID> -aK" options work properly. + */ + if ((Selflags & SELTASK)) { + (void) make_proc_path(pidpath, n, &taskpath, &taskpathl, + "task"); + tx = n + 4; + if ((ts = opendir(taskpath))) { + + /* + * Process the PID's tasks. Record the open files of those + * whose TIDs do not match the PID and which are themselves + * not zombies. + */ + while ((dp = readdir(ts))) { + + /* + * Get the task ID. Skip the task if its ID matches the + * process PID. + */ + if (nm2id(dp->d_name, &tid, &nl)) + continue; + if (tid == pid) { + pidts = 1; + continue; + } + /* + * Form the path for the TID. + */ + if ((tx + 1 + nl + 1 + 4) > tidpathl) { + tidpathl = tx + 1 + n + 1 + 4 + 64; + if (tidpath) + tidpath = (char *)realloc((MALLOC_P *)tidpath, + tidpathl); + else + tidpath = (char *)malloc((MALLOC_S)tidpathl); + if (!tidpath) { + (void) fprintf(stderr, + "%s: can't allocate %d task bytes", Pn, + tidpathl); + (void) fprintf(stderr, " for \"%s/%s/stat\"\n", + taskpath, dp->d_name); + Exit(1); + } + } + (void) snpf(tidpath, tidpathl, "%s/%s/stat", taskpath, + dp->d_name); + /* + * Check the task state. + */ + rv = read_id_stat(1, tidpath, tid, &tcmd, &tppid, + &tpgid); + if ((rv < 0) || (rv == 1)) + continue; + /* + * Attempt to record the task. + */ + if (!process_id(tidpath, (tx + 1 + nl+ 1), tcmd, uid, + pid, tppid, tpgid, tid)) + { + ht = 1; + } + } + (void) closedir(ts); + } + } +#endif /* defined(HASTASKS) */ + + /* + * If the main process is a task and task selection has been specified + * along with option ANDing, enter the main process temporarily as a + * task, so that the "-aK" option set lists the main process along + * with its tasks. + */ + (void) make_proc_path(pidpath, n, &path, &pathl, "stat"); + if (((rv = read_id_stat(0, path, pid, &cmd, &ppid, &pgid)) >= 0) + && (rv != 1)) + { + tid = (Fand && ht && pidts && (Selflags & SELTASK)) ? pid : 0; + if ((!process_id(pidpath, n, cmd, uid, pid, ppid, pgid, tid)) + && tid) + { + Lp->tid = 0; + } + } + } +} + + +/* + * get_fdinfo() - get values from /proc/<PID>fdinfo/FD + */ + +static int +get_fdinfo(p, fi) + char *p; /* path to fdinfo file */ + struct l_fdinfo *fi; /* pointer to local fdinfo values + * return structure */ +{ + char buf[MAXPATHLEN + 1], *ep, **fp; + FILE *fs; + int rv = 0; + unsigned long ul; + unsigned long long ull; +/* + * Signal no values returned (0) if no fdinfo pointer was provided or if the + * fdinfo path can't be opened. + */ + if (!fi) + return(0); + if (!p || !*p || !(fs = fopen(p, "r"))) + return(0); +/* + * Read the fdinfo file. + */ + while (fgets(buf, sizeof(buf), fs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 2) + continue; + if (!fp[0] || !*fp[0] || !fp[1] || !*fp[1]) + continue; + if (!strcmp(fp[0], "flags:")) { + + /* + * Process a "flags:" line. + */ + ep = (char *)NULL; + if ((ul = strtoul(fp[1], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + fi->flags = (unsigned int)ul; + if ((rv |= FDINFO_FLAGS) == FDINFO_ALL) + break; + } else if (!strcmp(fp[0], "pos:")) { + + /* + * Process a "pos:" line. + */ + ep = (char *)NULL; + if ((ull = strtoull(fp[1], &ep, 0)) == ULLONG_MAX + || !ep || *ep) + continue; + fi->pos = (off_t)ull; + if ((rv |= FDINFO_POS) == FDINFO_ALL) + break; + } + } + fclose(fs); +/* + * Signal via the return value what information was obtained. (0 == none) + */ + return(rv); +} + + +/* + * getlinksrc() - get the source path name for the /proc/<PID>/fd/<FD> link + */ + + +static int +getlinksrc(ln, src, srcl, rest) + char *ln; /* link path */ + char *src; /* link source path return address */ + int srcl; /* length of src[] */ + char **rest; /* pointer to what follows the ':' in + * the link source path (NULL if no + * return requested) */ +{ + char *cp; + int ll; + + if (rest) + *rest = (char *)NULL; + if ((ll = readlink(ln, src, srcl - 1)) < 1 + || ll >= srcl) + return(-1); + src[ll] = '\0'; + if (*src == '/') + return(ll); + if ((cp = strchr(src, ':'))) { + *cp = '\0'; + ll = strlen(src); + if (rest) + *rest = cp + 1; + } + return(ll); +} + + +/* + * initialize() - perform all initialization + */ + +void +initialize() +{ + int fd; + struct l_fdinfo fi; + char path[MAXPATHLEN]; + struct stat sb; +/* + * Test for -i and -X option conflict. + */ + if (Fxopt && (Fnet || Nwad)) { + (void) fprintf(stderr, "%s: -i is useless when -X is specified.\n", + Pn); + usage(1, 0, 0); + } +/* + * Open LSTAT_TEST_FILE and seek to byte LSTAT_TEST_SEEK, then lstat the + * /proc/<PID>/fd/<FD> for LSTAT_TEST_FILE to see what position is reported. + * If the result is LSTAT_TEST_SEEK, enable offset reporting. + * + * If the result isn't LSTAT_TEST_SEEK, next check the fdinfo file for the + * open LSTAT_TEST_FILE file descriptor. If it exists and contains a "pos:" + * value, and if the value is LSTAT_TEST_SEEK, enable offset reporting. + */ + if ((fd = open(LSTAT_TEST_FILE, O_RDONLY)) >= 0) { + if (lseek(fd, (off_t)LSTAT_TEST_SEEK, SEEK_SET) + == (off_t)LSTAT_TEST_SEEK) { + (void) snpf(path, sizeof(path), "%s/%d/fd/%d", PROCFS, Mypid, + fd); + if (!lstat(path, &sb)) { + if (sb.st_size == (off_t)LSTAT_TEST_SEEK) + OffType = 1; + } + } + if (!OffType) { + (void) snpf(path, sizeof(path), "%s/%d/fdinfo/%d", PROCFS, + Mypid, fd); + if (get_fdinfo(path, &fi) & FDINFO_POS) { + if (fi.pos == (off_t)LSTAT_TEST_SEEK) + OffType = 2; + } + } + (void) close(fd); + } + if (!OffType) { + if (Foffset && !Fwarn) + (void) fprintf(stderr, + "%s: WARNING: can't report offset; disregarding -o.\n", + Pn); + Foffset = 0; + Fsize = 1; + } + if (Fsv && (OffType != 2)) { + if (!Fwarn && FsvByf) + (void) fprintf(stderr, + "%s: WARNING: can't report file flags; disregarding +f.\n", + Pn); + Fsv = 0; + } +/* + * Make sure the local mount info table is loaded if doing anything other + * than just Internet lookups. (HasNFS is defined during the loading of the + * local mount table.) + */ + if (Selinet == 0) + (void) readmnt(); +} + + +/* + * make_proc_path() - make a path in a /proc directory + * + * entry: + * pp = pointer to /proc prefix + * lp = length of prefix + * np = pointer to malloc'd buffer to receive new file's path + * nl = length of new file path buffer + * sf = new path's suffix + * + * return: length of new path + * np = updated with new path + * nl = updated with new path length + */ + +int +make_proc_path(pp, pl, np, nl, sf) + char *pp; /* path prefix -- e.g., /proc/<pid>/ */ + int pl; /* strlen(pp) */ + char **np; /* malloc'd receiving buffer */ + int *nl; /* strlen(*np) */ + char *sf; /* suffix of new path */ +{ + char *cp; + MALLOC_S rl, sl; + + sl = strlen(sf); + if ((rl = pl + sl + 1) > *nl) { + if ((cp = *np)) + cp = (char *)realloc((MALLOC_P *)cp, rl); + else + cp = (char *)malloc(rl); + if (!cp) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for %s%s\n", + Pn, (int)rl, pp, sf); + Exit(1); + } + *nl = rl; + *np = cp; + } + (void) snpf(*np, *nl, "%s", pp); + (void) snpf(*np + pl, *nl - pl, "%s", sf); + return(rl - 1); +} + + +/* + * isefsys() -- is path on a file system exempted with -e + * + * Note: alloc_lfile() must have been called in advance. + */ + +static int +isefsys(path, type, l, rep, lfr) + char *path; /* path to file */ + char *type; /* unknown file type */ + int l; /* link request: 0 = report + * 1 = link */ + efsys_list_t **rep; /* returned Efsysl pointer, if not + * NULL */ + struct lfile **lfr; /* allocated struct lfile pointer */ +{ + efsys_list_t *ep; + int ds, len; + struct mounts *mp; + char nmabuf[MAXPATHLEN + 1]; + + len = (int) strlen(path); + for (ep = Efsysl; ep; ep = ep->next) { + + /* + * Look for a matching exempt file system path at the beginning of + * the file path. + */ + if (ep->pathl > len) + continue; + if (strncmp(ep->path, path, ep->pathl)) + continue; + /* + * If only reporting, return information as requested. + */ + if (!l) { + if (rep) + *rep = ep; + return(0); + } + /* + * Process an exempt file. + */ + ds = 0; + if ((mp = ep->mp)) { + if (mp->ds & SB_DEV) { + Lf->dev = mp->dev; + ds = Lf->dev_def = 1; + } + if (mp->ds & SB_RDEV) { + Lf->rdev = mp->rdev; + ds = Lf->rdev_def = 1; + } + } + if (!ds) + (void) enter_dev_ch("UNKNOWN"); + Lf->ntype = N_UNKN; + (void) snpf(Lf->type, sizeof(Lf->type), "%s", + (type ? type : "UNKN")); + (void) enter_nm(path); + (void) snpf(nmabuf, sizeof(nmabuf), "(%ce %s)", + ep->rdlnk ? '+' : '-', ep->path); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + if (Lf->sf) { + if (lfr) + *lfr = Lf; + link_lfile(); + } else if (lfr) + *lfr = (struct lfile *)NULL; + return(0); + } + return(1); +} + + +/* + * nm2id() - convert a name to an integer ID + */ + +static int +nm2id(nm, id, idl) + char *nm; /* pointer to name */ + int *id; /* pointer to ID receiver */ + int *idl; /* pointer to ID length receiver */ +{ + register int tid, tidl; + + for (*id = *idl = tid = tidl = 0; *nm; nm++) { + +#if defined(__STDC__) /* { */ + if (!isdigit((unsigned char)*nm)) +#else /* !defined(__STDC__) } { */ + if (!isascii(*nm) || !isdigit((unsigned char)*cp)) +#endif /* defined(__STDC__) } */ + + { + return(1); + } + tid = tid * 10 + (int)(*nm - '0'); + tidl++; + } + *id = tid; + *idl = tidl; + return(0); +} + + +/* + * open_proc_stream() -- open a /proc stream + */ + +FILE * +open_proc_stream(p, m, buf, sz, act) + char *p; /* pointer to path to open */ + char *m; /* pointer to mode -- e.g., "r" */ + char **buf; /* pointer tp setvbuf() address + * (NULL if none) */ + size_t *sz; /* setvbuf() size (0 if none or if + * getpagesize() desired */ + int act; /* fopen() failure action: + * 0 : return (FILE *)NULL + * <>0 : fprintf() an error message + * and Exit(1) + */ +{ + FILE *fs; /* opened stream */ + static size_t psz = (size_t)0; /* page size */ + size_t tsz; /* temporary size */ +/* + * Open the stream. + */ + if (!(fs = fopen(p, m))) { + if (!act) + return((FILE *)NULL); + (void) fprintf(stderr, "%s: can't fopen(%s, \"%s\"): %s\n", + Pn, p, m, strerror(errno)); + Exit(1); + } +/* + * Return the stream if no buffer change is required. + */ + if (!buf) + return(fs); +/* + * Determine the buffer size required. + */ + if (!(tsz = *sz)) { + if (!psz) + psz = getpagesize(); + tsz = psz; + } +/* + * Allocate a buffer for the stream, as required. + */ + if (!*buf) { + if (!(*buf = (char *)malloc((MALLOC_S)tsz))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for %s stream buffer\n", + Pn, (int)tsz, p); + Exit(1); + } + *sz = tsz; + } +/* + * Assign the buffer to the stream. + */ + if (setvbuf(fs, *buf, _IOFBF, tsz)) { + (void) fprintf(stderr, "%s: setvbuf(%s)=%d failure: %s\n", + Pn, p, (int)tsz, strerror(errno)); + Exit(1); + } + return(fs); +} + + +/* + * process_id - process ID: PID or LWP + * + * return: 0 == ID processed + * 1 == ID not processed + */ + +static int +process_id(idp, idpl, cmd, uid, pid, ppid, pgid, tid) + char *idp; /* pointer to ID's path */ + int idpl; /* pointer to ID's path length */ + char *cmd; /* pointer to ID's command */ + UID_ARG uid; /* ID's UID */ + int pid; /* ID's PID */ + int ppid; /* parent PID */ + int pgid; /* parent GID */ + int tid; /* task ID, if non-zero */ +{ + int av; + static char *dpath = (char *)NULL; + static int dpathl = 0; + short efs, enls, enss, lnk, oty, pn, pss, sf; + int fd, i, ls, n, ss, sv; + struct l_fdinfo fi; + DIR *fdp; + struct dirent *fp; + static char *ipath = (char *)NULL; + static int ipathl = 0; + int j = 0; + struct lfile *lfr; + struct stat lsb, sb; + char nmabuf[MAXPATHLEN + 1], pbuf[MAXPATHLEN + 1]; + static char *path = (char *)NULL; + static int pathl = 0; + static char *pathi = (char *)NULL; + static int pathil = 0; + char *rest; + int txts = 0; + +#if defined(HASSELINUX) + cntxlist_t *cntxp; +#endif /* defined(HASSELINUX) */ + +/* + * See if process is excluded. + */ + if (is_proc_excl(pid, pgid, uid, &pss, &sf, tid) + || is_cmd_excl(cmd, &pss, &sf)) + return(1); + 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; + } + alloc_lproc(pid, pgid, ppid, uid, cmd, (int)pss, (int)sf); + Lp->tid = tid; + Plf = (struct lfile *)NULL; +/* + * Process the ID's current working directory info. + */ + if (!Ckscko) { + (void) make_proc_path(idp, idpl, &path, &pathl, "cwd"); + alloc_lfile(CWD, -1); + efs = 0; + if (getlinksrc(path, pbuf, sizeof(pbuf), (char **)NULL) < 1) { + if (!Fwarn) { + (void) memset((void *)&sb, 0, sizeof(sb)); + lnk = ss = 0; + (void) snpf(nmabuf, sizeof(nmabuf), "(readlink: %s)", + strerror(errno)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + pn = 1; + } else + pn = 0; + } else { + lnk = pn = 1; + if (Efsysl && !isefsys(pbuf, "UNKNcwd", 1, NULL, &lfr)) { + efs = 1; + pn = 0; + } else { + ss = SB_ALL; + if (HasNFS) { + if ((sv = statsafely(path, &sb))) + sv = statEx(pbuf, &sb, &ss); + } else + sv = stat(path, &sb); + if (sv) { + ss = 0; + if (!Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)", + strerror(errno)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + } + } + } + if (pn) { + (void) process_proc_node(lnk ? pbuf : path, + path, &sb, ss, + (struct stat *)NULL, 0); + if (Lf->sf) + link_lfile(); + } + } +/* + * Process the ID's root directory info. + */ + if (!Ckscko) { + (void) make_proc_path(idp, idpl, &path, &pathl, "root"); + alloc_lfile(RTD, -1); + if (getlinksrc(path, pbuf, sizeof(pbuf), (char **)NULL) < 1) { + if (!Fwarn) { + (void) memset((void *)&sb, 0, sizeof(sb)); + lnk = ss = 0; + (void) snpf(nmabuf, sizeof(nmabuf), "(readlink: %s)", + strerror(errno)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + pn = 1; + } else + pn = 0; + } else { + lnk = pn = 1; + if (Efsysl && !isefsys(pbuf, "UNKNrtd", 1, NULL, NULL)) + pn = 0; + else { + ss = SB_ALL; + if (HasNFS) { + if ((sv = statsafely(path, &sb))) + sv = statEx(pbuf, &sb, &ss); + } else + sv = stat(path, &sb); + if (sv) { + ss = 0; + if (!Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)", + strerror(errno)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + } + } + } + if (pn) { + (void) process_proc_node(lnk ? pbuf : path, + path, &sb, ss, + (struct stat *)NULL, 0); + if (Lf->sf) + link_lfile(); + } + } +/* + * Process the ID's execution info. + */ + if (!Ckscko) { + txts = 0; + (void) make_proc_path(idp, idpl, &path, &pathl, "exe"); + alloc_lfile("txt", -1); + if (getlinksrc(path, pbuf, sizeof(pbuf), (char **)NULL) < 1) { + (void) memset((void *)&sb, 0, sizeof(sb)); + lnk = ss = 0; + if (!Fwarn) { + if ((errno != ENOENT) || uid) { + (void) snpf(nmabuf, sizeof(nmabuf), "(readlink: %s)", + strerror(errno)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + pn = 1; + } else + pn = 0; + } else { + lnk = pn = 1; + if (Efsysl && !isefsys(pbuf, "UNKNtxt", 1, NULL, NULL)) + pn = 0; + else { + ss = SB_ALL; + if (HasNFS) { + if ((sv = statsafely(path, &sb))) { + sv = statEx(pbuf, &sb, &ss); + if (!sv && (ss & SB_DEV) && (ss & SB_INO)) + txts = 1; + } + } else + sv = stat(path, &sb); + if (sv) { + ss = 0; + if (!Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)", + strerror(errno)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + } else + txts = 1; + } + } + if (pn) { + (void) process_proc_node(lnk ? pbuf : path, + path, &sb, ss, + (struct stat *)NULL, 0); + if (Lf->sf) + link_lfile(); + } + } +/* + * Process the ID's memory map info. + */ + if (!Ckscko) { + (void) make_proc_path(idp, idpl, &path, &pathl, "maps"); + (void) process_proc_map(path, txts ? &sb : (struct stat *)NULL, + txts ? ss : 0); + } + +#if defined(HASSELINUX) +/* + * Process the PID's SELinux context. + */ + if (Fcntx) { + + /* + * If the -Z (cntx) option was specified, match the valid contexts. + */ + errno = 0; + if (getpidcon(pid, &Lp->cntx) == -1) { + Lp->cntx = (char *)NULL; + if (!Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), + "(getpidcon: %s)", strerror(errno)); + if (!(Lp->cntx = strdup(nmabuf))) { + (void) fprintf(stderr, + "%s: no context error space: PID %ld", + Pn, (long)Lp->pid); + Exit(1); + } + } + } else if (CntxArg) { + + /* + * See if context includes the process. + */ + for (cntxp = CntxArg; cntxp; cntxp = cntxp->next) { + if (cmp_cntx_eq(Lp->cntx, cntxp->cntx)) { + cntxp->f = 1; + Lp->pss |= PS_PRI; + Lp->sf |= SELCNTX; + break; + } + } + } + } +#endif /* defined(HASSELINUX) */ + +/* + * Process the ID's file descriptor directory. + */ + if ((i = make_proc_path(idp, idpl, &dpath, &dpathl, "fd/")) < 3) + return(0); + dpath[i - 1] = '\0'; + if ((OffType == 2) + && ((j = make_proc_path(idp, idpl, &ipath, &ipathl, "fdinfo/")) >= 7)) + oty = 1; + else + oty = 0; + if (!(fdp = opendir(dpath))) { + if (!Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), "%s (opendir: %s)", + dpath, strerror(errno)); + alloc_lfile("NOFD", -1); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + link_lfile(); + } + return(0); + } + dpath[i - 1] = '/'; + while ((fp = readdir(fdp))) { + if (nm2id(fp->d_name, &fd, &n)) + continue; + (void) make_proc_path(dpath, i, &path, &pathl, fp->d_name); + (void) alloc_lfile((char *)NULL, fd); + if (getlinksrc(path, pbuf, sizeof(pbuf), &rest) < 1) { + (void) memset((void *)&sb, 0, sizeof(sb)); + lnk = ss = 0; + if (!Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), "(readlink: %s)", + strerror(errno)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + pn = 1; + } else + pn = 0; + } else { + lnk = 1; + if (Efsysl && !isefsys(pbuf, "UNKNfd", 1, NULL, &lfr)) { + efs = 1; + pn = 0; + } else { + if (HasNFS) { + if (lstatsafely(path, &lsb)) { + (void) statEx(pbuf, &lsb, &ls); + enls = errno; + } else { + enls = 0; + ls = SB_ALL; + } + if (statsafely(path, &sb)) { + (void) statEx(pbuf, &sb, &ss); + enss = errno; + } else { + enss = 0; + ss = SB_ALL; + } + } else { + ls = lstat(path, &lsb) ? 0 : SB_ALL; + enls = errno; + ss = stat(path, &sb) ? 0 : SB_ALL; + enss = errno; + } + if (!ls && !Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), "lstat: %s)", + strerror(enls)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + if (!ss && !Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)", + strerror(enss)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + if (Ckscko) { + if ((ss & SB_MODE) + && ((sb.st_mode & S_IFMT) == S_IFSOCK)) + { + pn = 1; + } else + pn = 0; + } else + pn = 1; + } + } + if (pn || (efs && lfr && oty)) { + if (oty) { + (void) make_proc_path(ipath, j, &pathi, &pathil, + fp->d_name); + if ((av = get_fdinfo(pathi, &fi)) & FDINFO_POS) { + if (efs) { + if (Foffset) { + lfr->off = (SZOFFTYPE)fi.pos; + lfr->off_def = 1; + } + } else { + ls |= SB_SIZE; + lsb.st_size = fi.pos; + } + } else + ls &= ~SB_SIZE; + +#if !defined(HASNOFSFLAGS) + if ((av & FDINFO_FLAGS) && (Fsv & FSV_FG)) { + if (efs) { + lfr->ffg = (long)fi.flags; + lfr->fsv |= FSV_FG; + } else { + Lf->ffg = (long)fi.flags; + Lf->fsv |= FSV_FG; + } + } +# endif /* !defined(HASNOFSFLAGS) */ + + } + if (pn) { + process_proc_node(lnk ? pbuf : path, path, &sb, ss, &lsb, + ls); + if ((Lf->ntype == N_ANON_INODE) && rest && *rest) + enter_nm(rest); + if (Lf->sf) + link_lfile(); + } + } + } + (void) closedir(fdp); + return(0); +} + + +/* + * process_proc_map() - process the memory map of a process + */ + +static void +process_proc_map(p, s, ss) + char *p; /* path to process maps file */ + struct stat *s; /* executing text file state buffer */ + int ss; /* *s status -- i.e., SB_* values */ +{ + char buf[MAXPATHLEN + 1], *ep, fmtbuf[32], **fp, nmabuf[MAXPATHLEN + 1]; + dev_t dev; + int ds, efs, en, i, mss, nf, sv; + int eb = 6; + INODETYPE inode; + MALLOC_S len; + long maj, min; + FILE *ms; + int ns = 0; + struct stat sb; + struct saved_map { + dev_t dev; + INODETYPE inode; + }; + static struct saved_map *sm = (struct saved_map *)NULL; + efsys_list_t *rep; + static int sma = 0; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; +/* + * Open the /proc/<pid>/maps file, assign a page size buffer to its stream, + * and read it/ + */ + if (!(ms = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf), ms)) { + if ((nf = get_fields(buf, ":", &fp, &eb, 1)) < 7) + continue; /* not enough fields */ + if (!fp[6] || !*fp[6]) + continue; /* no path name */ + /* + * See if the path ends in " (deleted)". If it does, strip the + * " (deleted)" characters and remember that they were there. + */ + if (((ds = (int)strlen(fp[6])) > 10) + && !strcmp(fp[6] + ds - 10, " (deleted)")) + { + *(fp[6] + ds - 10) = '\0'; + } else + ds = 0; + /* + * Assemble the major and minor device numbers. + */ + ep = (char *)NULL; + if (!fp[3] || !*fp[3] + || (maj = strtol(fp[3], &ep, 16)) == LONG_MIN || maj == LONG_MAX + || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[4] || !*fp[4] + || (min = strtol(fp[4], &ep, 16)) == LONG_MIN || min == LONG_MAX + || !ep || *ep) + continue; + /* + * Assemble the device and inode numbers. If they are both zero, skip + * the entry. + */ + dev = (dev_t)makedev((int)maj, (int)min); + if (!fp[5] || !*fp[5]) + continue; + ep = (char *)NULL; + if ((inode = strtoull(fp[5], &ep, 0)) == ULLONG_MAX + || !ep || *ep) + continue; + if (!dev && !inode) + continue; + /* + * See if the device + inode pair match that of the executable. + * If they do, skip this map entry. + */ + if (s && (ss & SB_DEV) && (ss & SB_INO) + && (dev == s->st_dev) && (inode == (INODETYPE)s->st_ino)) + continue; + /* + * See if this device + inode pair has already been processed as + * a map entry. + */ + for (i = 0; i < ns; i++) { + if (dev == sm[i].dev && inode == sm[i].inode) + break; + } + if (i < ns) + continue; + /* + * Record the processing of this map entry's device and inode pair. + */ + if (ns >= sma) { + sma += 10; + len = (MALLOC_S)(sma * sizeof(struct saved_map)); + if (sm) + sm = (struct saved_map *)realloc(sm, len); + else + sm = (struct saved_map *)malloc(len); + if (!sm) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for saved maps, PID %d\n", + Pn, (int)len, Lp->pid); + Exit(1); + } + } + sm[ns].dev = dev; + sm[ns++].inode = inode; + /* + * Allocate space for the mapped file, then get stat(2) information + * for it. Skip the stat(2) operation if this is on an exempt file + * system. + */ + alloc_lfile("mem", -1); + if (Efsysl && !isefsys(fp[6], (char *)NULL, 0, &rep, NULL)) + efs = sv = 1; + else + efs = 0; + if (!efs) { + if (HasNFS) + sv = statsafely(fp[6], &sb); + else + sv = stat(fp[6], &sb); + } + if (sv || efs) { + en = errno; + /* + * Applying stat(2) to the file was not possible (file is on an + * exempt file system) or stat(2) failed, so manufacture a partial + * stat(2) reply from the process' maps file entry. + * + * If the file has been deleted, reset its type to "DEL"; otherwise + * generate a stat() error name addition. + */ + (void) memset((void *)&sb, 0, sizeof(sb)); + sb.st_dev = dev; + sb.st_ino = (ino_t)inode; + sb.st_mode = S_IFREG; + mss = SB_DEV | SB_INO | SB_MODE; + if (ds) + alloc_lfile("DEL", -1); + else if (!efs && !Fwarn) { + (void) snpf(nmabuf, sizeof(nmabuf), "(stat: %s)", + strerror(en)); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + } else if ((sb.st_dev != dev) || ((INODETYPE)sb.st_ino != inode)) { + + /* + * The stat(2) device and inode numbers don't match those obtained + * from the process' maps file. + * + * If the file has been deleted, reset its type to "DEL"; otherwise + * generate inconsistency name additions. + * + * Manufacture a partial stat(2) reply from the maps file + * information. + */ + if (ds) + alloc_lfile("DEL", -1); + else if (!Fwarn) { + char *sep; + + if (sb.st_dev != dev) { + (void) snpf(nmabuf, sizeof(nmabuf), + "(path dev=%d,%d%s", + GET_MAJ_DEV(sb.st_dev), GET_MIN_DEV(sb.st_dev), + ((INODETYPE)sb.st_ino == inode) ? ")" : ","); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + sep = ""; + } else + sep = "(path "; + if ((INODETYPE)sb.st_ino != inode) { + (void) snpf(fmtbuf, sizeof(fmtbuf), "%%sinode=%s)", + InodeFmt_d); + (void) snpf(nmabuf, sizeof(nmabuf), fmtbuf, + sep, (INODETYPE)sb.st_ino); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + } + (void) memset((void *)&sb, 0, sizeof(sb)); + sb.st_dev = dev; + sb.st_ino = (ino_t)inode; + sb.st_mode = S_IFREG; + mss = SB_DEV | SB_INO | SB_MODE; + } else + mss = SB_ALL; + /* + * Record the file's information. + */ + if (!efs) + process_proc_node(fp[6], fp[6], &sb, mss, (struct stat *)NULL, + 0); + else { + + /* + * If this file is on an exempt file system, complete the lfile + * structure, but change its type and add the exemption note to + * the NAME column. + */ + Lf->dev = sb.st_dev; + Lf->inode = (ino_t)sb.st_ino; + Lf->dev_def = Lf->inp_ty = 1; + (void) enter_nm(fp[6]); + (void) snpf(Lf->type, sizeof(Lf->type), "%s", + (ds ? "UNKNdel" : "UNKNmem")); + (void) snpf(nmabuf, sizeof(nmabuf), "(%ce %s)", + rep->rdlnk ? '+' : '-', rep->path); + nmabuf[sizeof(nmabuf) - 1] = '\0'; + (void) add_nma(nmabuf, strlen(nmabuf)); + } + if (Lf->sf) + link_lfile(); + } + (void) fclose(ms); +} + + +/* + * read_id_stat() - read ID (PID or LWP ID) status + * + * return: -1 == ID is unavailable + * 0 == ID OK + * 1 == ID is a zombie + * 2 == ID is a thread + */ + +static int +read_id_stat(ty, p, id, cmd, ppid, pgid) + int ty; /* type: 0 == PID, 1 == LWP */ + char *p; /* path to status file */ + int id; /* ID: PID or LWP */ + char **cmd; /* malloc'd command name */ + int *ppid; /* returned parent PID for PID type */ + int *pgid; /* returned process group ID for PID + * type */ +{ + char buf[MAXPATHLEN], *cp, *cp1, **fp; + static char *cbf = (char *)NULL; + static MALLOC_S cbfa = 0; + FILE *fs; + MALLOC_S len; + int nf; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; +/* + * Open the stat file path, assign a page size buffer to its stream, + * and read the file's first line. + */ + if (!(fs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return(-1); + cp = fgets(buf, sizeof(buf), fs); + (void) fclose(fs); + if (!cp) + return(-1); +/* + * Separate the line into fields on white space separators. Expect five fields + * for a PID type and three for an LWP type. + */ + if ((nf = get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0)) + < (ty ? 5 : 3)) + { + return(-1); + } +/* + * Convert the first field to an integer; its conversion must match the + * ID argument. + */ + if (!fp[0] || (atoi(fp[0]) != id)) + return(-1); +/* + * Get the command name from the second field. Strip a starting '(' and + * an ending ')'. Allocate space to hold the result and return the space + * pointer. + */ + if (!(cp = fp[1])) + return(-1); + if (cp && *cp == '(') + cp++; + if ((cp1 = strrchr(cp, ')'))) + *cp1 = '\0'; + if ((len = strlen(cp) + 1) > cbfa) { + cbfa = len; + if (cbf) + cbf = (char *)realloc((MALLOC_P *)cbf, cbfa); + else + cbf = (char *)malloc(cbfa); + if (!cbf) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for command \"%s\"\n", + Pn, (int)cbfa, cp); + Exit(1); + } + } + (void) snpf(cbf, len, "%s", cp); + *cmd = cbf; +/* + * Convert and return parent process (fourth field) and process group (fifth + * field) IDs. + */ + if (fp[3] && *fp[3]) + *ppid = atoi(fp[3]); + else + return(-1); + if (fp[4] && *fp[4]) + *pgid = atoi(fp[4]); + else + return(-1); +/* + * Check the state in the third field. If it is 'Z', return that indication. + */ + if (fp[2] && !strcmp(fp[2], "Z")) + return(1); + else if (fp[2] && !strcmp(fp[2], "T")) + return(2); + return(0); +} + + +/* + * statEx() - extended stat() to get device numbers when a "safe" stat has + * failed and the system has an NFS mount + * + * Note: this function was suggested by Paul Szabo as a way to get device + * numbers for NFS files when an NFS mount point has the root_squash + * option set. In that case, even if lsof is setuid(root), the identity + * of its requests to stat() NFS files lose root permission and may fail. + * + * This function should be used only when links have been successfully + * resolved in the /proc path by getlinksrc(). + */ + +static int +statEx(p, s, ss) + char *p; /* file path */ + struct stat *s; /* stat() result -- NULL if none + * wanted */ + int *ss; /* stat() status -- SB_* values */ +{ + static size_t ca = 0; + static char *cb = NULL; + char *cp; + int ensv = ENOENT; + struct stat sb; + int st = 0; + size_t sz; +/* + * Make a copy of the path. + */ + sz = strlen(p); + if ((sz + 1) > ca) { + if (cb) + cb = (char *)realloc((MALLOC_P *)cb, sz + 1); + else + cb = (char *)malloc(sz + 1); + if (!cb) { + (void) fprintf(stderr, + "%s: PID %ld: no statEx path space: %s\n", + Pn, (long)Lp->pid, p); + Exit(1); + } + ca = sz + 1; + } + (void) strcpy(cb, p); +/* + * Trim trailing leaves from the end of the path one at a time and do a safe + * stat() on each trimmed result. Stop when a safe stat() succeeds or doesn't + * fail because of EACCES or EPERM. + */ + for (cp = strrchr(cb, '/'); cp && (cp != cb);) { + *cp = '\0'; + if (!statsafely(cb, &sb)) { + st = 1; + break; + } + ensv = errno; + if ((ensv != EACCES) && (ensv != EPERM)) + break; + cp = strrchr(cb, '/'); + } +/* + * If a stat() on a trimmed result succeeded, form partial results containing + * only the device and raw device numbers. + */ + memset((void *)s, 0, sizeof(struct stat)); + if (st) { + errno = 0; + s->st_dev = sb.st_dev; + s->st_rdev = sb.st_rdev; + *ss = SB_DEV | SB_RDEV; + return(0); + } + errno = ensv; + *ss = 0; + return(1); +} diff --git a/dialects/linux/dproto.h b/dialects/linux/dproto.h new file mode 100644 index 0000000..1574bbc --- /dev/null +++ b/dialects/linux/dproto.h @@ -0,0 +1,51 @@ +/* + * dproto.h - Linux function prototypes for /proc-based lsof + * + * The _PROTOTYPE macro is defined in the common proto.h. + */ + + +/* + * 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. + */ + + +/* + * $Id: dproto.h,v 1.9 2013/01/02 17:02:36 abe Exp $ + */ + + +#if defined(HASSELINUX) +_PROTOTYPE(extern int enter_cntx_arg,(char *cnxt)); +#endif /* defined(HASSELINUX) */ + +_PROTOTYPE(extern int get_fields,(char *ln, char *sep, char ***fr, int *eb, int en)); +_PROTOTYPE(extern void get_locks,(char *p)); +_PROTOTYPE(extern int is_file_named,(int ty, char *p, struct mounts *mp, int cd)); +_PROTOTYPE(extern int make_proc_path,(char *pp, int lp, char **np, int *npl, char *sf)); +_PROTOTYPE(extern FILE *open_proc_stream,(char *p, char *mode, char **buf, size_t *sz, int act)); +_PROTOTYPE(extern void process_proc_node,(char *p, char *pbr, struct stat *s, int ss, struct stat *l, int ls)); +_PROTOTYPE(extern void process_proc_sock,(char *p, char *pbr, struct stat *s, int ss, struct stat *l, int ls)); +_PROTOTYPE(extern void set_net_paths,(char *p, int pl)); diff --git a/dialects/linux/dsock.c b/dialects/linux/dsock.c new file mode 100644 index 0000000..9109b2f --- /dev/null +++ b/dialects/linux/dsock.c @@ -0,0 +1,3946 @@ +/* + * dsock.c - Linux socket processing 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: dsock.c,v 1.39 2013/01/02 17:02:36 abe Exp $"; +#endif + + +#include "lsof.h" +#include <sys/xattr.h> + + +/* + * Local definitions + */ + +#define INOBUCKS 128 /* inode hash bucket count -- must be + * a power of two */ +#define INOHASH(ino) ((int)((ino * 31415) >> 3) & (INOBUCKS - 1)) +#define TCPUDPHASH(ino) ((int)((ino * 31415) >> 3) & (TcpUdp_bucks - 1)) +#define TCPUDP6HASH(ino) ((int)((ino * 31415) >> 3) & (TcpUdp6_bucks - 1)) + + +/* + * Local structures + */ + +struct ax25sin { /* AX25 socket information */ + char *da; /* destination address */ + char *dev_ch; /* device characters */ + char *sa; /* source address */ + INODETYPE inode; + unsigned long sq, rq; /* send and receive queue values */ + unsigned char sqs, rqs; /* send and receive queue states */ + int state; + struct ax25sin *next; +}; + +struct icmpin { + INODETYPE inode; /* node number */ + char *la; /* local address */ + char *ra; /* remote address */ + MALLOC_S lal; /* strlen(la) */ + MALLOC_S ral; /* strlen(ra) */ + struct icmpin *next; +}; + +struct ipxsin { /* IPX socket information */ + INODETYPE inode; + char *la; /* local address */ + char *ra; /* remote address */ + int state; + unsigned long txq, rxq; /* transmit and receive queue values */ + struct ipxsin *next; +}; + +struct nlksin { /* Netlink socket information */ + INODETYPE inode; /* node number */ + unsigned int pr; /* protocol */ + struct nlksin *next; +}; + +struct packin { /* packet information */ + INODETYPE inode; + int ty; /* socket type */ + int pr; /* protocol */ + struct packin *next; +}; + +struct rawsin { /* raw socket information */ + INODETYPE inode; + char *la; /* local address */ + char *ra; /* remote address */ + char *sp; /* state characters */ + MALLOC_S lal; /* strlen(la) */ + MALLOC_S ral; /* strlen(ra) */ + MALLOC_S spl; /* strlen(sp) */ + struct rawsin *next; +}; + +struct sctpsin { /* SCTP socket information */ + INODETYPE inode; + int type; /* type: 0 = assoc + * 1 = eps + * 2 assoc and eps */ + char *addr; /* association or endpoint address */ + char *assocID; /* association ID */ + char *lport; /* local port */ + char *rport; /* remote port */ + char *laddrs; /* local address */ + char *raddrs; /* remote address */ + struct sctpsin *next; +}; + +struct tcp_udp { /* IPv4 TCP and UDP socket + * information */ + INODETYPE inode; + unsigned long faddr, laddr; /* foreign & local IPv6 addresses */ + int fport, lport; /* foreign & local ports */ + unsigned long txq, rxq; /* transmit & receive queue values */ + int proto; /* 0 = TCP, 1 = UDP, 2 = UDPLITE */ + int state; /* protocol state */ + struct tcp_udp *next; +}; + +#if defined(HASIPv6) +struct tcp_udp6 { /* IPv6 TCP and UDP socket + * information */ + INODETYPE inode; + struct in6_addr faddr, laddr; /* foreign and local IPv6 addresses */ + int fport, lport; /* foreign & local ports */ + unsigned long txq, rxq; /* transmit & receive queue values */ + int proto; /* 0 = TCP, 1 = UDP, 2 = UDPLITE */ + int state; /* protocol state */ + struct tcp_udp6 *next; +}; +#endif /* defined(HASIPv6) */ + +struct uxsin { /* UNIX socket information */ + INODETYPE inode; /* node number */ + char *pcb; /* protocol control block */ + char *path; /* file path */ + unsigned char sb_def; /* stat(2) buffer definitions */ + dev_t sb_dev; /* stat(2) buffer device */ + INODETYPE sb_ino; /* stat(2) buffer node number */ + dev_t sb_rdev; /* stat(2) raw device number */ + struct uxsin *next; +}; + + +/* + * Local static values + */ + +static char *AX25path = (char *)NULL; /* path to AX25 /proc information */ +static struct ax25sin **AX25sin = (struct ax25sin **)NULL; + /* AX25 socket info, hashed by inode */ +static char *ax25st[] = { + "LISTENING", /* 0 */ + "SABM SENT", /* 1 */ + "DISC SENT", /* 2 */ + "ESTABLISHED", /* 3 */ + "RECOVERY" /* 4 */ +}; +#define NAX25ST (sizeof(ax25st) / sizeof(char *)) +static char *ICMPpath = (char *)NULL; /* path to ICMP /proc information */ +static struct icmpin **Icmpin = (struct icmpin **)NULL; + /* ICMP socket info, hashed by inode */ +static char *Ipxpath = (char *)NULL; /* path to IPX /proc information */ +static struct ipxsin **Ipxsin = (struct ipxsin **)NULL; + /* IPX socket info, hashed by inode */ +static char *Nlkpath = (char *)NULL; /* path to Netlink /proc information */ +static struct nlksin **Nlksin = (struct nlksin **)NULL; + /* Netlink socket info, hashed by + * inode */ +static struct packin **Packin = (struct packin **)NULL; + /* packet info, hashed by inode */ +static char *Packpath = (char *)NULL; /* path to packet /proc information */ +static char *Rawpath = (char *)NULL; /* path to raw socket /proc + * information */ +static struct rawsin **Rawsin = (struct rawsin **)NULL; + /* raw socket info, hashed by inode */ +static char *SCTPPath[] = { /* paths to /proc/net STCP info */ + (char *)NULL, /* 0 = /proc/net/sctp/assocs */ + (char *)NULL /* 1 = /proc/net/sctp/eps */ +}; +#define NSCTPPATHS sizeof(SCTPPath)/sizeof(char *) +static char *SCTPSfx[] = { /* /proc/net suffixes */ + "sctp/assocs", /* 0 = /proc/net/sctp/assocs */ + "sctp/eps" /* 1 = /proc/net/sctp/eps */ +}; +static struct sctpsin **SCTPsin = (struct sctpsin **)NULL; + /* SCTP info, hashed by inode */ +static char *SockStatPath = (char *)NULL; + /* path to /proc/net socket status */ +static char *TCPpath = (char *)NULL; /* path to TCP /proc information */ +static struct tcp_udp **TcpUdp = (struct tcp_udp **)NULL; + /* IPv4 TCP & UDP info, hashed by + * inode */ +static int TcpUdp_bucks = 0; /* dynamically sized hash bucket + * count for TCP and UDP -- will + * be a power of two */ + +#if defined(HASIPv6) +static char *Raw6path = (char *)NULL; /* path to raw IPv6 /proc information */ +static struct rawsin **Rawsin6 = (struct rawsin **)NULL; + /* IPv6 raw socket info, hashed by + * inode */ +static char *SockStatPath6 = (char *)NULL; + /* path to /proc/net IPv6 socket + * status */ +static char *TCP6path = (char *)NULL; /* path to IPv6 TCP /proc information */ +static struct tcp_udp6 **TcpUdp6 = (struct tcp_udp6 **)NULL; + /* IPv6 TCP & UDP info, hashed by + * inode */ +static int TcpUdp6_bucks = 0; /* dynamically sized hash bucket + * count for IPv6 TCP and UDP -- will + * be a power of two */ +static char *UDP6path = (char *)NULL; /* path to IPv6 UDP /proc information */ +static char *UDPLITE6path = (char *)NULL; + /* path to IPv6 UDPLITE /proc + * information */ +#endif /* defined(HASIPv6) */ + +static char *UDPpath = (char *)NULL; /* path to UDP /proc information */ +static char *UDPLITEpath = (char *)NULL; + /* path to UDPLITE /proc information */ +static char *UNIXpath = (char *)NULL; /* path to UNIX /proc information */ +static struct uxsin **Uxsin = (struct uxsin **)NULL; + /* UNIX socket info, hashed by inode */ + + +/* + * Local function prototypes + */ + +_PROTOTYPE(static struct ax25sin *check_ax25,(INODETYPE i)); +_PROTOTYPE(static struct icmpin *check_icmp,(INODETYPE i)); +_PROTOTYPE(static struct ipxsin *check_ipx,(INODETYPE i)); +_PROTOTYPE(static struct nlksin *check_netlink,(INODETYPE i)); +_PROTOTYPE(static struct packin *check_pack,(INODETYPE i)); +_PROTOTYPE(static struct rawsin *check_raw,(INODETYPE i)); +_PROTOTYPE(static struct sctpsin *check_sctp,(INODETYPE i)); +_PROTOTYPE(static struct tcp_udp *check_tcpudp,(INODETYPE i, char **p)); +_PROTOTYPE(static struct uxsin *check_unix,(INODETYPE i)); +_PROTOTYPE(static void get_ax25,(char *p)); +_PROTOTYPE(static void get_icmp,(char *p)); +_PROTOTYPE(static void get_ipx,(char *p)); +_PROTOTYPE(static void get_netlink,(char *p)); +_PROTOTYPE(static void get_pack,(char *p)); +_PROTOTYPE(static void get_raw,(char *p)); +_PROTOTYPE(static void get_sctp,(void)); +_PROTOTYPE(static char *get_sctpaddrs,(char **fp, int i, int nf, int *x)); +_PROTOTYPE(static void get_tcpudp,(char *p, int pr, int clr)); +_PROTOTYPE(static void get_unix,(char *p)); +_PROTOTYPE(static int isainb,(char *a, char *b)); +_PROTOTYPE(static void print_ax25info,(struct ax25sin *ap)); +_PROTOTYPE(static void print_ipxinfo,(struct ipxsin *ip)); + +#if defined(HASIPv6) +_PROTOTYPE(static struct rawsin *check_raw6,(INODETYPE i)); +_PROTOTYPE(static struct tcp_udp6 *check_tcpudp6,(INODETYPE i, char **p)); +_PROTOTYPE(static void get_raw6,(char *p)); +_PROTOTYPE(static void get_tcpudp6,(char *p, int pr, int clr)); +_PROTOTYPE(static int net6a2in6,(char *as, struct in6_addr *ad)); +#endif /* defined(HASIPv6) */ + + +/* + * build_IPstates() -- build the TCP and UDP state tables + */ + +void +build_IPstates() +{ + if (!TcpSt) { + (void) enter_IPstate("TCP", "ESTABLISHED", TCP_ESTABLISHED); + (void) enter_IPstate("TCP", "SYN_SENT", TCP_SYN_SENT); + (void) enter_IPstate("TCP", "SYN_RECV", TCP_SYN_RECV); + (void) enter_IPstate("TCP", "FIN_WAIT1", TCP_FIN_WAIT1); + (void) enter_IPstate("TCP", "FIN_WAIT2", TCP_FIN_WAIT2); + (void) enter_IPstate("TCP", "TIME_WAIT", TCP_TIME_WAIT); + (void) enter_IPstate("TCP", "CLOSE", TCP_CLOSE); + (void) enter_IPstate("TCP", "CLOSE_WAIT", TCP_CLOSE_WAIT); + (void) enter_IPstate("TCP", "LAST_ACK", TCP_LAST_ACK); + (void) enter_IPstate("TCP", "LISTEN", TCP_LISTEN); + (void) enter_IPstate("TCP", "CLOSING", TCP_CLOSING); + (void) enter_IPstate("TCP", "CLOSED", 0); + (void) enter_IPstate("TCP", (char *)NULL, 0); + } +} + + +/* + * check_ax25() - check for AX25 socket file + */ + +static struct ax25sin * +check_ax25(i) + INODETYPE i; /* socket file's inode number */ +{ + struct ax25sin *ap; + int h; + + h = INOHASH(i); + for (ap = AX25sin[h]; ap; ap = ap->next) { + if (i == ap->inode) + return(ap); + } + return((struct ax25sin *)NULL); +} + + + +/* + * check_icmp() - check for ICMP socket + */ + +static struct icmpin * +check_icmp(i) + INODETYPE i; /* socket file's inode number */ +{ + int h; + struct icmpin *icmpp; + + h = INOHASH(i); + for (icmpp = Icmpin[h]; icmpp; icmpp = icmpp->next) { + if (i == icmpp->inode) + return(icmpp); + } + return((struct icmpin *)NULL); +} + + +/* + * check_ipx() - check for IPX socket file + */ + +static struct ipxsin * +check_ipx(i) + INODETYPE i; /* socket file's inode number */ +{ + int h; + struct ipxsin *ip; + + h = INOHASH(i); + for (ip = Ipxsin[h]; ip; ip = ip->next) { + if (i == ip->inode) + return(ip); + } + return((struct ipxsin *)NULL); +} + + +/* + * check_netlink() - check for Netlink socket file + */ + +static struct nlksin * +check_netlink(i) + INODETYPE i; /* socket file's inode number */ +{ + int h; + struct nlksin *lp; + + h = INOHASH(i); + for (lp = Nlksin[h]; lp; lp = lp->next) { + if (i == lp->inode) + return(lp); + } + return((struct nlksin *)NULL); +} + + +/* + * check_pack() - check for packet file + */ + +static struct packin * +check_pack(i) + INODETYPE i; /* packet file's inode number */ +{ + int h; + struct packin *pp; + + h = INOHASH(i); + for (pp = Packin[h]; pp; pp = pp->next) { + if (i == pp->inode) + return(pp); + } + return((struct packin *)NULL); +} + + +/* + * check_raw() - check for raw socket file + */ + +static struct rawsin * +check_raw(i) + INODETYPE i; /* socket file's inode number */ +{ + int h; + struct rawsin *rp; + + h = INOHASH(i); + for (rp = Rawsin[h]; rp; rp = rp->next) { + if (i == rp->inode) + return(rp); + } + return((struct rawsin *)NULL); +} + + +/* + * check_sctp() - check for SCTP socket file + */ + +static struct sctpsin * +check_sctp(i) + INODETYPE i; /* socket file's inode number */ +{ + int h; + struct sctpsin *sp; + + h = INOHASH(i); + for (sp = SCTPsin[h]; sp; sp = sp->next) { + if (i == sp->inode) + return(sp); + } + return((struct sctpsin *)NULL); +} + + +/* + * check_tcpudp() - check for IPv4 TCP or UDP socket file + */ + +static struct tcp_udp * +check_tcpudp(i, p) + INODETYPE i; /* socket file's inode number */ + char **p; /* protocol return */ +{ + int h; + struct tcp_udp *tp; + + h = TCPUDPHASH(i); + for (tp = TcpUdp[h]; tp; tp = tp->next) { + if (i == tp->inode) { + switch (tp->proto) { + case 0: + *p = "TCP"; + break; + case 1: + *p = "UDP"; + break; + case 2: + *p = "UDPLITE"; + break; + default: + *p = "unknown"; + } + return(tp); + } + } + return((struct tcp_udp *)NULL); +} + + +#if defined(HASIPv6) +/* + * check_raw6() - check for raw IPv6 socket file + */ + +static struct rawsin * +check_raw6(i) + INODETYPE i; /* socket file's inode number */ +{ + int h; + struct rawsin *rp; + + h = INOHASH(i); + for (rp = Rawsin6[h]; rp; rp = rp->next) { + if (i == rp->inode) + return(rp); + } + return((struct rawsin *)NULL); +} + + +/* + * check_tcpudp6() - check for IPv6 TCP or UDP socket file + */ + +static struct tcp_udp6 * +check_tcpudp6(i, p) + INODETYPE i; /* socket file's inode number */ + char **p; /* protocol return */ +{ + int h; + struct tcp_udp6 *tp6; + + h = TCPUDP6HASH(i); + for (tp6 = TcpUdp6[h]; tp6; tp6 = tp6->next) { + if (i == tp6->inode) { + switch (tp6->proto) { + case 0: + *p = "TCP"; + break; + case 1: + *p = "UDP"; + break; + case 2: + *p = "UDPLITE"; + break; + default: + *p = "unknown"; + } + return(tp6); + } + } + return((struct tcp_udp6 *)NULL); +} +#endif /* defined(HASIPv6) */ + + +/* + * check_unix() - check for UNIX domain socket + */ + +static struct uxsin * +check_unix(i) + INODETYPE i; /* socket file's inode number */ +{ + int h; + struct uxsin *up; + + h = INOHASH(i); + for (up = Uxsin[h]; up; up = up->next) { + if (i == up->inode) + return(up); + } + return((struct uxsin *)NULL); +} + + +/* + * get_ax25() - get /proc/net/ax25 info + */ + +static void +get_ax25(p) + char *p; /* /proc/net/ipx path */ +{ + struct ax25sin *ap, *np; + FILE *as; + char buf[MAXPATHLEN], *da, *dev_ch, *ep, **fp, *sa; + int h, nf; + INODETYPE inode; + unsigned long rq, sq, state; + MALLOC_S len; + unsigned char rqs, sqs; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; +/* + * Do second time cleanup or first time setup. + */ + if (AX25sin) { + for (h = 0; h < INOBUCKS; h++) { + for (ap = AX25sin[h]; ap; ap = np) { + np = ap->next; + if (ap->da) + (void) free((FREE_P *)ap->da); + if (ap->dev_ch) + (void) free((FREE_P *)ap->dev_ch); + if (ap->sa) + (void) free((FREE_P *)ap->sa); + (void) free((FREE_P *)ap); + } + AX25sin[h] = (struct ax25sin *)NULL; + } + } else { + AX25sin = (struct ax25sin **)calloc(INOBUCKS, + sizeof(struct ax25sin *)); + if (!AX25sin) { + (void) fprintf(stderr, + "%s: can't allocate %d AX25 hash pointer bytes\n", + Pn, (int)(INOBUCKS * sizeof(struct ax25sin *))); + Exit(1); + } + } +/* + * Open the /proc/net/ax25 file, assign a page size buffer to the stream, + * and read it. Store AX25 socket info in the AX25sin[] hash buckets. + */ + if (!(as = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf) - 1, as)) { + if ((nf = get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0)) < 24) + continue; + /* + * /proc/net/ax25 has no title line, a very poor deficiency in its + * implementation. + * + * The ax25_get_info() function in kern module .../net/ax25/af_ax25.c + * says the format of the lines in the file is: + * + * magic dev src_addr dest_addr,digi1,digi2,.. st vs vr va t1 t1 \ + * t2 t2 t3 t3 idle idle n2 n2 rtt window paclen Snd-Q Rcv-Q \ + * inode + * + * The code in this function is forced to assume that format is in + * effect.. + */ + + /* + * Assemble the inode number and see if it has already been recorded. + * If it has, skip this line. + */ + ep = (char *)NULL; + if (!fp[23] || !*fp[23] + || (inode = strtoull(fp[23], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + h = INOHASH((INODETYPE)inode); + for (ap = AX25sin[h]; ap; ap = ap->next) { + if (inode == ap->inode) + break; + } + if (ap) + continue; + /* + * Assemble the send and receive queue values and the state. + */ + rq = sq = (unsigned long)0; + rqs = sqs = (unsigned char)0; + ep = (char *)NULL; + if (!fp[21] || !*fp[21] + || (sq = strtoul(fp[21], &ep, 0)) == ULONG_MAX || !ep || *ep) + continue; + sqs = (unsigned char)1; + ep = (char *)NULL; + if (!fp[22] || !*fp[22] + || (rq = strtoul(fp[22], &ep, 0)) == ULONG_MAX || !ep || *ep) + continue; + rqs = (unsigned char)1; + ep = (char *)NULL; + if (!fp[4] || !*fp[4] + || (state = strtoul(fp[4], &ep, 0)) == ULONG_MAX || !ep || *ep) + continue; + /* + * Allocate space for the destination address. + */ + if (!fp[3] || !*fp[3]) + da = (char *)NULL; + else if ((len = strlen(fp[3]))) { + if (!(da = (char *)malloc(len + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d destination AX25 addr bytes: %s\n", + Pn, (int)(len + 1), fp[3]); + Exit(1); + } + (void) snpf(da, len + 1, "%s", fp[3]); + } else + da = (char *)NULL; + /* + * Allocate space for the source address. + */ + if (!fp[2] || !*fp[2]) + sa = (char *)NULL; + else if ((len = strlen(fp[2]))) { + if (!(sa = (char *)malloc(len + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d source AX25 address bytes: %s\n", + Pn, (int)(len + 1), fp[2]); + Exit(1); + } + (void) snpf(sa, len + 1, "%s", fp[2]); + } else + sa = (char *)NULL; + /* + * Allocate space for the device characters. + */ + if (!fp[1] || !*fp[1]) + dev_ch = (char *)NULL; + else if ((len = strlen(fp[1]))) { + if (!(dev_ch = (char *)malloc(len + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d destination AX25 dev bytes: %s\n", + Pn, (int)(len + 1), fp[1]); + Exit(1); + } + (void) snpf(dev_ch, len + 1, "%s", fp[1]); + } else + dev_ch = (char *)NULL; + /* + * Allocate space for an ax25sin entry, fill it, and link it to its + * hash bucket. + */ + if (!(ap = (struct ax25sin *)malloc(sizeof(struct ax25sin)))) { + (void) fprintf(stderr, + "%s: can't allocate %d byte ax25sin structure\n", + Pn, (int)sizeof(struct ax25sin)); + Exit(1); + } + ap->da = da; + ap->dev_ch = dev_ch; + ap->inode = inode; + ap->rq = rq; + ap->rqs = rqs; + ap->sa = sa; + ap->sq = sq; + ap->sqs = sqs; + ap->state = (int)state; + ap->next = AX25sin[h]; + AX25sin[h] = ap; + } + (void) fclose(as); +} + + +/* + * get_icmp() - get ICMP net info + */ + +static void +get_icmp(p) + char *p; /* /proc/net/icmp path */ +{ + char buf[MAXPATHLEN], *ep, **fp, *la, *ra; + int fl = 1; + int h; + INODETYPE inode; + struct icmpin *np, *icmpp; + unsigned long pr; + MALLOC_S lal, ral, spl; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + FILE *xs; +/* + * Do second time cleanup or first time setup. + */ + if (Icmpin) { + for (h = 0; h < INOBUCKS; h++) { + for (icmpp = Icmpin[h]; icmpp; icmpp = np) { + np = icmpp->next; + (void) free((FREE_P *)icmpp); + } + Icmpin[h] = (struct icmpin *)NULL; + } + } else { + Icmpin = (struct icmpin **)calloc(INOBUCKS, + sizeof(struct icmpin *)); + if (!Icmpin) { + (void) fprintf(stderr, + "%s: can't allocate %d icmp hash pointer bytes\n", + Pn, (int)(INOBUCKS * sizeof(struct icmpin *))); + Exit(1); + } + } +/* + * Open the /proc/net/icmp file, assign a page size buffer to its stream, + * and read the file. Store icmp info in the Icmpin[] hash buckets. + */ + if (!(xs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf) - 1, xs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 11) + continue; + if (fl) { + + /* + * Check the column labels in the first line. + * + * NOTE: + * In column header, "inode" is at the 11th column. + * However, in data rows, inode appears at the 9th column. + * + * In column header, "tx_queue" and "rx_queue" are separated + * by a space. It is the same for "tr" and "tm->when"; in + * data rows they are connected with ":". + */ + if (!fp[1] || strcmp(fp[1], "local_address") + || !fp[2] || strcmp(fp[2], "rem_address") + || !fp[11] || strcmp(fp[11], "inode")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + fl = 0; + continue; + } + /* + * Assemble the inode number and see if the inode is already + * recorded. + */ + ep = (char *)NULL; + if (!fp[9] || !*fp[9] + || (inode = strtoull(fp[9], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + h = INOHASH(inode); + for (icmpp = Icmpin[h]; icmpp; icmpp = icmpp->next) { + if (inode == icmpp->inode) + break; + } + if (icmpp) + continue; + /* + * Save the local address, and remote address. + */ + if (!fp[1] || !*fp[1] || (lal = strlen(fp[1])) < 1) { + la = (char *)NULL; + lal = (MALLOC_S)0; + } else { + if (!(la = (char *)malloc(lal + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d local icmp address bytes: %s\n", + Pn, (int)(lal + 1), fp[1]); + Exit(1); + } + (void) snpf(la, lal + 1, "%s", fp[1]); + } + if (!fp[2] || !*fp[2] || (ral = strlen(fp[2])) < 1) { + ra = (char *)NULL; + ral = (MALLOC_S)0; + } else { + if (!(ra = (char *)malloc(ral + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d remote icmp address bytes: %s\n", + Pn, (int)(ral + 1), fp[2]); + Exit(1); + } + (void) snpf(ra, ral + 1, "%s", fp[2]); + } + /* + * Allocate space for a icmpin entry, fill it, and link it to its + * hash bucket. + */ + if (!(icmpp = (struct icmpin *)malloc(sizeof(struct icmpin)))) { + (void) fprintf(stderr, + "%s: can't allocate %d byte icmp structure\n", + Pn, (int)sizeof(struct icmpin)); + Exit(1); + } + icmpp->inode = inode; + icmpp->la = la; + icmpp->lal = lal; + icmpp->ra = ra; + icmpp->ral = ral; + icmpp->next = Icmpin[h]; + Icmpin[h] = icmpp; + } + (void) fclose(xs); +} + + + +/* + * get_ipx() - get /proc/net/ipx info + */ + +static void +get_ipx(p) + char *p; /* /proc/net/ipx path */ +{ + char buf[MAXPATHLEN], *ep, **fp, *la, *ra; + int fl = 1; + int h; + INODETYPE inode; + unsigned long rxq, state, txq; + struct ipxsin *ip, *np; + MALLOC_S len; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + FILE *xs; +/* + * Do second time cleanup or first time setup. + */ + if (Ipxsin) { + for (h = 0; h < INOBUCKS; h++) { + for (ip = Ipxsin[h]; ip; ip = np) { + np = ip->next; + if (ip->la) + (void) free((FREE_P *)ip->la); + if (ip->ra) + (void) free((FREE_P *)ip->ra); + (void) free((FREE_P *)ip); + } + Ipxsin[h] = (struct ipxsin *)NULL; + } + } else { + Ipxsin = (struct ipxsin **)calloc(INOBUCKS, + sizeof(struct ipxsin *)); + if (!Ipxsin) { + (void) fprintf(stderr, + "%s: can't allocate %d IPX hash pointer bytes\n", + Pn, (int)(INOBUCKS * sizeof(struct ipxsin *))); + Exit(1); + } + } +/* + * Open the /proc/net/ipx file, assign a page size buffer to the stream, + * and read it. Store IPX socket info in the Ipxsin[] hash buckets. + */ + if (!(xs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf) - 1, xs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 7) + continue; + if (fl) { + + /* + * Check the column labels in the first line. + */ + if (!fp[0] || strcmp(fp[0], "Local_Address") + || !fp[1] || strcmp(fp[1], "Remote_Address") + || !fp[2] || strcmp(fp[2], "Tx_Queue") + || !fp[3] || strcmp(fp[3], "Rx_Queue") + || !fp[4] || strcmp(fp[4], "State") + || !fp[5] || strcmp(fp[5], "Uid") + || !fp[6] || strcmp(fp[6], "Inode")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + fl = 0; + continue; + } + /* + * Assemble the inode number and see if the inode is already + * recorded. + */ + ep = (char *)NULL; + if (!fp[6] || !*fp[6] + || (inode = strtoull(fp[6], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + h = INOHASH(inode); + for (ip = Ipxsin[h]; ip; ip = ip->next) { + if (inode == ip->inode) + break; + } + if (ip) + continue; + /* + * Assemble the transmit and receive queue values and the state. + */ + ep = (char *)NULL; + if (!fp[2] || !*fp[2] + || (txq = strtoul(fp[2], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[3] || !*fp[3] + || (rxq = strtoul(fp[3], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[4] || !*fp[4] + || (state = strtoul(fp[4], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + /* + * Allocate space for the local address, unless it is "Not_Connected". + */ + if (!fp[0] || !*fp[0] || strcmp(fp[0], "Not_Connected") == 0) + la = (char *)NULL; + else if ((len = strlen(fp[0]))) { + if (!(la = (char *)malloc(len + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d local IPX address bytes: %s\n", + Pn, (int)(len + 1), fp[0]); + Exit(1); + } + (void) snpf(la, len + 1, "%s", fp[0]); + } else + la = (char *)NULL; + /* + * Allocate space for the remote address, unless it is "Not_Connected". + */ + if (!fp[1] || !*fp[1] || strcmp(fp[1], "Not_Connected") == 0) + ra = (char *)NULL; + else if ((len = strlen(fp[1]))) { + if (!(ra = (char *)malloc(len + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d remote IPX address bytes: %s\n", + Pn, (int)(len + 1), fp[1]); + Exit(1); + } + (void) snpf(ra, len + 1, "%s", fp[1]); + } else + ra = (char *)NULL; + /* + * Allocate space for an ipxsin entry, fill it, and link it to its + * hash bucket. + */ + if (!(ip = (struct ipxsin *)malloc(sizeof(struct ipxsin)))) { + (void) fprintf(stderr, + "%s: can't allocate %d byte ipxsin structure\n", + Pn, (int)sizeof(struct ipxsin)); + Exit(1); + } + ip->inode = inode; + ip->la = la; + ip->ra = ra; + ip->txq = txq; + ip->rxq = rxq; + ip->state = (int)state; + ip->next = Ipxsin[h]; + Ipxsin[h] = ip; + } + (void) fclose(xs); +} + + +/* + * get_netlink() - get /proc/net/netlink info + */ + +static void +get_netlink(p) + char *p; /* /proc/net/netlink path */ +{ + char buf[MAXPATHLEN], *ep, **fp; + int fr = 1; + int h, pr; + INODETYPE inode; + struct nlksin *np, *lp; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + FILE *xs; +/* + * Do second time cleanup or first time setup. + */ + if (Nlksin) { + for (h = 0; h < INOBUCKS; h++) { + for (lp = Nlksin[h]; lp; lp = np) { + np = lp->next; + (void) free((FREE_P *)lp); + } + Nlksin[h] = (struct nlksin *)NULL; + } + } else { + Nlksin = (struct nlksin **)calloc(INOBUCKS,sizeof(struct nlksin *)); + if (!Nlksin) { + (void) fprintf(stderr, + "%s: can't allocate %d netlink hash pointer bytes\n", + Pn, (int)(INOBUCKS * sizeof(struct nlksin *))); + Exit(1); + } + } +/* + * Open the /proc/net/netlink file, assign a page size buffer to its stream, + * and read the file. Store Netlink info in the Nlksin[] hash buckets. + */ + if (!(xs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf) - 1, xs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 10) + continue; + if (fr) { + + /* + * Check the column labels in the first line. + */ + if (!fp[1] || strcmp(fp[1], "Eth") + || !fp[9] || strcmp(fp[9], "Inode")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + fr = 0; + continue; + } + /* + * Assemble the inode number and see if the inode is already + * recorded. + */ + ep = (char *)NULL; + if (!fp[9] || !*fp[9] + || (inode = strtoull(fp[9], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + h = INOHASH(inode); + for (lp = Nlksin[h]; lp; lp = lp->next) { + if (inode == lp->inode) + break; + } + if (lp) + continue; + /* + * Save the protocol from the Eth column. + */ + if (!fp[1] || !*fp[1] || (strlen(fp[1])) < 1) + continue; + pr = atoi(fp[1]); + /* + * Allocate space for a nlksin entry, fill it, and link it to its + * hash bucket. + */ + if (!(lp = (struct nlksin *)malloc(sizeof(struct nlksin)))) { + (void) fprintf(stderr, + "%s: can't allocate %d byte Netlink structure\n", + Pn, (int)sizeof(struct nlksin)); + Exit(1); + } + lp->inode = inode; + lp->pr = pr; + lp->next = Nlksin[h]; + Nlksin[h] = lp; + } + (void) fclose(xs); +} + + +/* + * get_pack() - get /proc/net/packet info + */ + +static void +get_pack(p) + char *p; /* /proc/net/raw path */ +{ + char buf[MAXPATHLEN], *ep, **fp; + int fl = 1; + int h, ty; + INODETYPE inode; + struct packin *np, *pp; + unsigned long pr; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + FILE *xs; +/* + * Do second time cleanup or first time setup. + */ + if (Packin) { + for (h = 0; h < INOBUCKS; h++) { + for (pp = Packin[h]; pp; pp = np) { + np = pp->next; + (void) free((FREE_P *)pp); + } + Packin[h] = (struct packin *)NULL; + } + } else { + Packin = (struct packin **)calloc(INOBUCKS, + sizeof(struct packin *)); + if (!Packin) { + (void) fprintf(stderr, + "%s: can't allocate %d packet hash pointer bytes\n", + Pn, (int)(INOBUCKS * sizeof(struct packin *))); + Exit(1); + } + } +/* + * Open the /proc/net/packet file, assign a page size buffer to its stream, + * and read the file. Store packet info in the Packin[] hash buckets. + */ + if (!(xs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf) - 1, xs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < 9) + continue; + if (fl) { + + /* + * Check the column labels in the first line. + */ + if (!fp[2] || strcmp(fp[2], "Type") + || !fp[3] || strcmp(fp[3], "Proto") + || !fp[8] || strcmp(fp[8], "Inode")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + fl = 0; + continue; + } + /* + * Assemble the inode number and see if the inode is already + * recorded. + */ + ep = (char *)NULL; + if (!fp[8] || !*fp[8] + || (inode = strtoull(fp[8], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + h = INOHASH(inode); + for (pp = Packin[h]; pp; pp = pp->next) { + if (inode == pp->inode) + break; + } + if (pp) + continue; + /* + * Save the socket type and protocol. + */ + if (!fp[2] || !*fp[2] || (strlen(fp[2])) < 1) + continue; + ty = atoi(fp[2]); + ep = (char *)NULL; + if (!fp[3] || !*fp[3] || (strlen(fp[3]) < 1) + || ((pr = strtoul(fp[3], &ep, 16)) == ULONG_MAX) || !ep || *ep) + continue; + /* + * Allocate space for a packin entry, fill it, and link it to its + * hash bucket. + */ + if (!(pp = (struct packin *)malloc(sizeof(struct packin)))) { + (void) fprintf(stderr, + "%s: can't allocate %d byte packet structure\n", + Pn, (int)sizeof(struct packin)); + Exit(1); + } + pp->inode = inode; + pp->pr = (int)pr; + pp->ty = ty; + pp->next = Packin[h]; + Packin[h] = pp; + } + (void) fclose(xs); +} + + +/* + * get_raw() - get /proc/net/raw info + */ + +static void +get_raw(p) + char *p; /* /proc/net/raw path */ +{ + char buf[MAXPATHLEN], *ep, **fp, *la, *ra, *sp; + int h; + INODETYPE inode; + int nf = 12; + struct rawsin *np, *rp; + MALLOC_S lal, ral, spl; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + FILE *xs; +/* + * Do second time cleanup or first time setup. + */ + if (Rawsin) { + for (h = 0; h < INOBUCKS; h++) { + for (rp = Rawsin[h]; rp; rp = np) { + np = rp->next; + if (rp->la) + (void) free((FREE_P *)rp->la); + if (rp->ra) + (void) free((FREE_P *)rp->ra); + (void) free((FREE_P *)rp); + } + Rawsin[h] = (struct rawsin *)NULL; + } + } else { + Rawsin = (struct rawsin **)calloc(INOBUCKS, + sizeof(struct rawsin *)); + if (!Rawsin) { + (void) fprintf(stderr, + "%s: can't allocate %d raw hash pointer bytes\n", + Pn, (int)(INOBUCKS * sizeof(struct rawsin *))); + Exit(1); + } + } +/* + * Open the /proc/net/raw file, assign a page size buffer to its stream, + * and read the file. Store raw socket info in the Rawsin[] hash buckets. + */ + if (!(xs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf) - 1, xs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < nf) + continue; + if (nf == 12) { + + /* + * Check the column labels in the first line. + */ + if (!fp[1] || strcmp(fp[1], "local_address") + || !fp[2] || strcmp(fp[2], "rem_address") + || !fp[3] || strcmp(fp[3], "st") + || !fp[11] || strcmp(fp[11], "inode")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + nf = 10; + continue; + } + /* + * Assemble the inode number and see if the inode is already + * recorded. + */ + ep = (char *)NULL; + if (!fp[9] || !*fp[9] + || (inode = strtoull(fp[9], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + h = INOHASH(inode); + for (rp = Rawsin[h]; rp; rp = rp->next) { + if (inode == rp->inode) + break; + } + if (rp) + continue; + /* + * Save the local address, remote address, and state. + */ + if (!fp[1] || !*fp[1] || (lal = strlen(fp[1])) < 1) { + la = (char *)NULL; + lal = (MALLOC_S)0; + } else { + if (!(la = (char *)malloc(lal + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d local raw address bytes: %s\n", + Pn, (int)(lal + 1), fp[1]); + Exit(1); + } + (void) snpf(la, lal + 1, "%s", fp[1]); + } + if (!fp[2] || !*fp[2] || (ral = strlen(fp[2])) < 1) { + ra = (char *)NULL; + ral = (MALLOC_S)0; + } else { + if (!(ra = (char *)malloc(ral + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d remote raw address bytes: %s\n", + Pn, (int)(ral + 1), fp[2]); + Exit(1); + } + (void) snpf(ra, ral + 1, "%s", fp[2]); + } + if (!fp[3] || !*fp[3] || (spl = strlen(fp[3])) < 1) { + sp = (char *)NULL; + spl = (MALLOC_S)0; + } else { + if (!(sp = (char *)malloc(spl + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d remote raw state bytes: %s\n", + Pn, (int)(spl + 1), fp[2]); + Exit(1); + } + (void) snpf(sp, spl + 1, "%s", fp[3]); + } + /* + * Allocate space for an rawsin entry, fill it, and link it to its + * hash bucket. + */ + if (!(rp = (struct rawsin *)malloc(sizeof(struct rawsin)))) { + (void) fprintf(stderr, + "%s: can't allocate %d byte rawsin structure\n", + Pn, (int)sizeof(struct rawsin)); + Exit(1); + } + rp->inode = inode; + rp->la = la; + rp->lal = lal; + rp->ra = ra; + rp->ral = ral; + rp->sp = sp; + rp->spl = spl; + rp->next = Rawsin[h]; + Rawsin[h] = rp; + } + (void) fclose(xs); +} + + +/* + * get_sctp() - get /proc/net/sctp/assocs info + */ + +static void +get_sctp() +{ + char buf[MAXPATHLEN], *a, *ep, **fp, *id, *la, *lp, *ra, *rp, *ta; + int d, err, fl, h, i, j, nf, ty, x; + INODETYPE inode; + MALLOC_S len, plen; + struct sctpsin *sp, *np; + FILE *ss; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; +/* + * Do second time cleanup or first time setup. + */ + if (SCTPsin) { + for (h = 0; h < INOBUCKS; h++) { + for (sp = SCTPsin[h]; sp; sp = np) { + np = sp->next; + if (sp->addr) + (void) free((FREE_P *)sp->addr); + if (sp->assocID) + (void) free((FREE_P *)sp->assocID); + if (sp->lport) + (void) free((FREE_P *)sp->lport); + if (sp->rport) + (void) free((FREE_P *)sp->rport); + if (sp->laddrs) + (void) free((FREE_P *)sp->laddrs); + if (sp->raddrs) + (void) free((FREE_P *)sp->raddrs); + (void) free((FREE_P *)sp); + } + SCTPsin[h] = (struct sctpsin *)NULL; + } + } else { + SCTPsin = (struct sctpsin **)calloc(INOBUCKS, + sizeof(struct sctpsin *)); + if (!SCTPsin) { + (void) fprintf(stderr, + "%s: can't allocate %d SCTP hash pointer bytes\n", + Pn, (int)(INOBUCKS * sizeof(struct sctpsin *))); + Exit(1); + } + } +/* + * Open the /proc/net/sctp files, assign a page size buffer to the streams, + * and read them. Store SCTP socket info in the SCTPsin[] hash buckets. + */ + for (i = 0; i < NSCTPPATHS; i++ ) { + if (!(ss = open_proc_stream(SCTPPath[i], "r", &vbuf, &vsz, 0))) + continue; + fl = 1; + while (fgets(buf, sizeof(buf) - 1, ss)) { + if ((nf = get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0)) + < (i ? 9 : 16) + ) { + continue; + } + if (fl) { + + /* + * Check the column labels in the first line. + */ + err = 0; + switch (i) { + case 0: + if (!fp[0] || strcmp(fp[0], "ASSOC") + || !fp[6] || strcmp(fp[6], "ASSOC-ID") + || !fp[10] || strcmp(fp[10], "INODE") + || !fp[11] || strcmp(fp[11], "LPORT") + || !fp[12] || strcmp(fp[12], "RPORT") + || !fp[13] || strcmp(fp[13], "LADDRS") + || !fp[14] || strcmp(fp[14], "<->") + || !fp[15] || strcmp(fp[15], "RADDRS") + ) { + err = 1; + } + break; + case 1: + if (!fp[0] || strcmp(fp[0], "ENDPT") + || !fp[5] || strcmp(fp[5], "LPORT") + || !fp[7] || strcmp(fp[7], "INODE") + || !fp[8] || strcmp(fp[8], "LADDRS") + ) { + err = 1; + } + } + if (err) { + if (!Fwarn) + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, SCTPPath[i]); + break; + } + fl = 0; + continue; + } + /* + * Assemble the inode number and see if it has already been + * recorded. + */ + ep = (char *)NULL; + j = i ? 7 : 10; + if (!fp[j] || !*fp[j] + || (inode = strtoull(fp[j], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + h = INOHASH((INODETYPE)inode); + for (sp = SCTPsin[h]; sp; sp = sp->next) { + if (inode == sp->inode) + break; + } + /* + * Set the entry type. + */ + if (sp) + ty = (sp->type == i) ? i : 3; + else + ty = i; + /* + * Allocate space for this line's sctpsin members. + * + * The association or endpoint address is in the first field. + */ + a = sp ? sp->addr : (char *)NULL; + if (fp[0] && *fp[0] && (len = strlen(fp[0]))) { + if (a) { + if (isainb(fp[0], a)) { + plen = strlen(a); + a = (char *)realloc((MALLOC_P *)a, plen + len + 2); + d = 0; + } else + d = 1; + } else { + plen = (MALLOC_S)0; + a = (char *)malloc(len + 1); + d = 0; + } + if (!a) { + (void) fprintf(stderr, + "%s: can't allocate %d SCTP ASSOC bytes: %s\n", + Pn, (int)(len + 1), fp[0]); + Exit(1); + } + if (!d) { + if (plen) + (void) snpf((a + plen), len + 2, ",%s", fp[0]); + else + (void) snpf(a, len + 1, "%s", fp[0]); + } + } + /* + * The association ID is in the seventh field. + */ + id = sp ? sp->assocID : (char *)NULL; + if (!i && fp[6] && *fp[6] && (len = strlen(fp[6]))) { + if (id) { + if (isainb(fp[6], id)) { + plen = strlen(id); + id = (char *)realloc((MALLOC_P *)id,plen+len+2); + d = 0; + } else + d = 1; + } else { + plen = (MALLOC_S)0; + id = (char *)malloc(len + 1); + d = 0; + } + if (!id) { + (void) fprintf(stderr, + "%s: can't allocate %d SCTP ASSOC-ID bytes: %s\n", + Pn, (int)(len + 1), fp[6]); + Exit(1); + } + if (!d) { + if (plen) + (void) snpf((id + plen), len + 2, ",%s", fp[6]); + else + (void) snpf(id, len + 1, "%s", fp[6]); + } + } + /* + * The field number for the local port depends on the entry type. + */ + j = i ? 5 : 11; + lp = sp ? sp->lport : (char *)NULL; + if (fp[j] && *fp[j] && (len = strlen(fp[j]))) { + if (lp) { + if (isainb(fp[j], lp)) { + plen = strlen(lp); + lp = (char *)realloc((MALLOC_P *)lp,plen+len+2); + d = 0; + } else + d = 1; + } else { + plen = (MALLOC_S)0; + lp = (char *)malloc(len + 1); + d = 0; + } + if (!lp) { + (void) fprintf(stderr, + "%s: can't allocate %d SCTP LPORT bytes: %s\n", + Pn, (int)(len + 1), fp[j]); + Exit(1); + } + if (!d) { + if (plen) + (void) snpf((lp + plen), len + 2, ",%s", fp[j]); + else + (void) snpf(lp, len + 1, "%s", fp[j]); + } + } + /* + * The field number for the remote port depends on the entry type. + */ + rp = sp ? sp->rport : (char *)NULL; + if (!i && fp[12] && *fp[12] && (len = strlen(fp[12]))) { + if (rp) { + if (isainb(fp[12], rp)) { + plen = strlen(rp); + rp = (char *)realloc((MALLOC_P *)rp,plen+len+2); + d = 0; + } else + d = 1; + } else { + plen = (MALLOC_S)0; + rp = (char *)malloc(len + 1); + d = 0; + } + if (!rp) { + (void) fprintf(stderr, + "%s: can't allocate %d SCTP RPORT bytes: %s\n", + Pn, (int)(len + 1), fp[12]); + Exit(1); + } + if (!d) { + if (plen) + (void) snpf((rp + plen), len + 2, ",%s", fp[12]); + else + (void) snpf(rp, len + 1, "%s", fp[12]); + } + } + /* + * The local addresses begin in a field whose number depends on + * the entry type. + */ + j = i ? 8 : 13; + la = sp ? sp->laddrs : (char *)NULL; + if (fp[j] && *fp[j] && (len = strlen(fp[j]))) { + if (!(ta = get_sctpaddrs(fp, j, nf, &x))) { + (void) fprintf(stderr, + "%s: can't allocate %d SCTP LADDRS bytes\n", + Pn, (int)len); + Exit(1); + } + if (la) { + if (isainb(ta, la)) { + len = strlen(ta); + plen = strlen(la); + if (!(la=(char *)realloc((MALLOC_P *)la,plen+len+2)) + ) { + (void) fprintf(stderr, + "%s: can't reallocate %d SCTP LADDRS bytes\n", + Pn, (int)len); + Exit(1); + } + (void) snpf(la + plen, len + 2, ",%s", ta); + (void) free((FREE_P *)ta); + } + } else + la = ta; + } + /* + * The remote addresses begin after the local addresses, but only + * for the ASSOC type. + */ + ra = sp ? sp->raddrs : (char *)NULL; + if (!i && x && fp[x+1] && *fp[x+1] && (len = strlen(fp[x+1]))) { + if (!(ta = get_sctpaddrs(fp, x + 1, nf, &x))) { + (void) fprintf(stderr, + "%s: can't allocate %d SCTP RADDRS bytes\n", + Pn, (int)len); + Exit(1); + } + if (ra) { + if (isainb(ta, ra)) { + len = strlen(ta); + plen = strlen(ra); + if (!(ra=(char *)realloc((MALLOC_P *)ra,plen+len+2)) + ) { + (void) fprintf(stderr, + "%s: can't reallocate %d SCTP RADDRS bytes\n", + Pn, (int)len); + Exit(1); + } + (void) snpf(ra + plen, len + 2, ",%s", ta); + (void) free((FREE_P *)ta); + } + } else + ra = ta; + } + /* + * If no matching sctpsin entry was found for this inode, allocate + * space for a new sctpsin entry, fill it, and link it to its hash + * bucket. Update a matching entry. + */ + if (!sp) { + if (!(sp = (struct sctpsin *)malloc(sizeof(struct sctpsin))) ) { + (void) fprintf(stderr, + "%s: can't allocate %d byte sctpsin structure\n", + Pn, (int)sizeof(struct sctpsin)); + Exit(1); + } + sp->inode = inode; + sp->next = SCTPsin[h]; + SCTPsin[h] = sp; + } + sp->addr = a; + sp->assocID = id; + sp->lport = lp; + sp->rport = rp; + sp->laddrs = la; + sp->raddrs = ra; + sp->type = ty; + } + (void) fclose(ss); + } +} + + +static char * +get_sctpaddrs(fp, i, nf, x) + char **fp; /* field pointers */ + int i; /* first address field index in fp */ + int nf; /* number of fields */ + int *x; /* index of first "<->" field entry */ +{ + MALLOC_S al = (MALLOC_S)0; + char *cp = (char *)NULL; + MALLOC_S tl; + + *x = 0; + do { + if ((i >= nf) || !fp[i] || !*fp[i] || !(tl = strlen(fp[i]))) + break; + if (!strcmp(fp[i], "<->")) { + *x = i; + break; + } + if (!strchr(fp[i], (int)'.') && !strchr(fp[i], (int)':')) + break; + if (cp) + cp = (char *)realloc((MALLOC_P *)cp, al + tl + 1); + else + cp = (char *)malloc(al + tl + 1); + if (!cp) + break; + if (al) + *(cp + al - 1) = ','; + (void) strncpy(al ? (cp + al) : cp, fp[i], tl); + al += (tl + 1); + *(cp + al - 1) = '\0'; + } while (++i < nf); + return(cp); +} + + +/* + * get_tcpudp() - get IPv4 TCP, UDP or UDPLITE net info + */ + +static void +get_tcpudp(p, pr, clr) + char *p; /* /proc/net/{tcp,udp} path */ + int pr; /* protocol: 0 = TCP, 1 = UDP, + * 2 = UDPLITE */ + int clr; /* 1 == clear the table */ +{ + char buf[MAXPATHLEN], *ep, **fp; + unsigned long faddr, fport, laddr, lport, rxq, state, txq; + FILE *fs; + int h, nf; + INODETYPE inode; + struct tcp_udp *np, *tp; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; +/* + * Delete previous table contents. + */ + if (TcpUdp) { + if (clr) { + for (h = 0; h < TcpUdp_bucks; h++) { + for (tp = TcpUdp[h]; tp; tp = np) { + np = tp->next; + (void) free((FREE_P *)tp); + } + TcpUdp[h] = (struct tcp_udp *)NULL; + } + } +/* + * If no hash buckets have been allocated, do so now. + */ + } else { + + /* + * Open the /proc/net/sockstat file and establish the hash bucket + * count from its "sockets: used" line. + */ + TcpUdp_bucks = INOBUCKS; + if ((fs = fopen(SockStatPath, "r"))) { + while(fgets(buf, sizeof(buf) - 1, fs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) != 3) + continue; + if (!fp[0] || strcmp(fp[0], "sockets:") + || !fp[1] || strcmp(fp[1], "used") + || !fp[2] || !*fp[2]) + continue; + if ((h = atoi(fp[2])) < 1) + h = INOBUCKS; + while (TcpUdp_bucks < h) + TcpUdp_bucks *= 2; + break; + } + (void) fclose(fs); + } + if (!(TcpUdp = (struct tcp_udp **)calloc(TcpUdp_bucks, + sizeof(struct tcp_udp *)))) + { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for TCP&UDP hash buckets\n", + Pn, (int)(TcpUdp_bucks * sizeof(struct tcp_udp *))); + Exit(1); + } + } +/* + * Open the /proc/net file, assign a page size buffer to the stream, and + * read it. + */ + if (!(fs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + nf = 12; + while(fgets(buf, sizeof(buf) - 1, fs)) { + if (get_fields(buf, + (nf == 12) ? (char *)NULL : ":", + &fp, (int *)NULL, 0) + < nf) + continue; + if (nf == 12) { + if (!fp[1] || strcmp(fp[1], "local_address") + || !fp[2] || strcmp(fp[2], "rem_address") + || !fp[3] || strcmp(fp[3], "st") + || !fp[4] || strcmp(fp[4], "tx_queue") + || !fp[5] || strcmp(fp[5], "rx_queue") + || !fp[11] || strcmp(fp[11], "inode")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + nf = 14; + continue; + } + /* + * Get the local and remote addresses. + */ + ep = (char *)NULL; + if (!fp[1] || !*fp[1] + || (laddr = strtoul(fp[1], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[2] || !*fp[2] + || (lport = strtoul(fp[2], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[3] || !*fp[3] + || (faddr = strtoul(fp[3], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[4] || !*fp[4] + || (fport = strtoul(fp[4], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + /* + * Get the state and queue sizes. + */ + ep = (char *)NULL; + if (!fp[5] || !*fp[5] + || (state = strtoul(fp[5], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[6] || !*fp[6] + || (txq = strtoul(fp[6], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[7] || !*fp[7] + || (rxq = strtoul(fp[7], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + /* + * Get the inode and use it for hashing and searching. + */ + ep = (char *)NULL; + if (!fp[13] || !*fp[13] + || (inode = strtoull(fp[13], &ep, 0)) == ULONG_MAX || !ep || *ep) + continue; + h = TCPUDPHASH(inode); + for (tp = TcpUdp[h]; tp; tp = tp->next) { + if (tp->inode == inode) + break; + } + if (tp) + continue; + /* + * Create a new entry and link it to its hash bucket. + */ + if (!(tp = (struct tcp_udp *)malloc(sizeof(struct tcp_udp)))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for tcp_udp struct\n", + Pn, (int)sizeof(struct tcp_udp)); + Exit(1); + } + tp->inode = inode; + tp->faddr = faddr; + tp->fport = (int)(fport & 0xffff); + tp->laddr = laddr; + tp->lport = (int)(lport & 0xffff); + tp->txq = txq; + tp->rxq = rxq; + tp->proto = pr; + tp->state = (int)state; + tp->next = TcpUdp[h]; + TcpUdp[h] = tp; + } + (void) fclose(fs); +} + + +#if defined(HASIPv6) +/* + * get_raw6() - get /proc/net/raw6 info + */ + +static void +get_raw6(p) + char *p; /* /proc/net/raw path */ +{ + char buf[MAXPATHLEN], *ep, **fp, *la, *ra, *sp; + int h; + INODETYPE inode; + int nf = 12; + struct rawsin *np, *rp; + MALLOC_S lal, ral, spl; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; + FILE *xs; +/* + * Do second time cleanup or first time setup. + */ + if (Rawsin6) { + for (h = 0; h < INOBUCKS; h++) { + for (rp = Rawsin6[h]; rp; rp = np) { + np = rp->next; + if (rp->la) + (void) free((FREE_P *)rp->la); + if (rp->ra) + (void) free((FREE_P *)rp->ra); + (void) free((FREE_P *)rp); + } + Rawsin6[h] = (struct rawsin *)NULL; + } + } else { + Rawsin6 = (struct rawsin **)calloc(INOBUCKS, + sizeof(struct rawsin *)); + if (!Rawsin6) { + (void) fprintf(stderr, + "%s: can't allocate %d raw6 hash pointer bytes\n", + Pn, (int)(INOBUCKS * sizeof(struct rawsin *))); + Exit(1); + } + } +/* + * Open the /proc/net/raw6 file, assign a page size buffer to the stream, + * and read it. Store raw6 socket info in the Rawsin6[] hash buckets. + */ + if (!(xs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf) - 1, xs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) < nf) + continue; + if (nf == 12) { + + /* + * Check the column labels in the first line. + */ + if (!fp[1] || strcmp(fp[1], "local_address") + || !fp[2] || strcmp(fp[2], "remote_address") + || !fp[3] || strcmp(fp[3], "st") + || !fp[11] || strcmp(fp[11], "inode")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + nf = 10; + continue; + } + /* + * Assemble the inode number and see if the inode is already + * recorded. + */ + ep = (char *)NULL; + if (!fp[9] || !*fp[9] + || (inode = strtoull(fp[9], &ep, 0)) == ULONG_MAX + || !ep || *ep) + continue; + h = INOHASH(inode); + for (rp = Rawsin6[h]; rp; rp = rp->next) { + if (inode == rp->inode) + break; + } + if (rp) + continue; + /* + * Save the local address, remote address, and state. + */ + if (!fp[1] || !*fp[1] || (lal = strlen(fp[1])) < 1) { + la = (char *)NULL; + lal = (MALLOC_S)0; + } else { + if (!(la = (char *)malloc(lal + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d local raw6 address bytes: %s\n", + Pn, (int)(lal + 1), fp[1]); + Exit(1); + } + (void) snpf(la, lal + 1, "%s", fp[1]); + } + if (!fp[2] || !*fp[2] || (ral = strlen(fp[2])) < 1) { + ra = (char *)NULL; + ral = (MALLOC_S)0; + } else { + if (!(ra = (char *)malloc(ral + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d remote raw6 address bytes: %s\n", + Pn, (int)(ral + 1), fp[2]); + Exit(1); + } + (void) snpf(ra, ral + 1, "%s", fp[2]); + } + if (!fp[3] || !*fp[3] || (spl = strlen(fp[3])) < 1) { + sp = (char *)NULL; + spl = (MALLOC_S)0; + } else { + if (!(sp = (char *)malloc(spl + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d remote raw6 state bytes: %s\n", + Pn, (int)(spl + 1), fp[2]); + Exit(1); + } + (void) snpf(sp, spl + 1, "%s", fp[3]); + } + /* + * Allocate space for an rawsin entry, fill it, and link it to its + * hash bucket. + */ + if (!(rp = (struct rawsin *)malloc(sizeof(struct rawsin)))) { + (void) fprintf(stderr, + "%s: can't allocate %d byte rawsin structure for IPv6\n", + Pn, (int)sizeof(struct rawsin)); + Exit(1); + } + rp->inode = inode; + rp->la = la; + rp->lal = lal; + rp->ra = ra; + rp->ral = ral; + rp->sp = sp; + rp->spl = spl; + rp->next = Rawsin6[h]; + Rawsin6[h] = rp; + } + (void) fclose(xs); +} + + +/* + * get_tcpudp6() - get IPv6 TCP, UDP or UDPLITE net info + */ + +static void +get_tcpudp6(p, pr, clr) + char *p; /* /proc/net/{tcp,udp} path */ + int pr; /* protocol: 0 = TCP, 1 = UDP */ + int clr; /* 1 == clear the table */ +{ + char buf[MAXPATHLEN], *ep, **fp; + struct in6_addr faddr, laddr; + unsigned long fport, lport, rxq, state, txq; + FILE *fs; + int h, i, nf; + INODETYPE inode; + struct tcp_udp6 *np6, *tp6; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; +/* + * Delete previous table contents. Allocate a table for the first time. + */ + if (TcpUdp6) { + if (clr) { + for (h = 0; h < TcpUdp6_bucks; h++) { + for (tp6 = TcpUdp6[h]; tp6; tp6 = np6) { + np6 = tp6->next; + (void) free((FREE_P *)tp6); + } + TcpUdp6[h] = (struct tcp_udp6 *)NULL; + } + } + } else { + + /* + * Open the /proc/net/sockstat6 file and establish the hash bucket + * count from its "TCP6: inuse" and "UDP6: inuse" lines. + */ + TcpUdp6_bucks = INOBUCKS; + h = i = nf = 0; + if ((fs = fopen(SockStatPath6, "r"))) { + while(fgets(buf, sizeof(buf) - 1, fs)) { + if (get_fields(buf, (char *)NULL, &fp, (int *)NULL, 0) != 3) + continue; + if (!fp[0] + || !fp[1] || strcmp(fp[1], "inuse") + || !fp[2] || !*fp[2]) + continue; + if (!strcmp(fp[0], "TCP6:")) { + nf |= 1; + if ((h = atoi(fp[2])) < 1) + h = INOBUCKS; + i += h; + } else if (!strcmp(fp[0], "UDP6:")) { + nf |= 2; + if ((h = atoi(fp[2])) < 1) + h = INOBUCKS; + i += h; + } else + continue; + if (nf == 3) { + while (TcpUdp6_bucks < i) + TcpUdp6_bucks *= 2; + break; + } + } + (void) fclose(fs); + } + if (!(TcpUdp6 = (struct tcp_udp6 **)calloc(TcpUdp6_bucks, + sizeof(struct tcp_udp6 *)))) + { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for TCP6&UDP6 hash buckets\n", + Pn, (int)(TcpUdp6_bucks * sizeof(struct tcp_udp6 *))); + Exit(1); + } + } +/* + * Open the /proc/net file, assign a page size buffer to the stream, + * and read it. + */ + if (!(fs = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + nf = 12; + while(fgets(buf, sizeof(buf) - 1, fs)) { + if (get_fields(buf, + (nf == 12) ? (char *)NULL : ":", + &fp, (int *)NULL, 0) + < nf) + continue; + if (nf == 12) { + if (!fp[1] || strcmp(fp[1], "local_address") + || !fp[2] || strcmp(fp[2], "remote_address") + || !fp[3] || strcmp(fp[3], "st") + || !fp[4] || strcmp(fp[4], "tx_queue") + || !fp[5] || strcmp(fp[5], "rx_queue") + || !fp[11] || strcmp(fp[11], "inode")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + nf = 14; + continue; + } + /* + * Get the local and remote addresses. + */ + if (!fp[1] || !*fp[1] || net6a2in6(fp[1], &laddr)) + continue; + ep = (char *)NULL; + if (!fp[2] || !*fp[2] + || (lport = strtoul(fp[2], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + if (!fp[3] || !*fp[3] || net6a2in6(fp[3], &faddr)) + continue; + ep = (char *)NULL; + if (!fp[4] || !*fp[4] + || (fport = strtoul(fp[4], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + /* + * Get the state and queue sizes. + */ + ep = (char *)NULL; + if (!fp[5] || !*fp[5] + || (state = strtoul(fp[5], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[6] || !*fp[6] + || (txq = strtoul(fp[6], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + ep = (char *)NULL; + if (!fp[7] || !*fp[7] + || (rxq = strtoul(fp[7], &ep, 16)) == ULONG_MAX || !ep || *ep) + continue; + /* + * Get the inode and use it for hashing and searching. + */ + ep = (char *)NULL; + if (!fp[13] || !*fp[13] + || (inode = strtoull(fp[13], &ep, 0)) == ULONG_MAX || !ep || *ep) + continue; + h = TCPUDP6HASH(inode); + for (tp6 = TcpUdp6[h]; tp6; tp6 = tp6->next) { + if (tp6->inode == inode) + break; + } + if (tp6) + continue; + /* + * Create a new entry and link it to its hash bucket. + */ + if (!(tp6 = (struct tcp_udp6 *)malloc(sizeof(struct tcp_udp6)))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for tcp_udp6 struct\n", + Pn, (int)sizeof(struct tcp_udp6)); + Exit(1); + } + tp6->inode = inode; + tp6->faddr = faddr; + tp6->fport = (int)(fport & 0xffff); + tp6->laddr = laddr; + tp6->lport = (int)(lport & 0xffff); + tp6->txq = txq; + tp6->rxq = rxq; + tp6->proto = pr; + tp6->state = (int)state; + tp6->next = TcpUdp6[h]; + TcpUdp6[h] = tp6; + } + (void) fclose(fs); +} +#endif /* defined(HASIPv6) */ + + +/* + * get_unix() - get UNIX net info + */ + +static void +get_unix(p) + char *p; /* /proc/net/unix path */ +{ + char buf[MAXPATHLEN], *ep, **fp, *path, *pcb; + int fl = 1; + int h, nf; + INODETYPE inode; + MALLOC_S len; + struct uxsin *np, *up; + FILE *us; + static char *vbuf = (char *)NULL; + static size_t vsz = (size_t)0; +/* + * Do second time cleanup or first time setup. + */ + if (Uxsin) { + for (h = 0; h < INOBUCKS; h++) { + for (up = Uxsin[h]; up; up = np) { + np = up->next; + if (up->path) + (void) free((FREE_P *)up->path); + if (up->pcb) + (void) free((FREE_P *)up->pcb); + (void) free((FREE_P *)up); + } + Uxsin[h] = (struct uxsin *)NULL; + } + } else { + Uxsin = (struct uxsin **)calloc(INOBUCKS, sizeof(struct uxsin *)); + if (!Uxsin) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for Unix socket info\n", + Pn, (int)(INOBUCKS * sizeof(struct uxsin *))); + } + } +/* + * Open the /proc/net/unix file, assign a page size buffer to the stream, + * read the file's contents, and add them to the Uxsin hash buckets. + */ + if (!(us = open_proc_stream(p, "r", &vbuf, &vsz, 0))) + return; + while (fgets(buf, sizeof(buf) - 1, us)) { + if ((nf = get_fields(buf, ":", &fp, (int *)NULL, 0)) < 7) + continue; + if (fl) { + + /* + * Check the first line for header words. + */ + if (!fp[0] || strcmp(fp[0], "Num") + || !fp[1] || strcmp(fp[1], "RefCount") + || !fp[2] || strcmp(fp[2], "Protocol") + || !fp[3] || strcmp(fp[3], "Flags") + || !fp[4] || strcmp(fp[4], "Type") + || !fp[5] || strcmp(fp[5], "St") + || !fp[6] || strcmp(fp[6], "Inode") + || nf < 8 + || !fp[7] || strcmp(fp[7], "Path")) + { + if (!Fwarn) { + (void) fprintf(stderr, + "%s: WARNING: unsupported format: %s\n", + Pn, p); + } + break; + } + fl = 0; + continue; + } + /* + * Assemble PCB address, inode number, and path name. If this + * inode is already represented in Uxsin, skip it. + */ + ep = (char *)NULL; + if (!fp[6] || !*fp[6] + || (inode = strtoull(fp[6], &ep, 0)) == ULONG_MAX || !ep || *ep) + continue; + h = INOHASH(inode); + for (up = Uxsin[h]; up; up = up->next) { + if (inode == up->inode) + break; + } + if (up) + continue; + if (!fp[0] || !*fp[0]) + pcb = (char *)NULL; + else { + len = strlen(fp[0]) + 2; + if (!(pcb = (char *)malloc(len + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for UNIX PCB: %s\n", + Pn, (int)(len + 1), fp[0]); + Exit(1); + } + (void) snpf(pcb, len + 1, "0x%s", fp[0]); + } + if (nf >= 8 && fp[7] && *fp[7] && (len = strlen(fp[7]))) { + if (!(path = (char *)malloc(len + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for UNIX path \"%s\"\n", + Pn, (int)(len + 1), fp[7]); + Exit(1); + } + (void) snpf(path, len + 1, "%s", fp[7]); + } else + path = (char *)NULL; + /* + * Allocate and fill a Unix socket info structure; link it to its + * hash bucket. + */ + if (!(up = (struct uxsin *)malloc(sizeof(struct uxsin)))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for uxsin struct\n", + Pn, (int)sizeof(struct uxsin)); + Exit(1); + } + up->inode = inode; + up->pcb = pcb; + up->sb_def = 0; + if ((up->path = path) && (*path == '/')) { + + /* + * If an absolute path (i.e., one that begins with a '/') exists + * for the line, attempt to stat(2) it and save the device and + * node numbers reported in the stat buffer. + */ + struct stat sb; + int sr; + + if (HasNFS) + sr = statsafely(path, &sb); + else + sr = stat(path, &sb); + if (sr && ((sb.st_mode & S_IFMT) == S_IFSOCK)) { + up->sb_def = 1; + up->sb_dev = sb.st_dev; + up->sb_ino = (INODETYPE)sb.st_ino; + up->sb_rdev = sb.st_rdev; + } + } + up->next = Uxsin[h]; + Uxsin[h] = up; + } + (void) fclose(us); +} + + +#if defined(HASIPv6) +/* + * net6a2in6() - convert ASCII IPv6 address in /proc/net/{tcp,udp} form to + * an in6_addr + */ + +static int +net6a2in6(as, ad) + char *as; /* address source */ + struct in6_addr *ad; /* address destination */ +{ + char buf[9], *ep; + int i; + size_t len; +/* + * Assemble four uint32_t's from 4 X 8 hex digits into s6_addr32[]. + */ + for (i = 0, len = strlen(as); + (i < 4) && (len >= 8); + as += 8, i++, len -= 8) + { + (void) strncpy(buf, as, 8); + buf[8] = '\0'; + ep = (char *)NULL; + if ((ad->s6_addr32[i] = (uint32_t)strtoul(buf, &ep, 16)) + == (uint32_t)UINT32_MAX || !ep || *ep) + break; + } + return((*as || (i != 4) || len) ? 1 : 0); +} +#endif /* defined(HASIPv6) */ + + +/* + * isainb(a,b) is string a in string b + */ + +static int +isainb(a, b) + char *a; /*string a */ + char *b; /* string b */ +{ + char *cp, *pp; + MALLOC_S la, lb, lt; + + if (!a || !b) + return(1); + if (!(la = strlen(a)) || !(lb = strlen(b))) + return(1); + if (!(cp = strchr(b, (int)','))) { + if (la != lb) + return(1); + return(strcmp(a, b)); + } + for (pp = b; pp && *pp; ) { + lt = (MALLOC_S)(cp - pp); + if ((la == lt) && !strncmp(a, pp, lt)) + return(0); + if (*cp) { + pp = cp + 1; + if (!(cp = strchr(pp, (int)','))) + cp = b + lb; + } else + pp = cp; + } + return(1); +} + + +/* + * print_ax25info() - print AX25 socket info + */ + +static void +print_ax25info(ap) + struct ax25sin *ap; /* AX25 socket info */ +{ + char *cp, pbuf[1024]; + int ds; + MALLOC_S pl = (MALLOC_S)0; + + if (Lf->nma) + return; + if (ap->sa) { + ds = (ap->da && strcmp(ap->da, "*")) ? 1 : 0; + (void) snpf(&pbuf[pl], sizeof(pbuf) - pl, "%s%s%s ", ap->sa, + ds ? "->" : "", + ds ? ap->da : ""); + pl = strlen(pbuf); + } + if (ap->sqs) { + (void) snpf(&pbuf[pl], sizeof(pbuf) - pl, "(Sq=%lu ", ap->sq); + pl = strlen(pbuf); + cp = ""; + } else + cp = "("; + if (ap->rqs) { + (void) snpf(&pbuf[pl], sizeof(pbuf) - pl, "%sRq=%lu ", cp, ap->rq); + pl = strlen(pbuf); + cp = ""; + } + (void) snpf(&pbuf[pl], sizeof(pbuf) - pl, "%sState=%d", cp, ap->state); + pl = strlen(pbuf); + if ((ap->state >= 0) && (ap->state < NAX25ST)) + cp = ax25st[ap->state]; + else + cp = NULL; + (void) snpf(&pbuf[pl], sizeof(pbuf) - pl, "%s%s)", + cp ? ", " : "", + cp ? cp : ""); + pl = strlen(pbuf); + if (!(cp = (char *)malloc(pl + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for AX25 sock state, PID: %d\n", + Pn, (int)(pl + 1), Lp->pid); + Exit(1); + } + (void) snpf(cp, pl + 1, "%s", pbuf); + Lf->nma = cp; +} + + +/* + * print_ipxinfo() - print IPX socket info + */ + +static void +print_ipxinfo(ip) + struct ipxsin *ip; /* IPX socket info */ +{ + char *cp, pbuf[256]; + MALLOC_S pl; + + if (Lf->nma) + return; + (void) snpf(pbuf, sizeof(pbuf), "(Tx=%lx Rx=%lx State=%02x)", + ip->txq, ip->rxq, ip->state); + pl = strlen(pbuf); + if (!(cp = (char *)malloc(pl + 1))) { + (void) fprintf(stderr, + "%s: can't allocate %d bytes for IPX sock state, PID: %d\n", + Pn, (int)(pl + 1), Lp->pid); + Exit(1); + } + (void) snpf(cp, pl + 1, "%s", pbuf); + Lf->nma = cp; +} + + +/* + * print_tcptpi() - print TCP/TPI state + */ + +void +print_tcptpi(nl) + int nl; /* 1 == '\n' required */ +{ + char buf[128]; + char *cp = (char *)NULL; + int ps = 0; + int s; + + if ((Ftcptpi & TCPTPI_STATE) && Lf->lts.type == 0) { + if (!TcpSt) + (void) build_IPstates(); + if ((s = Lf->lts.state.i + TcpStOff) < 0 || s >= TcpNstates) { + (void) snpf(buf, sizeof(buf), "UNKNOWN_TCP_STATE_%d", + Lf->lts.state.i); + cp = buf; + } else + cp = TcpSt[s]; + if (cp) { + if (Ffield) + (void) printf("%cST=%s%c", LSOF_FID_TCPTPI, cp, Terminator); + else { + putchar('('); + (void) fputs(cp, stdout); + } + ps++; + } + } + +# if defined(HASTCPTPIQ) + if (Ftcptpi & TCPTPI_QUEUES) { + if (Lf->lts.rqs) { + if (Ffield) + putchar(LSOF_FID_TCPTPI); + else { + if (ps) + putchar(' '); + else + putchar('('); + } + (void) printf("QR=%lu", Lf->lts.rq); + if (Ffield) + putchar(Terminator); + ps++; + } + if (Lf->lts.sqs) { + if (Ffield) + putchar(LSOF_FID_TCPTPI); + else { + if (ps) + putchar(' '); + else + putchar('('); + } + (void) printf("QS=%lu", Lf->lts.sq); + if (Ffield) + putchar(Terminator); + ps++; + } + } +# endif /* defined(HASTCPTPIQ) */ + +# if defined(HASTCPTPIW) + if (Ftcptpi & TCPTPI_WINDOWS) { + if (Lf->lts.rws) { + if (Ffield) + putchar(LSOF_FID_TCPTPI); + else { + if (ps) + putchar(' '); + else + putchar('('); + } + (void) printf("WR=%lu", Lf->lts.rw); + if (Ffield) + putchar(Terminator); + ps++; + } + if (Lf->lts.wws) { + if (Ffield) + putchar(LSOF_FID_TCPTPI); + else { + if (ps) + putchar(' '); + else + putchar('('); + } + (void) printf("WW=%lu", Lf->lts.ww); + if (Ffield) + putchar(Terminator); + ps++; + } + } +# endif /* defined(HASTCPTPIW) */ + + if (!Ffield && ps) + putchar(')'); + if (nl) + putchar('\n'); +} + + +/* + * process_proc_sock() - process /proc-based socket + */ + +void +process_proc_sock(p, pbr, s, ss, l, lss) + 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 lss; /* *l status -- i.e, SB_* values */ +{ + struct ax25sin *ap; + char *cp, *path, tbuf[64]; + unsigned char *fa, *la; + struct in_addr fs, ls; + struct icmpin *icmpp; + struct ipxsin *ip; + int i, len, nl; + struct nlksin *np; + struct packin *pp; + char *pr; + static char *prp = (char *)NULL; + struct rawsin *rp; + struct sctpsin *sp; + static ssize_t sz; + struct tcp_udp *tp; + struct uxsin *up; + +#if defined(HASIPv6) + int af; + struct tcp_udp6 *tp6; +#endif /* defined(HASIPv6) */ + +/* + * Enter offset, if possible. + */ + if (Foffset || !Fsize) { + if (l && (lss & SB_SIZE) && OffType) { + Lf->off = (SZOFFTYPE)l->st_size; + Lf->off_def = 1; + } + } +/* + * Check for socket's inode presence in the protocol info caches. + */ + if (AX25path) { + (void) get_ax25(AX25path); + (void) free((FREE_P *)AX25path); + AX25path = (char *)NULL; + } + if ((ss & SB_INO) + && (ap = check_ax25((INODETYPE)s->st_ino)) + ) { + + /* + * The inode is connected to an AX25 /proc record. + * + * Set the type to "ax25"; save the device name; save the inode number; + * save the destination and source addresses; save the send and receive + * queue sizes; and save the connection state. + */ + (void) snpf(Lf->type, sizeof(Lf->type), "ax25"); + if (ap->dev_ch) + (void) enter_dev_ch(ap->dev_ch); + Lf->inode = ap->inode; + Lf->inp_ty = 1; + print_ax25info(ap); + return; + } + if (Ipxpath) { + (void) get_ipx(Ipxpath); + (void) free((FREE_P *)Ipxpath); + Ipxpath = (char *)NULL; + } + if ((ss & SB_INO) + && (ip = check_ipx((INODETYPE)s->st_ino)) + ) { + + /* + * The inode is connected to an IPX /proc record. + * + * Set the type to "ipx"; enter the inode and device numbers; store + * the addresses, queue sizes, and state in the NAME column. + */ + (void) snpf(Lf->type, sizeof(Lf->type), "ipx"); + if (ss & SB_INO) { + Lf->inode = (INODETYPE)s->st_ino; + Lf->inp_ty = 1; + } + if (ss & SB_DEV) { + Lf->dev = s->st_dev; + Lf->dev_def = 1; + } + cp = Namech; + nl = Namechl; + *cp = '\0'; + if (ip->la && nl) { + + /* + * Store the local IPX address. + */ + len = strlen(ip->la); + if (len > nl) + len = nl; + (void) strncpy(cp, ip->la, len); + cp += len; + *cp = '\0'; + nl -= len; + } + if (ip->ra && nl) { + + /* + * Store the remote IPX address, prefixed with "->". + */ + if (nl > 2) { + (void) snpf(cp, nl, "->"); + cp += 2; + nl -= 2; + } + if (nl) { + (void) snpf(cp, nl, "%s", ip->ra); + cp += len; + nl -= len; + } + } + (void) print_ipxinfo(ip); + if (Namech[0]) + enter_nm(Namech); + return; + } + if (Rawpath) { + (void) get_raw(Rawpath); + (void) free((FREE_P *)Rawpath); + Rawpath = (char *)NULL; + } + if ((ss & SB_INO) + && (rp = check_raw((INODETYPE)s->st_ino)) + ) { + + /* + * The inode is connected to a raw /proc record. + * + * Set the type to "raw"; enter the inode number; store the local + * address, remote address, and state in the NAME column. + */ + (void) snpf(Lf->type, sizeof(Lf->type), "raw"); + if (ss & SB_INO) { + Lf->inode = (INODETYPE)s->st_ino; + Lf->inp_ty = 1; + } + cp = Namech; + nl = Namechl - 2; + *cp = '\0'; + if (rp->la && rp->lal) { + + /* + * Store the local raw address. + */ + if (nl > rp->lal) { + (void) snpf(cp, nl, "%s", rp->la); + cp += rp->lal; + *cp = '\0'; + nl -= rp->lal; + } + } + if (rp->ra && rp->ral) { + + /* + * Store the remote raw address, prefixed with "->". + */ + if (nl > (rp->ral + 2)) { + (void) snpf(cp, nl, "->%s", rp->ra); + cp += (rp->ral + 2); + *cp = '\0'; + nl -= (rp->ral + 2); + } + } + if (rp->sp && rp->spl) { + + /* + * Store the state, optionally prefixed by a space, in the + * form "st=x...x". + */ + + if (nl > (len = ((cp == Namech) ? 0 : 1) + 3 + rp->spl)) { + (void) snpf(cp, nl, "%sst=%s", + (cp == Namech) ? "" : " ", rp->sp); + cp += len; + *cp = '\0'; + nl -= len; + } + } + if (Namech[0]) + enter_nm(Namech); + return; + } + if (Nlkpath) { + (void) get_netlink(Nlkpath); + (void) free((FREE_P *) Nlkpath); + Nlkpath = (char *)NULL; + } + if ((ss & SB_INO) + && (np = check_netlink((INODETYPE)s->st_ino)) + ) { + /* + * The inode is connected to a Netlink /proc record. + * + * Set the type to "netlink" and store the protocol in the NAME + * column. Save the inode number. + */ + + (void) snpf(Lf->type, sizeof(Lf->type), "netlink"); + switch (np->pr) { + +#if defined(NETLINK_ROUTE) + case NETLINK_ROUTE: + cp = "ROUTE"; + break; +#endif /* defined(NETLINK_ROUTE) */ + +#if defined(NETLINK_UNUSED) + case NETLINK_UNUSED: + cp = "UNUSED"; + break; +#endif /* defined(NETLINK_UNUSED) */ + +#if defined(NETLINK_USERSOCK) + case NETLINK_USERSOCK: + cp = "USERSOCK"; + break; +#endif /* defined(NETLINK_USERSOCK) */ + +#if defined(NETLINK_FIREWALL) + case NETLINK_FIREWALL: + cp = "FIREWALL"; + break; +#endif /* defined(NETLINK_FIREWALL) */ + +#if defined(NETLINK_INET_DIAG) + case NETLINK_INET_DIAG: + cp = "INET_DIAG"; + break; +#endif /* defined(NETLINK_INET_DIAG) */ + +#if defined(NETLINK_NFLOG) + case NETLINK_NFLOG: + cp = "NFLOG"; + break; +#endif /* defined(NETLINK_NFLOG) */ + +#if defined(NETLINK_XFRM) + case NETLINK_XFRM: + cp = "XFRM"; + break; +#endif /* defined(NETLINK_XFRM) */ + +#if defined(NETLINK_SELINUX) + case NETLINK_SELINUX: + cp = "SELINUX"; + break; +#endif /* defined(NETLINK_SELINUX) */ + +#if defined(NETLINK_ISCSI) + case NETLINK_ISCSI: + cp = "ISCSI"; + break; +#endif /* defined(NETLINK_ISCSI) */ + +#if defined(NETLINK_AUDIT) + case NETLINK_AUDIT: + cp = "AUDIT"; + break; +#endif /* defined(NETLINK_AUDIT) */ + +#if defined(NETLINK_FIB_LOOKUP) + case NETLINK_FIB_LOOKUP: + cp = "FIB_LOOKUP"; + break; +#endif /* defined(NETLINK_FIB_LOOKUP) */ + +#if defined(NETLINK_CONNECTOR) + case NETLINK_CONNECTOR: + cp = "CONNECTOR"; + break; +#endif /* defined(NETLINK_CONNECTOR) */ + +#if defined(NETLINK_NETFILTER) + case NETLINK_NETFILTER: + cp = "NETFILTER"; + break; +#endif /* defined(NETLINK_NETFILTER) */ + +#if defined(NETLINK_IP6_FW) + case NETLINK_IP6_FW: + cp = "IP6_FW"; + break; +#endif /* defined(NETLINK_IP6_FW) */ + +#if defined(NETLINK_DNRTMSG) + case NETLINK_DNRTMSG: + cp = "DNRTMSG"; + break; +#endif /* defined(NETLINK_DNRTMSG) */ + +#if defined(NETLINK_KOBJECT_UEVENT) + case NETLINK_KOBJECT_UEVENT: + cp = "KOBJECT_UEVENT"; + break; +#endif /* defined(NETLINK_KOBJECT_UEVENT) */ + +#if defined(NETLINK_GENERIC) + case NETLINK_GENERIC: + cp = "GENERIC"; + break; +#endif /* defined(NETLINK_GENERIC) */ + +#if defined(NETLINK_SCSITRANSPORT) + case NETLINK_SCSITRANSPORT: + cp = "SCSITRANSPORT"; + break; +#endif /* defined(NETLINK_SCSITRANSPORT) */ + +#if defined(NETLINK_ECRYPTFS) + case NETLINK_ECRYPTFS: + cp = "ECRYPTFS"; + break; +#endif /* defined(NETLINK_ECRYPTFS) */ + + default: + (void) snpf(Namech, Namechl, "unknown protocol: %d", np->pr); + cp = (char *)NULL; + } + if (cp) + (void) snpf(Namech, Namechl, "%s", cp); + Lf->inode = (INODETYPE)s->st_ino; + Lf->inp_ty = 1; + if (Namech[0]) + enter_nm(Namech); + return; + } + if (Packpath) { + (void) get_pack(Packpath); + (void) free((FREE_P *)Packpath); + Packpath = (char *)NULL; + } + if ((ss & SB_INO) + && (pp = check_pack((INODETYPE)s->st_ino)) + ) { + + /* + * The inode is connected to a packet /proc record. + * + * Set the type to "pack" and store the socket type in the NAME + * column. Put the protocol name in the NODE column and the inode + * number in the DEVICE column. + */ + (void) snpf(Lf->type, sizeof(Lf->type), "pack"); + switch(pp->ty) { + +#if defined(SOCK_STREAM) + case SOCK_STREAM: + cp = "STREAM"; + break; +#endif /* defined(SOCK_STREAM) */ + +#if defined(SOCK_DGRAM) + case SOCK_DGRAM: + cp = "DGRAM"; + break; +#endif /* defined(SOCK_DGRAM) */ + +#if defined(SOCK_RAW) + case SOCK_RAW: + cp = "RAW"; + break; +#endif /* defined(SOCK_RAW) */ + +#if defined(SOCK_RDM) + case SOCK_RDM: + cp = "RDM"; + break; +#endif /* defined(SOCK_RDM) */ + +#if defined(SOCK_SEQPACKET) + case SOCK_SEQPACKET: + cp = "SEQPACKET"; + break; +#endif /* defined(SOCK_SEQPACKET) */ + +#if defined(SOCK_PACKET) + case SOCK_PACKET: + cp = "PACKET"; + break; +#endif /* defined(SOCK_PACKET) */ + + default: + (void) snpf(Namech, Namechl, "unknown type: %d", pp->ty); + cp = (char *)NULL; + } + if (cp) + (void) snpf(Namech, Namechl, "type=SOCK_%s", cp); + switch (pp->pr) { + +#if defined(ETH_P_LOOP) + case ETH_P_LOOP: + cp = "LOOP"; + break; +#endif /* defined(ETH_P_LOOP) */ + +#if defined(ETH_P_PUP) + case ETH_P_PUP: + cp = "PUP"; + break; +#endif /* defined(ETH_P_PUP) */ + +#if defined(ETH_P_PUPAT) + case ETH_P_PUPAT: + cp = "PUPAT"; + break; +#endif /* defined(ETH_P_PUPAT) */ + +#if defined(ETH_P_IP) + case ETH_P_IP: + cp = "IP"; + break; +#endif /* defined(ETH_P_IP) */ + +#if defined(ETH_P_X25) + case ETH_P_X25: + cp = "X25"; + break; +#endif /* defined(ETH_P_X25) */ + +#if defined(ETH_P_ARP) + case ETH_P_ARP: + cp = "ARP"; + break; +#endif /* defined(ETH_P_ARP) */ + +#if defined(ETH_P_BPQ) + case ETH_P_BPQ: + cp = "BPQ"; + break; +#endif /* defined(ETH_P_BPQ) */ + +#if defined(ETH_P_IEEEPUP) + case ETH_P_IEEEPUP: + cp = "I3EPUP"; + break; +#endif /* defined(ETH_P_IEEEPUP) */ + +#if defined(ETH_P_IEEEPUPAT) + case ETH_P_IEEEPUPAT: + cp = "I3EPUPA"; + break; +#endif /* defined(ETH_P_IEEEPUPAT) */ + +#if defined(ETH_P_DEC) + case ETH_P_DEC: + cp = "DEC"; + break; +#endif /* defined(ETH_P_DEC) */ + +#if defined(ETH_P_DNA_DL) + case ETH_P_DNA_DL: + cp = "DNA_DL"; + break; +#endif /* defined(ETH_P_DNA_DL) */ + +#if defined(ETH_P_DNA_RC) + case ETH_P_DNA_RC: + cp = "DNA_RC"; + break; +#endif /* defined(ETH_P_DNA_RC) */ + +#if defined(ETH_P_DNA_RT) + case ETH_P_DNA_RT: + cp = "DNA_RT"; + break; +#endif /* defined(ETH_P_DNA_RT) */ + +#if defined(ETH_P_LAT) + case ETH_P_LAT: + cp = "LAT"; + break; +#endif /* defined(ETH_P_LAT) */ + +#if defined(ETH_P_DIAG) + case ETH_P_DIAG: + cp = "DIAG"; + break; +#endif /* defined(ETH_P_DIAG) */ + +#if defined(ETH_P_CUST) + case ETH_P_CUST: + cp = "CUST"; + break; +#endif /* defined(ETH_P_CUST) */ + +#if defined(ETH_P_SCA) + case ETH_P_SCA: + cp = "SCA"; + break; +#endif /* defined(ETH_P_SCA) */ + +#if defined(ETH_P_RARP) + case ETH_P_RARP: + cp = "RARP"; + break; +#endif /* defined(ETH_P_RARP) */ + +#if defined(ETH_P_ATALK) + case ETH_P_ATALK: + cp = "ATALK"; + break; +#endif /* defined(ETH_P_ATALK) */ + +#if defined(ETH_P_AARP) + case ETH_P_AARP: + cp = "AARP"; + break; +#endif /* defined(ETH_P_AARP) */ + +#if defined(ETH_P_8021Q) + case ETH_P_8021Q: + cp = "8021Q"; + break; +#endif /* defined(ETH_P_8021Q) */ + +#if defined(ETH_P_IPX) + case ETH_P_IPX: + cp = "IPX"; + break; +#endif /* defined(ETH_P_IPX) */ + +#if defined(ETH_P_IPV6) + case ETH_P_IPV6: + cp = "IPV6"; + break; +#endif /* defined(ETH_P_IPV6) */ + +#if defined(ETH_P_SLOW) + case ETH_P_SLOW: + cp = "SLOW"; + break; +#endif /* defined(ETH_P_SLOW) */ + +#if defined(ETH_P_WCCP) + case ETH_P_WCCP: + cp = "WCCP"; + break; +#endif /* defined(ETH_P_WCCP) */ + +#if defined(ETH_P_PPP_DISC) + case ETH_P_PPP_DISC: + cp = "PPP_DIS"; + break; +#endif /* defined(ETH_P_PPP_DISC) */ + +#if defined(ETH_P_PPP_SES) + case ETH_P_PPP_SES: + cp = "PPP_SES"; + break; +#endif /* defined(ETH_P_PPP_SES) */ + +#if defined(ETH_P_MPLS_UC) + case ETH_P_MPLS_UC: + cp = "MPLS_UC"; + break; +#endif /* defined(ETH_P_MPLS_UC) */ + +#if defined(ETH_P_ATMMPOA) + case ETH_P_ATMMPOA: + cp = "ATMMPOA"; + break; +#endif /* defined(ETH_P_ATMMPOA) */ + +#if defined(ETH_P_MPLS_MC) + case ETH_P_MPLS_MC: + cp = "MPLS_MC"; + break; +#endif /* defined(ETH_P_MPLS_MC) */ + +#if defined(ETH_P_ATMFATE) + case ETH_P_ATMFATE: + cp = "ATMFATE"; + break; +#endif /* defined(ETH_P_ATMFATE) */ + +#if defined(ETH_P_AOE) + case ETH_P_AOE: + cp = "AOE"; + break; +#endif /* defined(ETH_P_AOE) */ + +#if defined(ETH_P_TIPC) + case ETH_P_TIPC: + cp = "TIPC"; + break; +#endif /* defined(ETH_P_TIPC) */ + +#if defined(ETH_P_802_3) + case ETH_P_802_3: + cp = "802.3"; + break; +#endif /* defined(ETH_P_802_3) */ + +#if defined(ETH_P_AX25) + case ETH_P_AX25: + cp = "AX25"; + break; +#endif /* defined(ETH_P_AX25) */ + +#if defined(ETH_P_ALL) + case ETH_P_ALL: + cp = "ALL"; + break; +#endif /* defined(ETH_P_ALL) */ + +#if defined(ETH_P_802_2) + case ETH_P_802_2: + cp = "802.2"; + break; +#endif /* defined(ETH_P_802_2) */ + +#if defined(ETH_P_SNAP) + case ETH_P_SNAP: + cp = "SNAP"; + break; +#endif /* defined(ETH_P_SNAP) */ + +#if defined(ETH_P_DDCMP) + case ETH_P_DDCMP: + cp = "DDCMP"; + break; +#endif /* defined(ETH_P_DDCMP) */ + +#if defined(ETH_P_WAN_PPP) + case ETH_P_WAN_PPP: + cp = "WAN_PPP"; + break; +#endif /* defined(ETH_P_WAN_PPP) */ + +#if defined(ETH_P_PPP_MP) + case ETH_P_PPP_MP: + cp = "PPP MP"; + break; +#endif /* defined(ETH_P_PPP_MP) */ + +#if defined(ETH_P_LOCALTALK) + case ETH_P_LOCALTALK: + cp = "LCLTALK"; + break; +#endif /* defined(ETH_P_LOCALTALK) */ + +#if defined(ETH_P_PPPTALK) + case ETH_P_PPPTALK: + cp = "PPPTALK"; + break; +#endif /* defined(ETH_P_PPPTALK) */ + +#if defined(ETH_P_TR_802_2) + case ETH_P_TR_802_2: + cp = "802.2"; + break; +#endif /* defined(ETH_P_TR_802_2) */ + +#if defined(ETH_P_MOBITEX) + case ETH_P_MOBITEX: + cp = "MOBITEX"; + break; +#endif /* defined(ETH_P_MOBITEX) */ + +#if defined(ETH_P_CONTROL) + case ETH_P_CONTROL: + cp = "CONTROL"; + break; +#endif /* defined(ETH_P_CONTROL) */ + +#if defined(ETH_P_IRDA) + case ETH_P_IRDA: + cp = "IRDA"; + break; +#endif /* defined(ETH_P_IRDA) */ + +#if defined(ETH_P_ECONET) + case ETH_P_ECONET: + cp = "ECONET"; + break; +#endif /* defined(ETH_P_ECONET) */ + +#if defined(ETH_P_HDLC) + case ETH_P_HDLC: + cp = "HDLC"; + break; +#endif /* defined(ETH_P_HDLC) */ + +#if defined(ETH_P_ARCNET) + case ETH_P_ARCNET: + cp = "ARCNET"; + break; +#endif /* defined(ETH_P_ARCNET) */ + + default: + (void) snpf(tbuf, sizeof(tbuf) - 1, "%d", pp->pr); + tbuf[sizeof(tbuf) - 1] = '\0'; + cp = tbuf; + } + (void) snpf(Lf->iproto, sizeof(Lf->iproto), "%.*s", IPROTOL-1, cp); + Lf->inp_ty = 2; + if (ss & SB_INO) { + (void) snpf(tbuf, sizeof(tbuf), InodeFmt_d, + (INODETYPE)s->st_ino); + tbuf[sizeof(tbuf) - 1] = '\0'; + enter_dev_ch(tbuf); + } + if (Namech[0]) + enter_nm(Namech); + return; + } + if (UNIXpath) { + (void) get_unix(UNIXpath); + (void) free((FREE_P *)UNIXpath); + UNIXpath = (char *)NULL; + } + if ((ss & SB_INO) + && (up = check_unix((INODETYPE)s->st_ino)) + ) { + + /* + * The inode is connected to a UNIX /proc record. + * + * Set the type to "unix"; enter the PCB address in the DEVICE column; + * enter the inode number; and save the optional path. + */ + if (Funix) + Lf->sf |= SELUNX; + (void) snpf(Lf->type, sizeof(Lf->type), "unix"); + if (up->pcb) + enter_dev_ch(up->pcb); + if (ss & SB_INO) { + Lf->inode = (INODETYPE)s->st_ino; + Lf->inp_ty = 1; + } + path = up->path ? up->path : p; + (void) enter_nm(path); + if (Sfile) { + + /* + * See if this UNIX domain socket was specified as a search + * argument. + * + * Search first by device and node numbers, if that is possible; + * then search by name. + */ + unsigned char f = 0; /* file-found flag */ + + if (up->sb_def) { + + /* + * If the UNIX socket information includes stat(2) results, do + * a device and node number search. + * + * Note: that requires the saving, temporary modification and + * restoration of some *Lf values. + */ + unsigned char sv_dev_def; /* saved dev_def */ + unsigned char sv_inp_ty; /* saved inp_ty */ + unsigned char sv_rdev_def; /* saved rdev_def */ + dev_t sv_dev; /* saved dev */ + INODETYPE sv_inode; /* saved inode */ + dev_t sv_rdev; /* saved rdev */ + + sv_dev_def = Lf->dev_def; + sv_dev = Lf->dev; + sv_inode = Lf->inode; + sv_inp_ty = Lf->inp_ty; + sv_rdev_def = Lf->rdev_def; + sv_rdev = Lf->rdev; + Lf->dev_def = Lf->inp_ty = Lf->rdev_def = 1; + Lf->dev = up->sb_dev; + Lf->inode = up->sb_ino; + Lf->rdev = up->sb_rdev; + if (is_file_named(0, path, (struct mounts *)NULL, 0)) { + f = 1; + Lf->sf |= SELNM; + } + Lf->dev_def = sv_dev_def; + Lf->dev = sv_dev; + Lf->inode = sv_inode; + Lf->inp_ty = sv_inp_ty; + Lf->rdev_def = sv_rdev_def; + Lf->rdev = sv_rdev; + } + if (!f && (ss & SB_MODE)) { + + /* + * If the file has not yet been found and the stat buffer has + * st_mode, search for the file by full path. + */ + if (is_file_named(2, path, (struct mounts *)NULL, + ((s->st_mode & S_IFMT) == S_IFCHR)) ? 1 : 0) + { + Lf->sf |= SELNM; + } + } + } + return; + } + +#if defined(HASIPv6) + if (Raw6path) { + if (!Fxopt) + (void) get_raw6(Raw6path); + (void) free((FREE_P *)Raw6path); + Raw6path = (char *)NULL; + } + if (!Fxopt && (ss & SB_INO) + && (rp = check_raw6((INODETYPE)s->st_ino)) + ) { + + /* + * The inode is connected to a raw IPv6 /proc record. + * + * Set the type to "raw6"; enter the inode number; store the local + * address, remote address, and state in the NAME column. + */ + (void) snpf(Lf->type, sizeof(Lf->type), "raw6"); + if (ss & SB_INO) { + Lf->inode = (INODETYPE)s->st_ino; + Lf->inp_ty = 1; + } + cp = Namech; + nl = MAXPATHLEN - 2; + if (rp->la && rp->lal) { + + /* + * Store the local raw IPv6 address. + */ + if (nl > rp->lal) { + (void) snpf(cp, nl, "%s", rp->la); + cp += rp->lal; + *cp = '\0'; + nl -= rp->lal; + } + } + if (rp->ra && rp->ral) { + + /* + * Store the remote raw address, prefixed with "->". + */ + if (nl > (rp->ral + 2)) { + (void) snpf(cp, nl, "->%s", rp->ra); + cp += (rp->ral + 2); + nl -= (rp->ral + 2); + } + } + if (rp->sp && rp->spl) { + + /* + * Store the state, optionally prefixed by a space, in the + * form "st=x...x". + */ + + if (nl > (len = ((cp == Namech) ? 0 : 1) + 3 + rp->spl)) { + (void) snpf(cp, nl, "%sst=%s", + (cp == Namech) ? "" : " ", rp->sp); + cp += len; + *cp = '\0'; + nl -= len; + } + } + if (Namech[0]) + enter_nm(Namech); + return; + } + if (TCP6path) { + if (!Fxopt) + (void) get_tcpudp6(TCP6path, 0, 1); + (void) free((FREE_P *)TCP6path); + TCP6path = (char *)NULL; + } + if (UDP6path) { + if (!Fxopt) + (void) get_tcpudp6(UDP6path, 1, 0); + (void) free((FREE_P *)UDP6path); + UDP6path = (char *)NULL; + } + if (UDPLITE6path) { + if (!Fxopt) + (void) get_tcpudp6(UDPLITE6path, 2, 0); + (void) free((FREE_P *)UDPLITE6path); + UDPLITE6path = (char *)NULL; + } + if (!Fxopt && (ss & SB_INO) + && (tp6 = check_tcpudp6((INODETYPE)s->st_ino, &pr)) + ) { + + /* + * The inode is connected to an IPv6 TCP or UDP /proc record. + * + * Set the type to "IPv6"; enter the protocol; put the inode number + * in the DEVICE column in lieu of the PCB address; save the local + * and foreign IPv6 addresses; save the type and protocol; and + * (optionally) save the queue sizes. + */ + i = tp6->state + TcpStOff; + if (TcpStXn) { + + /* + * Check for state exclusion. + */ + if (i >= 0 && i < TcpNstates) { + if (TcpStX[i]) { + Lf->sf |= SELEXCLF; + return; + } + } + } + if (TcpStIn) { + + /* + * Check for state inclusion. + */ + if (i >= 0 && i < TcpNstates) { + if (TcpStI[i]) + TcpStI[i] = 2; + else { + Lf->sf |= SELEXCLF; + return; + } + } + } + if (Fnet && (FnetTy != 4)) + Lf->sf |= SELNET; + (void) snpf(Lf->type, sizeof(Lf->type), "IPv6"); + (void) snpf(Lf->iproto, sizeof(Lf->iproto), "%.*s", IPROTOL-1, pr); + Lf->inp_ty = 2; + if (ss & SB_INO) { + (void) snpf(tbuf, sizeof(tbuf), InodeFmt_d, + (INODETYPE)s->st_ino); + tbuf[sizeof(tbuf) - 1] = '\0'; + enter_dev_ch(tbuf); + } + af = AF_INET6; + if (!IN6_IS_ADDR_UNSPECIFIED(&tp6->faddr) || tp6->fport) + fa = (unsigned char *)&tp6->faddr; + else + fa = (unsigned char *)NULL; + if (!IN6_IS_ADDR_UNSPECIFIED(&tp6->laddr) || tp6->lport) + la = (unsigned char *)&tp6->laddr; + else + la = (unsigned char *)NULL; + if ((fa && IN6_IS_ADDR_V4MAPPED(&tp6->faddr)) + || (la && IN6_IS_ADDR_V4MAPPED(&tp6->laddr))) { + af = AF_INET; + if (fa) + fa += 12; + if (la) + la += 12; + } + ent_inaddr(la, tp6->lport, fa, tp6->fport, af); + Lf->lts.type = tp6->proto; + Lf->lts.state.i = tp6->state; + +#if defined(HASTCPTPIQ) + Lf->lts.rq = tp6->rxq; + Lf->lts.sq = tp6->txq; + Lf->lts.rqs = Lf->lts.sqs = 1; +#endif /* defined(HASTCPTPIQ) */ + + return; + } +#endif /* defined(HASIPv6) */ + + if (TCPpath) { + if (!Fxopt) + (void) get_tcpudp(TCPpath, 0, 1); + (void) free((FREE_P *)TCPpath); + TCPpath = (char *)NULL; + } + if (UDPpath) { + if (!Fxopt) + (void) get_tcpudp(UDPpath, 1, 0); + (void) free((FREE_P *)UDPpath); + UDPpath = (char *)NULL; + } + if (UDPLITEpath) { + if (!Fxopt) + (void) get_tcpudp(UDPLITEpath, 2, 0); + (void) free((FREE_P *)UDPLITEpath); + UDPLITEpath = (char *)NULL; + } + if (!Fxopt && (ss & SB_INO) + && (tp = check_tcpudp((INODETYPE)s->st_ino, &pr)) + ) { + + /* + * The inode is connected to an IPv4 TCP or UDP /proc record. + * + * Set the type to "inet" or "IPv4"; enter the protocol; put the + * inode number in the DEVICE column in lieu of the PCB address; + * save the local and foreign IPv4 addresses; save the type and + * protocol; and (optionally) save the queue sizes. + */ + i = tp->state + TcpStOff; + if (TcpStXn) { + + /* + * Check for state exclusion. + */ + if (i >= 0 && i < TcpNstates) { + if (TcpStX[i]) { + Lf->sf |= SELEXCLF; + return; + } + } + } + if (TcpStIn) { + + /* + * Check for state inclusion. + */ + if (i >= 0 && i < TcpNstates) { + if (TcpStI[i]) + TcpStI[i] = 2; + else { + Lf->sf |= SELEXCLF; + return; + } + } + } + if (Fnet && (FnetTy != 6)) + Lf->sf |= SELNET; + +#if defined(HASIPv6) + (void) snpf(Lf->type, sizeof(Lf->type), "IPv4"); +#else /* !defined(HASIPv6) */ + (void) snpf(Lf->type, sizeof(Lf->type), "inet"); +#endif /* defined(HASIPv6) */ + + (void) snpf(Lf->iproto, sizeof(Lf->iproto), "%.*s", IPROTOL-1, pr); + Lf->inp_ty = 2; + if (ss & SB_INO) { + (void) snpf(tbuf, sizeof(tbuf), InodeFmt_d, + (INODETYPE)s->st_ino); + tbuf[sizeof(tbuf) - 1] = '\0'; + enter_dev_ch(tbuf); + } + if (tp->faddr || tp->fport) { + fs.s_addr = tp->faddr; + fa = (unsigned char *)&fs; + } else + fa = (unsigned char *)NULL; + if (tp->laddr || tp->lport) { + ls.s_addr = tp->laddr; + la = (unsigned char *)&ls; + } else + la = (unsigned char *)NULL; + ent_inaddr(la, tp->lport, fa, tp->fport, AF_INET); + Lf->lts.type = tp->proto; + Lf->lts.state.i = tp->state; + +#if defined(HASTCPTPIQ) + Lf->lts.rq = tp->rxq; + Lf->lts.sq = tp->txq; + Lf->lts.rqs = Lf->lts.sqs = 1; +#endif /* defined(HASTCPTPIQ) */ + + return; + } + if (SCTPPath[0]) { + (void) get_sctp(); + for (i = 0; i < NSCTPPATHS; i++) { + (void) free((FREE_P *)SCTPPath[i]); + SCTPPath[i] = (char *)NULL; + } + } + if ((ss & SB_INO) && (sp = check_sctp((INODETYPE)s->st_ino)) + ) { + + /* + * The inode is connected to an SCTP /proc record. + * + * Set the type to "sock"; enter the inode number in the DEVICE + * column; set the protocol to SCTP; and fill in the NAME column + * with ASSOC, ASSOC-ID, ENDPT, LADDRS, LPORT, RADDRS and RPORT. + */ + (void) snpf(Lf->type, sizeof(Lf->type), "sock"); + (void) snpf(Lf->iproto, sizeof(Lf->iproto), "%.*s", IPROTOL-1, + "SCTP"); + Lf->inp_ty = 2; + (void) snpf(tbuf, sizeof(tbuf), InodeFmt_d, (INODETYPE)s->st_ino); + tbuf[sizeof(tbuf) - 1] = '\0'; + enter_dev_ch(tbuf); + Namech[0] = '\0'; + if (sp->type == 1) { + + /* + * This is an ENDPT SCTP file. + */ + (void) snpf(Namech, Namechl, + "ENDPT: %s%s%s%s%s%s", + sp->addr ? sp->addr : "", + (sp->laddrs || sp->lport) ? " " : "", + sp->laddrs ? sp->laddrs : "", + sp->lport ? "[" : "", + sp->lport ? sp->lport : "", + sp->lport ? "]" : "" + ); + } else { + + /* + * This is an ASSOC, or ASSOC and ENDPT socket file. + */ + (void) snpf(Namech, Namechl, + "%s: %s%s%s %s%s%s%s%s%s%s%s%s", + sp->type ? "ASSOC+ENDPT" : "ASSOC", + sp->addr ? sp->addr : "", + (sp->addr && sp->assocID) ? "," : "", + sp->assocID ? sp->assocID : "", + sp->laddrs ? sp->laddrs : "", + sp->lport ? "[" : "", + sp->lport ? sp->lport : "", + sp->lport ? "]" : "", + ((sp->laddrs || sp->lport) && (sp->raddrs || sp->rport)) + ? "<->" : "", + sp->raddrs ? sp->raddrs : "", + sp->rport ? "[" : "", + sp->rport ? sp->rport : "", + sp->rport ? "]" : "" + ); + } + if (Namech[0]) + enter_nm(Namech); + return; + } + if (ICMPpath) { + (void) get_icmp(ICMPpath); + (void) free((FREE_P *)ICMPpath); + ICMPpath = (char *)NULL; + } + if ((ss & SB_INO) + && (icmpp = check_icmp((INODETYPE)s->st_ino)) + ) { + + /* + * The inode is connected to an ICMP /proc record. + * + * Set the type to "icmp" and store the type in the NAME + * column. Save the inode number. + */ + (void) snpf(Lf->type, sizeof(Lf->type), "icmp"); + Lf->inode = (INODETYPE)s->st_ino; + Lf->inp_ty = 1; + cp = Namech; + nl = Namechl- 2; + *cp = '\0'; + if (icmpp->la && icmpp->lal) { + + /* + * Store the local raw address. + */ + if (nl > icmpp->lal) { + (void) snpf(cp, nl, "%s", icmpp->la); + cp += icmpp->lal; + *cp = '\0'; + nl -= icmpp->lal; + } + } + if (icmpp->ra && icmpp->ral) { + + /* + * Store the remote raw address, prefixed with "->". + */ + if (nl > (icmpp->ral + 2)) { + (void) snpf(cp, nl, "->%s", icmpp->ra); + cp += (icmpp->ral + 2); + *cp = '\0'; + nl -= (icmpp->ral + 2); + } + } + if (Namech[0]) + enter_nm(Namech); + return; + } +/* + * The socket's protocol can't be identified. + */ + (void) snpf(Lf->type, sizeof(Lf->type), "sock"); + if (ss & SB_INO) { + Lf->inode = (INODETYPE)s->st_ino; + Lf->inp_ty = 1; + } + if (ss & SB_DEV) { + Lf->dev = s->st_dev; + Lf->dev_def = 1; + } + if (Fxopt) + enter_nm("can't identify protocol (-X specified)"); + else { + (void) snpf(Namech, Namechl, "protocol: "); + if (!prp) { + i = (int)strlen(Namech); + prp = &Namech[i]; + sz = (ssize_t)(Namechl - i - 1); + } + if ((getxattr(pbr, "system.sockprotoname", prp, sz)) < 0) + enter_nm("can't identify protocol"); + else + enter_nm(Namech); + } +} + + +/* + * set_net_paths() - set /proc/net paths + */ + +void +set_net_paths(p, pl) + char *p; /* path to /proc/net/ */ + int pl; /* strlen(p) */ +{ + int i; + int pathl; + + pathl = 0; + (void) make_proc_path(p, pl, &AX25path, &pathl, "ax25"); + pathl = 0; + (void) make_proc_path(p, pl, &ICMPpath, &pathl, "icmp"); + pathl = 0; + (void) make_proc_path(p, pl, &Ipxpath, &pathl, "ipx"); + pathl = 0; + (void) make_proc_path(p, pl, &Nlkpath, &pathl, "netlink"); + pathl = 0; + (void) make_proc_path(p, pl, &Packpath, &pathl, "packet"); + pathl = 0; + (void) make_proc_path(p, pl, &Rawpath, &pathl, "raw"); + for (i = 0; i < NSCTPPATHS; i++) { + pathl = 0; + (void) make_proc_path(p, pl, &SCTPPath[i], &pathl, SCTPSfx[i]); + } + pathl = 0; + (void) make_proc_path(p, pl, &SockStatPath, &pathl, "sockstat"); + pathl = 0; + (void) make_proc_path(p, pl, &TCPpath, &pathl, "tcp"); + pathl = 0; + (void) make_proc_path(p, pl, &UDPpath, &pathl, "udp"); + pathl = 0; + (void) make_proc_path(p, pl, &UDPLITEpath, &pathl, "udplite"); + +#if defined(HASIPv6) + pathl = 0; + (void) make_proc_path(p, pl, &Raw6path, &pathl, "raw6"); + pathl = 0; + (void) make_proc_path(p, pl, &SockStatPath6, &pathl, "sockstat6"); + pathl = 0; + (void) make_proc_path(p, pl, &TCP6path, &pathl, "tcp6"); + pathl = 0; + (void) make_proc_path(p, pl, &UDP6path, &pathl, "udp6"); + pathl = 0; + (void) make_proc_path(p, pl, &UDPLITE6path, &pathl, "udplite6"); +#endif /* defined(HASIPv6) */ + + pathl = 0; + (void) make_proc_path(p, pl, &UNIXpath, &pathl, "unix"); +} diff --git a/dialects/linux/dstore.c b/dialects/linux/dstore.c new file mode 100644 index 0000000..80e2826 --- /dev/null +++ b/dialects/linux/dstore.c @@ -0,0 +1,114 @@ +/* + * dstore.c - Linux global storage 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: dstore.c,v 1.4 2011/09/07 19:07:45 abe Exp $"; +#endif + + +#include "lsof.h" + +int HasNFS = 0; /* NFS mount point status: + * 1 == there is an NFS mount point, + * but its device number is + * unknown + * 2 == there is an NFS mount point + * and its device number is + * known + */ +int OffType = 0; /* offset type: + * 0 == unknown + * 1 == lstat's st_size + * 2 == from /proc/<PID>/fdinfo */ + +/* + * Pff_tab[] - table for printing file flags + */ + +struct pff_tab Pff_tab[] = { + { (long)O_WRONLY, FF_WRITE }, + { (long)O_RDWR, FF_RDWR }, + { (long)O_CREAT, FF_CREAT }, + { (long)O_EXCL, FF_EXCL }, + { (long)O_NOCTTY, FF_NOCTTY }, + { (long)O_TRUNC, FF_TRUNC }, + { (long)O_APPEND, FF_APPEND }, + { (long)O_NDELAY, FF_NDELAY }, + { (long)O_SYNC, FF_SYNC }, + { (long)O_ASYNC, FF_ASYNC }, + +#if defined(O_DIRECT) + { (long)O_DIRECT, FF_DIRECT }, +#endif /* defined(O_DIRECT) */ + +#if defined(O_DIRECTORY) + { (long)O_DIRECTORY, FF_DIRECTORY }, +#endif /* defined(O_DIRECTORY) */ + +#if defined(O_NOFOLLOW) + { (long)O_NOFOLLOW, FF_NOFOLNK }, +#endif /* defined(O_NOFOLLOW) */ + +#if defined(O_NOATIME) + { (long)O_NOATIME, FF_NOATM }, +#endif /* defined(O_NOATIME) */ + +#if defined(O_DSYNC) + { (long)O_DSYNC, FF_DSYNC }, +#endif /* defined(O_DSYNC) */ + +#if defined(O_RSYNC) + { (long)O_RSYNC, FF_RSYNC }, +#endif /* defined(O_RSYNC) */ + +#if defined(O_LARGEFILE) +# if O_LARGEFILE==0 + { (long)0100000, FF_LARGEFILE }, +# else /* O_LARGEFILE!=0 */ + { (long)O_LARGEFILE, FF_LARGEFILE }, +# endif /* O_LARGEFILE==0 */ +#else /* !defined(O_LARGEFILE) */ + { (long)0100000, FF_LARGEFILE }, +#endif /* defined(O_LARGEFILE) */ + + { (long)0, NULL } +}; + + +/* + * Pof_tab[] - table for print process open file flags + */ + +struct pff_tab Pof_tab[] = { + { (long)0, NULL } +}; diff --git a/dialects/linux/machine.h b/dialects/linux/machine.h new file mode 100644 index 0000000..2b33128 --- /dev/null +++ b/dialects/linux/machine.h @@ -0,0 +1,644 @@ +/* + * machine.h - Linux definitions 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. + */ + + +/* + * $Id: machine.h,v 1.36 2012/04/10 16:39:50 abe Exp $ + */ + + +#if !defined(LSOF_MACHINE_H) +#define LSOF_MACHINE_H 1 + + +#include <sys/types.h> +#include <sys/param.h> + + +/* + * CAN_USE_CLNT_CREATE is defined for those dialects where RPC clnt_create() + * can be used to obtain a CLIENT handle in lieu of clnttcp_create(). + */ + +#define CAN_USE_CLNT_CREATE 1 + + +/* + * DEVDEV_PATH defines the path to the directory that contains device + * nodes. + */ + +#define DEVDEV_PATH "/dev" + + +/* + * GET_MAX_FD is defined for those dialects that provide a function other than + * getdtablesize() to obtain the maximum file descriptor number plus one. + */ + +/* #define GET_MAX_FD ? */ + + +/* + * HASAOPT is defined for those dialects that have AFS support; it specifies + * that the default path to an alternate AFS kernel name list file may be + * supplied with the -A <path> option. + */ + +/* #define HASAOPT 1 */ + + +/* + * HASBLKDEV is defined for those dialects that want block device information + * recorded in BDevtp[]. + */ + +/* #define HASBLKDEV 1 */ + + +/* + * HASDCACHE is defined for those dialects that support a device cache + * file. + * + * CAUTION!!! Do not enable HASDCACHE for /proc-based Linux lsof. The source + * code cannot support it. + * + * The presence of NEVER_HASDCACHE in this comment prevents the Customize + * script from offering to change HASDCACHE. + * + * + * HASENVDC defined the name of an environment variable that contains the + * device cache file path. The HASENVDC environment variable is ignored when + * the lsof process is setuid(root) or its real UID is 0. + * + * HASPERSDC defines the format for the last component of a personal device + * cache file path. The first will be the home directory of the real UID that + * executes lsof. + * + * HASPERSDCPATH defines the environment variable whose value is the middle + * component of the personal device cache file path. The middle component + * follows the home directory and precedes the results of applying HASPERSDC. + * The HASPERSDCPATH environment variable is ignored when the lsof process is + * setuid(root) or its real UID is 0. + * + * HASSYSDC defines a public device cache file path. When it's defined, it's + * used as the path from which to read the device cache. + * + * Consult the 00DCACHE and 00FAQ files of the lsof distribution for more + * information on device cache file path construction. + * + * CAUTION!!! Do not enable HASDCACHE for /proc-based Linux lsof. The source + * code cannot support it. + */ + +/* #define HASDCACHE 1 !!!DON'T ENABLE!!! -- see above comment */ +/* #define HASENVDC "LSOFDEVCACHE" */ +/* #define HASPERSDC "%h/%p.lsof_%L" */ +/* #define HASPERSDCPATH "LSOFPERSDCPATH" */ +/* #define HASSYSDC "/your/choice/of/path" */ + + +/* + * HASCDRNODE is defined for those dialects that have CD-ROM nodes. + */ + +/* #define HASCDRNODE 1 */ + + +/* + * HASFIFONODE is defined for those dialects that have FIFO nodes. + */ + +/* #define HASFIFONODE 1 */ + + +/* + * HASEOPT is defined for dialects that support the -e option + */ + +#define HASEOPT 1 + + +/* + * HASFSINO is defined for those dialects that have the file system + * inode element, fs_ino, in the lfile structure definition in lsof.h. + */ + +/* #define HASFSINO 1 */ + + +/* + * HASFSTRUCT is defined if the dialect has a file structure. + * + * FSV_DEFAULT defines the default set of file structure values to list. + * It defaults to zero (0), but may be made up of a combination of the + * FSV_* symbols from lsof.h. + * + * HASNOFSADDR -- has no file structure address + * HASNOFSFLAGS -- has no file structure flags + * HASNOFSCOUNT -- has no file structure count + * HASNOFSNADDR -- has no file structure node address + */ + +#define HASFSTRUCT 1 +/* #define FSV_DEFAULT FSV_? | FSV_? | FSV_? */ +#define HASNOFSADDR 1 /* has no file structure address */ +/* #define HASNOFSFLAGS 1 has no file structure flags */ +#define HASNOFSCOUNT 1 /* has no file structure count */ +#define HASNOFSNADDR 1 /* has no file structure node address */ + + +/* + * HASGNODE is defined for those dialects that have gnodes. + */ + +/* #define HASGNODE 1 */ + + +/* + * HASHSNODE is defined for those dialects that have High Sierra nodes. + */ + +/* #define HASHSNODE 1 */ + + +/* + * HASINODE is defined for those dialects that have inodes and wish to + * use readinode() from node.c. + */ + +/* #define HASINODE 1 */ + + +/* + * HASINTSIGNAL is defined for those dialects whose signal function returns + * an int. + */ + +/* #define HASINTSIGNAL 1 */ + + +/* + * HASKERNIDCK is defined for those dialects that support the comparison of + * the build to running kernel identity. + */ + +/* #define HASKERNIDCK 1 */ + + +/* + * HASKOPT is defined for those dialects that support the -k option of + * reading the kernel's name list from an optional file. + */ + +/* #define HASKOPT 1 */ + + +/* + * HASLFILEADD is defined for those dialects that need additional elements + * in struct lfile. The HASLFILEADD definition is a macro that defines + * them. If any of the additional elements need to be preset in the + * alloc_lfile() function of proc.c, the SETLFILEADD macro may be defined + * to do that. + * + * If any additional elements need to be cleared in alloc_lfile() or in the + * free_proc() function of proc.c, the CLRLFILEADD macro may be defined to + * do that. Note that CLRLFILEADD takes one argument, the pointer to the + * lfile struct. The CLRLFILEADD macro is expected to expand to statements + * that are complete -- i.e., have terminating semi-colons -- so the macro is + * called without a terminating semicolon by proc.c. + * + * The HASXOPT definition may be used to select the conditions under which + * private lfile elements are used. + */ + +/* #define HASLFILEADD int ... */ +/* #define CLRLFILEADD(lf) (lf)->... = (type)NULL; */ +/* #define SETLFILEADD Lf->... */ + + +/* + * HASLWP is defined for dialects that have LWP support inside processes. + */ + +#define HASLWP 1 + + +/* + * HASMNTSTAT indicates the dialect supports the mount stat(2) result option + * in its l_vfs and mounts structures. + */ + +/* #define HASMNTSTAT 1 */ + + +/* + * HASMNTSUP is defined for those dialects that support the mount supplement + * option. + */ + +#define HASMNTSUP 1 + + +/* + * HASMOPT is defined for those dialects that support the reading of + * kernel memory from an alternate file. + */ + +/* #define HASMOPT 1 */ + + +/* + * HASNCACHE is defined for those dialects that have a kernel name cache + * that lsof can search. A value of 1 directs printname() to prefix the + * cache value with the file system directory name; 2, avoid the prefix. + * + * NCACHELDPFX is a set of C commands to execute before calling ncache_load(). + * + * NCACHELDSFX is a set of C commands to execute after calling ncache_load(). + */ + +/* #define HASNCACHE 1 */ +/* #define NCACHELDPFX ??? */ +/* #define NCACHELDSFX ??? */ + + +/* + * HASNLIST is defined for those dialects that use nlist() to acccess + * kernel symbols. + */ + +/* #define HASNLIST 1 */ + + +/* + * HASPIPEFN is defined for those dialects that have a special function to + * process DTYPE_PIPE file structure entries. Its value is the name of the + * function. + * + * NOTE: don't forget to define a prototype for this function in dproto.h. + */ + +/* #define HASPIPEFN process_pipe? */ + + +/* + * HASPIPENODE is defined for those dialects that have pipe nodes. + */ + +/* #define HASPIPENODE 1 */ + + +/* + * HASPMAPENABLED is defined when the reporting of portmapper registration + * info is enabled by default. + */ + +/* #define HASPMAPENABLED 1 */ + + +/* + * HASPPID is defined for those dialects that support identification of + * the parent process IDentifier (PPID) of a process. + */ + +#define HASPPID 1 + + +/* + * HASPRINTDEV, HASPRINTINO, HASPRINTNM, HASPRINTOFF, and HASPRINTSZ + * define private dialect-specific functions for printing DEVice numbers, + * INOde numbers, NaMes, file OFFsets, and file SiZes. The functions are + * called from print_file(). + */ + +/* #define HASPRINTDEV print_dev? */ +/* #define HASPRINTINO print_ino? */ +/* #define HASPRINTNM print_nm? */ +/* #define HASPRINTOFF print_off? */ +/* #define HASPRINTSZ print_sz? */ + + +/* + * HASPRIVFILETYPE and PRIVFILETYPE are defined for dialects that have a + * file structure type that isn't defined by a DTYPE_* symbol. They are + * used in lib/prfp.c to select the type's processing. + * + * PRIVFILETYPE is the definition of the f_type value in the file struct. + * + * HASPRIVFILETYPE is the name of the processing function. + */ + +/* #define HASPRIVFILETYPE process_shmf? */ +/* #define PRIVFILETYPE ?? */ + + +/* + * HASPRIVNMCACHE is defined for dialects that have a private method for + * printing cached NAME column values for some files. HASPRIVNAMECACHE + * is defined to be the name of the function. + * + * The function takes one argument, a struct lfile pointer to the file, and + * returns non-zero if it prints a name to stdout. + */ + +/* #define HASPRIVNMCACHE <function name> */ + + +/* + * HASPRIVPRIPP is defined for dialects that have a private function for + * printing IP protocol names. When HASPRIVPRIPP isn't defined, the + * IP protocol name printing function defaults to printiprto(). + */ + +/* #define HASPRIVPRIPP 1 */ + + +/* + * HASPROCFS is defined for those dialects that have a proc file system -- + * usually /proc and usually in SYSV4 derivatives. + * + * HASFSTYPE is defined as 1 for those systems that have a file system type + * string, st_fstype, in the stat() buffer; 2, for those systems that have a + * file system type integer in the stat() buffer, named MOUNTS_STAT_FSTYPE; + * 0, for systems whose stat(2) structure has no file system type member. The + * additional symbols MOUNTS_FSTYPE, RMNT_FSTYPE, and RMNT_STAT_FSTYPE may be + * defined in dlsof.h to direct how the readmnt() function in lib/rmnt.c + * preserves these stat(2) and getmntent(3) buffer values in the local mounts + * structure. + * + * The defined value is the string that names the file system type. + * + * The HASPROCFS definition usually must be accompanied by the HASFSTYPE + * definition and the providing of an fstype element in the local mounts + * structure (defined in dlsof.h). + * + * The HASPROCFS definition may be accompanied by the HASPINODEN definition. + * HASPINODEN specifies that searching for files in HASPROCFS is to be done + * by inode number. + */ + +/* #define HASPROCFS "proc?" */ +/* #define HASFSTYPE 1 */ +/* #define HASPINODEN 1 */ + + +/* + * HASRNODE is defined for those dialects that have rnodes. + */ + +/* #define HASRNODE 1 */ + + +/* + * Define HASSECURITY to restrict the listing of all open files to the + * root user. When HASSECURITY is defined, the non-root user may list + * only files whose processes have the same user ID as the real user ID + * (the one that its user logged on with) of the lsof process. + */ + +/* #define HASSECURITY 1 */ + + +/* + * If HASSECURITY is defined, define HASNOSOCKSECURITY to allow users + * restricted by HASSECURITY to list any open socket files, provide their + * listing is selected by the "-i" option. + */ + +/* #define HASNOSOCKSECURITY 1 */ + + +/* + * HASSETLOCALE is defined for those dialects that have <locale.h> and + * setlocale(). + * + * If the dialect also has wide character support for language locales, + * HASWIDECHAR activates lsof's wide character support and WIDECHARINCL + * defines the header file (if any) that must be #include'd to use the + * mblen() and mbtowc() functions. + */ + +#define HASSETLOCALE 1 +#define HASWIDECHAR 1 +#define WIDECHARINCL <wctype.h> + + +/* + * HASSNODE is defined for those dialects that have snodes. + */ + +/* #define HASSNODE 1 */ + + +/* + * HASSOOPT, HASSOSTATE and HASTCPOPT define the availability of information + * on socket options (SO_* symbols), socket states (SS_* symbols) and TCP + * options. + */ + +/* #define HASSOOPT 1 has socket option information */ +/* #define HASSOSTATE 1 has socket state information */ +/* #define HASTCPOPT 1 has TCP options or flags */ + + +/* + * Define HASSPECDEVD to be the name of a function that handles the results + * of a successful stat(2) of a file name argument. + * + * For example, HASSPECDEVD() for Darwin makes sure that st_dev is set to + * what stat("/dev") returns -- i.e., what's in DevDev. + * + * The function takes two arguments: + * + * 1: pointer to the full path name of file + * 2: pointer to the stat(2) result + * + * The function returns void. + */ + +/* #define HASSPECDEVD process_dev_stat */ + + +/* + * HASSTREAMS is defined for those dialects that support streams. + */ + +/* #define HASSTREAMS 1 */ + + +/* + * HASTASKS is defined for those dialects that have task reporting support. + */ + +#define HASTASKS 1 + + +/* + * HASTCPTPIQ is defined for dialects where it is possible to report the + * TCP/TPI Recv-Q and Send-Q values produced by netstat. + */ + +#define HASTCPTPIQ 1 + + +/* + * HASTCPTPIW is defined for dialects where it is possible to report the + * TCP/TPI send and receive window sizes produced by netstat. + */ + +/* #define HASTCPTPIW 1 */ + + +/* + * HASTCPUDPSTATE is defined for dialects that have TCP and UDP state + * support -- i.e., for the "-stcp|udp:state" option and its associated + * speed improvements. + */ + +#define HASTCPUDPSTATE 1 + + +/* + * HASTMPNODE is defined for those dialects that have tmpnodes. + */ + +/* #define HASTMPNODE 1 */ + + +/* + * HASVNODE is defined for those dialects that use the Sun virtual file system + * node, the vnode. BSD derivatives usually do; System V derivatives prior to + * R4 usually don't. + * doesn't. + */ + +/* #define HASVNODE 1 */ + + +/* + * HASXOPT is defined for those dialects that have an X option. It + * defines the text for the usage display. HASXOPT_VALUE defines the + * option's default binary value -- 0 or 1. + */ + +#define HASXOPT "skip TCP&UDP* files" +#define HASXOPT_VALUE 0 + + +/* + * INODETYPE and INODEPSPEC define the internal node number type and its + * printf specification modifier. These need not be defined and lsof.h + * can be allowed to define defaults. + * + * These are defined here, because they must be used in dlsof.h. + */ + +#define INODETYPE unsigned long long + /* inode number internal storage type */ +#define INODEPSPEC "ll" /* INODETYPE printf specification + * modifier */ + + +/* + * UID_ARG defines the size of a User ID number when it is passed + * as a function argument. + */ + +#define UID_ARG u_int + + +/* + * Each USE_LIB_<function_name> is defined for dialects that use the + * <function_name> in the lsof library. + * + * Note: other definitions and operations may be required to condition the + * library function source code. They may be found in the dialect dlsof.h + * header files. + */ + +/* #define USE_LIB_CKKV 1 ckkv.c */ +/* #define USE_LIB_COMPLETEVFS 1 cvfs.c */ +/* #define USE_LIB_FIND_CH_INO 1 fino.c */ +/* #define USE_LIB_IS_FILE_NAMED 1 isfn.c */ +/* #define USE_LIB_LKUPDEV 1 lkud.c */ +/* #define USE_LIB_PRINTDEVNAME 1 pdvn.c */ +/* #define USE_LIB_PROCESS_FILE 1 prfp.c */ +/* #define USE_LIB_PRINT_TCPTPI 1 ptti.c */ +/* #define USE_LIB_READDEV 1 rdev.c */ +/* #define USE_LIB_READMNT 1 rmnt.c */ +/* #define USE_LIB_REGEX 1 regex.c */ +/* #define USE_LIB_RNAM 1 rnam.c */ +/* #define USE_LIB_RNCH 1 rnch.c */ +/* #define USE_LIB_RNMH 1 rnmh.c */ +/* #define USE_LIB_SNPF 1 snpf.c */ +#define snpf snprintf /* use the system's snprintf() */ + + +/* + * WARNDEVACCESS is defined for those dialects that should issue a warning + * when lsof can't access /dev (or /device) or one of its sub-directories. + * The warning can be inhibited by the lsof caller with the -w option. + * + * CAUTION!!! Don't enable the WARNDEVACCESS definiton for /proc-based Linux + * lsof; it doesn't process /dev at all. + * + * The presence of NEVER_WARNDEVACCESS in this comment prevents the Customize + * script from offering to change WARNDEVACCESS. + */ + +/* #define WARNDEVACCESS 1 DON'T ENABLE!!! -- see above comment */ + + +/* + * WARNINGSTATE is defined for those dialects that want to suppress all lsof + * warning messages. + */ + +/* #define WARNINGSTATE 1 warnings are enabled by default */ + + +/* + * WILLDROPGID is defined for those dialects whose lsof executable runs + * setgid(not_real_GID) and whose setgid power can be relinquished after + * the dialect's initialize() function has been executed. + */ + +/* #define WILLDROPGID 1 */ + + +/* + * zeromem is a macro that uses bzero or memset. + */ + +#define zeromem(a, l) bzero(a, l) + +#endif /* !defined(LSOF_MACHINE_H) */ |