Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing an array to execvp() from the user's input

Tags:

c

execvp

I'm trying to pass arguments entered by the user to execvp().

So far I've split up the string. If the user types ls -a, temp is saved as "ls" and "-a" followed by a NULL character. I'm not quite sure how to point to this properly in execvp. In examples I've seen it using execvp(temp[position], temp). I know the way I'm trying to do it at the moment is wrong, but I'm not sure how to do it properly! At the moment I'm getting a segmentation fault.

int main(int argc, char *argv[]) 
{
    char line[124];
    int size = 124;
    char *temp = NULL;

    while(fgets(line, size, stdin) != NULL ) {
        if (strcmp(line, "exit\n") == 0) {
            exit(EXIT_SUCCESS);
        }
        temp = strtok(line, " ");
        while (temp != NULL) {
            printf("%s\n", temp);
            temp = strtok(NULL, " ");
        }
        execvp(temp, &temp);    
    }
    return EXIT_SUCCESS;
}
like image 310
caerulean Avatar asked Mar 21 '13 04:03

caerulean


1 Answers

Your problem is that temp is a single pointer, and you need to pass an array of pointers to execvp().

Something like:

    enum { MAX_ARGS = 64 };
    char *args[MAX_ARGS];
    char **next = args;

    temp = strtok(line, " ");
    while (temp != NULL)
    {
        *next++ = temp;
        printf("%s\n", temp);
        temp = strtok(NULL, " ");
    }
    *next = NULL;
    execvp(args[0], args);

Note that the argument list has been given a null pointer as a terminator, just like argv[argc] == NULL in main(). Clearly, I've skimped on the error checking (if you pass more than 63 arguments, you're going to overflow args array). But this contains the core idea.


With this example, I can't seem to get the simple command of ls to work, I've tried mkdir and echo and they seem to work fine. Passing ls returns -1 from execvp().

I'm not sure what the problem might be — all of these work for me:

  • ls
  • ls -l
  • ls -l madump.c (where madump.c happens to be a file in the directory I'm testing in)

The code I used was:

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

int main(void) 
{
    char line[1024];

    while (fgets(line, sizeof(line), stdin) != NULL)
    {
        if (strcmp(line, "exit\n") == 0)
            exit(EXIT_SUCCESS);

        char *args[64];
        char **next = args;
        char *temp = strtok(line, " \n");
        while (temp != NULL)
        {
            *next++ = temp;
            printf("%s\n", temp);
            temp = strtok(NULL, " \n");
        }
        *next = NULL;

        puts("Checking:");
        for (next = args; *next != 0; next++)
            puts(*next);

        execvp(args[0], args);
    }

    return EXIT_SUCCESS;
}

Note that I added \n to the strtok() token list, after creating a directory with a newline at the end of its name. Good for annoying friends and baffling semi-educated enemies, but a nuisance from most other perspectives. Note how I print out the data that's going to be passed to execvp() just before actually doing so. Often, I'd use printf("<<%s>>\n", *next); instead of just puts() to get an unambiguous indication of where the arguments start and end.

The output from running the command (doit) was:

$ ./doit
ls -l madump.c
ls
-l
madump.c
Checking:
ls
-l
madump.c
-rw-r--r--  1 jleffler  staff  2352 Jul 28  2011 madump.c
$

What did you get from your version?

like image 50
Jonathan Leffler Avatar answered Sep 25 '22 20:09

Jonathan Leffler