9

Here is the prototype of the function execlp:

int execlp(const char *file, const char *arg, ...);

The man page says that the first argument of arg(i.e. arg0), "by convention, should point to the filename associated with the file being executed."

Then I did these experiments:

/*These three lines all produce the expected result:
 .  ..  a.out  main.c */
execlp("ls", "ls", "-a", 0);
execlp("ls", "arg0 is meaningless", "-a" , 0);
execlp("ls", "", "-a" , 0);

/*But this one does not work:
 a.out  main.c */
execlp("ls", "-a" , 0);

So the question is, is the arg0 parameter meaningful under any circumstances? Why the interface was designed like this?

duleshi
  • 1,966
  • 2
  • 21
  • 32
  • This might help http://stackoverflow.com/a/21559499/612920 – Mansuro Jul 10 '14 at 13:48
  • 3
    Interestingly, historically in the UNIX world the arg0 parameter was often the one shown by tools like `ps`, so people would run their games or chat clients using execlp and change `arg0` to be some more proper-sounding program (in environment where they couldn't rename or copy the game directly)... – Tony Delroy Jul 10 '14 at 13:53

5 Answers5

9

The main function signature is

int main(int argc, char ** argv);

Where argv[0] is the name of the executable (arg0 in your case), so the application expects its command line from argv[1].

In some cases single binary can have multiple names (busybox, for example, sometimes uses symbolic links with the different names, pointing to the single binary). In such cases argv[0] is used to determine which link was used to call the binary.

kayrick
  • 187
  • 4
6

Programs can use argv[0] to behave differently depending on how they are called. For example, see this snippet from args.c from xz-utils:

        const char *name = strrchr(argv[0], '/');
    if (name == NULL)
        name = argv[0];
    else
        ++name;

    // Look for full command names instead of substrings like
    // "un", "cat", and "lz" to reduce possibility of false
    // positives when the programs have been renamed.
    if (strstr(name, "xzcat") != NULL) {
        opt_mode = MODE_DECOMPRESS;
        opt_stdout = true;
    } else if (strstr(name, "unxz") != NULL) {
        opt_mode = MODE_DECOMPRESS;
    } else if (strstr(name, "lzcat") != NULL) {
        opt_format = FORMAT_LZMA;
        opt_mode = MODE_DECOMPRESS;
        opt_stdout = true;
    } else if (strstr(name, "unlzma") != NULL) {
        opt_format = FORMAT_LZMA;
        opt_mode = MODE_DECOMPRESS;
    } else if (strstr(name, "lzma") != NULL) {
        opt_format = FORMAT_LZMA;
    }
Chris Smeele
  • 966
  • 4
  • 8
6

You might try execlp("ls", "not_ls", "--help", 0) to see the difference. ls will then be deceived into thinking it is not_ls and print something like:

Usage: not_ls [OPTION]... [FILE]...
ach
  • 2,314
  • 1
  • 13
  • 23
2

Yes, the parameters are all meaningful.

The first argument determines which executable is called, the rest determine what arguments that executable will receive, including what it thinks it was called as.

A C program receives all but the first argument through argc and argv:

int main(int argc, char* argv[]);

That is especially useful for multi-call binaries, like busybox, which behave differently depending on how they were called.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • gzip and gunzip are two different binaries on my system. It's almost surely because of the GNU philosophy, see my answer for details. – Fizz Jul 14 '14 at 18:41
  • @user3588161 - On Oracle Enterprise Linux, `gunzip` is just a script that `exec` gzip. Clearly they use argv[0] to determine behavior. – alvits Jul 14 '14 at 18:52
  • Well, on different systems a different set of utilities are really the same one using multiple names... – Deduplicator Jul 14 '14 at 18:53
  • On a GNU/Debian system, gunzip is a script which executes "gzip -d". It doesn't exec gzip under a different argv[0] as you claim/imply. So your point about gzip behaving differently depending on its argv[0] is **invalid**; argv[1] ("-d") is used to alter gzip's behavior, as you could well do from the command line. This is in line with GNU's philosophy on argv[0] discussed by me below. – Fizz Jul 14 '14 at 22:47
2

Your last call

execlp("ls", "-a" , 0);

does not have enough parameters to work as the previous ones. If you instead call

execlp("ls", "-a" , "-a", 0);

it should work just the same as your first 3 calls, unless the ls from your distro has some coded behavior to work differently if its name starts with a "-", but I doubt it has.

Normally ls just does a set_program_name(argv[0]) and that's all it uses argv[0] for. See source: http://git.savannah.gnu.org/gitweb/?p=coreutils.git;a=blob;f=src/ls.c;h=cd5996eb979f9e9319089e8c065b1276a1fbece8;hb=refs/heads/master. That call sets variable called program_name, which then used to print the usage

printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name);

That's why you get the different help output as Andrey has shown.

As Chris S says, some programs are coded to behave differently depending how they were called in terms of argv[0]. But GNU ls is not one of those programs. Frankly I wonder why not, because they use the same ls.c code to compile both ls and also "dir" and "vdir", but these are compiled as different binaries via ls-vdir.c and ls-dir.c. There's a header, ls. h that has

#define LS_LS 1

/* This is for the 'dir' program.  */
#define LS_MULTI_COL 2

/* This is for the 'vdir' program.  */
#define LS_LONG_FORMAT 3

extern int ls_mode;

Then look in ls-vdir.c for example. The entire file consists of

#include "ls.h"
int ls_mode = LS_LONG_FORMAT;

Finally go back to ls.c and now the comments at the top like

If ls_mode is LS_LONG_FORMAT, the long format is the default regardless of the type of output device. This is for the 'vdir' program.

should start to make sense.

Some people even thought that shipping 3 binaries (ls, dir and vdir) instead of one was a bug https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=16312 But it's apparently by design as the last comment in https://bugs.archlinux.org/task/2767 (quoting from http://www.gnu.org/prep/standards/html_node/User-Interfaces.html) says

Please don't make the behavior of a utility depend on the name used to invoke it. It is useful sometimes to make a link to a utility with a different name, and that should not change what it does.

So GNU is not Unix ;-) Even though they could make ls vary its behavior via argv[0], they chose not to do that for philosophical reasons (and ship you three binaries instead). Clearly other Unix coders (e.g. the buysbox guys) don't share that philosophy.

Fizz
  • 4,782
  • 1
  • 24
  • 51