rahmu
rahmu

Reputation: 5878

Implementing a Unix shell in C: check if file is executable

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

Answers (6)

Wolf
Wolf

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

davep
davep

Reputation: 286

How are you creating the process??

Use execlp or execvp.

Upvotes: 0

asveikau
asveikau

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

Jim Lewis
Jim Lewis

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

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

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

sukru
sukru

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

Related Questions