Цель, насколько я понимаю, состоит в том, чтобы дочерний процесс повторно выполнял ту же программу, которая представляет родительский процесс.
В отсутствие трюков с использованием данных из файловой системы /proc
я не верю, что есть абсолютно надежный способ сделать это.
Обычно вы полагаетесь на то, что значения argv[0]
достаточно для поиска программы, и используете execvp()
для поиска программы через $PATH
.
Попытка 2 - может быть, менее запутанная
Я перефразирую [вопрос], если вы не возражаете: прямо сейчас execl()
выполняет двоичный файл, но только если я загружаю родителя непосредственно из его папки. Если я зайду во вложенную папку, назовем ее fd, тогда путь будет "correctPath/fd/shell", а не "correctPath/shell". Похоже, проблема в том, что вызов getenv("PWD") для поиска пути не всегда правильный.
Таким образом, цель состоит в том, чтобы загрузить один и тот же процесс или двоичный файл; давайте предположим, что двоичный файл называется «оболочка». Проблема в том, чтобы найти путь. Код, который я показываю, работает в том случае, если оболочка загружается (родительская) из папки, в которой она находится сама по себе, иначе она не работает. Я подозреваю, что вызывать getenv("PWD") неправильно, но я не уверен, что еще вызывать.
Вы правы в том, что использование getenv("PWD")
или getcwd()
обычно неверно.
char *arg0 = 0;
int main(int argc, char **argv)
{
...declarations...
arg0 = argv[0];
...actions...
}
Поэтому основная программа сохраняет значение своего argv[0]
в глобальной переменной arg0
, чтобы сделать его доступным для других частей процесса — в частности, для кода, который собирается (повторно) запустить команду.
Если программа вызывается в том же каталоге, где находится исполняемый файл, с использованием «./shell», то argv[0]
(и, следовательно, arg0
) будет содержать этот путь. Если он выполняется с использованием «оболочки», полагаясь на $PATH для поиска программы, то argv[0]
будет содержать либо просто «оболочку», либо абсолютный путь «оболочки» (реже).
Если программа вызывается из подкаталога fd
, она может вызываться как «../shell», или как «/absolute/path/to/shell», или как «shell», полагаясь на $ PATH, чтобы найти исполняемый файл. Опять же, в любом из этих случаев значение в arg0
является именем, под которым программа была первоначально вызвана, или эквивалентно ему.
Единственный случай, когда это не удается, - это если кто-то намеренно пытается запутать исполняемый файл, что (к счастью) бывает редко.
Итак, в дочернем коде вы можете использовать:
char *args[] = { arg0, 0 };
execvp(arg0, args);
для повторного выполнения команды в том виде, в каком она была первоначально выполнена (за исключением любых вспомогательных аргументов, которые были переданы изначально).
Попытка 1 - несколько запутанная
Предполагая, что значение argv[0]
доступно через переменную char *arg0
, все, что необходимо, это:
char *args[] = { arg0, 0 };
execvp(arg0, args);
Если вы должны использовать execl()
, вам нужно:
execl(arg0, arg0, (char *)0);
Актерский состав необходим; execl()
— это функция списка переменных аргументов, и если вы напишете 0, она будет преобразована в int
, которая не будет работать в 64-битной системе, где int
— 32-битная, а указатель — 64-битный. Однако это не удастся, если arg0
не представляет путь (относительный или абсолютный) к исполняемому файлу. Затем вам нужно будет решить, что делать, если execl()
вернется — вы можете либо сдаться, либо искать программу через $PATH
, но в этом случае, почему бы не использовать execvp()
в первую очередь, чтобы избавить себя от боли.
person
Jonathan Leffler
schedule
25.09.2010
execl()
, который вы показываете, не дает вызываемой программеargv[0]
(или, точнее, он дает нулевой указатель какargv[0]
- если NULL определен как(void *)0
; в противном случае он дает нулевое целое число, которое может быть недостаточно большим. на 64-битной машине). Вы должны использоватьexecl(path, path, (char *)NULL);
, чтобы быть в безопасности (r). - person Jonathan Leffler   schedule 25.09.2010