1

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?

Timo Cengiz
  • 3,367
  • 4
  • 23
  • 45

2 Answers2

5

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().

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • I tried changing it to execvp() but i get the same response. getenv() should work to get the pathname right? – Timo Cengiz Nov 19 '15 at 20:45
  • 1
    @TimoCengiz: `getenv()` is largely immaterial. It will get the current value of `PATH` if that's what you ask it for, but `execv()` won't use that. Behind the scenes, `execvp()` will use `PATH` if the name in first argument has no slash in it. It will then prefix the command with each element of PATH and try to execute that. If you're converting `"ls"` to `"/bin/ls"` etc, you have to do the concatenation yourself. If you use `execvp()`, if the first argument contains `"ls"`, then `execvp()` will find and execute the first `ls` executable found in a directory on the PATH. – Jonathan Leffler Nov 19 '15 at 21:01
0

The first argument to execv is the command to run. This means you're trying to run /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games as a command.

Assuming the first value in the line array is the program you want to call, you should instead do this:

execv(line[0], line);

And if you want to perform a path-based search, use execvp instead (no need to manually extract the PATH variable):

execvp(line[0], line);

EDIT:

For example, suppose you wanted to run ls -l /usr/bin /var/log, your array would look like this:

char *line[] = { "ls", "-l", "/usr/bin", "/var/log", NULL};
dbush
  • 205,898
  • 23
  • 218
  • 273
  • ok so i get that line[0] equals to "ls" but what is the second argument line in the execvp() function that you described? – Timo Cengiz Nov 19 '15 at 20:49
  • @TimoCengiz The second argument is an array of `char *` which are the command line arguments. Index 0 of this array contains the name of the command and it is what appears in `ps` listings. The rest are the command line arguments, with the last element in the array being NULL. – dbush Nov 19 '15 at 20:51
  • Okay so i tried using execvp the way you mentioned. It worked i dont get any error message now but i dont get an answer to. Nothing basically happens. I can write another command and it does not matter what i write. I tried writing "sdfsdf" but it did not give me any errors? – Timo Cengiz Nov 19 '15 at 21:00
  • @TimoCengiz Did you close either `stdin` or `stdout`? – dbush Nov 19 '15 at 21:01
  • no. So execvp(line[0], line) will print out the result if it's a success?(just to clarify) – Timo Cengiz Nov 19 '15 at 21:04
  • @TimoCengiz Assuming `line[0]` can be found, `execvp` runs whatever it is. Then whatever it outputs, success or fail, should be seen assuming you haven't messed with any open file descriptors. – dbush Nov 19 '15 at 21:07
  • @TimoCengiz A more complete code example would be required to see what's happening. – dbush Nov 19 '15 at 21:07