tags:

views:

1765

answers:

5

On Linux, an application can easily get its absolute path by querying /proc/self/exe. On FreeBSD, it's more involved, since you have to build up a sysctl call:

int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
char buf[1024];
size_t cb = sizeof(buf);
sysctl(mib, 4, buf, &cb, NULL, 0);

but it's still completely doable. Yet I cannot find a way to determine this on OS X for a command-line application. If you're running from within an app bundle, you can determine it by running [[NSBundle mainBundle] bundlePath], but because command-line applications are not in bundles, this doesn't help.

(Note: consulting argv[0] is not a reasonable answer, since, if launched from a symlink, argv[0] will be that symlink--not the ultimate path to the executable called. argv[0] can also lie if a dumb application uses an exec() call and forget to initialize argv properly, which I have seen in the wild.)

+2  A: 

There is no guaranteed way I think. If argv[0] is a symlink then you could use readlink(). If command is executed through the $PATH then one could try some of: search(getenv("PATH")), getenv("_"), dladdr()

pixelbeat
That will cover many cases, but still fails in the case you were launched by an application that neglected to initialize argv[0] properly--which, from personal experience, applies to a disturbing number of them.
Benjamin Pollack
Can you give an example of such an application? It is not the application which initializes argv, it's the libc and the application would need to do something very special to scramble argv[0].
bortzmeyer
I can't give an example of an application off the top of my head that gets it wrong, but all they have to do to screw up argv[0] is forget to set it properly when invoking an application via one of the exec* calls. libc would only get involved if invoking the application via system().
Benjamin Pollack
+4  A: 
Brian Campbell
I really, really hate accepting answers that say, "You can't," but that quote certainly seems to put the nail in my question's coffin quite painfully.
Benjamin Pollack
Yeah, I hated giving the answer, too. I spent a while on a wild goose chase, trying to see if I could figure out how to get the information out of p_textvp, before I discovered this.
Brian Campbell
A: 

Why not simply realpath(argv[0], actualpath);? True, realpath has some limits (documented in the manual page) but it handles symbolic links fine. Tested on FreeBSD and Linux

    % ls -l foobar 
    lrwxr-xr-x  1 bortzmeyer  bortzmeyer  22 Apr 29 07:39 foobar -> /tmp/get-real-name-exe

    % ./foobar 
    My real path: /tmp/get-real-name-exe
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>

int
main(argc, argv)
    int             argc;
    char          **argv;
{
    char            actualpath[PATH_MAX + 1];

    if (argc > 1) {
        fprintf(stderr, "Usage: %s\n", argv[0]);
        exit(1);
    }
    realpath(argv[0], actualpath);
    fprintf(stdout, "My real path: %s\n", actualpath);
    exit(0);
}

If the program is launched via PATH, see pixelbeat's solution.

bortzmeyer
This fails when a dumb program invokes you via an exec* call and improperly initializes the argv structure so that argv[0] is either just the executable name (i.e., not the full path) or flat-out wrong (absent, null string, or what have you).
Benjamin Pollack
If argv[0] is not the full path, no problem, realpath() will handle it. If it is empty or NULL, well, that's the fault of the caller, not of my program :-)
bortzmeyer
A: 

http://developer.apple.com/documentation/Carbon/Reference/Process_Manager/Reference/reference.html#//apple_ref/c/func/GetProcessBundleLocation

GetProcessBundleLocation seems to work.

It works, provided your application is a GUI application launched via the Finder and links against Carbon. In that case, though, `[[NSBundle mainBundle] bundlePath]` would work, too--and avoid creating the `FSRef` and locating the process serial number.
Benjamin Pollack
+8  A: 

The function _NSGetExecutablePath will return a full path to the executable (GUI or not). The path may contain symbolic links, "..", etc. but the realpath function can be used to clean those up if needed. See man 3 dyld for more information.

char path[1024];
uint32_t size = sizeof(path);
if (_NSGetExecutablePath(path, &size) == 0)
    printf("executable path is %s\n", path);
else
    printf("buffer too small; need size %u\n", size);

The secret to this function is that the Darwin kernel puts the executable path on the process stack immediately after the envp array when it creates the process. The dynamic link editor dyld grabs this on initialization and keeps a pointer to it. This function uses that pointer.

mark4o
Why hasn't this been accepted as the answer?
Pedro d'Aquino