1#include "process.h"
2
3#include "dbg.h"
4#include "str.h"
5#include "windows.h"
6
7#if defined(CONF_FAMILY_UNIX)
8#include <sys/wait.h> // waitpid
9#include <unistd.h> // _exit, fork, getpid
10
11#include <csignal> // kill
12#include <cstdlib> // malloc, free
13#elif defined(CONF_FAMILY_WINDOWS)
14#include "mem.h"
15
16#include <objbase.h> // required for shellapi.h
17#include <process.h> // _getpid
18#include <shellapi.h> // ShellExecuteExW
19#include <windows.h> // CloseHandle, GetExitCodeProcess, TerminateProcess
20
21#include <cfenv> // fesetenv
22#include <cstring> // std::wstring
23#else
24#error NOT IMPLEMENTED
25#endif
26
27int process_id()
28{
29#if defined(CONF_FAMILY_WINDOWS)
30 return _getpid();
31#else
32 return getpid();
33#endif
34}
35
36#if !defined(CONF_PLATFORM_ANDROID)
37PROCESS process_execute(const char *file, EShellExecuteWindowState window_state, const char **arguments, const size_t num_arguments)
38{
39 dbg_assert((arguments == nullptr) == (num_arguments == 0), "Invalid number of arguments");
40#if defined(CONF_FAMILY_WINDOWS)
41 dbg_assert(str_endswith_nocase(file, ".bat") == nullptr && str_endswith_nocase(file, ".cmd") == nullptr, "Running batch files not allowed");
42 dbg_assert(str_endswith(file, ".exe") != nullptr || num_arguments == 0, "Arguments only allowed with .exe files");
43
44 const std::wstring wide_file = windows_utf8_to_wide(file);
45 std::wstring wide_arguments = windows_args_to_wide(arguments, num_arguments);
46
47 SHELLEXECUTEINFOW info;
48 mem_zero(&info, sizeof(SHELLEXECUTEINFOW));
49 info.cbSize = sizeof(SHELLEXECUTEINFOW);
50 info.lpVerb = L"open";
51 info.lpFile = wide_file.c_str();
52 info.lpParameters = num_arguments > 0 ? wide_arguments.c_str() : nullptr;
53 switch(window_state)
54 {
55 case EShellExecuteWindowState::FOREGROUND:
56 info.nShow = SW_SHOW;
57 break;
58 case EShellExecuteWindowState::BACKGROUND:
59 info.nShow = SW_SHOWMINNOACTIVE;
60 break;
61 default:
62 dbg_assert_failed("Invalid window_state: %d", static_cast<int>(window_state));
63 }
64 info.fMask = SEE_MASK_NOCLOSEPROCESS;
65 // Save and restore the FPU control word because ShellExecute might change it
66 fenv_t floating_point_environment;
67 int fegetenv_result = fegetenv(&floating_point_environment);
68 ShellExecuteExW(&info);
69 if(fegetenv_result == 0)
70 fesetenv(&floating_point_environment);
71 return info.hProcess;
72#elif defined(CONF_FAMILY_UNIX)
73 char **argv = (char **)malloc(size: (num_arguments + 2) * sizeof(*argv));
74 pid_t pid;
75 argv[0] = (char *)file;
76 for(size_t i = 0; i < num_arguments; ++i)
77 {
78 argv[i + 1] = (char *)arguments[i];
79 }
80 argv[num_arguments + 1] = NULL;
81 pid = fork();
82 if(pid == -1)
83 {
84 free(ptr: argv);
85 return 0;
86 }
87 if(pid == 0)
88 {
89 execvp(file: file, argv: argv);
90 _exit(status: 1);
91 }
92 free(ptr: argv);
93 return pid;
94#endif
95}
96
97int process_kill(PROCESS process)
98{
99#if defined(CONF_FAMILY_WINDOWS)
100 BOOL success = TerminateProcess(process, 0);
101 BOOL is_alive = process_is_alive(process);
102 if(success || !is_alive)
103 {
104 CloseHandle(process);
105 return true;
106 }
107 return false;
108#elif defined(CONF_FAMILY_UNIX)
109 if(!process_is_alive(process))
110 return true;
111 int status;
112 kill(pid: process, SIGTERM);
113 return waitpid(pid: process, stat_loc: &status, options: 0) != -1;
114#endif
115}
116
117bool process_is_alive(PROCESS process)
118{
119 if(process == INVALID_PROCESS)
120 return false;
121#if defined(CONF_FAMILY_WINDOWS)
122 DWORD exit_code;
123 GetExitCodeProcess(process, &exit_code);
124 return exit_code == STILL_ACTIVE;
125#else
126 return waitpid(pid: process, stat_loc: nullptr, WNOHANG) == 0;
127#endif
128}
129
130#endif // !defined(CONF_PLATFORM_ANDROID)
131