Getting path to executed program file

Jonathan I. Kamens jik at athena.mit.edu
Wed Jul 25 16:49:56 AEST 1990


In article <9995 at pt.cs.cmu.edu>, tgl at zog.cs.cmu.edu (Tom Lane) writes:
|> Is there any way for a program to discover the path name of the file
|> from which it was executed?

  Appended to this message is a message posted to comp.unix.wizards by
Greg Limes the nth time this was asked (your message was the mth time,
where m is about three or four more than n if I recall correctly, and
n is nonnegligable :-).  It says just about all there is to say about
this question.  I haven't had occasion to use his source code yet, so
I don't know whether or not it has bugs, but I doubt it...

Jonathan Kamens			              USnail:
MIT Project Athena				11 Ashford Terrace
jik at Athena.MIT.EDU				Allston, MA  02134
Office: 617-253-8495			      Home: 617-782-0710

Article 19066 of comp.unix.wizards:
Path: bloom-beacon!usc!apple!sun-barr!newstop!sun!limes
From: limes at sun.com (Greg Limes)
Newsgroups: comp.unix.wizards
Subject: Re: Reading the symbol table of the currently running executable
Message-ID: <LIMES.89Sep7153103 at ouroborous.wseng.sun.com>
Date: 7 Sep 89 22:31:03 GMT
References: <9104 at june.cs.washington.edu> <6131 at lynx.UUCP>
Sender: news at sun.Eng.Sun.COM
Organization: Sun Microsystems, Inc.
Lines: 243
In-reply-to: mitch at lynx.uucp's message of 5 Sep 89 17:17:36 GMT

In article <6131 at lynx.UUCP> mitch at lynx.uucp (Mitch Bunnell) writes:

> In article <9104 at june.cs.washington.edu> bcn at cs.washington.edu (Clifford Neuman) writes:
> >  2) Obtaining the full path name of the presently running executable.

> 2 - Not possible.

Back before I knew this was impossible, I wrote the following piece of
support code. It has been doing the impossible for me for quite some
time (geez, has it been that long?) with limitations as stated.

/*
 * findx package		25may88 limes at sun.com
 * 
 * Over the last few days (weeks?) there has been some traffic about how to
 * tell where a running program came from. Well, there is a way to find
 * out without changing the shell, the kernel, C language startup
 * conventions, or whatever.
 * 
 * Anyway, here is the basic idea, presented as a package that should compile
 * and run without too many problems.
 * 
 * WHAT IT DOES First, it locates the path to the executable that was used by
 * the exec() that started this process. If the command name starts with a
 * "/", it must be taken literally; if it contains a "/", then it is
 * always relative to the current working directory at the start of the
 * program; otherwise, we have to chase across the PATH value in the
 * environment. If there is no PATH, or the PATH is empty, check the
 * current working directory.
 * 
 * On systems with symbolic links, we are not through yet. The purpose is to
 * locate the directory it is in, so we can get at any related data files.
 * So, we chase symbolic links until we have the real path name of the
 * final resolution file.
 * 
 * SECURITY This can be spoofed easily by making a hard link to, or a copy
 * of, the executable. If you want your program to be sure that it has
 * found the one true installation location, you will have to verify that
 * for yourself. findx() just locates the most likely candidate.
 * 
 * PORTABILITY This was developed on a Sun3 running SunOS 4.0, but I think I
 * at least made the algorithm portable. You may need to mess with include
 * files and such. Symbolic link searching is turned on if your errno.h
 * supplies ELOOP, and off otherwise; I assume that all systems with
 * symlinks have a readlink() call.
 * 
 * HOW TO USE IT Here is the definition of the various parameters. Further
 * down you will find an example main, so fear not ...
 * 
 * findx (cmd, cwd, dir, pgm, run, path)
 * 
 * cmd	pass the command name (argv[0]) here.  findx() knows how to handle
 *	just about anything. If it starts with /, then we use the absolute
 *	name, and ignore the path. If it contains a /, then use the relative
 *	name and ignore the path. Otherwise, look for the file in each
 *	directory named in the path for the file; if there is no path, pretend
 *	its "." like the execvp does.
 *	
 * cwd	pass a big buffer here. if this begins with a slash, I will assume
 *	it is filled in with the current working directory; otherwise, I will
 *	fill it in using getcwd(). Should be at least MAXPATHLEN bytes, if you
 *	do not fill it in yourself.
 *	
 * dir	pass a big buffer here. this gets the full path name of the
 *	directory that the executable was read from. Should be at least
 *	MAXPATHLEN bytes.
 *	
 * pgm	pass THE ADDRESS of a pointer variable here. findx() will fill the
 *	pointer variable with a pointer to the final component of the string
 *	passed as cmd above. Send a (char **)0 if you don't care about this.
 *	
 * run	pass THE ADDRESS of a pointer variable here. findx() will fill the
 *	pointer variable with a pointer to the final component of the name of
 *	the runnning program. Send a (char **)0 if you don't care about this.
 *	
 * path	pass the user's PATH variable here. I made it a parameter so you
 *	can fiddle with the path first. If you do not want to fiddle, pass
 *	getenv("PATH").
 * 
 * RETURN VALUES: Normally, findx() will return zero if all is well. If
 * something goes wrong, it will return -1 with the global variable
 * "errno" set to a corresponding error number.
 */

