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 към nargs] = this_cmd-›args[0 към 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