Execvp linux: пытаюсь заставить мою оболочку работать на C

Я пытаюсь сделать простую оболочку, но с определенным условием я должен использовать следующую структуру:

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

Внутри cmd я сохраню основную команду и аргументы в args.

Затем я читаю из файла разные команды и сохраняю их в массив cmd_type. Моя программа или фейковая оболочка, запрашивает номер и должна брать его из этого массива.

Моя функция, которая выполняет команду, выглядит так:

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

Как видите, когда я вызываю системный вызов execvp(), я делаю это неправильно. Первый аргумент я считаю правильным, второй совершенно неверным.

Во-первых, у меня тут проблема с преобразованием, а вторая проблема в том, что массив должен содержать основную команду, arg1, arg2... а у меня только аргументы. Я ошибаюсь?

Есть ли способ добавить основную команду с помощью таких сервисов, как sscanf()? И самое главное, есть ли у меня шанс заставить это работать таким образом?


person Aleix Martí    schedule 29.01.2021    source источник
comment
Вы правы, первая запись в таблице — это имя команды. Более того, таблица аргументов должна быть завершена NULL: последняя запись должна содержать NULL, так как ядру нужно знать, когда список аргументов остановится.   -  person Rachid K.    schedule 29.01.2021
comment
Изучите исходный код sash.   -  person Basile Starynkevitch    schedule 29.01.2021
comment
Спасибо @RachidK. расскажите мне о параметре NULL, я этого не знал. И спасибо, что показали мне, каким путем следовать, чтобы получить окончательный ответ.   -  person Aleix Martí    schedule 30.01.2021


Ответы (2)


В том же духе ответа от @pts вы можете скопировать параметры для execvp() в динамически выделяемую таблицу:

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");
  }
}
person Rachid K.    schedule 29.01.2021

С char args[10][80]; execvp(cmds_arg[idxCmd].cmd,&cmds_arg[idxCmd].args); не будет работать (он даже не скомпилируется), потому что execvp нуждается в указателе const char* для каждого аргумента, а ваш .args не имеет указателей.

Что-то вроде этого может работать:

const char *p[11];  /* Contains up to 10 pointers + trailing NULL. */
cmd_type *this_cmd = &cmds_arg[idxCmd];
for (int i = 0; i < this_cmd->nargs; ++i) {
  p[i] = &this_cmd->args[i];
}
p[this_cmd->nargs] = NULL;
execvp(this_cmd->cmd, p);
person pts    schedule 29.01.2021
comment
p[] должно быть 12 указателей: cmd_name, не более 10 аргументов и NULL. p[0] должен указывать на this_cmd->cmd, p[1 to nargs] = this_cmd->args[0 to nargs - 1], p[nargs + 1] = NULL - person Rachid K.; 29.01.2021
comment
@RachidK.: Конечно, p[0] должен указывать на $0 (имя программы), и подходящее значение для него уже хранится в this_cmd->cmd. Однако, читая вопрос, не очевидно, так ли это. - person pts; 29.01.2021