2

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.

  • argv[0] could be anything, see unix.stackexchange.com/a/315819/64248. – countermode Oct 18 '16 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! – Johan Myréen Oct 18 '16 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 Oct 18 '16 at 16:47
3

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.