1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 **************************************************************************/
30 #include "vogl_applauncher.h"
31 #include "base/posix/eintr_wrapper.h"
33 extern char **__environ;
38 app_launcher::app_launcher()
40 m_status_code = STATUS_NOTLAUNCHED;
47 memset(m_stdout_pipe, -1, sizeof(m_stdout_pipe));
48 memset(m_stderr_pipe, -1, sizeof(m_stderr_pipe));
51 app_launcher::~app_launcher()
57 set_signal_mask(const sigset_t& sigmask_new)
60 pthread_sigmask(SIG_SETMASK, &sigmask_new, &sigmask_old);
65 app_launcher::run(const char *file, const char *const argv[], const char *const envp[], uint flags)
67 assert(!is_running());
70 m_status_code = STATUS_NOTLAUNCHED;
73 const char *argv_def[2];
86 vogl::vector<char *> new_envp;
87 if (flags & RUN_REMOVE_LD_PRELOAD_ENV)
89 // Remove LD_PRELOAD. Need to do this to keep glxinfo from preloading us again (for example).
90 static const char ld_preload_str[] = "LD_PRELOAD=";
91 static const size_t ld_preload_len = sizeof(ld_preload_str) - 1;
93 for(int i = 0; environ[i]; i++)
95 if (strncmp(ld_preload_str, environ[i], ld_preload_len))
96 new_envp.push_back(environ[i]);
99 new_envp.push_back(NULL);
100 envp = new_envp.get_ptr();
103 // [0]: read end, [1]: write end
104 // Use pipe2 with O_CLOEXEC? O_NONBLK?
105 int ret_stdout = pipe(m_stdout_pipe);
106 int ret_stderr = pipe(m_stderr_pipe);
107 if ((ret_stdout < 0) || (ret_stderr < 0))
110 sigset_t sigset_full;
111 sigfillset(&sigset_full);
112 const sigset_t sigmask_orig = set_signal_mask(sigset_full);
114 // Careful: fork() can return pid of a script...
115 m_child_pid = fork();
117 if (m_child_pid != 0)
119 // Restore sigmask for parent.
120 set_signal_mask(sigmask_orig);
126 m_status_code = STATUS_ERROR;
127 m_status = m_child_pid;
130 else if (m_child_pid == 0)
132 // According to chromium LaunchProcess(), readline processes can block forever
133 // unless we set stdin to /dev/null.
134 int null_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY));
138 int new_stdin = HANDLE_EINTR(dup2(null_fd, STDIN_FILENO));
139 ret_stdout = HANDLE_EINTR(dup2(m_stdout_pipe[1], STDOUT_FILENO));
140 ret_stderr = HANDLE_EINTR(dup2(m_stderr_pipe[1], STDERR_FILENO));
141 if ((new_stdin < 0) || (ret_stdout < 0) || (ret_stderr < 0))
144 close(m_stdout_pipe[0]); // close read pipe
145 close(m_stderr_pipe[0]); // close read pipe
146 m_stdout_pipe[0] = -1;
147 m_stderr_pipe[0] = -1;
150 execvpe(file, (char * const *)argv, (char * const *)envp);
154 close(m_stdout_pipe[1]);
155 close(m_stderr_pipe[1]);
156 m_stdout_pipe[1] = -1;
157 m_stderr_pipe[1] = -1;
159 m_status_code = STATUS_RUNNING;
164 app_launcher::update_status(bool wait)
166 if (m_status_code != STATUS_RUNNING)
169 assert(m_child_pid >= 0);
171 int options = WEXITED; // WSTOPPED: wait for children stopped by delivery of signal.
175 siginfo_t signalinfo;
176 int ret = waitid(P_PID, m_child_pid, &signalinfo, options);
180 m_status_code = STATUS_ERROR;
187 // If we ever care about signals and whatnot:
188 // signalinfo.si_signo; // Signal number.
189 // signalinfo.si_code; // Signal code: CLD_EXITED, CLD_DUMPED, CLD_KILLED.
190 // signalinfo.si_errno; // Error number associated with signal.
191 // signalinfo.si_status; // Exit value.
192 switch(signalinfo.si_code)
197 m_status_code = STATUS_EXITED;
198 m_status = signalinfo.si_status;
201 m_status_code = STATUS_KILLED;
205 m_status_code = STATUS_DUMPED;
215 app_launcher::get_status(STATUS_CODE *code, int *status, bool wait)
219 *code = m_status_code;
224 app_launcher::is_running()
226 update_status(false);
228 return (m_status_code == STATUS_RUNNING);
235 if (m_status_code == STATUS_RUNNING)
237 kill(m_child_pid, SIGTERM);
240 if (m_status_code == STATUS_RUNNING)
242 kill(m_child_pid, SIGKILL);
246 assert(m_status_code != STATUS_RUNNING);
259 for (int i = 0; i < 2; i++)
261 if (m_stdout_pipe[i] >= 0)
263 close(m_stdout_pipe[i]);
264 m_stdout_pipe[i] = -1;
266 if (m_stderr_pipe[i] >= 0)
268 close(m_stderr_pipe[i]);
269 m_stderr_pipe[i] = -1;
277 app_launcher::get_output(int pipe, vogl::dynamic_string &output, size_t max_output)
287 size_t nbytes = max_output;
288 if (nbytes >= sizeof(buf))
289 nbytes = sizeof(buf);
291 // Try to read in nbytes. read returns 0:end of file, -1:error.
292 ssize_t length = HANDLE_EINTR(read(pipe, buf, nbytes));
296 max_output -= length;
297 output.append(buf, (uint)length);
299 if ((length != (ssize_t)nbytes) || (max_output <= 0))
308 app_launcher::get_stdout(vogl::dynamic_string &output, size_t max_output)
310 return get_output(m_stdout_pipe[0], output, max_output);
314 app_launcher::get_stderr(vogl::dynamic_string &output, size_t max_output)
316 return get_output(m_stdout_pipe[1], output, max_output);
320 app_launcher::get_line(char **line_stdout, size_t *line_stdout_size, char **line_stderr, size_t *line_stderr_size)
324 m_stdout = fdopen(m_stdout_pipe[0], "r");
325 setvbuf(m_stdout, NULL, _IONBF, 0);
329 m_stderr = fdopen(m_stderr_pipe[0], "r");
330 setvbuf(m_stderr, NULL, _IONBF, 0);
332 if (!m_stdout || !m_stderr)
338 *line_stdout_size = 0;
339 *line_stderr_size = 0;
343 FD_SET(m_stdout_pipe[0], &set);
344 FD_SET(m_stderr_pipe[0], &set);
346 bool got_line = false;
347 struct timeval timeout;
351 int ret = select(FD_SETSIZE, &set, NULL, NULL, &timeout);
357 if (!feof(m_stdout) && FD_ISSET(m_stdout_pipe[0], &set))
359 getline(line_stdout, line_stdout_size, m_stdout);
362 if (!feof(m_stderr) && FD_ISSET(m_stderr_pipe[0], &set))
364 getline(line_stderr, line_stderr_size, m_stderr);