execvp failing with multiple or no arguments

sven picture sven · Dec 9, 2013 · Viewed 7.2k times · Source

I'm working on a pretty basic UNIX shell in C. In this project I am attempting to use fork() and execvp() to execute the actual shell commands. I'm running into an issue though, where it seems to work fine with commands that have a single argument (for example ls -l and echo howareyoutoday work perfectly), but commands with multiple arguments fail to execute (echo how are you today does not run). I'll walk you through my code/rationale in an effort to help locate the reason behind this problem.

                char *token;
                int count=0;
                pid_t childProc;
                int childStatus;
                pid_t tpid;
                char* argv[256];
                int i=1;

                        childProc = fork();

                            if (childProc==0) {
                                    //CHILD PROCESS IS HERE
                                    argv[0] = malloc(strlen(token));
                                    argv[0] = token;

                                    token=strtok(NULL," \n\t()<>|&;");
                                    while(token!=NULL) {
                                            argv[i]=malloc(strlen(token));
                                            argv[i]=token;
                                            token=strtok(NULL," \n\t()<>|&;");
                                            i++;
                                    }
                                    execvp(argv[0],argv);

                                    //if this executes execvp fails
                                    printf("failure to execute\n");
                                    i=0;
                                    exit(0);
                            }
                            else {
                                    //PARENT PROCESS IS HERE
                                    do {
                                            tpid = wait(&childStatus);
                                    } while(tpid != childProc);
                            }

So it starts out with a basic fork() call to create the child process. In that child process, I allocate memory for the first element in my argv array. token which comes from a previous strtok call is assigned to argv[0]. A new token is generated and added to the next argv element. This process is repeated for the remaining tokens remaining.

Once the argv array has been completed, execvp is called, with the first argument containing the command name, and the second being the whole argv array. If the command fails to execute, execvp will return and a message indicating this will be printed.

I can't figure out why I am having the multiple arguments problem I mentioned above. Any help or suggestions will be greatly appreciated!

For reference, the full program's code is as follows:

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

int main() {
    char buffer[256];
    char *token;
    int count=0;
    pid_t childProc;
    int childStatus;
    pid_t tpid;
    char* argv[256];
    int i=1;
    int j=0;

    while(1) {
            fgets(buffer, 256, stdin);

            token=strtok(buffer," \n\t()<>|&;");

            while (token==NULL) {
                    fgets(buffer,256,stdin);
                    token=strtok(buffer," \n\t()<>|&;");
            }

                    if (strcmp(token,"exit")==0) {
                            exit(0);
                    }
                    else if (strcmp(token,"cd")==0) {
                            token = strtok(NULL, " \n\t()<>|&;");

                            if (chdir(token)!=0) {
                                    perror("Error: ");
                            }
                    }
                    else {
                            childProc = fork();

                            if (childProc==0) {
                                    argv[0] = malloc(strlen(token));
                                    argv[0] = token;

                                    token=strtok(NULL," \n\t()<>|&;");
                                    while(token!=NULL) {
                                            argv[i]=malloc(strlen(token));
                                            argv[i]=token;
                                            token=strtok(NULL," \n\t()<>|&;");
                                            i++;
                                    }
                                    execvp(argv[0],argv);

                                    //if this executes execvp fails
                                    printf("failure to execute\n");
                                    i=0;
                                    exit(0);
                            }
                            else {
                                    do {
                                            tpid = wait(&childStatus);
                                    } while(tpid != childProc);
                            }
                    }
    }
}

Answer

Duck picture Duck · Dec 9, 2013

You need to null terminate your argument strings to execvp.

if (childProc == 0)
{
    argv[0] = malloc(strlen(token));
    argv[0] = token;

    token = strtok(NULL, " \n\t()<>|&;");

    while (token != NULL)
    {
        argv[i] = malloc(strlen(token));
        argv[i] = token;
        token = strtok(NULL, " \n\t()<>|&;");
        i++;
    }

    argv[i] = NULL;  //<--- insert here

    if (execvp(argv[0], argv) == -1)
    {
        printf("failure to execute because %s\n", strerror(errno));
        exit(0);
    }
}