waitpid / wexitstatus возвращает 0 вместо правильного кода возврата

У меня есть вспомогательная функция ниже, используемая для выполнения команды и получения возвращаемого значения в системах posix. Я использовал, чтобы использовать popen, но невозможно получить код возврата приложения с popen если он работает и выходит раньше popen/pclose получает шанс сделать свою работу.

Следующая вспомогательная функция создает процесс fork, использует execvp запустить нужный внешний процесс, а затем родительский использует waitpid чтобы получить код возврата. Я вижу странные случаи, когда он отказывается бежать.

Когда вызывается с wait знак равно true, waitpid должен вернуть код завершения приложения, несмотря ни на что. Тем не менее, я вижу stdout вывод, который указывает код возврата, должен быть ненулевым, но код возврата равен нулю. Тестирование внешнего процесса в обычной оболочке, затем echoИНГ $? возвращает ненулевое значение, так что это не проблема, если внешний процесс не возвращает правильный код. Если это поможет, запускается внешний процесс mount(8) (да, я знаю, что могу использовать mount(2) но это не главное).

Заранее извиняюсь за дамп кода. Большая часть этого — отладка / регистрация:

inline int ForkAndRun(const std::string &command, const std::vector<std::string> &args, bool wait = false, std::string *output = NULL)
{
std::string debug;

std::vector<char*> argv;
for(size_t i = 0; i < args.size(); ++i)
{
argv.push_back(const_cast<char*>(args[i].c_str()));
debug += "\"";
debug += args[i];
debug += "\" ";
}
argv.push_back((char*)NULL);

neosmart::logger.Debug("Executing %s", debug.c_str());

int pipefd[2];

if (pipe(pipefd) != 0)
{
neosmart::logger.Error("Failed to create pipe descriptor when trying to launch %s", debug.c_str());
return EXIT_FAILURE;
}

pid_t pid = fork();

if (pid == 0)
{
close(pipefd[STDIN_FILENO]); //child isn't going to be reading
dup2(pipefd[STDOUT_FILENO], STDOUT_FILENO);
close(pipefd[STDOUT_FILENO]); //now that it's been dup2'd
dup2(pipefd[STDOUT_FILENO], STDERR_FILENO);

if (execvp(command.c_str(), &argv[0]) != 0)
{
exit(EXIT_FAILURE);
}
return 0;
}
else if (pid < 0)
{
neosmart::logger.Error("Failed to fork when trying to launch %s", debug.c_str());
return EXIT_FAILURE;
}
else
{
close(pipefd[STDOUT_FILENO]);

int exitCode = 0;

if (wait)
{
waitpid(pid, &exitCode, wait ? __WALL : (WNOHANG | WUNTRACED));

std::string result;
char buffer[128];
ssize_t bytesRead;
while ((bytesRead = read(pipefd[STDIN_FILENO], buffer, sizeof(buffer)-1)) != 0)
{
buffer[bytesRead] = '\0';
result += buffer;
}

if (wait)
{
if ((WIFEXITED(exitCode)) == 0)
{
neosmart::logger.Error("Failed to run command %s", debug.c_str());
neosmart::logger.Info("Output:\n%s", result.c_str());
}
else
{
neosmart::logger.Debug("Output:\n%s", result.c_str());
exitCode = WEXITSTATUS(exitCode);
if (exitCode != 0)
{
neosmart::logger.Info("Return code %d", (exitCode));
}
}
}

if (output)
{
result.swap(*output);
}
}

close(pipefd[STDIN_FILENO]);

return exitCode;
}
}

Обратите внимание, что команда выполняется с правильными параметрами, функция выполняется без проблем, и WIFEXITED возвращается TRUE, Тем не мение, WEXITSTATUS возвращает 0, когда он должен возвращать что-то еще.

1

Решение

Возможно, это не ваша главная проблема, но я думаю, что вижу небольшую проблему. В вашем дочернем процессе у вас есть …

dup2(pipefd[STDOUT_FILENO], STDOUT_FILENO);
close(pipefd[STDOUT_FILENO]); //now that it's been dup2'd
dup2(pipefd[STDOUT_FILENO], STDERR_FILENO); //but wait, this pipe is closed!

Но я думаю, что вы хотите это:

dup2(pipefd[STDOUT_FILENO], STDOUT_FILENO);
dup2(pipefd[STDOUT_FILENO], STDERR_FILENO);
close(pipefd[STDOUT_FILENO]); //now that it's been dup2'd for both, can close

У меня нет большого опыта работы с вилками и трубами в Linux, но я недавно написал похожую функцию. Вы можете взглянуть на код для сравнения, если хотите. Я знаю, что моя функция работает.

execAndRedirect.cpp

2

Другие решения

Я использую мангуста библиотека, и мой код SIGCHLD показал, что с помощью mg_start от мангуста результаты в настройке SIGCHLD в SIG_IGN,

От waitpid справочная страница, в Linux SIGCHLD установлен в SIG_IGN не будет создавать процесс зомби, так waitpid потерпит неудачу, если процесс уже успешно запущен и завершен, но будет работать нормально, если еще не был. Это было причиной случайного сбоя моего кода.

Просто перенастройка SIGCHLD после звонка mg_start функции void, которая абсолютно ничего не делает, было достаточно, чтобы предотвратить немедленное удаление записей зомби.

в @ Geoff_Montee’s совет, была ошибка в моем редиректе STDERR, но это не было причиной проблемы, так как execvp не сохраняет возвращаемое значение в STDERR или даже STDOUT, а точнее в объекте ядра, связанном с родительским процессом (запись зомби).

предупреждение @jilles о несмежности vector в C ++ не применяется для C ++ 03 и выше (действительно только для C ++ 98, хотя на практике большинство компиляторов C ++ 98 все равно использовали непрерывное хранилище) и не было связано с этой проблемой. Тем не менее, совет по чтению из канала перед блокировкой и проверкой вывода waitpid на месте.

2

Я нашел это pclose НЕ блокирует и ожидает завершения процесса, в отличие от документации (это на CentOS 6). Я обнаружил, что мне нужно позвонить pclose а затем позвоните waitpid(pid,&status,0); чтобы получить истинное возвращаемое значение.

0
По вопросам рекламы [email protected]