2

Why does the command execl("/bin/ls", NULL); return a usage error, whereas the command execl("/bin/ls", "badfsafds", NULL); return a list of files in the directory in a simple C program?

I know that the first parameter of execl is path specification, but I thought it was only for path specification, not command execution. Thank you.

Math Student
  • 21
  • 1
  • 3
  • Q: Is your question resolved? The basic problem (per the example in my [response](https://stackoverflow.com/a/66393258/3135317) below) was your assumption that the 1st "arg" parameter in [execl()](https://man7.org/linux/man-pages/man3/exec.3.html) was the 1st argument passed to "/bin/ls". It isn't. By convention, the 1st argument ("argv[0]") is actually the name/filepath of the program being executed. 'Hope that clarifies things... at least a bit... – paulsm4 Feb 27 '21 at 00:03
  • It is resolved! The solutions below are very helpful. – Math Student Feb 28 '21 at 02:37
  • "The solutions below are very helpful" Then please be sure to "upvote" and/or "accept" the responses you found helpful ;) – paulsm4 Feb 28 '21 at 19:17
  • I will be sure to do this once I have more than 15 reputation! – Math Student Mar 01 '21 at 20:06
  • 1
    well here's a +1 ;) – paulsm4 Mar 01 '21 at 22:05

3 Answers3

4

The first parameter specifies the path, yes.

The second and subsequent parameters give the arguments which are passed to the program, e.g. as its argv argument to main(), starting with argv[0].

So your first example causes ls to run with an empty argv vector, in which argv[0] is NULL. Most programs assume that argv[0] will be a valid string which specifies the name of the program in some form or other, and they will crash or otherwise not work if it is NULL. Thus you should not do this.

Your second example causes ls to run with argv[0] set to "badfsafds" and no other arguments. The unusual value for argv[0] probably won't affect its behavior for the most part. But for instance if it gives an error message, say if you run it in a directory for which you don't have read permissions, it will include the given argv[0] and report something like badfsafds: cannot open directory '.': Permission denied. Other than that, it works just like any other time you run ls from your shell with no arguments, and lists the current directory.

(Some programs do change their behavior depending on the value of argv[0]. For instance, on my system cal and ncal are symlinks to the same executable. This executable looks at its argv[0] to determine how it was run, and depending whether it sees cal or ncal it will adjust its output format accordingly. But I don't think ls has any such behavior.)

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82
3

Here's a good example of correct usage:

https://linuxhint.com/exec_linux_system_call_c/

#include <unistd.h>

int main(void) {
  char *binaryPath = "/bin/ls";
  char *arg1 = "-lh";
  char *arg2 = "/home";
 
  execl(binaryPath, binaryPath, arg1, arg2, NULL);
 
  return 0;
}

You'll notice that if you print argv[0] of any standard C program, it will show you the command that's being executed.

paulsm4
  • 114,292
  • 17
  • 138
  • 190
2

(Note: in all explanation below, it is assumed that you use the convention of calling argc and argv to the parameters of main() routine, if you don't call them like here, then you'll need to change the names where they appear below.)

The parameters to execl() are, in order:

  • the first is the file name to be loaded for execution. This is used by the kernel to get the program loaded on memory. This string is used only by the kernel and the executed program doesn't have access to it.
  • the second is argv[0], this is, the program name (again). You can put any other string that will be seen from the inside of the new program as argv[0]. (see below for a funny effect of this)
  • the third, etc. are the command line arguments, and will appear as argv[1], argv[2], etc. upto a final NULL value that indicates to execl the end of the list of arguments. This is also seen in the program as argv[argc], and is also equal to NULL.

so, to call ls, you need to use:

execl("/bin/ls", "ls", NULL);

or, if you want to pass it arguments:

execl("/bin/ls", "ls", "-l", "/etc/passwd", NULL);

the first call you used gives you an error because you pass NULL as the program name (argv[0]), the second is accepted but as a call to ls with a wrong name (which ls internally doesn't mind if you change it) and no command line parameters, so it lists the directory ".".

To be valid you could call execl as:

execl("/bin/ls", "What the hell?", "badfsafds", NULL);

and you should get:

What the hell?: badfsafds: No such file or directory

and you'll see that, despite of not checking that it has been called with a different name than the file that was loaded, it uses it to announce itself in the error message.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31