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 dbg_assert(num_arguments < 1024, "Too many arguments: %d", (int)num_arguments);
41#if defined(CONF_FAMILY_WINDOWS)
42 dbg_assert(str_endswith_nocase(file, ".bat") == nullptr && str_endswith_nocase(file, ".cmd") == nullptr, "Running batch files not allowed");
43 dbg_assert(str_endswith(file, ".exe") != nullptr || num_arguments == 0, "Arguments only allowed with .exe files");
44
45 const std::wstring wide_file = windows_utf8_to_wide(file);
46 std::wstring wide_arguments = windows_args_to_wide(arguments, num_arguments);
47
48 SHELLEXECUTEINFOW info;
49 mem_zero(&info, sizeof(SHELLEXECUTEINFOW));
50 info.cbSize = sizeof(SHELLEXECUTEINFOW);
51 info.lpVerb = L"open";
52 info.lpFile = wide_file.c_str();
53 info.lpParameters = num_arguments > 0 ? wide_arguments.c_str() : nullptr;
54 switch(window_state)
55 {
56 case EShellExecuteWindowState::FOREGROUND:
57 info.nShow = SW_SHOW;
58 break;
59 case EShellExecuteWindowState::BACKGROUND:
60 info.nShow = SW_SHOWMINNOACTIVE;
61 break;
62 default:
63 dbg_assert_failed("Invalid window_state: %d", static_cast<int>(window_state));
64 }
65 info.fMask = SEE_MASK_NOCLOSEPROCESS;
66 // Save and restore the FPU control word because ShellExecute might change it
67 fenv_t floating_point_environment;
68 int fegetenv_result = fegetenv(&floating_point_environment);
69 ShellExecuteExW(&info);
70 if(fegetenv_result == 0)
71 fesetenv(&floating_point_environment);
72 return info.hProcess;
73#elif defined(CONF_FAMILY_UNIX)
74 char **argv = (char **)malloc(size: (num_arguments + 2) * sizeof(*argv));
75 pid_t pid;
76 argv[0] = (char *)file;
77 for(size_t i = 0; i < num_arguments; ++i)
78 {
79 argv[i + 1] = (char *)arguments[i];
80 }
81 argv[num_arguments + 1] = nullptr;
82 pid = fork();
83 if(pid == -1)
84 {
85 free(ptr: argv);
86 return 0;
87 }
88 if(pid == 0)
89 {
90 execvp(file: file, argv: argv);
91 _exit(status: 1);
92 }
93 free(ptr: argv);
94 return pid;
95#endif
96}
97
98int process_kill(PROCESS process)
99{
100#if defined(CONF_FAMILY_WINDOWS)
101 BOOL success = TerminateProcess(process, 0);
102 BOOL is_alive = process_is_alive(process);
103 if(success || !is_alive)
104 {
105 CloseHandle(process);
106 return true;
107 }
108 return false;
109#elif defined(CONF_FAMILY_UNIX)
110 if(!process_is_alive(process))
111 return true;
112 int status;
113 kill(pid: process, SIGTERM);
114 return waitpid(pid: process, stat_loc: &status, options: 0) != -1;
115#endif
116}
117
118bool process_is_alive(PROCESS process)
119{
120 if(process == INVALID_PROCESS)
121 return false;
122#if defined(CONF_FAMILY_WINDOWS)
123 DWORD exit_code;
124 GetExitCodeProcess(process, &exit_code);
125 return exit_code == STILL_ACTIVE;
126#else
127 return waitpid(pid: process, stat_loc: nullptr, WNOHANG) == 0;
128#endif
129}
130
131#endif // !defined(CONF_PLATFORM_ANDROID)
132