no such file or directory when using execv()

Timo Cengiz picture Timo Cengiz · Nov 19, 2015 · Viewed 11.3k times · Source

Im trying to write a basic shell that can interpret simple commands like date, ls in the language c.

I start by getting the PATH variable like this to later pass it on to the execv() function.

const char *name = "PATH";
char *value;
value = getenv(name)

and i print out the value and i get this:

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

Note that i am using virutalbox to run Ubuntu. And this is the code that i am using to try a simple ls command. In the code below the variable line is the actual command that the user wrote, in our case it is "ls"

  pid_t pid, wpid;
  int status;

  pid = fork();
  if (pid == 0) {
      // Child process
      if (execv(value, line) == -1) {
          perror("lsh");
      }
      exit(EXIT_FAILURE);
  }
  else if (pid < 0) {
        // Error forking
        perror("lsh");
  }
  else {
      // Parent process
      do {
          wpid = waitpid(pid, &status, WUNTRACED);
      }
      while (!WIFEXITED(status) && !WIFSIGNALED(status));
    }

And the result i get is this:

lsh: no such file or directory

any ideas?

Answer

Jonathan Leffler picture Jonathan Leffler · Nov 19, 2015

The execv() system call uses the name you specify in the first argument as the file name of the executable; it does not do a PATH-based search.

That means that if you specify "lsh" as the first argument, there must be an executable file lsh in the current directory.

If you want a PATH-based search, replace execv() with execvp(). Otherwise, specify the pathname — absolute or relative, but absolute is more normal — of the command in the first argument.

Note that if any of the exec*() functions returns, it has failed. There's no need to test the return value; it will always be -1.

The contents of value and line need to be along the lines of:

char *value = "ls";
char *line[] = { "ls", "-l", 0 };

execvp(value, line);

or, more conventionally:

execvp(line[0], line);

If you are analyzing the PATH yourself, you'll need to have line[0] pointing at the complete file name that you've created from PATH, and then you use execv() instead of execvp().