У меня есть вспомогательная функция ниже, используемая для выполнения команды и получения возвращаемого значения в системах posix. Раньше я использовал popen
, но невозможно получить код возврата приложения с popen
, если оно запускается и завершается до того, как popen
/pclose
получит возможность выполнить свою работу.
Следующая вспомогательная функция создает ответвление процесса, использует execvp
для запуска желаемого внешнего процесса, а затем родительская функция использует waitpid
для получения кода возврата. Я вижу странные случаи, когда он отказывается работать.
При вызове с wait
= true
waitpid
должен возвращать код выхода приложения, несмотря ни на что. Однако я вижу вывод stdout
, который указывает, что код возврата должен быть ненулевым, хотя код возврата равен нулю. Тестирование внешнего процесса в обычной оболочке, затем echo
ing $?
возвращает ненулевое значение, так что это не проблема, когда внешний процесс не возвращает правильный код. Если это поможет, то внешний процесс запущен 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, хотя должно возвращать что-то другое.
dup2(pipefd[STDOUT_FILENO], STDERR_FILENO);
потерпит неудачу с [EBADF], иwaitpid()
, скорее всего, тоже не удастся (для чего__WALL
?). Кроме того, вы должны прочитать вывод перед ожиданием процесса, чтобы избежать взаимоблокировки, если вывод превышает буферизацию в канале. Кроме того, некоторые проверкиwait
являются избыточными. - person jilles   schedule 01.11.2012std::vector
непрерывны в памяти, неверно. Вместо этого разработчики стандарта C++ решили гарантировать, чтоpush_back
не изменит адрес существующих элементов. Вместо этого я предлагаю создать массивchar *
, но могут быть и другие приемы STL. - person jilles   schedule 01.11.2012vector
, не так ли? - person Mahmoud Al-Qudsi   schedule 01.11.2012