blob: 7c5bae2205feb00052311aa838c312efb7b00a2a [file] [log] [blame]
Todd Fiala6d6b55d2014-06-30 00:30:53 +00001//===-- ProcessLaunchInfo.cpp -----------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Todd Fiala2850b1b2014-06-30 23:51:35 +000010#include "lldb/Host/Config.h"
11
Todd Fiala6d6b55d2014-06-30 00:30:53 +000012#ifndef LLDB_DISABLE_POSIX
13#include <spawn.h>
14#endif
15
Zachary Turner696b5282014-08-14 16:01:25 +000016#include "lldb/Target/ProcessLaunchInfo.h"
17#include "lldb/Target/FileAction.h"
Todd Fiala6d6b55d2014-06-30 00:30:53 +000018#include "lldb/Target/Target.h"
19
20using namespace lldb;
21using namespace lldb_private;
22
23//----------------------------------------------------------------------------
Todd Fiala6d6b55d2014-06-30 00:30:53 +000024// ProcessLaunchInfo member functions
25//----------------------------------------------------------------------------
26
27ProcessLaunchInfo::ProcessLaunchInfo () :
28 ProcessInfo(),
29 m_working_dir (),
30 m_plugin_name (),
31 m_shell (),
32 m_flags (0),
33 m_file_actions (),
34 m_pty (),
35 m_resume_count (0),
36 m_monitor_callback (NULL),
37 m_monitor_callback_baton (NULL),
38 m_monitor_signals (false),
39 m_hijack_listener_sp ()
40{
41}
42
Zachary Turner696b5282014-08-14 16:01:25 +000043ProcessLaunchInfo::ProcessLaunchInfo (const char *stdin_path,
Todd Fiala6d6b55d2014-06-30 00:30:53 +000044 const char *stdout_path,
45 const char *stderr_path,
46 const char *working_directory,
47 uint32_t launch_flags) :
48 ProcessInfo(),
49 m_working_dir (),
50 m_plugin_name (),
51 m_shell (),
52 m_flags (launch_flags),
53 m_file_actions (),
54 m_pty (),
55 m_resume_count (0),
56 m_monitor_callback (NULL),
57 m_monitor_callback_baton (NULL),
58 m_monitor_signals (false),
59 m_hijack_listener_sp ()
60{
61 if (stdin_path)
62 {
Zachary Turner696b5282014-08-14 16:01:25 +000063 FileAction file_action;
Todd Fiala6d6b55d2014-06-30 00:30:53 +000064 const bool read = true;
65 const bool write = false;
66 if (file_action.Open(STDIN_FILENO, stdin_path, read, write))
67 AppendFileAction (file_action);
68 }
69 if (stdout_path)
70 {
Zachary Turner696b5282014-08-14 16:01:25 +000071 FileAction file_action;
Todd Fiala6d6b55d2014-06-30 00:30:53 +000072 const bool read = false;
73 const bool write = true;
74 if (file_action.Open(STDOUT_FILENO, stdout_path, read, write))
75 AppendFileAction (file_action);
76 }
77 if (stderr_path)
78 {
Zachary Turner696b5282014-08-14 16:01:25 +000079 FileAction file_action;
Todd Fiala6d6b55d2014-06-30 00:30:53 +000080 const bool read = false;
81 const bool write = true;
82 if (file_action.Open(STDERR_FILENO, stderr_path, read, write))
83 AppendFileAction (file_action);
84 }
85 if (working_directory)
86 SetWorkingDirectory(working_directory);
87}
88
89bool
90ProcessLaunchInfo::AppendCloseFileAction (int fd)
91{
92 FileAction file_action;
93 if (file_action.Close (fd))
94 {
95 AppendFileAction (file_action);
96 return true;
97 }
98 return false;
99}
100
101bool
102ProcessLaunchInfo::AppendDuplicateFileAction (int fd, int dup_fd)
103{
104 FileAction file_action;
105 if (file_action.Duplicate (fd, dup_fd))
106 {
107 AppendFileAction (file_action);
108 return true;
109 }
110 return false;
111}
112
113bool
114ProcessLaunchInfo::AppendOpenFileAction (int fd, const char *path, bool read, bool write)
115{
116 FileAction file_action;
117 if (file_action.Open (fd, path, read, write))
118 {
119 AppendFileAction (file_action);
120 return true;
121 }
122 return false;
123}
124
125bool
126ProcessLaunchInfo::AppendSuppressFileAction (int fd, bool read, bool write)
127{
128 FileAction file_action;
129 if (file_action.Open (fd, "/dev/null", read, write))
130 {
131 AppendFileAction (file_action);
132 return true;
133 }
134 return false;
135}
136
Zachary Turner696b5282014-08-14 16:01:25 +0000137const FileAction *
Todd Fiala6d6b55d2014-06-30 00:30:53 +0000138ProcessLaunchInfo::GetFileActionAtIndex (size_t idx) const
139{
140 if (idx < m_file_actions.size())
141 return &m_file_actions[idx];
142 return NULL;
143}
144
Zachary Turner696b5282014-08-14 16:01:25 +0000145const FileAction *
Todd Fiala6d6b55d2014-06-30 00:30:53 +0000146ProcessLaunchInfo::GetFileActionForFD (int fd) const
147{
148 for (size_t idx=0, count=m_file_actions.size(); idx < count; ++idx)
149 {
150 if (m_file_actions[idx].GetFD () == fd)
151 return &m_file_actions[idx];
152 }
153 return NULL;
154}
155
156const char *
157ProcessLaunchInfo::GetWorkingDirectory () const
158{
159 if (m_working_dir.empty())
160 return NULL;
161 return m_working_dir.c_str();
162}
163
164void
165ProcessLaunchInfo::SetWorkingDirectory (const char *working_dir)
166{
167 if (working_dir && working_dir[0])
168 m_working_dir.assign (working_dir);
169 else
170 m_working_dir.clear();
171}
172
173const char *
174ProcessLaunchInfo::GetProcessPluginName () const
175{
176 if (m_plugin_name.empty())
177 return NULL;
178 return m_plugin_name.c_str();
179}
180
181void
182ProcessLaunchInfo::SetProcessPluginName (const char *plugin)
183{
184 if (plugin && plugin[0])
185 m_plugin_name.assign (plugin);
186 else
187 m_plugin_name.clear();
188}
189
190const char *
191ProcessLaunchInfo::GetShell () const
192{
193 if (m_shell.empty())
194 return NULL;
195 return m_shell.c_str();
196}
197
198void
199ProcessLaunchInfo::SetShell (const char * path)
200{
201 if (path && path[0])
202 {
203 m_shell.assign (path);
204 m_flags.Set (lldb::eLaunchFlagLaunchInShell);
205 }
206 else
207 {
208 m_shell.clear();
209 m_flags.Clear (lldb::eLaunchFlagLaunchInShell);
210 }
211}
212
213void
214ProcessLaunchInfo::SetLaunchInSeparateProcessGroup (bool separate)
215{
216 if (separate)
217 m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
218 else
219 m_flags.Clear (lldb::eLaunchFlagLaunchInSeparateProcessGroup);
220
221}
222
223void
224ProcessLaunchInfo::Clear ()
225{
226 ProcessInfo::Clear();
227 m_working_dir.clear();
228 m_plugin_name.clear();
229 m_shell.clear();
230 m_flags.Clear();
231 m_file_actions.clear();
232 m_resume_count = 0;
233 m_hijack_listener_sp.reset();
234}
235
236void
237ProcessLaunchInfo::SetMonitorProcessCallback (Host::MonitorChildProcessCallback callback,
238 void *baton,
239 bool monitor_signals)
240{
241 m_monitor_callback = callback;
242 m_monitor_callback_baton = baton;
243 m_monitor_signals = monitor_signals;
244}
245
246bool
247ProcessLaunchInfo::MonitorProcess () const
248{
249 if (m_monitor_callback && ProcessIDIsValid())
250 {
251 Host::StartMonitoringChildProcess (m_monitor_callback,
252 m_monitor_callback_baton,
253 GetProcessID(),
254 m_monitor_signals);
255 return true;
256 }
257 return false;
258}
259
260void
261ProcessLaunchInfo::SetDetachOnError (bool enable)
262{
263 if (enable)
264 m_flags.Set(lldb::eLaunchFlagDetachOnError);
265 else
266 m_flags.Clear(lldb::eLaunchFlagDetachOnError);
267}
268
269void
270ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty)
271{
272 // If nothing for stdin or stdout or stderr was specified, then check the process for any default
273 // settings that were set with "settings set"
274 if (GetFileActionForFD(STDIN_FILENO) == NULL || GetFileActionForFD(STDOUT_FILENO) == NULL ||
275 GetFileActionForFD(STDERR_FILENO) == NULL)
276 {
277 if (m_flags.Test(eLaunchFlagDisableSTDIO))
278 {
279 AppendSuppressFileAction (STDIN_FILENO , true, false);
280 AppendSuppressFileAction (STDOUT_FILENO, false, true);
281 AppendSuppressFileAction (STDERR_FILENO, false, true);
282 }
283 else
284 {
285 // Check for any values that might have gotten set with any of:
286 // (lldb) settings set target.input-path
287 // (lldb) settings set target.output-path
288 // (lldb) settings set target.error-path
289 FileSpec in_path;
290 FileSpec out_path;
291 FileSpec err_path;
292 if (target)
293 {
294 in_path = target->GetStandardInputPath();
295 out_path = target->GetStandardOutputPath();
296 err_path = target->GetStandardErrorPath();
297 }
298
299 char path[PATH_MAX];
300 if (in_path && in_path.GetPath(path, sizeof(path)))
301 AppendOpenFileAction(STDIN_FILENO, path, true, false);
302
303 if (out_path && out_path.GetPath(path, sizeof(path)))
304 AppendOpenFileAction(STDOUT_FILENO, path, false, true);
305
306 if (err_path && err_path.GetPath(path, sizeof(path)))
307 AppendOpenFileAction(STDERR_FILENO, path, false, true);
308
309 if (default_to_use_pty && (!in_path || !out_path || !err_path)) {
310 if (m_pty.OpenFirstAvailableMaster(O_RDWR| O_NOCTTY, NULL, 0)) {
311 const char *slave_path = m_pty.GetSlaveName(NULL, 0);
312
313 if (!in_path) {
314 AppendOpenFileAction(STDIN_FILENO, slave_path, true, false);
315 }
316
317 if (!out_path) {
318 AppendOpenFileAction(STDOUT_FILENO, slave_path, false, true);
319 }
320
321 if (!err_path) {
322 AppendOpenFileAction(STDERR_FILENO, slave_path, false, true);
323 }
324 }
325 }
326 }
327 }
328}
329
330
331bool
332ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error,
333 bool localhost,
334 bool will_debug,
335 bool first_arg_is_full_shell_command,
336 int32_t num_resumes)
337{
338 error.Clear();
339
340 if (GetFlags().Test (eLaunchFlagLaunchInShell))
341 {
342 const char *shell_executable = GetShell();
343 if (shell_executable)
344 {
345 char shell_resolved_path[PATH_MAX];
346
347 if (localhost)
348 {
349 FileSpec shell_filespec (shell_executable, true);
350
351 if (!shell_filespec.Exists())
352 {
353 // Resolve the path in case we just got "bash", "sh" or "tcsh"
354 if (!shell_filespec.ResolveExecutableLocation ())
355 {
356 error.SetErrorStringWithFormat("invalid shell path '%s'", shell_executable);
357 return false;
358 }
359 }
360 shell_filespec.GetPath (shell_resolved_path, sizeof(shell_resolved_path));
361 shell_executable = shell_resolved_path;
362 }
363
364 const char **argv = GetArguments().GetConstArgumentVector ();
365 if (argv == NULL || argv[0] == NULL)
366 return false;
367 Args shell_arguments;
368 std::string safe_arg;
369 shell_arguments.AppendArgument (shell_executable);
370 shell_arguments.AppendArgument ("-c");
371 StreamString shell_command;
372 if (will_debug)
373 {
374 // Add a modified PATH environment variable in case argv[0]
375 // is a relative path
376 const char *argv0 = argv[0];
377 if (argv0 && (argv0[0] != '/' && argv0[0] != '~'))
378 {
379 // We have a relative path to our executable which may not work if
380 // we just try to run "a.out" (without it being converted to "./a.out")
381 const char *working_dir = GetWorkingDirectory();
382 // Be sure to put quotes around PATH's value in case any paths have spaces...
383 std::string new_path("PATH=\"");
384 const size_t empty_path_len = new_path.size();
385
386 if (working_dir && working_dir[0])
387 {
388 new_path += working_dir;
389 }
390 else
391 {
392 char current_working_dir[PATH_MAX];
393 const char *cwd = getcwd(current_working_dir, sizeof(current_working_dir));
394 if (cwd && cwd[0])
395 new_path += cwd;
396 }
397 const char *curr_path = getenv("PATH");
398 if (curr_path)
399 {
400 if (new_path.size() > empty_path_len)
401 new_path += ':';
402 new_path += curr_path;
403 }
404 new_path += "\" ";
405 shell_command.PutCString(new_path.c_str());
406 }
407
408 shell_command.PutCString ("exec");
409
410 // Only Apple supports /usr/bin/arch being able to specify the architecture
Greg Claytonbc766682014-08-12 21:38:59 +0000411 if (GetArchitecture().IsValid() && // Valid architecture
412 GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple && // Apple only
413 GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h) // Don't do this for x86_64h
Todd Fiala6d6b55d2014-06-30 00:30:53 +0000414 {
415 shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName());
416 // Set the resume count to 2:
417 // 1 - stop in shell
418 // 2 - stop in /usr/bin/arch
419 // 3 - then we will stop in our program
420 SetResumeCount(num_resumes + 1);
421 }
422 else
423 {
424 // Set the resume count to 1:
425 // 1 - stop in shell
426 // 2 - then we will stop in our program
427 SetResumeCount(num_resumes);
428 }
429 }
430
431 if (first_arg_is_full_shell_command)
432 {
433 // There should only be one argument that is the shell command itself to be used as is
434 if (argv[0] && !argv[1])
435 shell_command.Printf("%s", argv[0]);
436 else
437 return false;
438 }
439 else
440 {
441 for (size_t i=0; argv[i] != NULL; ++i)
442 {
443 const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg);
444 shell_command.Printf(" %s", arg);
445 }
446 }
447 shell_arguments.AppendArgument (shell_command.GetString().c_str());
448 m_executable.SetFile(shell_executable, false);
449 m_arguments = shell_arguments;
450 return true;
451 }
452 else
453 {
454 error.SetErrorString ("invalid shell path");
455 }
456 }
457 else
458 {
459 error.SetErrorString ("not launching in shell");
460 }
461 return false;
462}