#include <strings.h>
#include <errno.h>
#include <sys/param.h>

#define	X_OK	1

#ifndef	MAXPATHLEN
#define	MAXPATHLEN	1024
#endif

#ifndef	ENAMETOOLONG
#define	ENAMETOOLONG	EINVAL
#endif

int             findx ();	/* get location of directory */
int             resolve ();	/* get link resolution name */

#ifdef	TESTMAIN
extern char    *getenv ();	/* read value from environment */
char           *pn = (char *) 0;/* program name */
char           *rn = (char *) 0;/* run name */
char            rd[MAXPATHLEN];	/* run directory */
char            wd[MAXPATHLEN] = ".";	/* working directory */

int
main (argc, argv)
    int             argc;
    char          **argv;
{
    findx (*argv, wd, rd, &pn, &rn, getenv ("PATH"));
    printf ("%s: %s running in %s from %s\n", pn, rn, wd, rd);
    return 0;
}

#endif

/*-
 * findx - find executable file in PATH
 * PARAMETERS:
 *	cmd	filename as typed by user
 *	cwd	where to return working directory
 *	dir	where to return program's directory
 *	pgm	where to return what user called it
 *	run	where to return final resolution name
 *	path	user's path from environment
 * RETURNS: returns zero for success, -1 for error (with errno set properly).
 */
int
findx (cmd, cwd, dir, pgm, run, path)
    char           *cmd;
    char           *cwd;
    char           *dir;
    char          **pgm;
    char          **run;
    char           *path;
{
    int             rv = 0;
    char           *f, *s;

    if (!cmd || !*cmd || !cwd || !dir) {
	errno = EINVAL;		/* stupid arguments! */
	return -1;
    }
    if (!path || !*path)	/* missing or null path */
	path = ".";		/* assume sanity */

    if (*cwd != '/')
	if (!(getcwd (cwd, MAXPATHLEN)))
	    return -1;		/* cant get working directory */

    f = rindex (cmd, '/');
    if (pgm)			/* user wants program name */
	*pgm = f ? f + 1 : cmd;

    if (dir) {			/* user wants program directory */
	rv = -1;
	if (*cmd == '/')	/* absname given */
	    rv = resolve ("", cmd + 1, dir, run);
	else if (f)		/* relname given */
	    rv = resolve (cwd, cmd, dir, run);
	else if (f = path) {	/* from searchpath */
	    rv = -1;
	    errno = ENOENT;	/* errno gets this if path empty */
	    while (*f && (rv < 0)) {
		s = f;
		while (*f && (*f != ':'))
		    ++f;
		if (*f)
		    *f++ = 0;
		if (*s == '/')
		    rv = resolve (s, cmd, dir, run);
		else {
		    char            abuf[MAXPATHLEN];

		    sprintf (abuf, "%s/%s", cwd, s);
		    rv = resolve (abuf, cmd, dir, run);
		}
	    }
	}
    }
    return rv;
}

/*
 * resolve - check for specified file in specified directory sets up
 * dir, following symlinks. returns zero for success, or -1 for error
 * (with errno set properly)
 */
int
resolve (indir, cmd, dir, run)
    char           *indir;	/* search directory */
    char           *cmd;	/* search for name */
    char           *dir;	/* directory buffer */
    char          **run;	/* resultion name ptr ptr */
{
    char           *p;
    int             rv = -1;

#ifdef	ELOOP
    int             lcc = 0;
    int             sll;
    char            symlink[MAXPATHLEN + 1];

#endif

    do {
	errno = ENAMETOOLONG;
	if (strlen (indir) + strlen (cmd) + 2 > MAXPATHLEN)
	    break;

	sprintf (dir, "%s/%s", indir, cmd);
	if (access (dir, X_OK) < 0)
	    break;		/* not an executable program */

#ifdef	ELOOP
	while ((sll = readlink (dir, symlink, MAXPATHLEN)) >= 0) {
	    symlink[sll] = 0;
	    if (*symlink == '/')
		strcpy (dir, symlink);
	    else
		sprintf (rindex (dir, '/'), "/%s", symlink);
	}
	if (errno != EINVAL)
	    break;
#endif

	p = rindex (dir, '/');
	*p++ = 0;
	if (run)		/* user wants resolution name */
	    *run = p;
	rv = 0;			/* complete, with success! */

    } while (0);

    return rv;
}

--
-- Greg Limes	limes at sun.com	...!sun!limes	73327,2473	[choose one]



More information about the Comp.unix.wizards mailing list