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;
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;
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();
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;
334 if (arguments !=
nullptr)
336 for (
const auto& argument : *arguments)
338 command_line.append(L
" ");
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))
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)
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();
491 Process::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();
549 Process 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);
static std::wstring FromUTF8(std::string_view str)
Convert UTF-8 encoded string to system wide-string.
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.
static void Yield() noexcept
Yield to other threads.
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.