blob: 05d3eb5f9080444a5e4ce02837baa47684735b79 [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
10#include "lldb/Target/ProcessLaunchInfo.h"
11
12#ifndef LLDB_DISABLE_POSIX
13#include <spawn.h>
14#endif
15
16#include "lldb/Target/Target.h"
17
18using namespace lldb;
19using namespace lldb_private;
20
21//----------------------------------------------------------------------------
22// ProcessLaunchInfo::FileAction member functions
23//----------------------------------------------------------------------------
24
25ProcessLaunchInfo::FileAction::FileAction () :
26 m_action (eFileActionNone),
27 m_fd (-1),
28 m_arg (-1),
29 m_path ()
30{
31}
32
33void
34ProcessLaunchInfo::FileAction::Clear()
35{
36 m_action = eFileActionNone;
37 m_fd = -1;
38 m_arg = -1;
39 m_path.clear();
40}
41
42const char *
43ProcessLaunchInfo::FileAction::GetPath () const
44{
45 if (m_path.empty())
46 return NULL;
47 return m_path.c_str();
48}
49
50//----------------------------------------------------------------------------
51// ProcessLaunchInfo member functions
52//----------------------------------------------------------------------------
53
54ProcessLaunchInfo::ProcessLaunchInfo () :
55 ProcessInfo(),
56 m_working_dir (),
57 m_plugin_name (),
58 m_shell (),
59 m_flags (0),
60 m_file_actions (),
61 m_pty (),
62 m_resume_count (0),
63 m_monitor_callback (NULL),
64 m_monitor_callback_baton (NULL),
65 m_monitor_signals (false),
66 m_hijack_listener_sp ()
67{
68}
69
70ProcessLaunchInfo::ProcessLaunchInfo (
71 const char *stdin_path,
72 const char *stdout_path,
73 const char *stderr_path,
74 const char *working_directory,
75 uint32_t launch_flags) :
76 ProcessInfo(),
77 m_working_dir (),
78 m_plugin_name (),
79 m_shell (),
80 m_flags (launch_flags),
81 m_file_actions (),
82 m_pty (),
83 m_resume_count (0),
84 m_monitor_callback (NULL),
85 m_monitor_callback_baton (NULL),
86 m_monitor_signals (false),
87 m_hijack_listener_sp ()
88{
89 if (stdin_path)
90 {
91 ProcessLaunchInfo::FileAction file_action;
92 const bool read = true;
93 const bool write = false;
94 if (file_action.Open(STDIN_FILENO, stdin_path, read, write))
95 AppendFileAction (file_action);
96 }
97 if (stdout_path)
98 {
99 ProcessLaunchInfo::FileAction file_action;
100 const bool read = false;
101 const bool write = true;
102 if (file_action.Open(STDOUT_FILENO, stdout_path, read, write))
103 AppendFileAction (file_action);
104 }
105 if (stderr_path)
106 {
107 ProcessLaunchInfo::FileAction file_action;
108 const bool read = false;
109 const bool write = true;
110 if (file_action.Open(STDERR_FILENO, stderr_path, read, write))
111 AppendFileAction (file_action);
112 }
113 if (working_directory)
114 SetWorkingDirectory(working_directory);
115}
116
117bool
118ProcessLaunchInfo::AppendCloseFileAction (int fd)
119{
120 FileAction file_action;
121 if (file_action.Close (fd))
122 {
123 AppendFileAction (file_action);
124 return true;
125 }
126 return false;
127}
128
129bool
130ProcessLaunchInfo::AppendDuplicateFileAction (int fd, int dup_fd)
131{
132 FileAction file_action;
133 if (file_action.Duplicate (fd, dup_fd))
134 {
135 AppendFileAction (file_action);
136 return true;
137 }
138 return false;
139}
140
141bool
142ProcessLaunchInfo::AppendOpenFileAction (int fd, const char *path, bool read, bool write)
143{
144 FileAction file_action;
145 if (file_action.Open (fd, path, read, write))
146 {
147 AppendFileAction (file_action);
148 return true;
149 }
150 return false;
151}
152
153bool
154ProcessLaunchInfo::AppendSuppressFileAction (int fd, bool read, bool write)
155{
156 FileAction file_action;
157 if (file_action.Open (fd, "/dev/null", read, write))
158 {
159 AppendFileAction (file_action);
160 return true;
161 }
162 return false;
163}
164
165const ProcessLaunchInfo::FileAction *
166ProcessLaunchInfo::GetFileActionAtIndex (size_t idx) const
167{
168 if (idx < m_file_actions.size())
169 return &m_file_actions[idx];
170 return NULL;
171}
172
173const ProcessLaunchInfo::FileAction *
174ProcessLaunchInfo::GetFileActionForFD (int fd) const
175{
176 for (size_t idx=0, count=m_file_actions.size(); idx < count; ++idx)
177 {
178 if (m_file_actions[idx].GetFD () == fd)
179 return &m_file_actions[idx];
180 }
181 return NULL;
182}
183
184const char *
185ProcessLaunchInfo::GetWorkingDirectory () const
186{
187 if (m_working_dir.empty())
188 return NULL;
189 return m_working_dir.c_str();
190}
191
192void
193ProcessLaunchInfo::SetWorkingDirectory (const char *working_dir)
194{
195 if (working_dir && working_dir[0])
196 m_working_dir.assign (working_dir);
197 else
198 m_working_dir.clear();
199}
200
201const char *
202ProcessLaunchInfo::GetProcessPluginName () const
203{
204 if (m_plugin_name.empty())
205 return NULL;
206 return m_plugin_name.c_str();
207}
208
209void
210ProcessLaunchInfo::SetProcessPluginName (const char *plugin)
211{
212 if (plugin && plugin[0])
213 m_plugin_name.assign (plugin);
214 else
215 m_plugin_name.clear();
216}
217
218const char *
219ProcessLaunchInfo::GetShell () const
220{
221 if (m_shell.empty())
222 return NULL;
223 return m_shell.c_str();
224}
225
226void
227ProcessLaunchInfo::SetShell (const char * path)
228{
229 if (path && path[0])
230 {
231 m_shell.assign (path);
232 m_flags.Set (lldb::eLaunchFlagLaunchInShell);
233 }
234 else
235 {
236 m_shell.clear();
237 m_flags.Clear (lldb::eLaunchFlagLaunchInShell);
238 }
239}
240
241void
242ProcessLaunchInfo::SetLaunchInSeparateProcessGroup (bool separate)
243{
244 if (separate)
245 m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
246 else
247 m_flags.Clear (lldb::eLaunchFlagLaunchInSeparateProcessGroup);
248
249}
250
251void
252ProcessLaunchInfo::Clear ()
253{
254 ProcessInfo::Clear();
255 m_working_dir.clear();
256 m_plugin_name.clear();
257 m_shell.clear();
258 m_flags.Clear();
259 m_file_actions.clear();
260 m_resume_count = 0;
261 m_hijack_listener_sp.reset();
262}
263
264void
265ProcessLaunchInfo::SetMonitorProcessCallback (Host::MonitorChildProcessCallback callback,
266 void *baton,
267 bool monitor_signals)
268{
269 m_monitor_callback = callback;
270 m_monitor_callback_baton = baton;
271 m_monitor_signals = monitor_signals;
272}
273
274bool
275ProcessLaunchInfo::MonitorProcess () const
276{
277 if (m_monitor_callback && ProcessIDIsValid())
278 {
279 Host::StartMonitoringChildProcess (m_monitor_callback,
280 m_monitor_callback_baton,
281 GetProcessID(),
282 m_monitor_signals);
283 return true;
284 }
285 return false;
286}
287
288void
289ProcessLaunchInfo::SetDetachOnError (bool enable)
290{
291 if (enable)
292 m_flags.Set(lldb::eLaunchFlagDetachOnError);
293 else
294 m_flags.Clear(lldb::eLaunchFlagDetachOnError);
295}
296
297void
298ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty)
299{
300 // If nothing for stdin or stdout or stderr was specified, then check the process for any default
301 // settings that were set with "settings set"
302 if (GetFileActionForFD(STDIN_FILENO) == NULL || GetFileActionForFD(STDOUT_FILENO) == NULL ||
303 GetFileActionForFD(STDERR_FILENO) == NULL)
304 {
305 if (m_flags.Test(eLaunchFlagDisableSTDIO))
306 {
307 AppendSuppressFileAction (STDIN_FILENO , true, false);
308 AppendSuppressFileAction (STDOUT_FILENO, false, true);
309 AppendSuppressFileAction (STDERR_FILENO, false, true);
310 }
311 else
312 {
313 // Check for any values that might have gotten set with any of:
314 // (lldb) settings set target.input-path
315 // (lldb) settings set target.output-path
316 // (lldb) settings set target.error-path
317 FileSpec in_path;
318 FileSpec out_path;
319 FileSpec err_path;
320 if (target)
321 {
322 in_path = target->GetStandardInputPath();
323 out_path = target->GetStandardOutputPath();
324 err_path = target->GetStandardErrorPath();
325 }
326
327 char path[PATH_MAX];
328 if (in_path && in_path.GetPath(path, sizeof(path)))
329 AppendOpenFileAction(STDIN_FILENO, path, true, false);
330
331 if (out_path && out_path.GetPath(path, sizeof(path)))
332 AppendOpenFileAction(STDOUT_FILENO, path, false, true);
333
334 if (err_path && err_path.GetPath(path, sizeof(path)))
335 AppendOpenFileAction(STDERR_FILENO, path, false, true);
336
337 if (default_to_use_pty && (!in_path || !out_path || !err_path)) {
338 if (m_pty.OpenFirstAvailableMaster(O_RDWR| O_NOCTTY, NULL, 0)) {
339 const char *slave_path = m_pty.GetSlaveName(NULL, 0);
340
341 if (!in_path) {
342 AppendOpenFileAction(STDIN_FILENO, slave_path, true, false);
343 }
344
345 if (!out_path) {
346 AppendOpenFileAction(STDOUT_FILENO, slave_path, false, true);
347 }
348
349 if (!err_path) {
350 AppendOpenFileAction(STDERR_FILENO, slave_path, false, true);
351 }
352 }
353 }
354 }
355 }
356}
357
358
359bool
360ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error,
361 bool localhost,
362 bool will_debug,
363 bool first_arg_is_full_shell_command,
364 int32_t num_resumes)
365{
366 error.Clear();
367
368 if (GetFlags().Test (eLaunchFlagLaunchInShell))
369 {
370 const char *shell_executable = GetShell();
371 if (shell_executable)
372 {
373 char shell_resolved_path[PATH_MAX];
374
375 if (localhost)
376 {
377 FileSpec shell_filespec (shell_executable, true);
378
379 if (!shell_filespec.Exists())
380 {
381 // Resolve the path in case we just got "bash", "sh" or "tcsh"
382 if (!shell_filespec.ResolveExecutableLocation ())
383 {
384 error.SetErrorStringWithFormat("invalid shell path '%s'", shell_executable);
385 return false;
386 }
387 }
388 shell_filespec.GetPath (shell_resolved_path, sizeof(shell_resolved_path));
389 shell_executable = shell_resolved_path;
390 }
391
392 const char **argv = GetArguments().GetConstArgumentVector ();
393 if (argv == NULL || argv[0] == NULL)
394 return false;
395 Args shell_arguments;
396 std::string safe_arg;
397 shell_arguments.AppendArgument (shell_executable);
398 shell_arguments.AppendArgument ("-c");
399 StreamString shell_command;
400 if (will_debug)
401 {
402 // Add a modified PATH environment variable in case argv[0]
403 // is a relative path
404 const char *argv0 = argv[0];
405 if (argv0 && (argv0[0] != '/' && argv0[0] != '~'))
406 {
407 // We have a relative path to our executable which may not work if
408 // we just try to run "a.out" (without it being converted to "./a.out")
409 const char *working_dir = GetWorkingDirectory();
410 // Be sure to put quotes around PATH's value in case any paths have spaces...
411 std::string new_path("PATH=\"");
412 const size_t empty_path_len = new_path.size();
413
414 if (working_dir && working_dir[0])
415 {
416 new_path += working_dir;
417 }
418 else
419 {
420 char current_working_dir[PATH_MAX];
421 const char *cwd = getcwd(current_working_dir, sizeof(current_working_dir));
422 if (cwd && cwd[0])
423 new_path += cwd;
424 }
425 const char *curr_path = getenv("PATH");
426 if (curr_path)
427 {
428 if (new_path.size() > empty_path_len)
429 new_path += ':';
430 new_path += curr_path;
431 }
432 new_path += "\" ";
433 shell_command.PutCString(new_path.c_str());
434 }
435
436 shell_command.PutCString ("exec");
437
438 // Only Apple supports /usr/bin/arch being able to specify the architecture
439 if (GetArchitecture().IsValid())
440 {
441 shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName());
442 // Set the resume count to 2:
443 // 1 - stop in shell
444 // 2 - stop in /usr/bin/arch
445 // 3 - then we will stop in our program
446 SetResumeCount(num_resumes + 1);
447 }
448 else
449 {
450 // Set the resume count to 1:
451 // 1 - stop in shell
452 // 2 - then we will stop in our program
453 SetResumeCount(num_resumes);
454 }
455 }
456
457 if (first_arg_is_full_shell_command)
458 {
459 // There should only be one argument that is the shell command itself to be used as is
460 if (argv[0] && !argv[1])
461 shell_command.Printf("%s", argv[0]);
462 else
463 return false;
464 }
465 else
466 {
467 for (size_t i=0; argv[i] != NULL; ++i)
468 {
469 const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg);
470 shell_command.Printf(" %s", arg);
471 }
472 }
473 shell_arguments.AppendArgument (shell_command.GetString().c_str());
474 m_executable.SetFile(shell_executable, false);
475 m_arguments = shell_arguments;
476 return true;
477 }
478 else
479 {
480 error.SetErrorString ("invalid shell path");
481 }
482 }
483 else
484 {
485 error.SetErrorString ("not launching in shell");
486 }
487 return false;
488}
489
490
491bool
492ProcessLaunchInfo::FileAction::Open (int fd, const char *path, bool read, bool write)
493{
494 if ((read || write) && fd >= 0 && path && path[0])
495 {
496 m_action = eFileActionOpen;
497 m_fd = fd;
498 if (read && write)
499 m_arg = O_NOCTTY | O_CREAT | O_RDWR;
500 else if (read)
501 m_arg = O_NOCTTY | O_RDONLY;
502 else
503 m_arg = O_NOCTTY | O_CREAT | O_WRONLY;
504 m_path.assign (path);
505 return true;
506 }
507 else
508 {
509 Clear();
510 }
511 return false;
512}
513
514bool
515ProcessLaunchInfo::FileAction::Close (int fd)
516{
517 Clear();
518 if (fd >= 0)
519 {
520 m_action = eFileActionClose;
521 m_fd = fd;
522 }
523 return m_fd >= 0;
524}
525
526
527bool
528ProcessLaunchInfo::FileAction::Duplicate (int fd, int dup_fd)
529{
530 Clear();
531 if (fd >= 0 && dup_fd >= 0)
532 {
533 m_action = eFileActionDuplicate;
534 m_fd = fd;
535 m_arg = dup_fd;
536 }
537 return m_fd >= 0;
538}
539
540
541
542#ifndef LLDB_DISABLE_POSIX
543bool
544ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (void *_file_actions,
545 const FileAction *info,
546 Log *log,
547 Error& error)
548{
549 if (info == NULL)
550 return false;
551
552 posix_spawn_file_actions_t *file_actions = reinterpret_cast<posix_spawn_file_actions_t *>(_file_actions);
553
554 switch (info->m_action)
555 {
556 case eFileActionNone:
557 error.Clear();
558 break;
559
560 case eFileActionClose:
561 if (info->m_fd == -1)
562 error.SetErrorString ("invalid fd for posix_spawn_file_actions_addclose(...)");
563 else
564 {
565 error.SetError (::posix_spawn_file_actions_addclose (file_actions, info->m_fd),
566 eErrorTypePOSIX);
567 if (log && (error.Fail() || log))
568 error.PutToLog(log, "posix_spawn_file_actions_addclose (action=%p, fd=%i)",
569 static_cast<void*>(file_actions), info->m_fd);
570 }
571 break;
572
573 case eFileActionDuplicate:
574 if (info->m_fd == -1)
575 error.SetErrorString ("invalid fd for posix_spawn_file_actions_adddup2(...)");
576 else if (info->m_arg == -1)
577 error.SetErrorString ("invalid duplicate fd for posix_spawn_file_actions_adddup2(...)");
578 else
579 {
580 error.SetError (::posix_spawn_file_actions_adddup2 (file_actions, info->m_fd, info->m_arg),
581 eErrorTypePOSIX);
582 if (log && (error.Fail() || log))
583 error.PutToLog(log, "posix_spawn_file_actions_adddup2 (action=%p, fd=%i, dup_fd=%i)",
584 static_cast<void*>(file_actions), info->m_fd,
585 info->m_arg);
586 }
587 break;
588
589 case eFileActionOpen:
590 if (info->m_fd == -1)
591 error.SetErrorString ("invalid fd in posix_spawn_file_actions_addopen(...)");
592 else
593 {
594 int oflag = info->m_arg;
595
596 mode_t mode = 0;
597
598 if (oflag & O_CREAT)
599 mode = 0640;
600
601 error.SetError (::posix_spawn_file_actions_addopen (file_actions,
602 info->m_fd,
603 info->m_path.c_str(),
604 oflag,
605 mode),
606 eErrorTypePOSIX);
607 if (error.Fail() || log)
608 error.PutToLog(log,
609 "posix_spawn_file_actions_addopen (action=%p, fd=%i, path='%s', oflag=%i, mode=%i)",
610 static_cast<void*>(file_actions), info->m_fd,
611 info->m_path.c_str(), oflag, mode);
612 }
613 break;
614 }
615 return error.Success();
616}
617#endif