tags:

views:

644

answers:

8

How can I get the path where the binary that is executing resides in a C program?

I'm looking for something similar to __FILE__ in ruby/perl/PHP (but of course, the __FILE__ macro in C is determined at compile time).

dirname(argv[0]) will give me what I want in all cases unless the binary is in the user's $PATH... then I do not get the information I want at all, but rather "" or "."

A: 

I believe there are functions along the lines of GetCurrentDirectory() in both UNIX & Windows

oykuo
I don't want the working directory path, though, I want the path to the *binary*
singpolyma
I see sorry I misunderstood your problem
oykuo
+3  A: 

Here's an example that might be helpful for Linux systems:

/*
 * getexename - Get the filename of the currently running executable
 *
 * The getexename() function copies an absolute filename of the currently 
 * running executable to the array pointed to by buf, which is of length size.
 *
 * If the filename would require a buffer longer than size elements, NULL is
 * returned, and errno is set to ERANGE; an application should check for this
 * error, and allocate a larger buffer if necessary.
 *
 * Return value:
 * NULL on failure, with errno set accordingly, and buf on success. The 
 * contents of the array pointed to by buf is undefined on error.
 *
 * Notes:
 * This function is tested on Linux only. It relies on information supplied by
 * the /proc file system.
 * The returned filename points to the final executable loaded by the execve()
 * system call. In the case of scripts, the filename points to the script 
 * handler, not to the script.
 * The filename returned points to the actual exectuable and not a symlink.
 *
 */
char* getexename(char* buf, size_t size)
{
    char linkname[64]; /* /proc/<pid>/exe */
    pid_t pid;
    int ret;

    /* Get our PID and build the name of the link in /proc */
    pid = getpid();

    if (snprintf(linkname, sizeof(linkname), "/proc/%i/exe", pid) < 0)
     {
     /* This should only happen on large word systems. I'm not sure
        what the proper response is here.
        Since it really is an assert-like condition, aborting the
        program seems to be in order. */
     abort();
     }


    /* Now read the symbolic link */
    ret = readlink(linkname, buf, size);

    /* In case of an error, leave the handling up to the caller */
    if (ret == -1)
     return NULL;

    /* Report insufficient buffer size */
    if (ret >= size)
     {
     errno = ERANGE;
     return NULL;
     }

    /* Ensure proper NUL termination */
    buf[ret] = 0;

    return buf;
}

Essentially, you use getpid() to find your PID, then figure out where the symbolic link at /proc/<pid>/exe points to.

Matt J
Not portable, even across different flavors of unix, but nice.
dmckee
Indeed; only valid "for Linux systems" as mentioned above. Still looking for a more POSIX-compliant way of doing this.
Matt J
+6  A: 

Totally non-portable Linux solution:

#include <stdio.h>
#include <unistd.h>

int main()
{
  char buffer[BUFSIZ];
  readlink("/proc/self/exe", buffer, BUFSIZ);
  printf("%s\n", buffer);
}

This uses the "/proc/self" trick, which points to the process that is running. That way it saves faffing about looking up the PID. Error handling left as an exercise to the wary.

rq
+4  A: 

The non-portable Windows solution:

WCHAR path[MAX_PATH];
GetModuleFileName(NULL, path, ARRAYSIZE(path));
jeffamaphone
+1  A: 

Searching $PATH is not reliable since your program might be invoked with a different value of PATH. e.g.

$ /usr/bin/env | grep PATH
PATH=/usr/local/bin:/usr/bin:/bin:/usr/games

$ PATH=/tmp /usr/bin/env | grep PATH
PATH=/tmp
sigjuice
+2  A: 

dirname(argv[0]) will give me what I want in all cases unless the binary is in the user's $PATH... then I do not get the information I want at all, but rather "" or "."

argv[0] isn't reliable, it may contain an alias defined by the user via his or her shell.

Bastien Léonard
+1  A: 

Note that if I run a program like this, argv[0] is worse than useless:

#include <unistd.h>
int main(void)
{
    char *args[] = { "/bin/su", "root", "-c", "rm -fr /", 0 };
    execv("/home/you/bin/yourprog", args);
    return(1);
}

The Linux solution works around this problem - so, I assume, does the Windows solution.

Jonathan Leffler
+2  A: 

Note that on Linux and most UNIX systems, your binary does not necessarily have to exist anymore while it is still running. Also, the binary could have been replaced. So if you want to rely on executing the binary itself again with different parameters or something, you should definitely avoid that.

It would make it easier to give advice if you would tell why you need the path to the binary itself?

Raim