8

Possible Duplicate:
Get path of executable

I'm programming on Windows using MinGW, gcc 4.4.3. When I use the main function like this:

int main(int argc, char* argv[]){
    cout << "path is " << argv[0] << endl;
}

On Windows I get a full path like this: "C:/dev/stuff/bin/Test". When I run the same application on Linux, however, I get some sort of relative path: "bin/Test". It's breaking my application! Any idea on how to make sure the path is absolute on both systems?

Community
  • 1
  • 1
Nate Glenn
  • 6,455
  • 8
  • 52
  • 95
  • Possibly related: http://stackoverflow.com/questions/2050961/is-argv0-name-of-executable-an-accepted-standard-or-just-a-common-conventi and http://stackoverflow.com/questions/1528298/get-path-of-executable – Seth Carnegie Oct 29 '11 at 20:41
  • 3
    I'm reasonably certain there is no way to guarantee any such thing. If you need an absolute path to the executable, you'll need to use something other that argv[0] to get it. – Jerry Coffin Oct 29 '11 at 20:41
  • This shouldn't work on Windows, either. Are you launching the program from the command line or via Explorer? – Harry Johnston Oct 30 '11 at 04:35
  • I'm using a makefile. The makefile command is bin/test. Strange, isn't it... – Nate Glenn Oct 30 '11 at 04:48

3 Answers3

10

No, there isn't. Under most shells on Linux, argv[0] contains exactly what the user typed to run the binary. This allows binaries to do different things depending on what the user types.

For example, a program with several different command-line commands may install the binary once, and then hard-link the various different commands to the same binary. For example, on my system:

$ ls -l /usr/bin/git*
-rwxr-xr-x  109 root  wheel  2500640 16 May 18:44 /usr/bin/git
-rwxr-xr-x    2 root  wheel   121453 16 May 18:43 /usr/bin/git-cvsserver
-rwxr-xr-x  109 root  wheel  2500640 16 May 18:44 /usr/bin/git-receive-pack
-rwxr-xr-x    2 root  wheel  1021264 16 May 18:44 /usr/bin/git-shell
-rwxr-xr-x  109 root  wheel  2500640 16 May 18:44 /usr/bin/git-upload-archive
-rwxr-xr-x    2 root  wheel  1042560 16 May 18:44 /usr/bin/git-upload-pack
-rwxr-xr-x    1 root  wheel   323897 16 May 18:43 /usr/bin/gitk

Notice how some of these files have exactly the same size. More investigation reveals:

$ stat /usr/bin/git
234881026 459240 -rwxr-xr-x 109 root wheel 0 2500640 "Oct 29 08:51:50 2011" "May 16 18:44:05 2011" "Jul 26 20:28:29 2011" "May 16 18:44:05 2011" 4096 4888 0 /usr/bin/git
$ stat /usr/bin/git-receive-pack 
234881026 459240 -rwxr-xr-x 109 root wheel 0 2500640 "Oct 29 08:51:50 2011" "May 16 18:44:05 2011" "Jul 26 20:28:29 2011" "May 16 18:44:05 2011" 4096 4888 0 /usr/bin/git-receive-pack

The inode number (459240) is identical and so these are two links to the same file on disk. When run, the binary uses the contents of argv[0] to determine which function to execute. You can see this (sort of) in the code for Git's main().

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 5
    Not to mention that whatever code that calls one of the `exec*` family of functions to run your code can put whatever it wants for `argv[0]`. – Adam Rosenfield Oct 29 '11 at 21:06
5

argv array

argv[0] is a parameter like any others: it can be an arbitrary NUL terminated byte string. It can be the empty string. It is whatever the launching process wants.

By default, the shell with set argv[0] to whatever is used to name the program: a name looked-up in $PATH, a relative or an absolute path. It can be a symbolic link or a regular file.

To invoke a program with some other value, with zsh (dunno with other shells) use:

ARGV0=whatever_you_want some_program arguments

If you really need the path to the executable, you cannot use the command line on Unix.

Linux only

On Linux: /proc/self/exe is a symbolic link to the executable file.

You can readlink it. You can also stat or open it directly.

Renaming and soft link

A normal soft link is a dumb string, and doesn't know what happens to its target (if it exists at all). But the /proc/self/exe soft link is magic.

In case of renaming, the soft-but-magic-link will follow renaming. In case there are several hard links, it will follow the name of the particular hard link that was used. (So different hard links to the same file are not perfectly equivalent under Linux.)

If this hard link is unlinked, I think " (deleted)" is appended to the value of the symbolic link. Note that this is a valid file name, so another unrelated file could have that name.

In any case, the symbolic link is a hard link to the file, so you can stat or open it directly.

I don't think you can count on anything on a network file system if the binary is renamed or unlinked on another system than the one where the executable is launched.

Security considerations

When your program gets to use the /proc/self/exe special file, it is possible for the file used to launch your program to be unlinked or renamed. This should be taken seriously in case the program is privileged (SUID or Set Capabilities): even if the user doesn't have write access to the original "Set Something" binary, he may be able to make a hard link to it if he has write access to a directory on the same file system, so he may be able to change the name if a running privileged binary.

By the time you readlink, the value returned may refer to another file. (Of course, there is always an unavoidable race condition with opening the result of readlink.)

As usual, NFS does not provides all the same guaranties that local file systems have.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
3

There is no way to ensure that argv[0] is an absolute path because it is supposed to be exactly how the user invoked the program. So, if on a Linux command line you invoke your program via ./bin/Test, then argv[0] should be exactly "./bin/Test".

It seems like a bug in MinGW's runtime if when you invoke the program from a command prompt via .\bin\Test, argv[0] is "C:/dev/stuff/bin/Test". With the latest MinGW (gcc version 4.5.2), invoking a binary via .\bin\Test means argv[0] is ".\bin\Test". A Microsoft Visual C++-built binary (cl version 16.00.40219.01) invoked via .\bin\Test also has ".\bin\Test" for argv[0].

Daniel Trebbien
  • 38,421
  • 18
  • 121
  • 193
  • 1
    _it is supposed to be exactly how the user invoked the program._ Unless it is a login shell, which will have a dash prepended. – ninjalj Oct 29 '11 at 21:10
  • 6
    The C standard (5.1.2.2.1p2) says "If the value of argc is greater than zero, the string pointed to by argv[0] represents the *program name*"; it's deliberately unclear about what the "program name" is or how argv[0] "represents" it. POSIX basically says that `argv[0]` is whatever was passed to the `exec*()` function when the program was invoked -- which means that it's up to the invoking shell. – Keith Thompson Oct 29 '11 at 21:13
  • @ninjalj: I didn't know that. That's interesting :) – Daniel Trebbien Oct 29 '11 at 21:15
  • @KeithThompson: Okay, so it isn't a MinGW bug. Good to know. – Daniel Trebbien Oct 29 '11 at 21:20