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