#include "../include/Exec.hpp" #include "../include/env.hpp" #if OS_FAMILY == OS_FAMILY_POSIX #include #endif using namespace std; const int BUFSIZE = 1024; // TODO: Redirect stderr to namespace sibs { #if OS_FAMILY == OS_FAMILY_POSIX Result exec(const _tinydir_char_t *cmd, bool print) { char buffer[BUFSIZE]; std::string execStdout; FILE *pipe = popen(cmd, "r"); if(!pipe) return Result::Err("popen() failed"); while(!feof(pipe)) { if(fgets(buffer, BUFSIZE, pipe)) { int bytesRead = strlen(buffer); execStdout.append(buffer, bytesRead); if(print) printf("%.*s", bytesRead, buffer); } } int processCloseResult = pclose(pipe); if(WIFEXITED(processCloseResult)) { int returned = WEXITSTATUS(processCloseResult); ExecResult execResult; execResult.execStdout = move(execStdout); execResult.exitCode = returned; return Result::Ok(execResult); } else if(WIFSIGNALED(processCloseResult)) { int signum = WSTOPSIG(processCloseResult); string errMsg = "Exited due to receiving signal "; errMsg += to_string(signum); return Result::Err(errMsg); } else if(WIFSTOPPED(processCloseResult)) { int signum = WSTOPSIG(processCloseResult); string errMsg = "Stopped due to receiving signal "; errMsg += to_string(signum); return Result::Err(errMsg); } else { string errMsg = "exec unexpected error on pclose: "; errMsg += to_string(processCloseResult); return Result::Err(errMsg); } } #else // Currently stdout is read in text mode so \n is replaced with \r\n, should we read in binary mode instead? Result exec(const _tinydir_char_t *cmd, bool print) { FileString cmdNonConst = cmd; std::string execStdout; SECURITY_ATTRIBUTES saAttr; saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.bInheritHandle = TRUE; saAttr.lpSecurityDescriptor = nullptr; HANDLE childReadHandle = nullptr; HANDLE childStdoutHandle = nullptr; if (!CreatePipe(&childReadHandle, &childStdoutHandle, &saAttr, 0)) { string errMsg = "exec unexpected error: "; errMsg += toUtf8(getLastErrorAsString()); return Result::Err(errMsg); } if (!SetHandleInformation(childReadHandle, HANDLE_FLAG_INHERIT, 0)) goto cleanupAndExit; PROCESS_INFORMATION piProcInfo; ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION)); STARTUPINFO siStartInfo; ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); siStartInfo.cb = sizeof(STARTUPINFO); siStartInfo.hStdError = nullptr; siStartInfo.hStdOutput = childStdoutHandle; siStartInfo.hStdInput = nullptr; siStartInfo.dwFlags |= STARTF_USESTDHANDLES; DWORD exitCode; if (!CreateProcessW(nullptr, (LPWSTR)cmdNonConst.data(), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &siStartInfo, &piProcInfo)) goto cleanupAndExit; CloseHandle(childStdoutHandle); childStdoutHandle = nullptr; DWORD bytesRead; CHAR buffer[BUFSIZE]; while (true) { BOOL bSuccess = ReadFile(childReadHandle, buffer, BUFSIZE, &bytesRead, nullptr); if (!bSuccess || bytesRead == 0) break; execStdout.append(buffer, bytesRead); if (print) printf("%.*s", bytesRead, buffer); } WaitForSingleObject(piProcInfo.hProcess, INFINITE); GetExitCodeProcess(piProcInfo.hProcess, &exitCode); CloseHandle(piProcInfo.hProcess); CloseHandle(piProcInfo.hThread); { ExecResult execResult; execResult.execStdout = move(execStdout); execResult.exitCode = exitCode; CloseHandle(childReadHandle); return Result::Ok(execResult); } cleanupAndExit: string errMsg = "exec unexpected error: "; errMsg += toUtf8(getLastErrorAsString()); CloseHandle(childReadHandle); CloseHandle(childStdoutHandle); return Result::Err(errMsg); } #endif Result exec(const FileString &cmd, bool print) { return exec(cmd.c_str(), print); } }