Параметры C execve() [порождают пример оболочки]

Я должен заполнить параметры для:

int execve(const char *filename, char *const argv[], char *const envp[]);

Если я выполню эту программу:

#include <unistd.h>
int main() {
        char *args[2];
        args[0] = "/bin/sh";
        args[1] = NULL;
        execve(args[0], args, NULL);
}

оболочка создается правильно, как и ожидалось.

Моя проблема в том, что оболочка создается правильно, даже если я передаю NULL в качестве второго параметра, например:

#include <unistd.h>

int main() {
        char *args[2];
        args[0] = "/bin/sh";
        args[1] = NULL;
        execve(args[0], NULL, NULL);
}

Итак, какова цель использования вектора args (с «/bin/sh» + NULL) в качестве второго параметра вместо NULL?


person rh0x    schedule 10.05.2015    source источник
comment
В этом случае вы не передаете никаких аргументов, поэтому нет никакой разницы. Вы можете передать какой-либо аргумент в args.   -  person Anirudh Ramanathan    schedule 10.05.2015
comment
Итак, почему во всех примерах, которые я вижу, люди используют /bin/sh + NULL в качестве аргумента? Это проще понять, если параметр arg напрямую установлен в NULL.   -  person rh0x    schedule 10.05.2015
comment
Вероятно, это потому, что это пример, чтобы понять, что если я передаю некоторые параметры, должен быть NULL в качестве ограничителя строки?   -  person rh0x    schedule 10.05.2015
comment
Напишите себе программу на C, которая выводит аргументы, вы должны увидеть разницу, если вызовете ее с двумя примерами программ.   -  person dav1d    schedule 10.05.2015


Ответы (2)


В этой строке: execve(args[0], NULL, NULL); вы просто используете первый элемент массива args. Вы также можете использовать что-то вроде char* command="/bin/sh". Вы должны что-то передать, потому что именно так было определено execve(). В вашем случае вы передаете NULL, потому что вам не нужно ничего передавать.

Пункт второго аргумента execve() состоит в том, чтобы передать аргументы команде, которую вы порождаете. Предположим, что вместо оболочки вы просто хотите выполнить ls, тогда вы можете передать f.e. эти аргументы:

#include <unistd.h>
int main() {
        char *args[2];
        args[0] = "/bin/ls";
        args[1] = "-lh";
        execve(args[0], args, NULL);
}

Также, цитируя man execve:

argv — это массив строк аргументов, передаваемых новой программе. По соглашению первая из этих строк должна содержать имя файла, связанного с исполняемым файлом. envp — это массив строк, обычно в форме ключ=значение, которые передаются в качестве среды новой программе.

person syntagma    schedule 10.05.2015

Если вы передаете нулевой указатель в качестве второго или третьего аргумента execve, ваша программа неверна в соответствии с POSIX; оба эти аргумента должны быть ненулевыми. (Это не все, что четко указано в спецификации execve, но это там.) В настоящее время я печатаю это в операционной системе, которая обрабатывает передачу нулевых указателей,

execve("executable", 0, 0);

как эквивалент передачи пустых массивов, например.

execve("executable", (char *[]){0}, (char *[]){0});

но я не удивлюсь, если узнаю, что другие операционные системы будут вызывать ошибку сегментации или возвращать -1 с errno, установленным в EFAULT или EINVAL.

Передача пустых массивов для этих аргументов разрешена, но только что выполненная программа получит нулевые аргументы в argc/argv, если второй аргумент является пустым массивом, и/или нулевые переменные среды в envp/environ, если третий аргумент является пустым массивом. В этих условиях многие программы будут работать со сбоями. Например, очень часто можно увидеть такие вещи, как

int main(int argc, char **argv)
{
   if (argc != 4) {
     fprintf(stderr, "usage: %s one two three\n", argv[0]);
     return 2;
   }
   // ...
}

где программа неявно предполагает, что argv[0] всегда будет ненулевым.

Таким образом, вы всегда должны предоставлять непустые массивы для обоих аргументов. Обычное соглашение заключается в том, что если вам больше нечего делать, вы используете

execve(program, (char *[]){program, 0}, environ);

который предоставляет собственное имя программы как argv[0] и никаких дополнительных аргументов, и тот же набор переменных среды, который вы получили от своего родителя.

person zwol    schedule 22.09.2017