Search code examples
clinuxexecvp

Execvp linux: trying to make my shell work in C


I'm trying to make simple shell, but with a specific condition, I have to use the following structure:

typedef  struct cmd_struct{
  char cmd[80];
  char args[10][80];
  int nargs;
} cmd_type;

Inside cmd I will save de main command and arguments in args.

Then I read from a file, different commands, and save them into and array of cmd_type. My program or fake shell, ask for a number and should take it from this array.

My function that executes the command looks like:

void execCmd(cmd_type* cmds_arg, int idxCmd){
  pid_t pid;
    printf("Father: my pid is %d\n", getpid());
    char* buff;
    pid = fork();
    if (pid == 0) {  
        printf("Child process: My pid is %d\n", getpid());
           printf("-------------- Child doing exec: %s\n", cmds_arg[idxCmd].cmd);
           execvp(cmds_arg[idxCmd].cmd,&cmds_arg[idxCmd].args);
           _exit(2);
        _exit(1);
    } 
    printf("Father: Gonna wait for Child\n");

    int status;
    wait(&status);
    printf("-------------- Father: Child finished\n");

    // WIFEXITED, WEXITSTATUS Macro of the gnu lib POSIX standard to recover end status
    if ( WIFEXITED(status) ) {   
        const int es = WEXITSTATUS(status);
        printf("Father: Child Complete with exit status %d\n", es);
        if(es == 1) printf("Father: Child didn't execute any command\n");
        else if(es == 2) printf("Father: Child command was not found\n");
    }
}

As you can see, when I'm calling the execvp() system call, I'm doing it wrong. First argument I think that it is right, second one it's totally wrong.

First of all, I have a conversion problem right there, and the second problem is that the array should contain "main command", "arg1", "arg2" ... and mine only has the arguments. Am I wrong?

Is there a way to add the "main command" using services like sscanf ()? And the most important part, do I have any chance to make it work this way?


Solution

  • In the same spirit of the answer from @pts, you can copy the parameters for execvp() in a dynamically allocated table:

    void execCmd(cmd_type* cmds_arg, int idxCmd){
      pid_t pid;
      printf("Father: my pid is %d\n", getpid());
      char* buff;
      pid = fork();
      if (pid == 0) {
    
        int i;
        char **args = (char **)malloc((1 + cmds_arg[idxCmd].nargs + 1) * sizeof(char *));
    
        args[0] = cmds_arg[idxCmd].cmd;
        for (i = 1; i < (cmds_arg[idxCmd].nargs + 1); i ++) {
          args[i] = cmds_arg[idxCmd].args[i - 1];
        }
        args[i] = NULL;
    
        printf("Child process: My pid is %d\n", getpid());
        printf("-------------- Child doing exec: %s\n", cmds_arg[idxCmd].cmd);
        execvp(cmds_arg[idxCmd].cmd, args);
        _exit(2);
      } 
      printf("Father: Gonna wait for Child\n");
    
      int status;
      wait(&status);
      printf("-------------- Father: Child finished\n");
    
      // WIFEXITED, WEXITSTATUS Macro of the gnu lib POSIX standard to recover end status
      if ( WIFEXITED(status) ) {   
        const int es = WEXITSTATUS(status);
        printf("Father: Child Complete with exit status %d\n", es);
        if(es == 1) printf("Father: Child didn't execute any command\n");
        else if(es == 2) printf("Father: Child command was not found\n");
      }
    }