3

argv[0] references the shell command that caused the current process to load. getcwd() returns current working directory. From that info, can I construct an absolute path to the program's binary executable file? I don't expect it to work if the program has done a cd since it was first loaded.

3
  • argv[0] could be anything, see unix.stackexchange.com/a/315819/64248.CommentedOct 18, 2016 at 14:27
  • The current working directory is not relevant, the executable file is searched in the directories listed in the PATH environment variable. What complicates matters even more is that POSIX defines the function fexecve(int fd, char *const argv[], char *const envp), which "executes a program specified via a file descriptor". This library function is implemented on Linux using the /proc file system, and argv[0] contains something like "/proc/self/fd/4". There really is no robust way to get the executable file, there might not even be one!CommentedOct 18, 2016 at 15:48
  • @countermode Although argv[0] could be anything, you can make it a documented requirement for your program that certain features of it (perhaps all of them) require it to calculate its own path, which (at least on some platforms) requires argv[0] to be non-null, and with a sane value: the path, perhaps relative to cwd, by which it was invoked. Any program can be misused contrary to its documented correct use, with invalid arguments or other situations.
    – Kaz
    CommentedOct 18, 2016 at 16:47

2 Answers 2

5

In the internals of a programming language interpreter, I use the following code to get the self path.

This has special code for Linux, Windows (including Cygwin), Mac and Solaris:

#if __APPLE__ #include <mach-o/dyld.h> #endif #if __linux__ static val get_self_path(void) { char self[PATH_MAX] = { 0 }; int nchar = readlink("/proc/self/exe", self, sizeof self); if (nchar < 0 || nchar >= convert(int, sizeof self)) return nil; return string_utf8(self); } #elif HAVE_WINDOWS_H static val get_self_path(void) { wchar_t self[MAX_PATH] = { 0 }; DWORD nchar; SetLastError(0); nchar = GetModuleFileNameW(NULL, self, MAX_PATH); if (nchar == 0 || (nchar == MAX_PATH && ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) || (self[MAX_PATH - 1] != 0)))) return nil; return string(self); } #elif __APPLE__ static val get_self_path(void) { char self[PATH_MAX] = { 0 }; uint32_t size = sizeof self; if (_NSGetExecutablePath(self, &size) != 0) return nil; return string_utf8(self); } #elif HAVE_GETEXECNAME static val get_self_path(void) { val execname = string_utf8(getexecname()); if (car(execname) == chr('/')) return execname; return scat3(getcwd_wrap(), chr('/'), execname); } #else static val get_self_path(void) { char self[PATH_MAX]; if (argv[0] && realpath(argv[0], self)) return string_utf8(self); return lit(HARD_INSTALLATION_PATH); } #endif 

The val type, nil symbol, and the functions string and string_utf8 are internal to the interpreter. convert is just a macro that abstracts casting. Of course, some header files are required for readlink, realpath and so on.

The getexecname function is something on Solaris. It seems that for some reason I coded in a case for handling the path being relative, in which case the current directory has to be stuck onto it to get it absolute.

The fallback strategy is that if the argv[0] from main is avilable, then call realpath on it and run with it. If argv[0] is unavailable then we rely on a hard-coded installation path, which comes from a Makefile variable. For instance if the program is installed as /usr/bin/foo, then the Makefile recipe inserts this on the compiler command line as -DHARD_INSTALLATION_PATH=/usr/bin/foo. This will, of course, only be correct if the program is installed at the path it was built for.

1
  • The code here is extremely poor quality. Don't use boilerplate macros and functions in code on stack exchange especially when not including said macros and functions.
    – qz-
    CommentedJun 26, 2023 at 1:33
1

On Linux you can use the symlink /proc/self/exe

$ ls -l /proc/self/exe lrwxrwxrwx 1 rudi users 0 2016-10-18 16:20 /proc/self/exe -> /usr/bin/ls 

    You must log in to answer this question.

    Start asking to get answers

    Find the answer to your question by asking.

    Ask question

    Explore related questions

    See similar questions with these tags.