5

I am building a shell and am having some trouble with the system call 'execvp'. I saw some other questions on this topic but they were vague, and did not appear to be completely addressed (whoever asked the questions didn't provide much information and didn't get good answers).

Obviously I have my own command line and I'm reading user input from stdin like

mysh some/path $ ps -a 

and I am building an args array as a char ** and the array itself works (I think) since when I print out the values within my function it shows

args[0] = 'ps'
args[1] = '-a'
args[2] = '(null)'

So, I call fork and execvp(cmnd, args) within my process, where cmnd is "ps" and args is as above, and perror etc.

I get

'Error: no such file or directory.'  

Do I need to put in the $PATH variable? Am I doing something else whacky?

Here's my code for generating the args array:

char ** get_args(char * cmnd) {
int index = 0;
char **args = (char **)emalloc(sizeof(char *));
char * copy = (char *) emalloc(sizeof(char)*(strlen(cmnd)));
strncpy(copy,cmnd,strlen(cmnd));
char * tok = strtok(copy," ");
while(tok != NULL) {
    args[index] = (char *) emalloc(sizeof(char)*(strlen(tok)+1));
    strncpy(args[index],tok,strlen(tok)+1);
    index++;
    tok = strtok(NULL," ");
    args = (char**) erealloc(args,sizeof(char*)*(index+1));
}
args[index] = NULL;
return args;
}

(emalloc and erealloc are just malloc and realloc with error checking built in)

So then I do this:

void exec_cmnd(char*cmnd, char**args) {
pid_t pid;
if((pid=fork())==0) {
    execvp(cmnd, args);
    perror("Error");
    free(args);
    free(cmnd);
    exit(1);
}
else {
    int ReturnCode;
    while(pid!=wait(&ReturnCode)) {
        ;
    }
}
}

And like I said above, when execvp is called inside my process it fails when I supply any arguments whatsoever but works without them (i.e. when argv == {'ps', NULL} )

If you need more information don't hesitate to ask. I need to solve this.

matchdav
  • 715
  • 1
  • 7
  • 16

2 Answers2

6

It think you passed whole command line in first argument to execvp

you have to separate first token(command name) from cmnd to pass as first argument of execvp

You can call it as

execvp(args[0], args);

ravi
  • 3,304
  • 6
  • 25
  • 27
  • This is exactly right. If you want to execute `ps -a`, you need to call `execvp("ps", {"ps", "-a", NULL})`. – caf May 22 '12 at 01:29
1

Note in passing that you have a non-terminated string as a result of:

char * copy = (char *) emalloc(sizeof(char)*(strlen(cmnd)));
strncpy(copy, cmnd, strlen(cmnd));

strncpy() does not null terminate for you when you use it like that. You also need to allocate one more byte for the null. Consider using strdup() if it is available to you. If not, consider writing it. This misallocation is a problem even with the error checking versions of emalloc() and erealloc().

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    Or in keeping with the theme, use (or implement and use) `estrdup()`. – Michael Burr May 22 '12 at 06:33
  • `char * estrdup(char * str) { int len = (int)(strlen(str)+1); char * copy = (char * ) emalloc(sizeof(char)*len); strncpy(copy,str,len); if(copy==NULL) { printf("Error duplicating '%s'\n",str); exit(1); } return copy; }` – matchdav May 22 '12 at 07:37
  • Good (almost): change it to `size_t len` and lose the cast since the argument to `malloc()` and `strncpy()` is also a `size_t`. Note that you should check `copy` _before_ copying to it, not _after_! And the argument should be `char const *str` since you are not going to modify it. – Jonathan Leffler May 22 '12 at 09:58