xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/metal/process.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2023 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // process.cpp:
7 //    Process manages a child process. This is largely copied from chrome.
8 //
9 
10 #include "libANGLE/renderer/metal/process.h"
11 
12 #include <crt_externs.h>
13 #include <fcntl.h>
14 #include <mach/mach.h>
15 #include <os/availability.h>
16 #include <spawn.h>
17 #include <string.h>
18 #include <sys/resource.h>
19 #include <sys/sysctl.h>
20 #include <sys/time.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 
24 #include "common/base/anglebase/logging.h"
25 #include "common/debug.h"
26 
27 namespace rx
28 {
29 namespace mtl
30 {
31 namespace
32 {
33 
34 // This code is copied from chrome's process launching code:
35 // (base/process/launch_mac.cc).
36 
37 typedef pid_t ProcessId;
38 constexpr ProcessId kNullProcessId = 0;
39 
40 // DPSXCHECK is a Debug Posix Spawn Check macro. The posix_spawn* family of
41 // functions return an errno value, as opposed to setting errno directly. This
42 // macro emulates a DPCHECK().
43 #define DPSXCHECK(expr)  \
44     do                   \
45     {                    \
46         int rv = (expr); \
47         DCHECK(rv == 0); \
48     } while (0)
49 
50 //         DCHECK(rv == 0) << #expr << ": -" << rv << " " << strerror(rv);
51 
52 class PosixSpawnAttr
53 {
54   public:
PosixSpawnAttr()55     PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_init(&attr_)); }
56 
~PosixSpawnAttr()57     ~PosixSpawnAttr() { DPSXCHECK(posix_spawnattr_destroy(&attr_)); }
58 
get()59     posix_spawnattr_t *get() { return &attr_; }
60 
61   private:
62     posix_spawnattr_t attr_;
63 };
64 
65 class PosixSpawnFileActions
66 {
67   public:
PosixSpawnFileActions()68     PosixSpawnFileActions() { DPSXCHECK(posix_spawn_file_actions_init(&file_actions_)); }
69 
70     PosixSpawnFileActions(const PosixSpawnFileActions &)            = delete;
71     PosixSpawnFileActions &operator=(const PosixSpawnFileActions &) = delete;
72 
~PosixSpawnFileActions()73     ~PosixSpawnFileActions() { DPSXCHECK(posix_spawn_file_actions_destroy(&file_actions_)); }
74 
Open(int filedes,const char * path,int mode)75     void Open(int filedes, const char *path, int mode)
76     {
77         DPSXCHECK(posix_spawn_file_actions_addopen(&file_actions_, filedes, path, mode, 0));
78     }
79 
Dup2(int filedes,int newfiledes)80     void Dup2(int filedes, int newfiledes)
81     {
82         DPSXCHECK(posix_spawn_file_actions_adddup2(&file_actions_, filedes, newfiledes));
83     }
84 
Inherit(int filedes)85     void Inherit(int filedes)
86     {
87         DPSXCHECK(posix_spawn_file_actions_addinherit_np(&file_actions_, filedes));
88     }
89 
90 #if TARGET_OS_OSX
Chdir(const char * path)91     void Chdir(const char *path) API_AVAILABLE(macos(10.15))
92     {
93         DPSXCHECK(posix_spawn_file_actions_addchdir_np(&file_actions_, path));
94     }
95 #endif
96 
get() const97     const posix_spawn_file_actions_t *get() const { return &file_actions_; }
98 
99   private:
100     posix_spawn_file_actions_t file_actions_;
101 };
102 
103 // This is a slimmed down version of chrome's LaunchProcess().
LaunchProcess(const std::vector<std::string> & argv)104 ProcessId LaunchProcess(const std::vector<std::string> &argv)
105 {
106     PosixSpawnAttr attr;
107 
108     DPSXCHECK(posix_spawnattr_setflags(attr.get(), POSIX_SPAWN_CLOEXEC_DEFAULT));
109 
110     PosixSpawnFileActions file_actions;
111 
112     // Process file descriptors for the child. By default, LaunchProcess will
113     // open stdin to /dev/null and inherit stdout and stderr.
114     bool inherit_stdout = true, inherit_stderr = true;
115     bool null_stdin = true;
116 
117     if (null_stdin)
118     {
119         file_actions.Open(STDIN_FILENO, "/dev/null", O_RDONLY);
120     }
121     if (inherit_stdout)
122     {
123         file_actions.Inherit(STDOUT_FILENO);
124     }
125     if (inherit_stderr)
126     {
127         file_actions.Inherit(STDERR_FILENO);
128     }
129 
130     std::vector<char *> argv_cstr;
131     argv_cstr.reserve(argv.size() + 1);
132     for (const auto &arg : argv)
133     {
134         argv_cstr.push_back(const_cast<char *>(arg.c_str()));
135     }
136     argv_cstr.push_back(nullptr);
137 
138     const bool clear_environment = false;
139     char *empty_environ          = nullptr;
140     char **new_environ           = clear_environment ? &empty_environ : *_NSGetEnviron();
141 
142     const char *executable_path = argv_cstr[0];
143 
144     pid_t pid;
145     // Use posix_spawnp as some callers expect to have PATH consulted.
146     int rv = posix_spawnp(&pid, executable_path, file_actions.get(), attr.get(), &argv_cstr[0],
147                           new_environ);
148 
149     if (rv != 0)
150     {
151         FATAL() << "posix_spawnp failed";
152         return kNullProcessId;
153     }
154 
155     return pid;
156 }
157 
GetParentProcessId(ProcessId process)158 ProcessId GetParentProcessId(ProcessId process)
159 {
160     struct kinfo_proc info;
161     size_t length = sizeof(struct kinfo_proc);
162     int mib[4]    = {CTL_KERN, KERN_PROC, KERN_PROC_PID, process};
163     if (sysctl(mib, 4, &info, &length, NULL, 0) < 0)
164     {
165         FATAL() << "sysctl failed";
166         return -1;
167     }
168     if (length == 0)
169         return -1;
170     return info.kp_eproc.e_ppid;
171 }
172 
173 #define HANDLE_EINTR(x)                                         \
174     ({                                                          \
175         decltype(x) eintr_wrapper_result;                       \
176         do                                                      \
177         {                                                       \
178             eintr_wrapper_result = (x);                         \
179         } while (eintr_wrapper_result == -1 && errno == EINTR); \
180         eintr_wrapper_result;                                   \
181     })
182 
WaitForExitImpl(ProcessId pid,int & exit_code)183 bool WaitForExitImpl(ProcessId pid, int &exit_code)
184 {
185     const ProcessId our_pid = getpid();
186     ASSERT(pid != our_pid);
187 
188     const bool exited = (GetParentProcessId(pid) < 0);
189     int status;
190     const bool wait_result = HANDLE_EINTR(waitpid(pid, &status, 0)) > 0;
191     if (!wait_result)
192     {
193         return exited;
194     }
195     if (WIFSIGNALED(status))
196     {
197         exit_code = -1;
198         return true;
199     }
200     if (WIFEXITED(status))
201     {
202         exit_code = WEXITSTATUS(status);
203         return true;
204     }
205     return exited;
206 }
207 
208 }  // namespace
209 
Process(const std::vector<std::string> & argv)210 Process::Process(const std::vector<std::string> &argv) : pid_(LaunchProcess(argv)) {}
211 
~Process()212 Process::~Process()
213 {
214     // TODO: figure out if should terminate/wait/whatever.
215 }
216 
WaitForExit(int & exit_code)217 bool Process::WaitForExit(int &exit_code)
218 {
219     ASSERT(pid_ != kNullProcessId);
220     return WaitForExitImpl(pid_, exit_code);
221 }
222 
DidLaunch() const223 bool Process::DidLaunch() const
224 {
225     return pid_ != kNullProcessId;
226 }
227 
228 }  // namespace mtl
229 }  // namespace rx
230