rh0x
rh0x

Reputation: 1463

C execve() parameters [spawn a shell example]

I have to fill the parameters for:

int execve(const char *filename, char *const argv[], char *const envp[]);

If I execute this program:

#include <unistd.h>
int main() {
        char *args[2];
        args[0] = "/bin/sh";
        args[1] = NULL;
        execve(args[0], args, NULL);
}

the shell is spawned correctly as expected.

My problem is that the shell is spawned correctly also if I pass a NULL as second parameter like that:

#include <unistd.h>

int main() {
        char *args[2];
        args[0] = "/bin/sh";
        args[1] = NULL;
        execve(args[0], NULL, NULL);
}

So what's the purpose to use the args vector (with the "/bin/sh" + NULL) as second parameter instead of a NULL?

Upvotes: 5

Views: 31601

Answers (2)

zwol
zwol

Reputation: 140465

If you pass a null pointer as the second, or the third, argument of execve, your program is incorrect according to POSIX; both of these arguments are required to be non-null. (This is not all that clearly stated in the specification of execve, but it's in there.) I am currently typing this on an operating system that treats passing null pointers,

execve("executable", 0, 0);

as equivalent to passing empty arrays, e.g.

execve("executable", (char *[]){0}, (char *[]){0});

but it would not surprise me to learn that other operating systems would fire a segmentation fault or return -1 with errno set to EFAULT or EINVAL.

Passing empty arrays for these arguments is allowed, but the newly executed program will receive zero arguments in argc/argv if the second argument is an empty array, and/or zero environment variables in envp/environ if the third argument is an empty array. Many programs will malfunction under these conditions. For instance, it is very common to see things like

int main(int argc, char **argv)
{
   if (argc != 4) {
     fprintf(stderr, "usage: %s one two three\n", argv[0]);
     return 2;
   }
   // ...
}

where the program implicitly assumes that argv[0] will always be non-null.

So, you should always provide non-empty arrays for both arguments. The usual convention is that if you don't have anything else to do, you use

execve(program, (char *[]){program, 0}, environ);

which supplies the program's own name as argv[0] and no further arguments, and the same set of environment variables you got from your own parent.

Upvotes: 5

syntagma
syntagma

Reputation: 24304

In this line: execve(args[0], NULL, NULL); you are simply using the first element of args array. You could also use something like char* command="/bin/sh". You have to pass something, because that's how execve() was defined. In your case you pass NULL because you don't need to pass anything.

The point of the second argument of execve() is to pass arguments to the command you are spawning. Suppose that instead of shell you simply want to execute ls, you could then pass f.e. these arguments:

#include <unistd.h>
int main() {
        char *args[2];
        args[0] = "/bin/ls";
        args[1] = "-lh";
        execve(args[0], args, NULL);
}

Also, quoting man execve:

argv is an array of argument strings passed to the new program. By convention, the first of these strings should contain the filename associated with the file being executed. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program.

Upvotes: 5

Related Questions