tags:

views:

224

answers:

3

Given a filename in C, I want to determine whether this file exists and has execute permission on it. All I've got currently is:

if( access( filename, X_OK) != 0 ) {

But this wont search the PATH for files and will also match directories (which I don't want). Could anyone please help?

Edit:

As an alternative, seeing as I'm running execvp() in a child process, is there a way to check the return value of execvp() and signal the parent process to die with an error message?

+1  A: 

This code's not quite what you want, as it simply blindly executes the first thing it comes to. But you can modify the search code so that instead of calling execve you call access and then stat to find out if it's not a directory. I think only the last function, execvepath, has to be replaced.

In the best Unix tradition, the code is "self-documenting" (i.e., undocumented).

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

#include "shellpath.h"

static void *malloc_check(const char *what, size_t n) {
  void *p = malloc(n);
  if (p == NULL) {
    fprintf(stderr, "Cannot allocate %zu bytes to %s\n", n, what);
    exit(2);
  }
  return p;
}

static char *strsave(const char *s, const char *lim) {
  if (lim == NULL)
    lim = s + strlen(s);
  char *p = malloc_check("save string", lim - s + 1);
  strncpy(p, s, lim-s);
  p[lim-s] = '\0';
  return p;
}

char ** shellpath(void) {
  const char *path = getenv("PATH");
  if (!path)
    path = "/bin:/usr/bin:/usr/local/bin";

  char **vector = // size is overkill
    malloc_check("hold path elements", strlen(path) * sizeof(*vector)); 
  const char *p = path;
  int next = 0;
  while (p) {
    char *q = strchr(p, ':');
    vector[next++] = strsave(p, q);
    p = q ? q + 1 : NULL;
  }
  vector[next] = NULL;
  return vector;
}

void freeshellpath (char *shellpath[]) {
  for (int i = 0; shellpath[i]; i++)
    free(shellpath[i]);
  free(shellpath);
}
unsigned maxpathlen(char *path[], const char *base) {
  unsigned blen = strlen(base);
  unsigned n = 0;
  for (int i = 0; path[i]; i++) {
    unsigned pn = strlen(path[i]);
    if (pn > n) n = pn;
  }
  return blen+n+1;
}



void execvepath(char *path[], const char *base, char *const argv[],
                char *const envp[])
{
  if (strchr(base, '/'))
    execve(base, argv, envp);
  else {
    size_t maxlen = maxpathlen(path, base)+1;
    char *buf = malloc_check("hold path", maxlen);
    for (int i = 0; path[i]; i++) {
      snprintf(buf, maxlen, "%s/%s", path[i], base);
      execve(buf, argv, envp);
    }
  }
}
Norman Ramsey
Hey - thanks for your help. Tis seems much more complex than what I thought it'd be. Is there no way of running an exec function and having it return if the file doesn't exist?
Gary
+1  A: 

You can use the following functions. like.. the following codes contains some pseudo codes, but should be easy to implement

if the given path contains the current directory path,
like /root/a or ./abc, then
        return access( filename, X_OK) == 0;

Else - if the given path only contains the filename,
{
        getenv( "PATH" );
        while( iterate the directories in PATH )
        {
                if( search( PATH_directory, filename ) )
                {
                        // create the full path string with strcat, strcpy, and/or etc.
                        full_path = blabla

                        if( !is_directory( full_path ) && access( filename, X_OK ) == 0 )
                                return 1; // Yeah~~ We got it!!!
                }
        }

        return 0;   // Nah, I don't think there is any of such a file.
}
int is_directory( const char* path )
{
        struct stat file_info;
        return ( stat( path, &file_info ) == 0 ) ? S_ISDIR( file_info.st_mode ) : 0;
}


int search( const char* file_name, const char* path )
{
        struct dirent* dptr;
        DIR* dirp;

        if( (dirp = opendir( path )) == NULL )
                return 0;

        while( dptr = readdir( dirp ) )
        {
                if( strcmp( file_name, dptr->d_name ) == 0 )
                {
                        closedir( dirp );
                        return 1;
                }
        }

        closedir( dirp );    
        return 0;
}
ddoman
A: 

Ended up having to set the FD_CLOEXEC flag on a new pipe and writing an error message through it after the exec if it had failed. I could then read the error message from the parent and determine whether exec was successful or not.

Thanks for your effort though guys, upvoted for the help

Gary