views:

1366

answers:

10

Hi,

I am programming a UNIX shell and I have to use the execv() system call to create a process. One of the parameters for execv() is the filepath for the executable. So if somebody types in /bin/ls, it will run the ls executable. But what I need is a function that when ls is typed, it will search for the filepath of ls (like the "which" command). Is there a function which allows me to do that?

+1  A: 

I think execvp() does what you need.

Edit: So you're actually asking how to do this manually? In that case...

  1. Find your PATH in envp (3rd argument to main())
  2. Split this into individual paths
  3. Check for your program's existance in each of these with stat()
  4. Execute the first one you find to exist

Or if you want a really solid implementation, use this. It might set off the plagarism detectors though :)

Using getenv("PATH") is as reliable as using envp; you have to have control of main() to be able to get at that.
Jonathan Leffler
A: 

execvp :-)

Edit: Okay. Here's a Perl version, which can serve as pseudocode for your problem.

use List::Util qw(first);

my @path = split /:/, $ENV{PATH};
my $dir = first {$_ ||= '.'; -x "$_/$name"} @path
    or die "Can't find program $name\n";
exec "$dir/$name", @args;

split splits a string into an array of strings, using the given separator. first finds the first item that matches some criterion; here, that the concatenation of the directory and the name being sought after is executable (-x). It then runs that.

Hope it helps!

Chris Jester-Young
One nasty trick not accounted for when I typed this - an empty field in PATH needs to be treated like a dot (current directory).
Jonathan Leffler
Thanks, Jonathan! I'll fix my program right away.
Chris Jester-Young
+1  A: 

You want execvp(), it will search the the path specified in the PATH variable unless the filename contains a '/'.

Terence Simpson
+1  A: 

Unfortunately, this is a school project an I am not allowed to use execvp(). I need to implement some path searching function and then addon that filepath to the execv() parameter.

Thanks for the thought though

Put this in the question please
A: 

From the execvp man page:

   The  functions  execlp() and execvp() will duplicate the actions of the
   shell in searching for an executable file  if  the  specified  filename
   does  not  contain  a slash (/) character.  The search path is the path
   specified in the environment by the PATH variable.   If  this  variable
   isn’t specified, the default path ‘‘:/bin:/usr/bin’’ is used.  In addi-
   tion, certain errors are treated specially.

Perhaps you are allowed to use execlp()? If you must use execv you'll need to get the value of the PATH environment variable, parse this using strtok() to get the individual paths, append your filename to each path and attempt to execute it with execv().

I'd provide code, but I'm not doing your homework for you.

mhawke
A: 

Use execvp.

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

e.g. this example will exec /bin/echo (assuming /bin is on your PATH).

#include <unistd.h>

int main()
{
    char *args[] = {"echo", "hello world", (char *) NULL};
    execvp("echo", args);
    return 0;
}
spiv
+2  A: 

If you can't use execvp, you could get the PATH variable from char** environ from <unistd.h> or char* getenv(const char* name) from <stdlib.h> then use int access(const char* filename, int mode) to see if the file exists and is executable. I'll leave the implementation up to you as it's a school project.

Terence Simpson
Using access() isn't entirely reliable; it is best to try 'exec' on the file. There are a variety of issues with access() - some related to SUID binaries. Using access() will tell you if the file exists, but so will a failed execv().
Jonathan Leffler
+1  A: 

Use getenv("PATH") to get the path string from the environment, then use successive calls to strtok("PATH",':')/strtok("PATH",NULL) to parse out the paths from the PATH string into an array of char **path, which you will need to allocate with malloc(). Place path[x] + '/' + argv[0] into a buffer, and use access(buffer, X_OK) to see if you can execute your file at that path location, if so, perform your execv(buffer,argv).

Steve Baker
A: 

Oh a school project...

Well if you want something "like which" why not just exec "which" itself (/usr/bin/which on linux, not the bash alias or tcsh builtin) to get the path to what you are looking for...

:-)

Andrew Stein
+1  A: 

A few people have suggested that you call access() or stat() before attempting to execute the program with execv(). You don't need to do this. execv() will return an error if it could not execute the requested file.

mhawke
True. And you might need to distinguish between ENOENT (file does not exist) and ENOEXEC (file is not an executable) to decide whether it is worth trying to execute the 'program' via the shell as a script.
Jonathan Leffler
Not really. If the file exists but is not executable then you can't run it anyway (unless as an arg to /bin/sh - but now you're guessing that it's a script). If it is a script (shell or otherwise) with the proper "shebang" at the top then it will execute as expected.
mhawke