blob: bbc4c9c70966506ceb369a8750fbab1c3ebed20c [file] [log] [blame]
/*
* ddev.c - DEC OSF/1, Digital UNIX, Tru64 UNIX device support functions for
* lsof
*/
/*
* Copyright 1994 Purdue Research Foundation, West Lafayette, Indiana
* 47907. All rights reserved.
*
* Written by Victor A. Abell
*
* This software is not subject to any license of the American Telephone
* and Telegraph Company or the Regents of the University of California.
*
* Permission is granted to anyone to use this software for any purpose on
* any computer system, and to alter it and redistribute it freely, subject
* to the following restrictions:
*
* 1. Neither the authors nor Purdue University are responsible for any
* consequences of the use of this software.
*
* 2. The origin of this software must not be misrepresented, either by
* explicit claim or by omission. Credit to the authors and Purdue
* University must appear in documentation and sources.
*
* 3. Altered versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 4. This notice may not be removed or altered.
*/
#ifndef lint
static char copyright[] =
"@(#) Copyright 1994 Purdue Research Foundation.\nAll rights reserved.\n";
static char *rcsid = "$Id: ddev.c,v 1.17 2005/08/12 15:35:14 abe Exp $";
#endif
#include "lsof.h"
/*
* Local static values
*/
#if defined(USELOCALREADDIR)
static struct stat Dirsb;
#endif /* defined(USELOCALREADDIR) */
/*
* Local definitions
*/
#define LIKE_BLK_SPEC "like block special"
#define LIKE_CHR_SPEC "like character special"
/*
* Local function prototypes
*/
_PROTOTYPE(static int rmdupdev,(struct l_dev ***dp, int n, char *nm));
#if defined(HASDCACHE)
/*
* clr_sect() - clear cached clone and pseudo sections
*/
void
clr_sect()
{
struct clone *c, *c1;
if (Clone) {
for (c = Clone; c; c = c1) {
c1 = c->next;
(void) free((FREE_P *)c);
}
Clone = (struct clone *)NULL;
}
}
#endif /* defined(HASDCACHE) */
/*
* printdevname() - print block and character device names
*/
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 */
{
struct clone *c;
struct l_dev *dp;
readdev(0);
/*
* Search for clone.
*/
#if defined(HASDCACHE)
printdevname_again:
#endif /* defined(HASDCACHE) */
if ((nty == N_CHR) && Clone && HAVECLONEMAJ && (*dev == DevDev)
&& (GET_MAJ_DEV(*rdev) == CLONEMAJ))
{
for (c = Clone; c; c = c->next) {
if (Devtp[c->dx].rdev == *rdev) {
#if defined(HASDCACHE)
if (DCunsafe && !Devtp[c->dx].v && !vfy_dev(&Devtp[c->dx]))
goto printdevname_again;
#endif /* defined(HASDCACHE) */
safestrprt(Devtp[c->dx].name, stdout, f);
return(1);
}
}
}
/*
* Search device table for a full match.
*/
#if defined(HASBLKDEV)
if (nty == N_BLK)
dp = lkupbdev(dev, rdev, 1, 0);
else
#endif /* defined(HASBLKDEV) */
dp = lkupdev(dev, rdev, 1, 0);
if (dp) {
safestrprt(dp->name, stdout, f);
return(1);
}
/*
* Search device table for a match without inode number and dev.
*/
#if defined(HASBLKDEV)
if (nty == N_BLK)
dp = lkupbdev(&DevDev, rdev, 0, 0);
else
#endif /* defined(HASBLKDEV) */
dp = lkupdev(&DevDev, rdev, 0, 0);
if (dp) {
/*
* A raw device match was found. Record it as a name column addition.
*/
char *cp, *ttl;
int len;
ttl = (nty == N_BLK) ? LIKE_BLK_SPEC : LIKE_CHR_SPEC;
len = (int)(1 + strlen(ttl) + 1 + strlen(dp->name) + 1);
if (!(cp = (char *)malloc((MALLOC_S)(len + 1)))) {
(void) fprintf(stderr, "%s: no nma space for: (%s %s)\n",
Pn, ttl, dp->name);
Exit(1);
}
(void) snpf(cp, len + 1, "(%s %s)", ttl, dp->name);
(void) add_nma(cp, len);
(void) free((FREE_P *)cp);
return(0);
}
#if defined(HASDCACHE)
/*
* If the device cache is "unsafe" and we haven't found any match, reload
* the device cache.
*/
if (DCunsafe) {
(void) rereaddev();
goto printdevname_again;
}
#endif /* defined(HASDCACHE) */
return(0);
}
/*
* readdev() - read names, modes and device types of everything in /dev
*/
void
readdev(skip)
int skip; /* skip device cache read if 1 */
{
#if defined(HASDCACHE)
int dcrd;
#endif /* defined(HASDCACHE) */
struct clone *c;
DIR *dfp;
struct DIRTYPE *dp;
char *fp = (char *)NULL;
int i = 0;
#if defined(HASBLKDEV)
int j = 0;
#endif /* defined(HASBLKDEV) */
MALLOC_S nl;
char *path = (char *)NULL;
MALLOC_S pl;
struct stat sb;
if (Sdev)
return;
#if defined(HASDCACHE)
/*
* Read device cache, as directed.
*/
if (!skip) {
if (DCstate == 2 || DCstate == 3) {
if ((dcrd = read_dcache()) == 0)
return;
}
} else
dcrd = 1;
#endif /* defined(HASDCACHE) */
Dstk = (char **)NULL;
Dstkn = Dstkx = 0;
(void) stkdir("/dev");
/*
* Unstack the next /dev or /dev/<subdirectory> directory.
*/
while (--Dstkx >= 0) {
if (!(dfp = OpenDir(Dstk[Dstkx]))) {
#if defined(WARNDEVACCESS)
if (!Fwarn) {
(void) fprintf(stderr, "%s: WARNING: can't open: ", Pn);
safestrprt(Dstk[Dstkx], stderr, 1);
}
#endif /* defined(WARNDEVACCESS) */
(void) free((FREE_P *)Dstk[Dstkx]);
Dstk[Dstkx] = (char *)NULL;
continue;
}
if (path) {
(void) free((FREE_P *)path);
path = (char *)NULL;
}
if (!(path = mkstrcat(Dstk[Dstkx], -1, "/", 1, (char *)NULL, -1,
&pl)))
{
(void) fprintf(stderr, "%s: no space for: ", Pn);
safestrprt(Dstk[Dstkx], stderr, 1);
Exit(1);
}
(void) free((FREE_P *)Dstk[Dstkx]);
Dstk[Dstkx] = (char *)NULL;
/*
* Scan the directory.
*/
for (dp = ReadDir(dfp); dp; dp = ReadDir(dfp)) {
if (dp->d_ino == 0 || dp->d_name[0] == '.')
continue;
/*
* Form the full path name and get its status.
*/
if (fp) {
(void) free((FREE_P *)fp);
fp = (char *)NULL;
}
if (!(fp = mkstrcat(path, (int)pl, dp->d_name, dp->d_namlen,
(char *)NULL, -1, (MALLOC_S *)NULL)))
{
(void) fprintf(stderr, "%s: no space for: ", Pn);
safestrprt(path, stderr, 0);
safestrprt(dp->d_name, stderr, 1);
Exit(1);
}
#if defined(USE_STAT)
if (stat(fp, &sb) != 0)
#else /* !defined(USE_STAT) */
if (lstat(fp, &sb) != 0)
#endif /* defined(USE_STAT) */
{
if (errno == ENOENT) /* symbolic link to nowhere? */
continue;
#if defined(WARNDEVACCESS)
if (!Fwarn) {
int errno_save = errno;
(void) fprintf(stderr, "%s: can't stat ", Pn);
safestrprt(fp, stderr, 0);
(void) fprintf(stderr, ": %s\n", strerror(errno_save));
}
#endif /* defined(WARNDEVACCESS) */
continue;
}
/*
* If it's a subdirectory, stack its name for later processing.
*/
if ((sb.st_mode & S_IFMT) == S_IFDIR) {
(void) stkdir(fp);
continue;
}
if ((sb.st_mode & S_IFMT) == S_IFCHR) {
/*
* Save character device information in Devtp[].
*/
if (i >= Ndev) {
Ndev += DEVINCR;
if (!Devtp)
Devtp = (struct l_dev *)malloc(
(MALLOC_S)(sizeof(struct l_dev)*Ndev));
else
Devtp = (struct l_dev *)realloc(
(MALLOC_P *)Devtp,
(MALLOC_S)(sizeof(struct l_dev)*Ndev));
if (!Devtp) {
(void) fprintf(stderr,
"%s: no space for character device\n", Pn);
Exit(1);
}
}
Devtp[i].inode = (INODETYPE)sb.st_ino;
if (!(Devtp[i].name = mkstrcpy(fp, (MALLOC_S *)NULL))) {
(void) fprintf(stderr, "%s: no space for: ", Pn);
safestrprt(fp, stderr, 1);
Exit(1);
}
Devtp[i].rdev = sb.st_rdev;
Devtp[i].v = 0;
/*
* Save clone device location.
*/
if (HAVECLONEMAJ
&& GET_MAJ_DEV(Devtp[i].rdev) == CLONEMAJ)
{
if (!(c = (struct clone *)malloc(sizeof(struct clone))))
{
(void) fprintf(stderr,
"%s: no space for clone device: ", Pn);
safestrprt(fp, stderr, 1);
Exit(1);
}
c->dx = i;
c->next = Clone;
Clone = c;
}
i++;
}
#if defined(HASBLKDEV)
if ((sb.st_mode & S_IFMT) == S_IFBLK) {
/*
* Save block device information in BDevtp[].
*/
if (j >= BNdev) {
BNdev += DEVINCR;
if (!BDevtp)
BDevtp = (struct l_dev *)malloc(
(MALLOC_S)(sizeof(struct l_dev)*BNdev));
else
BDevtp = (struct l_dev *)realloc(
(MALLOC_P *)BDevtp,
(MALLOC_S)(sizeof(struct l_dev)*BNdev));
if (!BDevtp) {
(void) fprintf(stderr,
"%s: no space for block device\n", Pn);
Exit(1);
}
}
BDevtp[j].inode = (INODETYPE)sb.st_ino;
BDevtp[j].name = fp;
fp = (char *)NULL;
BDevtp[j].rdev = sb.st_rdev;
BDevtp[j].v = 0;
j++;
}
#endif /* defined(HASBLKDEV) */
}
(void) CloseDir(dfp);
}
/*
* Free any allocated space.
*/
if (Dstk) {
(void) free((FREE_P *)Dstk);
Dstk = (char **)NULL;
Dstkn = Dstkx = 0;
}
if (fp)
(void) free((FREE_P *)fp);
if (path)
(void) free((FREE_P *)path);
/*
* Reduce the BDevtp[] (optional) and Devtp[] tables to their minimum
* sizes; allocate and build sort pointer lists; and sort the tables by
* device number.
*/
#if defined(HASBLKDEV)
if (BNdev) {
if (BNdev > j) {
BNdev = j;
BDevtp = (struct l_dev *)realloc((MALLOC_P *)BDevtp,
(MALLOC_S)(sizeof(struct l_dev) * BNdev));
}
if (!(BSdev = (struct l_dev **)malloc(
(MALLOC_S)(sizeof(struct l_dev *) * BNdev))))
{
(void) fprintf(stderr,
"%s: no space for block device sort pointers\n", Pn);
Exit(1);
}
for (j = 0; j < BNdev; j++) {
BSdev[j] = &BDevtp[j];
}
(void) qsort((QSORT_P *)BSdev, (size_t)BNdev,
(size_t)sizeof(struct l_dev *), compdev);
BNdev = rmdupdev(&BSdev, BNdev, "block");
}
# if !defined(NOWARNBLKDEV)
else {
if (!Fwarn)
(void) fprintf(stderr,
"%s: WARNING: no block devices found\n", Pn);
}
# endif /* !defined(NOWARNBLKDEV) */
#endif /* defined(HASBLKDEV) */
if (Ndev) {
if (Ndev > i) {
Ndev = i;
Devtp = (struct l_dev *)realloc((MALLOC_P *)Devtp,
(MALLOC_S)(sizeof(struct l_dev) * Ndev));
}
if (!(Sdev = (struct l_dev **)malloc(
(MALLOC_S)(sizeof(struct l_dev *) * Ndev))))
{
(void) fprintf(stderr,
"%s: no space for character device sort pointers\n", Pn);
Exit(1);
}
for (i = 0; i < Ndev; i++) {
Sdev[i] = &Devtp[i];
}
(void) qsort((QSORT_P *)Sdev, (size_t)Ndev,
(size_t)sizeof(struct l_dev *), compdev);
Ndev = rmdupdev(&Sdev, Ndev, "char");
} else {
(void) fprintf(stderr, "%s: no character devices found\n", Pn);
Exit(1);
}
#if defined(HASDCACHE)
/*
* Write device cache file, as required.
*/
if (DCstate == 1 || (DCstate == 3 && dcrd))
write_dcache();
#endif /* defined(HASDCACHE) */
}
#if defined(USELOCALREADDIR)
/*
* Copyright (c) 1983, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* This is a hacked version of NetBSD's readdir() function written to work
* around an apparent bug in the Digital UNIX 3.0 getdirentries() system call
* and/or their "/dev/fd" filesystem. The problem is that when applied to
* "/dev/fs", getdirentries() returns the wrong size, which can cause readdir()
* to run off the end of it's internal buffer and return bogus file names.
*
* The changes from the original NetBSD file are:
*
* - uses of the field dd_flags in the DIR structure have been removed since
* Digital UNIX doesn't have this field (it seems to be mostly used for
* dealing with whiteout's in NetBSD's union filesystem).
*
* - uses of the dd_len field were replaced with dd_bufsiz, since this appears
* to be where the Digital UNIX opendir() call stashes the size of the buffer
* it mallocs. Why does Digital UNIX have both? No idea -- as far as I
* could tell dd_len was always 0.
*
* - code within "#ifdef BROKEN_GETDIRENTRIES ... #endif" has been added to
* workaround the bug. Note: this code uses the (apparently) unused field,
* dd_len, in the Digital UNIX DIR structure. This is pretty nasty, but
* then, this whole routine *is* just a hack to get around a (hopefully)
* temporary problem in Digital UNIX.
*
* This routine has only been tested on a couple of Digital UNIX 3.0 systems.
* I make no guarantees that it will work for you...!
*
* Duncan McEwan (duncan@comp.vuw.ac.nz)
*/
/*
* Additional changes by Vic Abell <abe@cc.purdue.edu>:
*
* - The BROKEN_GETDIRENTRIES symbol was deleted. Use of this function
* is controlled in the lsof distribution by the HASLOCALREADDIR
* definition.
*/
/*
* CloseDir() - close directory
*/
int
CloseDir(dirp)
register DIR *dirp;
{
return(closedir(dirp));
}
/*
* OpenDir() - open directory
*/
DIR *
OpenDir(dir)
char *dir;
{
DIR *dirp;
if ((dirp = opendir(dir))) {
/*
* Get a stat(2) buffer for the directory.
*
* Warn if the stat(2) buffer operation fails, close the directory,
* and respond that the open failed.
*/
if (statsafely(dir, &Dirsb)) {
int en = errno;
if (!Fwarn) {
(void) fprintf(stderr,
"%s: WARNING: can't statsafely(", Pn);
safestrprt(dir, stderr, 0);
(void) fprintf(stderr, "): %s\n", strerror(en));
}
(void) CloseDir(dirp);
dirp = (DIR *)NULL;
errno = en;
}
}
return(dirp);
}
/*
* ReadDir() - read next directory entry
*/
extern struct DIRTYPE *
ReadDir(dirp)
register DIR *dirp;
{
register struct DIRTYPE *dp;
/*
* Loop through the directory.
*/
for (;;) {
if (dirp->dd_loc >= dirp->dd_size) {
dirp->dd_loc = 0;
}
if (dirp->dd_loc == 0) {
dirp->dd_size = getdirentries(dirp->dd_fd,
dirp->dd_buf, dirp->dd_bufsiz, &dirp->dd_seek);
if (dirp->dd_size <= 0)
return((struct DIRTYPE *)NULL);
/*
* If the size returned by getdirentries() exceeds what it
* should be (as determined by a stat(2) of the directory),
* set it to the proper value. This is an adjustment for an
* apparent bug in the Digital UNIX 3.[02] getdirentries()
* function, when applied to a /dev/fd mount point.
*
* This check was conceived by Duncan McEwan and modified by
* Vic Abell.
*/
if (dirp->dd_size > (long)Dirsb.st_size)
dirp->dd_size = (long)Dirsb.st_size;
Dirsb.st_size -= (off_t)dirp->dd_size;
}
dp = (struct DIRTYPE *)(dirp->dd_buf + dirp->dd_loc);
if ((long)dp & 03) /* bogus pointer check */
return((struct DIRTYPE *)NULL);
if (dp->d_reclen <= 0
|| dp->d_reclen > dirp->dd_bufsiz + 1 - dirp->dd_loc)
return((struct DIRTYPE *)NULL);
dirp->dd_loc += dp->d_reclen;
if (dp->d_ino == 0)
continue;
return (dp);
}
}
#endif /* defined(USELOCALREADDIR) */
#if defined(HASDCACHE)
/*
* rereaddev() - reread device names, modes and types
*/
void
rereaddev()
{
(void) clr_devtab();
# if defined(DCACHE_CLR)
(void) DCACHE_CLR();
# endif /* defined(DCACHE_CLR) */
readdev(1);
DCunsafe = 0;
}
#endif /* defined(HASDCACHE) */
/*
* rmdupdev() - remove duplicate (major/minor/inode) devices
*/
static int
rmdupdev(dp, n, nm)
struct l_dev ***dp; /* device table pointers address */
int n; /* number of pointers */
char *nm; /* device table name for error message */
{
struct clone *c, *cp;
int i, j, k;
struct l_dev **p;
for (i = j = 0, p = *dp; i < n ;) {
for (k = i + 1; k < n; k++) {
if (p[i]->rdev != p[k]->rdev || p[i]->inode != p[k]->inode)
break;
/*
* See if we're deleting a duplicate clone device. If so,
* delete its clone table entry.
*/
for (c = Clone, cp = (struct clone *)NULL;
c;
cp = c, c = c->next)
{
if (&Devtp[c->dx] != p[k])
continue;
if (!cp)
Clone = c->next;
else
cp->next = c->next;
(void) free((FREE_P *)c);
break;
}
}
if (i != j)
p[j] = p[i];
j++;
i = k;
}
if (n == j)
return(n);
if (!(*dp = (struct l_dev **)realloc((MALLOC_P *)*dp,
(MALLOC_S)(j * sizeof(struct l_dev *)))))
{
(void) fprintf(stderr, "%s: can't realloc %s device pointers\n",
Pn, nm);
Exit(1);
}
return(j);
}
#if defined(HASDCACHE)
/*
* vfy_dev() - verify a device table entry (usually when DCunsafe == 1)
*
* Note: rereads entire device table when an entry can't be verified.
*/
int
vfy_dev(dp)
struct l_dev *dp; /* device table pointer */
{
struct stat sb;
if (!DCunsafe || dp->v)
return(1);
#if defined(USE_STAT)
if (stat(dp->name, &sb) != 0
#else /* !defined(USE_STAT) */
if (lstat(dp->name, &sb) != 0
#endif /* defined(USE_STAT) */
|| dp->rdev != sb.st_rdev
|| dp->inode != (INODETYPE)sb.st_ino) {
(void) rereaddev();
return(0);
}
dp->v = 1;
return(1);
}
#endif /* defined(HASDCACHE) */