Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically retrieving the absolute path of an OS X command-line app

Tags:

c

unix

macos

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.)

like image 274
Benjamin Pollack Avatar asked Sep 02 '25 14:09

Benjamin Pollack


2 Answers

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.

like image 192
mark4o Avatar answered Sep 05 '25 02:09

mark4o


I believe there is much more elegant solution, which actually works for any PID, and also returns the absolute path directly:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <libproc.h>

int main (int argc, char* argv[])
{
    int ret;
    pid_t pid; 
    char pathbuf[PROC_PIDPATHINFO_MAXSIZE];

    pid = getpid();
    ret = proc_pidpath (pid, pathbuf, sizeof(pathbuf));
    if ( ret <= 0 ) {
        fprintf(stderr, "PID %d: proc_pidpath ();\n", pid);
        fprintf(stderr, "    %s\n", strerror(errno));
    } else {
        printf("proc %d: %s\n", pid, pathbuf);
    }

    return 0;
}
like image 35
Alen Stojanov Avatar answered Sep 05 '25 02:09

Alen Stojanov