Reputation: 5878
I am working on implementing a Unix shell in C and I'm currently dealing with the problem of relative paths. Notably while inputting commands. For now I have to enter the full path of the executable every time, when I would much rather simply put "ls" or "cat".
I have managed to get the $PATH env variable. My idea is to split the variable at the ":" character, then append each new string to the command name and check if the file exists and is executable.
For example if my PATH is: "/bin:/usr/bin" and I input "ls", I would like the program to check first if "/bin/ls" exists and is executable, if not move on to "/usr/bin/".
Two questions:
1) Is it a good way to do it? (Doesn't it have to be necessarily the best. I just want to make sure that it would work.
2) More importantly, How can I check in C, if a file exists and is executable?
I hope I'm clear enough, and ... well thanks :)
Upvotes: 4
Views: 12224
Reputation: 1
The access()
answer is the only sensible one.
If you use stat, you'll have to parse /etc/group to find out all the groups you are in BESIDE
getgid()
and test them together with the group-executable bit.
Upvotes: 0
Reputation: 40226
stat
? Psh. Way more than you need.
Check out the access()
syscall.
if (access(filename, F_OK|X_OK) == 0)
{
/* You can execute this file. */
}
Note that any check for file access or existence of a file has an inherent race condition in it. You can't guarantee at your call to execve
that someone didn't remove the executable bit, change the ownership of the file, delete the file, etc., in the time since your check happened. Keep this in mind when you write your code and decide how to handle error conditions.
Upvotes: 9
Reputation: 45025
Call stat
on the full pathname, and if it returns 0 (success), the target exists. In that
case you can use the st_mode
field in the returned structure to test whether the
target is a directory, device, named pipe, or ordinary file. If it's a file, st_mode
will also contain the permission bits. (If it's a directory, some "executable" bits might
be set, but that would imply "searchable" rather than "executable".)
Edit: As others have noted, this is subject to a race condition, and there are better ways of accomplishing what you want to do in this particular situation.
Upvotes: 4
Reputation: 215193
Don't. Performing this check is wrong; it's inherently subject to a race condition. Instead, try executing it with the appropriate exec
-family call. If it's not executable, you'll get an error.
Also note that you don't need to search the PATH
yourself; execvp
can do this for you.
Upvotes: 10
Reputation: 2259
Use the stat
function:
http://linux.die.net/man/2/stat
e.g.:
struct stat sb;
if(!stat(path, &sb))
{
if(IS_REG(sb.st_mode) && sb.st_mode & 0111)
printf("%s is executable\n", path);
}
Upvotes: 4