18#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
23#elif defined(_WIN32) || defined(_WIN64)
39#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
41#elif defined(_WIN32) || defined(_WIN64)
49#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
51#elif defined(_WIN32) || defined(_WIN64)
53 _process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE | SYNCHRONIZE, FALSE, _pid);
54 if (_process ==
nullptr)
55 throwex SystemException(
format(
"Failed to open a process with Id {}!", pid));
61#if defined(_WIN32) || defined(_WIN64)
62 if (_process !=
nullptr)
64 if (!CloseHandle(_process))
65 fatality(SystemException(
format(
"Failed to close a process with Id {}!", pid())));
71 uint64_t pid() const noexcept
73 return (uint64_t)_pid;
76 bool IsRunning()
const
78#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
79 return (kill(_pid, 0) == 0);
80#elif defined(_WIN32) || defined(_WIN64)
81 if (_process ==
nullptr)
82 throwex SystemException(
format(
"Failed to get exit code for a process with Id {}!", pid()));
85 if (!GetExitCodeProcess(_process, &dwExitCode))
86 throwex SystemException(
format(
"Failed to get exit code for a process with Id {}!", pid()));
88 return (dwExitCode == STILL_ACTIVE);
94#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
95 int result = kill(_pid, SIGKILL);
97 throwex SystemException(
format(
"Failed to kill a process with Id {}!", pid()));
98#elif defined(_WIN32) || defined(_WIN64)
99 if (_process ==
nullptr)
100 throwex SystemException(
format(
"Failed to kill a process with Id {}!", pid()));
102 if (!TerminateProcess(_process, 666))
103 throwex SystemException(
format(
"Failed to kill a process with Id {}!", pid()));
109#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
115 result = waitpid(_pid, &status, 0);
117 while ((result < 0) && (errno == EINTR));
120 throwex SystemException(
format(
"Failed to wait for a process with Id {}!", pid()));
122 if (WIFEXITED(status))
123 return WEXITSTATUS(status);
124 else if (WIFSIGNALED(status))
125 throwex SystemException(
format(
"Process with Id {} was killed by signal {}!", pid(), WTERMSIG(status)));
126 else if (WIFSTOPPED(status))
127 throwex SystemException(
format(
"Process with Id {} was stopped by signal {}!", pid(), WSTOPSIG(status)));
128 else if (WIFCONTINUED(status))
129 throwex SystemException(
format(
"Process with Id {} was continued by signal SIGCONT!", pid()));
131 throwex SystemException(
format(
"Process with Id {} has unknown wait status!", pid()));
132#elif defined(_WIN32) || defined(_WIN64)
133 if (_process ==
nullptr)
134 throwex SystemException(
format(
"Failed to wait for a process with Id {}!", pid()));
136 DWORD result = WaitForSingleObject(_process, INFINITE);
137 if (result != WAIT_OBJECT_0)
138 throwex SystemException(
format(
"Failed to wait for a process with Id {}!", pid()));
141 if (!GetExitCodeProcess(_process, &dwExitCode))
142 throwex SystemException(
format(
"Failed to get exit code for a process with Id {}!", pid()));
144 return (
int)dwExitCode;
148 int WaitFor(
const Timespan& timespan)
150#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
156 result = waitpid(_pid, &status, WNOHANG);
160 while ((result == 0) || ((result < 0) && (errno == EINTR)));
163 throwex SystemException(
format(
"Failed to wait for a process with Id {}!", pid()));
165 if (WIFEXITED(status))
166 return WEXITSTATUS(status);
167 else if (WIFSIGNALED(status))
168 throwex SystemException(
format(
"Process with Id {} was killed by signal {}!", pid(), WTERMSIG(status)));
169 else if (WIFSTOPPED(status))
170 throwex SystemException(
format(
"Process with Id {} was stopped by signal {}!", pid(), WSTOPSIG(status)));
171 else if (WIFCONTINUED(status))
172 throwex SystemException(
format(
"Process with Id {} was continued by signal SIGCONT!", pid()));
174 throwex SystemException(
format(
"Process with Id {} has unknown wait status!", pid()));
175#elif defined(_WIN32) || defined(_WIN64)
176 if (_process ==
nullptr)
177 throwex SystemException(
format(
"Failed to wait for a process with Id {}!", pid()));
179 DWORD result = WaitForSingleObject(_process, std::max((DWORD)1, (DWORD)timespan.milliseconds()));
180 if (result != WAIT_OBJECT_0)
181 return std::numeric_limits<int>::min();
184 if (!GetExitCodeProcess(_process, &dwExitCode))
185 throwex SystemException(
format(
"Failed to get exit code for a process with Id {}!", pid()));
187 return (
int)dwExitCode;
191 static uint64_t CurrentProcessId() noexcept
193#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
194 return (uint64_t)getpid();
195#elif defined(_WIN32) || defined(_WIN64)
196 return (uint64_t)GetCurrentProcessId();
200 static uint64_t ParentProcessId() noexcept
202#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
203 return (uint64_t)getppid();
204#elif defined(_WIN32) || defined(_WIN64)
205 DWORD current = GetCurrentProcessId();
208 PROCESSENTRY32 pe = { 0 };
209 pe.dwSize =
sizeof(PROCESSENTRY32);
210 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
211 if (hSnapshot == INVALID_HANDLE_VALUE)
215 auto snapshot =
resource(hSnapshot, [](HANDLE hObject) { CloseHandle(hObject); });
218 if (Process32First(snapshot.get(), &pe))
222 if (pe.th32ProcessID == current)
223 return (uint64_t)pe.th32ParentProcessID;
224 }
while(Process32Next(snapshot.get(), &pe));
231 static void Exit(
int result)
233#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
235#elif defined(_WIN32) || defined(_WIN64)
236 ExitProcess((UINT)result);
240 static Process Execute(
const std::string& command,
const std::vector<std::string>* arguments,
const std::map<std::string, std::string>* envars,
const std::string* directory, Pipe* input, Pipe* output, Pipe* error)
242#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
245 std::vector<char*> argv(1 + ((arguments !=
nullptr) ? arguments->size() : 0) + 1);
246 argv[index++] = (
char*)command.c_str();
247 if (arguments !=
nullptr)
249 for (
const auto& argument : *arguments)
251 argv[index++] = (
char*)argument.c_str();
254 argv[index++] =
nullptr;
257 std::vector<char> environment = PrepareEnvars(envars);
262 throwex SystemException(
"Failed to fork the current process!");
266 if (!environment.empty())
268 char* envar = environment.data();
269 while (*envar !=
'\0')
272 while (*envar !=
'\0')
279 if (directory !=
nullptr)
281 int result = chdir(directory->c_str());
287 if (input !=
nullptr)
288 dup2((
int)(
size_t)input->reader(), STDIN_FILENO);
291 if (output !=
nullptr)
292 dup2((
int)(
size_t)output->writer(), STDOUT_FILENO);
295 if (error !=
nullptr)
296 dup2((
int)(
size_t)error->writer(), STDERR_FILENO);
299 if (input !=
nullptr)
301 if (output !=
nullptr)
303 if (error !=
nullptr)
307 for (
int i = 3; i < sysconf(_SC_OPEN_MAX); ++i)
311 execvp(argv[0], argv.data());
318 if (input !=
nullptr)
320 if (output !=
nullptr)
321 output->CloseWrite();
322 if (error !=
nullptr)
327 result.impl()._pid = pid;
329#elif defined(_WIN32) || defined(_WIN64)
330 BOOL bInheritHandles = FALSE;
333 std::wstring command_line = Encoding::FromUTF8(command);
334 if (arguments !=
nullptr)
336 for (
const auto& argument : *arguments)
338 command_line.append(L
" ");
339 command_line.append(Encoding::FromUTF8(argument));
344 std::vector<wchar_t> environment = PrepareEnvars(envars);
348 GetStartupInfoW(&si);
349 si.cb =
sizeof(STARTUPINFOW);
350 si.lpReserved =
nullptr;
351 si.lpDesktop =
nullptr;
352 si.lpTitle =
nullptr;
353 si.dwFlags = STARTF_FORCEOFFFEEDBACK;
355 si.lpReserved2 =
nullptr;
358 HANDLE hCurrentProcess = GetCurrentProcess();
361 if (input !=
nullptr)
362 DuplicateHandle(hCurrentProcess, input->reader(), hCurrentProcess, &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
365 HANDLE hStdHandle = GetStdHandle(STD_INPUT_HANDLE);
366 if (hStdHandle !=
nullptr)
367 DuplicateHandle(hCurrentProcess, hStdHandle, hCurrentProcess, &si.hStdInput, 0, TRUE, DUPLICATE_SAME_ACCESS);
369 si.hStdInput =
nullptr;
373 if (output !=
nullptr)
374 DuplicateHandle(hCurrentProcess, output->writer(), hCurrentProcess, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
377 HANDLE hStdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
378 if (hStdHandle !=
nullptr)
379 DuplicateHandle(hCurrentProcess, hStdHandle, hCurrentProcess, &si.hStdOutput, 0, TRUE, DUPLICATE_SAME_ACCESS);
381 si.hStdOutput =
nullptr;
385 if (error !=
nullptr)
386 DuplicateHandle(hCurrentProcess, error->writer(), hCurrentProcess, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS);
389 HANDLE hStdHandle = GetStdHandle(STD_ERROR_HANDLE);
390 if (hStdHandle !=
nullptr)
391 DuplicateHandle(hCurrentProcess, hStdHandle, hCurrentProcess, &si.hStdError, 0, TRUE, DUPLICATE_SAME_ACCESS);
393 si.hStdError =
nullptr;
397 if (input !=
nullptr)
399 if (output !=
nullptr)
400 output->CloseWrite();
401 if (error !=
nullptr)
405 if ((si.hStdInput !=
nullptr) || (si.hStdOutput !=
nullptr) || (si.hStdError !=
nullptr))
407 bInheritHandles = TRUE;
408 si.dwFlags |= STARTF_USESTDHANDLES;
412 PROCESS_INFORMATION pi;
413 if (!CreateProcessW(
nullptr, (
wchar_t*)command_line.c_str(),
nullptr,
nullptr, bInheritHandles, CREATE_UNICODE_ENVIRONMENT, environment.empty() ?
nullptr : (LPVOID)environment.data(), (directory == nullptr) ? nullptr : Encoding::FromUTF8(*directory).c_str(), &si, &pi))
414 throwex SystemException(
CppCommon::
format(
"Failed to execute a new process with command '{}'!", command));
417 if (si.hStdInput !=
nullptr)
418 CloseHandle(si.hStdInput);
419 if (si.hStdOutput !=
nullptr)
420 CloseHandle(si.hStdOutput);
421 if (si.hStdError !=
nullptr)
422 CloseHandle(si.hStdError);
425 CloseHandle(pi.hThread);
429 result.impl()._pid = pi.dwProcessId;
430 result.impl()._process = pi.hProcess;
435#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
436 static std::vector<char> PrepareEnvars(
const std::map<std::string, std::string>* envars)
437#elif defined(_WIN32) || defined(_WIN64)
438 static std::vector<wchar_t> PrepareEnvars(
const std::map<std::string, std::string>* envars)
441#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
442 std::vector<char> result;
443#elif defined(_WIN32) || defined(_WIN64)
444 std::vector<wchar_t> result;
447 if (envars ==
nullptr)
450 for (
const auto& envar : *envars)
452#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
453 std::string key = envar.first;
454 std::string value = envar.second;
455#elif defined(_WIN32) || defined(_WIN64)
456 std::wstring key = Encoding::FromUTF8(envar.first);
457 std::wstring value = Encoding::FromUTF8(envar.second);
459 result.insert(result.end(), key.begin(), key.end());
460 result.insert(result.end(),
'=');
461 result.insert(result.end(), value.begin(), value.end());
462 result.insert(result.end(),
'\0');
464 result.insert(result.end(),
'\0');
470#if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
472#elif defined(_WIN32) || defined(_WIN64)
483 [[maybe_unused]] ValidateAlignedStorage<
sizeof(Impl),
alignof(Impl), StorageSize, StorageAlign> _;
484 static_assert((StorageSize >=
sizeof(Impl)),
"Process::StorageSize must be increased!");
485 static_assert(((StorageAlign %
alignof(Impl)) == 0),
"Process::StorageAlign must be adjusted!");
488 new(&_storage)Impl();
491Process::Process(uint64_t
id)
494 [[maybe_unused]] ValidateAlignedStorage<
sizeof(Impl),
alignof(Impl), StorageSize, StorageAlign> _;
495 static_assert((StorageSize >=
sizeof(Impl)),
"Process::StorageSize must be increased!");
496 static_assert(((StorageAlign %
alignof(Impl)) == 0),
"Process::StorageAlign must be adjusted!");
499 new(&_storage)Impl(
id);
506 static_assert((StorageSize >=
sizeof(Impl)),
"Process::StorageSize must be increased!");
507 static_assert(((StorageAlign %
alignof(Impl)) == 0),
"Process::StorageAlign must be adjusted!");
510 new(&_storage)Impl(process.
pid());
521 reinterpret_cast<Impl*
>(&_storage)->~Impl();
549Process Process::Execute(
const std::string& command,
const std::vector<std::string>* arguments,
const std::map<std::string, std::string>* envars,
const std::string* directory,
Pipe* input,
Pipe* output,
Pipe* error)
551 return Impl::Execute(command, arguments, envars, directory, input, output, error);
557 swap(_storage, process._storage);
friend void swap(Process &process1, Process &process2) noexcept
static Process Execute(const std::string &command, const std::vector< std::string > *arguments=nullptr, const std::map< std::string, std::string > *envars=nullptr, const std::string *directory=nullptr, Pipe *input=nullptr, Pipe *output=nullptr, Pipe *error=nullptr)
Execute a new process.
void Kill()
Kill the process.
static uint64_t ParentProcessId() noexcept
Get the parent process Id.
int Wait()
Wait the process to exit.
bool IsRunning() const
Is the process is running?
static uint64_t CurrentProcessId() noexcept
Get the current process Id.
void swap(Process &process) noexcept
Swap two instances.
Process & operator=(const Process &process)
static void Exit(int result)
Exit the current process.
uint64_t pid() const noexcept
Get the process Id.
int WaitFor(const Timespan ×pan)
Wait the process to exit for the given timespan.
Aligned storage validator.
Encoding utilities definition.
#define throwex
Throw extended exception macro.
Fatal abort execution definition.
#define fatality(...)
Fatal abort execution extended macro.
C++ Common project definitions.
std::string format(fmt::format_string< T... > pattern, T &&... args)
Format string.
auto resource(T handle, TCleaner cleaner)
Resource smart cleaner pattern.
void swap(FileCache &cache1, FileCache &cache2) noexcept
Resource smart cleaner pattern definition.
Aligned storage validator definition.