[comp.sources.unix] v24i087: A program to trace through symlinks

rsalz@uunet.uu.net (Rich Salz) (03/27/91)

Submitted-by: Roger Southwick <rogers@fangorn.wr.tek.com>
Posting-number: Volume 24, Issue 87
Archive-name: namei

[ I wrote the Makefile.  --r$ ]

This program takes a set of pathnames.  It scans each component in turn and
follows symlinks until it reaches the end of the pathname.  It prints
the results of interpreting each directory component.  Requires the lstat
system call and the S_IFLINK inode type.

	-Roger (rogers@fangorn.wr.tek.com)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  README namei.1 namei.c
# Wrapped by rogers@fangorn on Wed Mar 20 17:51:06 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"README\"
else
echo shar: Extracting \"README\" \(989 characters\)
sed "s/^X//" >README <<'END_OF_README'
XTired of running into "Too many levels of symlinks" problems on
Xyour 4.2 BSD derivitive machine?  
X
XWe sure did... our NFS'ed network of lots of Suns, Vaxen and so forth
Xmade it impossible at times to trace down where a file REALLY lived.
XI mean ls -l is nice, but wouldn't you like to follow things like
Xthe namei routine in the kernel does?
X
XWell here it is.... the namei program.  It follows things out until
Xa terminal state is found.
X
XThis program compiles and runs under:
X
X    SunOS 4.0.1 (sun3's)
X    SunOS 4.0.3 (sun4's)
X    SunOS 4.1.1 (sun4's)
X    Ultrix 3.1
X    BSD 4.3
X
Xand probably a host of other 4.2 derived systems (but probably not
XSystem V).
X
XAnyway, if anyone has any bugs (or enhancements), please send them to
Xme in E-mail form.
X
XAnd, by the way, if you make LOTS of money off of this program, please
Xdon't tell me :-).
X
X    -Roger      (rogers@fangorn.wr.tek.com)
X		UUCP:	...!uunet!tektronix!fangorn.wr.tek.com!rogers
X		ARPA:	<rogers%fangorn.wr.tek.com@RELAY.CS.NET>
END_OF_README
if test 989 -ne `wc -c <README`; then
    echo shar: \"README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f namei.1 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"namei.1\"
else
echo shar: Extracting \"namei.1\" \(1496 characters\)
sed "s/^X//" >namei.1 <<'END_OF_namei.1'
X.\" 
X.\" Version 1.4 of namei
X.\"
X.TH NAMEI 1 "Local"
X.SH NAME
Xnamei - follow a pathname until a terminal point is found
X.SH SYNOPSIS
X.B namei
X.I [-mx]
X.I pathname
X.I "[ pathname ... ]"
X.SH DESCRIPTION
X.I Namei
Xuses its arguments as pathnames to any type
Xof Unix file (symlinks, files, directories, and so forth). 
X.I Namei
Xthen follows each pathname until a terminal 
Xpoint is found (a file, directory, char device, etc).
XIf it finds a symbolic link, we show the link, and start
Xfollowing it, indenting the output to show the context.
X.PP
XThis program is useful for finding a "too many levels of
Xsymbolic links" problems.
X.PP
XFor each line output,
X.I namei
Xoutputs a the following characters to identify the file types found:
X.LP
X.nf
X   f: = the pathname we are currently trying to resolve
X    d = directory
X    l = symbolic link (both the link and it's contents are output)
X    s = socket
X    b = block device
X    c = character device
X    - = regular file
X    ? = an error of some kind
X.fi
X.PP
X.I Namei
Xprints an informative message when
Xthe maximum number of symbolic links this system can have has been exceeded.
X.SH OPTIONS
X.TP 8
X.B -x
XShow mount point directories with a 'D', rather than a 'd'.
X.TP 8
X.B -m
XShow the mode bits of each file type in octal notation.
X.SH AUTHOR
XRoger Southwick  (rogers@amadeus.wr.tek.com)
X.SH BUGS
XTo be discovered.
X.SH CAVEATS
X.I Namei
Xwill follow an infinite loop of symbolic links forever.  To escape, use
XSIGINT (usually ^C).
X.SH "SEE ALSO"
Xls(1), stat(1)
END_OF_namei.1
if test 1496 -ne `wc -c <namei.1`; then
    echo shar: \"namei.1\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f namei.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"namei.c\"
else
echo shar: Extracting \"namei.c\" \(5724 characters\)
sed "s/^X//" >namei.c <<'END_OF_namei.c'
X/*-------------------------------------------------------------
X
XThe namei program
X
XBy: Roger S. Southwick
X
XMay 2, 1990
X
Xusage: namei pathname [pathname ... ]
X
XThis program reads it's arguments as pathnames to any type
Xof Unix file (symlinks, files, directories, and so forth). 
XThe program then follows each pathname until a terminal 
Xpoint is found (a file, directory, char device, etc).
XIf it finds a symbolic link, we show the link, and start
Xfollowing it, indenting the output to show the context.
X
XThis program is useful for finding a "too many levels of
Xsymbolic links" problems.
X
XFor each line output, the program puts a file type first:
X
X   f: = the pathname we are currently trying to resolve
X    d = directory
X    D = directory that is a mount point
X    l = symbolic link (both the link and it's contents are output)
X    s = socket
X    b = block device
X    c = character device
X    - = regular file
X    ? = an error of some kind
X
XThe program prints an informative messages when we exceed
Xthe maximum number of symbolic links this system can have.
X
XThe program exits with a 1 status ONLY if it finds it cannot
Xchdir to /,  or if it encounters an unknown file type.
X
X-------------------------------------------------------------*/
X
X#ifndef lint
Xstatic char *RCSid = "$Id: namei.c,v 1.4 91/03/20 17:44:11 rogers Release_1 $";
X#endif
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/param.h>
X
Xextern char *sys_errlist[];
Xextern int errno;
X#define ERR	sys_errlist[errno],errno
X
Xint symcount;
Xint mflag = 0;
Xint xflag = 0;
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X    void namei(), usage();
X    char *getwd();
X    int getopt();
X    extern int optind;
X    register int c;
X    char curdir[MAXPATHLEN];
X
X    if(argc < 2)
X	usage();
X
X    while((c = getopt(argc, argv, "mx")) != EOF){
X	switch(c){
X	    case 'm':
X		mflag++;
X		break;
X	    
X	    case 'x':
X		xflag++;
X		break;
X
X	    case '?':
X	    default:
X		usage();
X	}
X    }
X
X    if(getwd(curdir) == NULL){
X	(void)fprintf(stderr, "namei: unable to get current directory - %s\n", curdir);
X	exit(1);
X    }
X
X
X    for(; optind < argc; optind++){
X	(void)printf("f: %s\n", argv[optind]);
X	symcount = 1;
X	namei(argv[optind], 0);
X
X	if(chdir(curdir) == -1){
X	    (void)fprintf(stderr, "namei: unable to chdir to %s - %s (%d)\n", curdir, ERR);
X	    exit(1);
X	}
X    }
X    exit(0);
X}
X
Xvoid
Xusage()
X{
X    (void)fprintf(stderr,"usage: namei [-mx] pathname [pathname ...]\n");
X    exit(1);
X}
X
X#ifndef NODEV
X#define NODEV		(dev_t)(-1)
X#endif
X
Xvoid
Xnamei(file, lev)
X
Xregister char *file;
Xregister int lev;
X{
X    register char *cp;
X    char buf[BUFSIZ], sym[BUFSIZ];
X    struct stat stb;
X    register int i;
X    dev_t lastdev = NODEV;
X
X    /*
X     * See if the file has a leading /, and if so cd to root
X     */
X    
X    if(*file == '/'){
X	while(*file == '/')
X	    file++;
X	
X	if(chdir("/") == -1){
X	    (void)fprintf(stderr,"namei: could not chdir to root!\n");
X	    exit(1);
X	}
X	for(i = 0; i < lev; i++)
X	    (void)printf("  ");
X
X	if(stat("/", &stb) == -1){
X	    (void)fprintf(stderr, "namei: could not stat root!\n");
X	    exit(1);
X	}
X	lastdev = stb.st_dev;
X
X	if(mflag)
X	    (void)printf(" d %04o /\n", stb.st_mode & 07777);
X	else
X	    (void)printf(" d /\n");
X    }
X
X    for(;;){
X
X	/*
X	 * Copy up to the next / (or nil) into buf
X	 */
X	
X	for(cp = buf; *file != '\0' && *file != '/'; cp++, file++)
X	    *cp = *file;
X	
X	while(*file == '/')	/* eat extra /'s	*/
X	    file++;
X
X	*cp = '\0';
X
X	if(buf[0] == '\0'){
X
X	    /*
X	     * Buf is empty, so therefore we are done
X	     * with this level of file
X	     */
X
X	    return;
X	}
X
X	for(i = 0; i < lev; i++)
X	    (void)printf("  ");
X
X	/*
X	 * See what type of critter this file is
X	 */
X	
X	if(lstat(buf, &stb) == -1){
X	    (void)printf(" ? %s - %s (%d)\n", buf, ERR);
X	    return;
X	}
X
X	switch(stb.st_mode & S_IFMT){
X	    case S_IFDIR:
X
X		/*
X		 * File is a directory, chdir to it
X		 */
X		
X		if(chdir(buf) == -1){
X		    (void)printf(" ? could not chdir into %s - %s (%d)\n", buf, ERR );
X		    return;
X		}
X		if(xflag && lastdev != stb.st_dev && lastdev != NODEV){
X		    /* Across mnt point */
X		    if(mflag)
X			(void)printf(" D %04o %s\n", stb.st_mode & 07777, buf);
X		    else
X			(void)printf(" D %s\n", buf);
X		}
X		else {
X		    if(mflag)
X			(void)printf(" d %04o %s\n", stb.st_mode & 07777, buf);
X		    else
X			(void)printf(" d %s\n", buf);
X		}
X		lastdev = stb.st_dev;
X
X		(void)fflush(stdout);
X		break;
X
X	    case S_IFLNK:
X		/*
X		 * Sigh, another symlink.  Read it's contents and
X		 * call namei()
X		 */
X		
X		bzero(sym, BUFSIZ);
X		if(readlink(buf, sym, BUFSIZ) == -1){
X		    (void)printf(" ? problems reading symlink %s - %s (%d)\n", buf, ERR);
X		    return;
X		}
X
X		if(mflag)
X		    (void)printf(" l %04o %s -> %s", stb.st_mode & 07777, buf, sym);
X		else
X		    (void)printf(" l %s -> %s", buf, sym);
X
X		if(symcount > 0 && symcount++ > MAXSYMLINKS){
X		    (void)printf("  *** EXCEEDED UNIX LIMIT OF SYMLINKS ***");
X		    symcount = -1;
X		}
X		(void)printf("\n");
X		namei(sym, lev + 1);
X		break;
X
X	    case S_IFCHR:
X		if(mflag)
X		    (void)printf(" c %04o %s\n", stb.st_mode & 07777, buf);
X		else
X		    (void)printf(" c %s\n", buf);
X		break;
X	    
X	    case S_IFBLK:
X		if(mflag)
X		    (void)printf(" b %04o %s\n", stb.st_mode & 07777, buf);
X		else
X		    (void)printf(" b %s\n", buf);
X		break;
X	    
X	    case S_IFSOCK:
X		if(mflag)
X		    (void)printf(" s %04o %s\n", stb.st_mode & 07777, buf);
X		else
X		    (void)printf(" s %s\n", buf);
X		break;
X
X	    case S_IFREG:
X		if(mflag)
X		    (void)printf(" - %04o %s\n", stb.st_mode & 07777, buf);
X		else
X		    (void)printf(" - %s\n", buf);
X		break;
X		
X	    default:
X		(void)fprintf(stderr,"namei: unknown file type 0%06o on file %s\n", stb.st_mode, buf );
X		exit(1);
X	    
X	}
X    }
X}
X
END_OF_namei.c
if test 5724 -ne `wc -c <namei.c`; then
    echo shar: \"namei.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 1 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
--
    -Roger      (rogers@fangorn.wr.tek.com)
		UUCP:	...!uunet!tektronix!fangorn.wr.tek.com!rogers
		ARPA:	<rogers%fangorn.wr.tek.com@RELAY.CS.NET>

exit 0 # Just in case...
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.