2

I am trying to execute another program from within my own, but i don't understand a few things. So i wrote this code:

void run() {
  cout << "##Run" << endl;
  pid_t process_id = fork();

  if (process_id == 0) {
    char* args[] = {  "sudo", "ls", "-l" };
    auto i = execvp("sudo", args);
    cout << "#Result: " << i << endl;
    return;
  } else if (process_id < 0) {
    throw std::runtime_error("fork() failed");
  } else if (process_id > 0) {
    wait(&process_id);
  }
  return;
}

void run_decorator() {
  cout << "###Run decorator: " << endl;
  run();
}

int main() {
  run();
  run_decorator();
  return 0;
}

And the output is

##Run
total 88
-rwxr-xr-x 1 bezik bezik 77560 Nov 29 19:53 colors
-rwxr-xr-x 1 bezik bezik    63 Nov 26 21:45 compile
drwxr-xr-x 2 bezik bezik  4096 Nov 26 20:46 headers
drwxr-xr-x 2 bezik bezik  4096 Nov 23 00:48 sources
###Run decorator:
##Run
#Result: -1

Can someone please explain to me, why execvp failed when called from run_decorator() function?

Kamil Bęben
  • 1,064
  • 8
  • 12

1 Answers1

1

Read carefully the documentation of execvp(3). It says about execl etc...:

The first argument, by convention, should point to the filename associated with the file being executed. The list of arguments must be terminated by a null pointer, and, since these are variadic functions, this pointer must be cast (char *) NULL.

and regarding execvp:

The array of pointers must be terminated by a null pointer.

So you should code:

char* args[] = {  "sudo", "ls", "-l",  NULL };
execvp("sudo", args);

BTW, execvp don't return at all, except on failure. No need to keep its result after the call.

But you need, when execvp returns (and this only happens on failure) to show some error message. I suggest:

perror("execvp");
exit(EXIT_FAILURE);

and you could find valid arguments to call, in that precise case, _exit(2) instead of exit(3). I still prefer exit (because it would flush the stdio buffers and run atexit(3)-registered handlers).

Can someone please explain to me, why execvp failed

Yes, errno(3). Which is used by perror(3).

Don't forget to read carefully the documentation of every used function. For system calls (listed in syscalls(2)) and standard C library functions (see intro(3)), you generally should handle the failure case (often using errno, at least thru perror then exit)

I don't understand why you need sudo with ls. There are few occasions where that could be useful, since most of the time your working directory is listable (then sudo is useless), and in that case you could even use opendir(3), readdir(3) with stat(2) (so no need to fork then execvp the /bin/ls program).

Read also about setuid techniques in execve(2) and credentials(7) (and setreuid(2)). See this (you could avoid sudo with your own setuid program).

BTW you should compile with all warnings and debug info (g++ -Wall -Wextra -g with GCC) and use the debugger gdb (and perhaps strace(1) and valgrind(1)).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • Minor note: You're quoting the wrong part of the docs. The part about casting `NULL` to `char*` only applies to the `execl*` family (which are varargs, and therefore don't impose a specific type on the variable arguments, causing problems if `NULL` is just `0`, and thus is `int`-width, not pointer width, or if pointer types aren't constant width for whatever reason). It doesn't apply to the `execv*` family (where a typed array is involved, so `NULL` would be seamlessly implicitly coerced to `char*`). Idiomatically, you want: `char *args[] = {"sudo", "ls", "-l", NULL};`, no need for the cast. – ShadowRanger Nov 29 '17 at 20:30
  • Thank for your reply, probably tomorrow i will study the documentation again (more carefully this time). And answering your question, i don't need to use sudo with ls, it was just an example. – Kamil Bęben Nov 29 '17 at 20:46
  • It could be the case you don't want `sudo`: https://stackoverflow.com/a/46782615/841108 – Basile Starynkevitch Nov 29 '17 at 20:52