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.

2 Answers 2

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);

Sign up to request clarification or add additional context in comments.

1 Comment

This is exactly right. If you want to execute ps -a, you need to call execvp("ps", {"ps", "-a", NULL}).
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().

3 Comments

Or in keeping with the theme, use (or implement and use) estrdup().
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; }
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.