1 //===-- 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 // C Includes
11 // C++ Includes
12 #include <climits>
13 
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Core/Debugger.h"
17 #include "lldb/Core/Log.h"
18 #include "lldb/Host/Config.h"
19 #include "lldb/Host/FileSystem.h"
20 #include "lldb/Host/HostInfo.h"
21 #include "lldb/Target/FileAction.h"
22 #include "lldb/Target/ProcessLaunchInfo.h"
23 #include "lldb/Target/Target.h"
24 
25 #include "llvm/Support/ConvertUTF.h"
26 
27 #if !defined(_WIN32)
28 #include <limits.h>
29 #endif
30 
31 using namespace lldb;
32 using namespace lldb_private;
33 
34 //----------------------------------------------------------------------------
35 // ProcessLaunchInfo member functions
36 //----------------------------------------------------------------------------
37 
38 ProcessLaunchInfo::ProcessLaunchInfo() :
39     ProcessInfo(),
40     m_working_dir(),
41     m_plugin_name(),
42     m_flags(0),
43     m_file_actions(),
44     m_pty(new lldb_utility::PseudoTerminal),
45     m_resume_count(0),
46     m_monitor_callback(nullptr),
47     m_monitor_callback_baton(nullptr),
48     m_monitor_signals(false),
49     m_listener_sp(),
50     m_hijack_listener_sp()
51 {
52 }
53 
54 ProcessLaunchInfo::ProcessLaunchInfo(const FileSpec &stdin_file_spec,
55                                      const FileSpec &stdout_file_spec,
56                                      const FileSpec &stderr_file_spec,
57                                      const FileSpec &working_directory,
58                                      uint32_t launch_flags) :
59     ProcessInfo(),
60     m_working_dir(),
61     m_plugin_name(),
62     m_flags(launch_flags),
63     m_file_actions(),
64     m_pty(new lldb_utility::PseudoTerminal),
65     m_resume_count(0),
66     m_monitor_callback(nullptr),
67     m_monitor_callback_baton(nullptr),
68     m_monitor_signals(false),
69     m_listener_sp (),
70     m_hijack_listener_sp()
71 {
72     if (stdin_file_spec)
73     {
74         FileAction file_action;
75         const bool read = true;
76         const bool write = false;
77         if (file_action.Open(STDIN_FILENO, stdin_file_spec, read, write))
78             AppendFileAction (file_action);
79     }
80     if (stdout_file_spec)
81     {
82         FileAction file_action;
83         const bool read = false;
84         const bool write = true;
85         if (file_action.Open(STDOUT_FILENO, stdout_file_spec, read, write))
86             AppendFileAction (file_action);
87     }
88     if (stderr_file_spec)
89     {
90         FileAction file_action;
91         const bool read = false;
92         const bool write = true;
93         if (file_action.Open(STDERR_FILENO, stderr_file_spec, read, write))
94             AppendFileAction (file_action);
95     }
96     if (working_directory)
97         SetWorkingDirectory(working_directory);
98 }
99 
100 bool
101 ProcessLaunchInfo::AppendCloseFileAction (int fd)
102 {
103     FileAction file_action;
104     if (file_action.Close (fd))
105     {
106         AppendFileAction (file_action);
107         return true;
108     }
109     return false;
110 }
111 
112 bool
113 ProcessLaunchInfo::AppendDuplicateFileAction (int fd, int dup_fd)
114 {
115     FileAction file_action;
116     if (file_action.Duplicate (fd, dup_fd))
117     {
118         AppendFileAction (file_action);
119         return true;
120     }
121     return false;
122 }
123 
124 bool
125 ProcessLaunchInfo::AppendOpenFileAction(int fd, const FileSpec &file_spec,
126                                         bool read, bool write)
127 {
128     FileAction file_action;
129     if (file_action.Open(fd, file_spec, read, write))
130     {
131         AppendFileAction (file_action);
132         return true;
133     }
134     return false;
135 }
136 
137 bool
138 ProcessLaunchInfo::AppendSuppressFileAction (int fd, bool read, bool write)
139 {
140     FileAction file_action;
141     if (file_action.Open(fd, FileSpec{FileSystem::DEV_NULL, false}, read, write))
142     {
143         AppendFileAction (file_action);
144         return true;
145     }
146     return false;
147 }
148 
149 const FileAction *
150 ProcessLaunchInfo::GetFileActionAtIndex(size_t idx) const
151 {
152     if (idx < m_file_actions.size())
153         return &m_file_actions[idx];
154     return nullptr;
155 }
156 
157 const FileAction *
158 ProcessLaunchInfo::GetFileActionForFD(int fd) const
159 {
160     for (size_t idx=0, count=m_file_actions.size(); idx < count; ++idx)
161     {
162         if (m_file_actions[idx].GetFD () == fd)
163             return &m_file_actions[idx];
164     }
165     return nullptr;
166 }
167 
168 const FileSpec &
169 ProcessLaunchInfo::GetWorkingDirectory() const
170 {
171     return m_working_dir;
172 }
173 
174 void
175 ProcessLaunchInfo::SetWorkingDirectory(const FileSpec &working_dir)
176 {
177     m_working_dir = working_dir;
178 }
179 
180 const char *
181 ProcessLaunchInfo::GetProcessPluginName () const
182 {
183     return (m_plugin_name.empty() ? nullptr : m_plugin_name.c_str());
184 }
185 
186 void
187 ProcessLaunchInfo::SetProcessPluginName (const char *plugin)
188 {
189     if (plugin && plugin[0])
190         m_plugin_name.assign (plugin);
191     else
192         m_plugin_name.clear();
193 }
194 
195 const FileSpec &
196 ProcessLaunchInfo::GetShell () const
197 {
198     return m_shell;
199 }
200 
201 void
202 ProcessLaunchInfo::SetShell (const FileSpec &shell)
203 {
204     m_shell = shell;
205     if (m_shell)
206     {
207         m_shell.ResolveExecutableLocation();
208         m_flags.Set (lldb::eLaunchFlagLaunchInShell);
209     }
210     else
211         m_flags.Clear (lldb::eLaunchFlagLaunchInShell);
212 }
213 
214 void
215 ProcessLaunchInfo::SetLaunchInSeparateProcessGroup (bool separate)
216 {
217     if (separate)
218         m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup);
219     else
220         m_flags.Clear (lldb::eLaunchFlagLaunchInSeparateProcessGroup);
221 }
222 
223 void
224 ProcessLaunchInfo::SetShellExpandArguments (bool expand)
225 {
226     if (expand)
227         m_flags.Set(lldb::eLaunchFlagShellExpandArguments);
228     else
229         m_flags.Clear(lldb::eLaunchFlagShellExpandArguments);
230 }
231 
232 void
233 ProcessLaunchInfo::Clear ()
234 {
235     ProcessInfo::Clear();
236     m_working_dir.Clear();
237     m_plugin_name.clear();
238     m_shell.Clear();
239     m_flags.Clear();
240     m_file_actions.clear();
241     m_resume_count = 0;
242     m_listener_sp.reset();
243     m_hijack_listener_sp.reset();
244 }
245 
246 void
247 ProcessLaunchInfo::SetMonitorProcessCallback(const Host::MonitorChildProcessCallback &callback, bool monitor_signals)
248 {
249     m_monitor_callback = callback;
250     m_monitor_signals = monitor_signals;
251 }
252 
253 bool
254 ProcessLaunchInfo::MonitorProcess () const
255 {
256     if (m_monitor_callback && ProcessIDIsValid())
257     {
258         Host::StartMonitoringChildProcess (m_monitor_callback,
259                                            GetProcessID(),
260                                            m_monitor_signals);
261         return true;
262     }
263     return false;
264 }
265 
266 void
267 ProcessLaunchInfo::SetDetachOnError (bool enable)
268 {
269     if (enable)
270         m_flags.Set(lldb::eLaunchFlagDetachOnError);
271     else
272         m_flags.Clear(lldb::eLaunchFlagDetachOnError);
273 }
274 
275 void
276 ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty)
277 {
278     Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
279 
280     // If nothing for stdin or stdout or stderr was specified, then check the process for any default
281     // settings that were set with "settings set"
282     if (GetFileActionForFD(STDIN_FILENO) == nullptr ||
283         GetFileActionForFD(STDOUT_FILENO) == nullptr ||
284         GetFileActionForFD(STDERR_FILENO) == nullptr)
285     {
286         if (log)
287             log->Printf ("ProcessLaunchInfo::%s at least one of stdin/stdout/stderr was not set, evaluating default handling",
288                          __FUNCTION__);
289 
290         if (m_flags.Test(eLaunchFlagLaunchInTTY))
291         {
292             // Do nothing, if we are launching in a remote terminal
293             // no file actions should be done at all.
294             return;
295         }
296 
297         if (m_flags.Test(eLaunchFlagDisableSTDIO))
298         {
299             if (log)
300                 log->Printf ("ProcessLaunchInfo::%s eLaunchFlagDisableSTDIO set, adding suppression action for stdin, stdout and stderr",
301                              __FUNCTION__);
302             AppendSuppressFileAction (STDIN_FILENO , true, false);
303             AppendSuppressFileAction (STDOUT_FILENO, false, true);
304             AppendSuppressFileAction (STDERR_FILENO, false, true);
305         }
306         else
307         {
308             // Check for any values that might have gotten set with any of:
309             // (lldb) settings set target.input-path
310             // (lldb) settings set target.output-path
311             // (lldb) settings set target.error-path
312             FileSpec in_file_spec;
313             FileSpec out_file_spec;
314             FileSpec err_file_spec;
315             if (target)
316             {
317                 // Only override with the target settings if we don't already have
318                 // an action for in, out or error
319                 if (GetFileActionForFD(STDIN_FILENO) == nullptr)
320                     in_file_spec = target->GetStandardInputPath();
321                 if (GetFileActionForFD(STDOUT_FILENO) == nullptr)
322                     out_file_spec = target->GetStandardOutputPath();
323                 if (GetFileActionForFD(STDERR_FILENO) == nullptr)
324                     err_file_spec = target->GetStandardErrorPath();
325             }
326 
327             if (log)
328                 log->Printf ("ProcessLaunchInfo::%s target stdin='%s', target stdout='%s', stderr='%s'",
329                              __FUNCTION__,
330                               in_file_spec ?  in_file_spec.GetCString() : "<null>",
331                              out_file_spec ? out_file_spec.GetCString() : "<null>",
332                              err_file_spec ? err_file_spec.GetCString() : "<null>");
333 
334             if (in_file_spec)
335             {
336                 AppendOpenFileAction(STDIN_FILENO, in_file_spec, true, false);
337                 if (log)
338                     log->Printf ("ProcessLaunchInfo::%s appended stdin open file action for %s",
339                                  __FUNCTION__, in_file_spec.GetCString());
340             }
341 
342             if (out_file_spec)
343             {
344                 AppendOpenFileAction(STDOUT_FILENO, out_file_spec, false, true);
345                 if (log)
346                     log->Printf ("ProcessLaunchInfo::%s appended stdout open file action for %s",
347                                  __FUNCTION__, out_file_spec.GetCString());
348             }
349 
350             if (err_file_spec)
351             {
352                 AppendOpenFileAction(STDERR_FILENO, err_file_spec, false, true);
353                 if (log)
354                     log->Printf ("ProcessLaunchInfo::%s appended stderr open file action for %s",
355                                  __FUNCTION__, err_file_spec.GetCString());
356             }
357 
358             if (default_to_use_pty && (!in_file_spec || !out_file_spec || !err_file_spec))
359             {
360                 if (log)
361                     log->Printf ("ProcessLaunchInfo::%s default_to_use_pty is set, and at least one stdin/stderr/stdout is unset, so generating a pty to use for it",
362                                  __FUNCTION__);
363 
364                 int open_flags = O_RDWR | O_NOCTTY;
365 #if !defined(_MSC_VER)
366                 // We really shouldn't be specifying platform specific flags
367                 // that are intended for a system call in generic code.  But
368                 // this will have to do for now.
369                 open_flags |= O_CLOEXEC;
370 #endif
371                 if (m_pty->OpenFirstAvailableMaster(open_flags, nullptr, 0))
372                 {
373                     const FileSpec slave_file_spec{m_pty->GetSlaveName(nullptr, 0), false};
374 
375                     // Only use the slave tty if we don't have anything specified for
376                     // input and don't have an action for stdin
377                     if (!in_file_spec && GetFileActionForFD(STDIN_FILENO) == nullptr)
378                     {
379                         AppendOpenFileAction(STDIN_FILENO, slave_file_spec, true, false);
380                     }
381 
382                     // Only use the slave tty if we don't have anything specified for
383                     // output and don't have an action for stdout
384                     if (!out_file_spec && GetFileActionForFD(STDOUT_FILENO) == nullptr)
385                     {
386                         AppendOpenFileAction(STDOUT_FILENO, slave_file_spec, false, true);
387                     }
388 
389                     // Only use the slave tty if we don't have anything specified for
390                     // error and don't have an action for stderr
391                     if (!err_file_spec && GetFileActionForFD(STDERR_FILENO) == nullptr)
392                     {
393                         AppendOpenFileAction(STDERR_FILENO, slave_file_spec, false, true);
394                     }
395                 }
396             }
397         }
398     }
399 }
400 
401 bool
402 ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error,
403                                                         bool localhost,
404                                                         bool will_debug,
405                                                         bool first_arg_is_full_shell_command,
406                                                         int32_t num_resumes)
407 {
408     error.Clear();
409 
410     if (GetFlags().Test (eLaunchFlagLaunchInShell))
411     {
412         if (m_shell)
413         {
414             std::string shell_executable = m_shell.GetPath();
415 
416             const char **argv = GetArguments().GetConstArgumentVector ();
417             if (argv == nullptr || argv[0] == nullptr)
418                 return false;
419             Args shell_arguments;
420             std::string safe_arg;
421             shell_arguments.AppendArgument (shell_executable.c_str());
422             const llvm::Triple &triple = GetArchitecture().GetTriple();
423             if (triple.getOS() == llvm::Triple::Win32 && !triple.isWindowsCygwinEnvironment())
424                 shell_arguments.AppendArgument("/C");
425             else
426                 shell_arguments.AppendArgument("-c");
427 
428             StreamString shell_command;
429             if (will_debug)
430             {
431                 // Add a modified PATH environment variable in case argv[0]
432                 // is a relative path.
433                 const char *argv0 = argv[0];
434                 FileSpec arg_spec(argv0, false);
435                 if (arg_spec.IsRelative())
436                 {
437                     // We have a relative path to our executable which may not work if
438                     // we just try to run "a.out" (without it being converted to "./a.out")
439                     FileSpec working_dir = GetWorkingDirectory();
440                     // Be sure to put quotes around PATH's value in case any paths have spaces...
441                     std::string new_path("PATH=\"");
442                     const size_t empty_path_len = new_path.size();
443 
444                     if (working_dir)
445                     {
446                         new_path += working_dir.GetPath();
447                     }
448                     else
449                     {
450                         char current_working_dir[PATH_MAX];
451                         const char *cwd = getcwd(current_working_dir, sizeof(current_working_dir));
452                         if (cwd && cwd[0])
453                             new_path += cwd;
454                     }
455                     std::string curr_path;
456                     if (HostInfo::GetEnvironmentVar("PATH", curr_path))
457                     {
458                         if (new_path.size() > empty_path_len)
459                             new_path += ':';
460                         new_path += curr_path;
461                     }
462                     new_path += "\" ";
463                     shell_command.PutCString(new_path.c_str());
464                 }
465 
466                 if (triple.getOS() != llvm::Triple::Win32 || triple.isWindowsCygwinEnvironment())
467                     shell_command.PutCString("exec");
468 
469                 // Only Apple supports /usr/bin/arch being able to specify the architecture
470                 if (GetArchitecture().IsValid() &&                                          // Valid architecture
471                     GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple &&     // Apple only
472                     GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h)          // Don't do this for x86_64h
473                 {
474                     shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName());
475                     // Set the resume count to 2:
476                     // 1 - stop in shell
477                     // 2 - stop in /usr/bin/arch
478                     // 3 - then we will stop in our program
479                     SetResumeCount(num_resumes + 1);
480                 }
481                 else
482                 {
483                     // Set the resume count to 1:
484                     // 1 - stop in shell
485                     // 2 - then we will stop in our program
486                     SetResumeCount(num_resumes);
487                 }
488             }
489 
490             if (first_arg_is_full_shell_command)
491             {
492                 // There should only be one argument that is the shell command itself to be used as is
493                 if (argv[0] && !argv[1])
494                     shell_command.Printf("%s", argv[0]);
495                 else
496                     return false;
497             }
498             else
499             {
500                 for (size_t i=0; argv[i] != nullptr; ++i)
501                 {
502                     const char *arg = Args::GetShellSafeArgument (m_shell,
503                                                                   argv[i],
504                                                                   safe_arg);
505                     shell_command.Printf(" %s", arg);
506                 }
507             }
508             shell_arguments.AppendArgument (shell_command.GetString().c_str());
509             m_executable = m_shell;
510             m_arguments = shell_arguments;
511             return true;
512         }
513         else
514         {
515             error.SetErrorString ("invalid shell path");
516         }
517     }
518     else
519     {
520         error.SetErrorString ("not launching in shell");
521     }
522     return false;
523 }
524 
525 ListenerSP
526 ProcessLaunchInfo::GetListenerForProcess (Debugger &debugger)
527 {
528     if (m_listener_sp)
529         return m_listener_sp;
530     else
531         return debugger.GetListener();
532 }
533