summaryrefslogtreecommitdiff
path: root/source/Host/common
diff options
context:
space:
mode:
Diffstat (limited to 'source/Host/common')
-rw-r--r--source/Host/common/Editline.cpp696
-rw-r--r--source/Host/common/File.cpp128
-rw-r--r--source/Host/common/Host.cpp418
-rw-r--r--source/Host/common/OptionParser.cpp8
-rw-r--r--source/Host/common/SocketAddress.cpp78
5 files changed, 1176 insertions, 152 deletions
diff --git a/source/Host/common/Editline.cpp b/source/Host/common/Editline.cpp
new file mode 100644
index 0000000000000..679aadd54c4f1
--- /dev/null
+++ b/source/Host/common/Editline.cpp
@@ -0,0 +1,696 @@
+//===-- Editline.cpp --------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include "lldb/Host/Editline.h"
+
+#include "lldb/Core/Error.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/StringList.h"
+#include "lldb/Host/Host.h"
+
+#include <limits.h>
+
+using namespace lldb;
+using namespace lldb_private;
+
+static const char k_prompt_escape_char = '\1';
+
+Editline::Editline (const char *prog, // prog can't be NULL
+ const char *prompt, // can be NULL for no prompt
+ FILE *fin,
+ FILE *fout,
+ FILE *ferr) :
+ m_editline (NULL),
+ m_history (NULL),
+ m_history_event (),
+ m_program (),
+ m_prompt (),
+ m_lines_prompt (),
+ m_getc_buffer (),
+ m_getc_mutex (Mutex::eMutexTypeNormal),
+ m_getc_cond (),
+// m_gets_mutex (Mutex::eMutexTypeNormal),
+ m_completion_callback (NULL),
+ m_completion_callback_baton (NULL),
+ m_line_complete_callback (NULL),
+ m_line_complete_callback_baton (NULL),
+ m_lines_command (Command::None),
+ m_lines_curr_line (0),
+ m_lines_max_line (0),
+ m_prompt_with_line_numbers (false),
+ m_getting_line (false),
+ m_got_eof (false),
+ m_interrupted (false)
+{
+ if (prog && prog[0])
+ {
+ m_program = prog;
+ m_editline = ::el_init(prog, fin, fout, ferr);
+ m_history = ::history_init();
+ }
+ else
+ {
+ m_editline = ::el_init("lldb-tmp", fin, fout, ferr);
+ }
+ if (prompt && prompt[0])
+ SetPrompt (prompt);
+
+ //::el_set (m_editline, EL_BIND, "^[[A", NULL); // Print binding for up arrow key
+ //::el_set (m_editline, EL_BIND, "^[[B", NULL); // Print binding for up down key
+
+ assert (m_editline);
+ ::el_set (m_editline, EL_CLIENTDATA, this);
+
+ // only defined for newer versions of editline
+#ifdef EL_PROMPT_ESC
+ ::el_set (m_editline, EL_PROMPT_ESC, GetPromptCallback, k_prompt_escape_char);
+#else
+ // fall back on old prompt setting code
+ ::el_set (m_editline, EL_PROMPT, GetPromptCallback);
+#endif
+ ::el_set (m_editline, EL_EDITOR, "emacs");
+ if (m_history)
+ {
+ ::el_set (m_editline, EL_HIST, history, m_history);
+ }
+ ::el_set (m_editline, EL_ADDFN, "lldb-complete", "Editline completion function", Editline::CallbackComplete);
+ ::el_set (m_editline, EL_ADDFN, "lldb-edit-prev-line", "Editline edit prev line", Editline::CallbackEditPrevLine);
+ ::el_set (m_editline, EL_ADDFN, "lldb-edit-next-line", "Editline edit next line", Editline::CallbackEditNextLine);
+
+ ::el_set (m_editline, EL_BIND, "^r", "em-inc-search-prev", NULL); // Cycle through backwards search, entering string
+ ::el_set (m_editline, EL_BIND, "^w", "ed-delete-prev-word", NULL); // Delete previous word, behave like bash does.
+ ::el_set (m_editline, EL_BIND, "\033[3~", "ed-delete-next-char", NULL); // Fix the delete key.
+ ::el_set (m_editline, EL_BIND, "\t", "lldb-complete", NULL); // Bind TAB to be autocompelte
+
+ // Source $PWD/.editrc then $HOME/.editrc
+ ::el_source (m_editline, NULL);
+
+ if (m_history)
+ {
+ ::history (m_history, &m_history_event, H_SETSIZE, 800);
+ ::history (m_history, &m_history_event, H_SETUNIQUE, 1);
+ }
+
+ // Always read through our callback function so we don't read
+ // stuff we aren't supposed to. This also stops the extra echoing
+ // that can happen when you have more input than editline can handle
+ // at once.
+ SetGetCharCallback(GetCharFromInputFileCallback);
+
+ LoadHistory();
+}
+
+Editline::~Editline()
+{
+ SaveHistory();
+
+ if (m_history)
+ {
+ ::history_end (m_history);
+ m_history = NULL;
+ }
+
+ // Disable edit mode to stop the terminal from flushing all input
+ // during the call to el_end() since we expect to have multiple editline
+ // instances in this program.
+ ::el_set (m_editline, EL_EDITMODE, 0);
+
+ ::el_end(m_editline);
+ m_editline = NULL;
+}
+
+void
+Editline::SetGetCharCallback (GetCharCallbackType callback)
+{
+ ::el_set (m_editline, EL_GETCFN, callback);
+}
+
+FileSpec
+Editline::GetHistoryFile()
+{
+ char history_path[PATH_MAX];
+ ::snprintf (history_path, sizeof(history_path), "~/.%s-history", m_program.c_str());
+ return FileSpec(history_path, true);
+}
+
+bool
+Editline::LoadHistory ()
+{
+ if (m_history)
+ {
+ FileSpec history_file(GetHistoryFile());
+ if (history_file.Exists())
+ ::history (m_history, &m_history_event, H_LOAD, history_file.GetPath().c_str());
+ return true;
+ }
+ return false;
+}
+
+bool
+Editline::SaveHistory ()
+{
+ if (m_history)
+ {
+ std::string history_path = GetHistoryFile().GetPath();
+ ::history (m_history, &m_history_event, H_SAVE, history_path.c_str());
+ return true;
+ }
+ return false;
+}
+
+
+Error
+Editline::PrivateGetLine(std::string &line)
+{
+ Error error;
+ if (m_interrupted)
+ {
+ error.SetErrorString("interrupted");
+ return error;
+ }
+
+ line.clear();
+ if (m_editline != NULL)
+ {
+ int line_len = 0;
+ const char *line_cstr = NULL;
+ // Call el_gets to prompt the user and read the user's input.
+// {
+// // Make sure we know when we are in el_gets() by using a mutex
+// Mutex::Locker locker (m_gets_mutex);
+ line_cstr = ::el_gets (m_editline, &line_len);
+// }
+
+ static int save_errno = (line_len < 0) ? errno : 0;
+
+ if (save_errno != 0)
+ {
+ error.SetError(save_errno, eErrorTypePOSIX);
+ }
+ else if (line_cstr)
+ {
+ // Decrement the length so we don't have newline characters in "line" for when
+ // we assign the cstr into the std::string
+ while (line_len > 0 &&
+ (line_cstr[line_len - 1] == '\n' ||
+ line_cstr[line_len - 1] == '\r'))
+ --line_len;
+
+ if (line_len > 0)
+ {
+ // We didn't strip the newlines, we just adjusted the length, and
+ // we want to add the history item with the newlines
+ if (m_history)
+ ::history (m_history, &m_history_event, H_ENTER, line_cstr);
+
+ // Copy the part of the c string that we want (removing the newline chars)
+ line.assign(line_cstr, line_len);
+ }
+ }
+ }
+ else
+ {
+ error.SetErrorString("the EditLine instance has been deleted");
+ }
+ return error;
+}
+
+
+Error
+Editline::GetLine(std::string &line)
+{
+ Error error;
+ line.clear();
+
+ // Set arrow key bindings for up and down arrows for single line
+ // mode where up and down arrows do prev/next history
+ ::el_set (m_editline, EL_BIND, "^[[A", "ed-prev-history", NULL); // Map up arrow
+ ::el_set (m_editline, EL_BIND, "^[[B", "ed-next-history", NULL); // Map down arrow
+ m_interrupted = false;
+
+ if (!m_got_eof)
+ {
+ if (m_getting_line)
+ {
+ error.SetErrorString("already getting a line");
+ return error;
+ }
+ if (m_lines_curr_line > 0)
+ {
+ error.SetErrorString("already getting lines");
+ return error;
+ }
+ m_getting_line = true;
+ error = PrivateGetLine(line);
+ m_getting_line = false;
+ }
+
+ if (m_got_eof && line.empty())
+ {
+ // Only set the error if we didn't get an error back from PrivateGetLine()
+ if (error.Success())
+ error.SetErrorString("end of file");
+ }
+
+ return error;
+}
+
+size_t
+Editline::Push (const char *bytes, size_t len)
+{
+ if (m_editline)
+ {
+ // Must NULL terminate the string for el_push() so we stick it
+ // into a std::string first
+ ::el_push(m_editline,
+ const_cast<char*>(std::string (bytes, len).c_str()));
+ return len;
+ }
+ return 0;
+}
+
+
+Error
+Editline::GetLines(const std::string &end_line, StringList &lines)
+{
+ Error error;
+ if (m_getting_line)
+ {
+ error.SetErrorString("already getting a line");
+ return error;
+ }
+ if (m_lines_curr_line > 0)
+ {
+ error.SetErrorString("already getting lines");
+ return error;
+ }
+
+ // Set arrow key bindings for up and down arrows for multiple line
+ // mode where up and down arrows do edit prev/next line
+ ::el_set (m_editline, EL_BIND, "^[[A", "lldb-edit-prev-line", NULL); // Map up arrow
+ ::el_set (m_editline, EL_BIND, "^[[B", "lldb-edit-next-line", NULL); // Map down arrow
+ ::el_set (m_editline, EL_BIND, "^b", "ed-prev-history", NULL);
+ ::el_set (m_editline, EL_BIND, "^n", "ed-next-history", NULL);
+ m_interrupted = false;
+
+ LineStatus line_status = LineStatus::Success;
+
+ lines.Clear();
+
+ FILE *out_file = GetOutputFile();
+ FILE *err_file = GetErrorFile();
+ m_lines_curr_line = 1;
+ while (line_status != LineStatus::Done)
+ {
+ const uint32_t line_idx = m_lines_curr_line-1;
+ if (line_idx >= lines.GetSize())
+ lines.SetSize(m_lines_curr_line);
+ m_lines_max_line = lines.GetSize();
+ m_lines_command = Command::None;
+ assert(line_idx < m_lines_max_line);
+ std::string &line = lines[line_idx];
+ error = PrivateGetLine(line);
+ if (error.Fail())
+ {
+ line_status = LineStatus::Error;
+ }
+ else
+ {
+ switch (m_lines_command)
+ {
+ case Command::None:
+ if (m_line_complete_callback)
+ {
+ line_status = m_line_complete_callback (this,
+ lines,
+ line_idx,
+ error,
+ m_line_complete_callback_baton);
+ }
+ else if (line == end_line)
+ {
+ line_status = LineStatus::Done;
+ }
+
+ if (line_status == LineStatus::Success)
+ {
+ ++m_lines_curr_line;
+ // If we already have content for the next line because
+ // we were editing previous lines, then populate the line
+ // with the appropriate contents
+ if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
+ ::el_push (m_editline,
+ const_cast<char*>(lines[line_idx+1].c_str()));
+ }
+ else if (line_status == LineStatus::Error)
+ {
+ // Clear to end of line ("ESC[K"), then print the error,
+ // then go to the next line ("\n") and then move cursor up
+ // two lines ("ESC[2A").
+ fprintf (err_file, "\033[Kerror: %s\n\033[2A", error.AsCString());
+ }
+ break;
+ case Command::EditPrevLine:
+ if (m_lines_curr_line > 1)
+ {
+ //::fprintf (out_file, "\033[1A\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size())); // Make cursor go up a line and clear that line
+ ::fprintf (out_file, "\033[1A\033[1000D\033[2K");
+ if (!lines[line_idx-1].empty())
+ ::el_push (m_editline,
+ const_cast<char*>(lines[line_idx-1].c_str()));
+ --m_lines_curr_line;
+ }
+ break;
+ case Command::EditNextLine:
+ // Allow the down arrow to create a new line
+ ++m_lines_curr_line;
+ //::fprintf (out_file, "\033[1B\033[%uD\033[2K", (uint32_t)(m_lines_prompt.size() + lines[line_idx].size()));
+ ::fprintf (out_file, "\033[1B\033[1000D\033[2K");
+ if (line_idx+1 < lines.GetSize() && !lines[line_idx+1].empty())
+ ::el_push (m_editline,
+ const_cast<char*>(lines[line_idx+1].c_str()));
+ break;
+ }
+ }
+ }
+ m_lines_curr_line = 0;
+ m_lines_command = Command::None;
+
+ // If we have a callback, call it one more time to let the
+ // user know the lines are complete
+ if (m_line_complete_callback)
+ m_line_complete_callback (this,
+ lines,
+ UINT32_MAX,
+ error,
+ m_line_complete_callback_baton);
+
+ return error;
+}
+
+unsigned char
+Editline::HandleCompletion (int ch)
+{
+ if (m_completion_callback == NULL)
+ return CC_ERROR;
+
+ const LineInfo *line_info = ::el_line(m_editline);
+ StringList completions;
+ int page_size = 40;
+
+ const int num_completions = m_completion_callback (line_info->buffer,
+ line_info->cursor,
+ line_info->lastchar,
+ 0, // Don't skip any matches (start at match zero)
+ -1, // Get all the matches
+ completions,
+ m_completion_callback_baton);
+
+ FILE *out_file = GetOutputFile();
+
+// if (num_completions == -1)
+// {
+// ::el_insertstr (m_editline, m_completion_key);
+// return CC_REDISPLAY;
+// }
+// else
+ if (num_completions == -2)
+ {
+ // Replace the entire line with the first string...
+ ::el_deletestr (m_editline, line_info->cursor - line_info->buffer);
+ ::el_insertstr (m_editline, completions.GetStringAtIndex(0));
+ return CC_REDISPLAY;
+ }
+
+ // If we get a longer match display that first.
+ const char *completion_str = completions.GetStringAtIndex(0);
+ if (completion_str != NULL && *completion_str != '\0')
+ {
+ el_insertstr (m_editline, completion_str);
+ return CC_REDISPLAY;
+ }
+
+ if (num_completions > 1)
+ {
+ int num_elements = num_completions + 1;
+ ::fprintf (out_file, "\nAvailable completions:");
+ if (num_completions < page_size)
+ {
+ for (int i = 1; i < num_elements; i++)
+ {
+ completion_str = completions.GetStringAtIndex(i);
+ ::fprintf (out_file, "\n\t%s", completion_str);
+ }
+ ::fprintf (out_file, "\n");
+ }
+ else
+ {
+ int cur_pos = 1;
+ char reply;
+ int got_char;
+ while (cur_pos < num_elements)
+ {
+ int endpoint = cur_pos + page_size;
+ if (endpoint > num_elements)
+ endpoint = num_elements;
+ for (; cur_pos < endpoint; cur_pos++)
+ {
+ completion_str = completions.GetStringAtIndex(cur_pos);
+ ::fprintf (out_file, "\n\t%s", completion_str);
+ }
+
+ if (cur_pos >= num_elements)
+ {
+ ::fprintf (out_file, "\n");
+ break;
+ }
+
+ ::fprintf (out_file, "\nMore (Y/n/a): ");
+ reply = 'n';
+ got_char = el_getc(m_editline, &reply);
+ if (got_char == -1 || reply == 'n')
+ break;
+ if (reply == 'a')
+ page_size = num_elements - cur_pos;
+ }
+ }
+
+ }
+
+ if (num_completions == 0)
+ return CC_REFRESH_BEEP;
+ else
+ return CC_REDISPLAY;
+}
+
+Editline *
+Editline::GetClientData (::EditLine *e)
+{
+ Editline *editline = NULL;
+ if (e && ::el_get(e, EL_CLIENTDATA, &editline) == 0)
+ return editline;
+ return NULL;
+}
+
+FILE *
+Editline::GetInputFile ()
+{
+ return GetFilePointer (m_editline, 0);
+}
+
+FILE *
+Editline::GetOutputFile ()
+{
+ return GetFilePointer (m_editline, 1);
+}
+
+FILE *
+Editline::GetErrorFile ()
+{
+ return GetFilePointer (m_editline, 2);
+}
+
+const char *
+Editline::GetPrompt()
+{
+ if (m_prompt_with_line_numbers && m_lines_curr_line > 0)
+ {
+ StreamString strm;
+ strm.Printf("%3u: ", m_lines_curr_line);
+ m_lines_prompt = std::move(strm.GetString());
+ return m_lines_prompt.c_str();
+ }
+ else
+ {
+ return m_prompt.c_str();
+ }
+}
+
+void
+Editline::SetPrompt (const char *p)
+{
+ if (p && p[0])
+ m_prompt = p;
+ else
+ m_prompt.clear();
+ size_t start_pos = 0;
+ size_t escape_pos;
+ while ((escape_pos = m_prompt.find('\033', start_pos)) != std::string::npos)
+ {
+ m_prompt.insert(escape_pos, 1, k_prompt_escape_char);
+ start_pos += 2;
+ }
+}
+
+FILE *
+Editline::GetFilePointer (::EditLine *e, int fd)
+{
+ FILE *file_ptr = NULL;
+ if (e && ::el_get(e, EL_GETFP, fd, &file_ptr) == 0)
+ return file_ptr;
+ return NULL;
+}
+
+unsigned char
+Editline::CallbackEditPrevLine (::EditLine *e, int ch)
+{
+ Editline *editline = GetClientData (e);
+ if (editline->m_lines_curr_line > 1)
+ {
+ editline->m_lines_command = Command::EditPrevLine;
+ return CC_NEWLINE;
+ }
+ return CC_ERROR;
+}
+unsigned char
+Editline::CallbackEditNextLine (::EditLine *e, int ch)
+{
+ Editline *editline = GetClientData (e);
+ if (editline->m_lines_curr_line < editline->m_lines_max_line)
+ {
+ editline->m_lines_command = Command::EditNextLine;
+ return CC_NEWLINE;
+ }
+ return CC_ERROR;
+}
+
+unsigned char
+Editline::CallbackComplete (::EditLine *e, int ch)
+{
+ Editline *editline = GetClientData (e);
+ if (editline)
+ return editline->HandleCompletion (ch);
+ return CC_ERROR;
+}
+
+const char *
+Editline::GetPromptCallback (::EditLine *e)
+{
+ Editline *editline = GetClientData (e);
+ if (editline)
+ return editline->GetPrompt();
+ return "";
+}
+
+size_t
+Editline::SetInputBuffer (const char *c, size_t len)
+{
+ if (c && len > 0)
+ {
+ Mutex::Locker locker(m_getc_mutex);
+ SetGetCharCallback(GetCharInputBufferCallback);
+ m_getc_buffer.append(c, len);
+ m_getc_cond.Broadcast();
+ }
+ return len;
+}
+
+int
+Editline::GetChar (char *c)
+{
+ Mutex::Locker locker(m_getc_mutex);
+ if (m_getc_buffer.empty())
+ m_getc_cond.Wait(m_getc_mutex);
+ if (m_getc_buffer.empty())
+ return 0;
+ *c = m_getc_buffer[0];
+ m_getc_buffer.erase(0,1);
+ return 1;
+}
+
+int
+Editline::GetCharInputBufferCallback (EditLine *e, char *c)
+{
+ Editline *editline = GetClientData (e);
+ if (editline)
+ return editline->GetChar(c);
+ return 0;
+}
+
+int
+Editline::GetCharFromInputFileCallback (EditLine *e, char *c)
+{
+ Editline *editline = GetClientData (e);
+ if (editline && editline->m_got_eof == false)
+ {
+ char ch = ::fgetc(editline->GetInputFile());
+ if (ch == '\x04')
+ {
+ // Only turn a CTRL+D into a EOF if we receive the
+ // CTRL+D an empty line, otherwise it will forward
+ // delete the character at the cursor
+ const LineInfo *line_info = ::el_line(e);
+ if (line_info != NULL &&
+ line_info->buffer == line_info->cursor &&
+ line_info->cursor == line_info->lastchar)
+ {
+ ch = EOF;
+ }
+ }
+
+ if (ch == EOF)
+ {
+ editline->m_got_eof = true;
+ }
+ else
+ {
+ *c = ch;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void
+Editline::Hide ()
+{
+ FILE *out_file = GetOutputFile();
+ if (out_file)
+ {
+ const LineInfo *line_info = ::el_line(m_editline);
+ if (line_info)
+ ::fprintf (out_file, "\033[%uD\033[K", (uint32_t)(strlen(GetPrompt()) + line_info->cursor - line_info->buffer));
+ }
+}
+
+
+void
+Editline::Refresh()
+{
+ ::el_set (m_editline, EL_REFRESH);
+}
+
+void
+Editline::Interrupt ()
+{
+ m_interrupted = true;
+ if (m_getting_line || m_lines_curr_line > 0)
+ el_insertstr(m_editline, "\n"); // True to force the line to complete itself so we get exit from el_gets()
+}
diff --git a/source/Host/common/File.cpp b/source/Host/common/File.cpp
index bbd11858aaba0..bb0ee39fbc7b2 100644
--- a/source/Host/common/File.cpp
+++ b/source/Host/common/File.cpp
@@ -13,10 +13,13 @@
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
+#include <stdio.h>
#include <sys/stat.h>
#ifdef _WIN32
#include "lldb/Host/windows/windows.h"
+#else
+#include <sys/ioctl.h>
#endif
#include "lldb/Core/DataBufferHeap.h"
@@ -76,8 +79,11 @@ FILE * File::kInvalidStream = NULL;
File::File(const char *path, uint32_t options, uint32_t permissions) :
m_descriptor (kInvalidDescriptor),
m_stream (kInvalidStream),
- m_options (0),
- m_owned (false)
+ m_options (),
+ m_own_stream (false),
+ m_own_descriptor (false),
+ m_is_interactive (eLazyBoolCalculate),
+ m_is_real_terminal (eLazyBoolCalculate)
{
Open (path, options, permissions);
}
@@ -88,7 +94,11 @@ File::File (const FileSpec& filespec,
m_descriptor (kInvalidDescriptor),
m_stream (kInvalidStream),
m_options (0),
- m_owned (false)
+ m_own_stream (false),
+ m_own_descriptor (false),
+ m_is_interactive (eLazyBoolCalculate),
+ m_is_real_terminal (eLazyBoolCalculate)
+
{
if (filespec)
{
@@ -100,7 +110,10 @@ File::File (const File &rhs) :
m_descriptor (kInvalidDescriptor),
m_stream (kInvalidStream),
m_options (0),
- m_owned (false)
+ m_own_stream (false),
+ m_own_descriptor (false),
+ m_is_interactive (eLazyBoolCalculate),
+ m_is_real_terminal (eLazyBoolCalculate)
{
Duplicate (rhs);
}
@@ -141,7 +154,7 @@ File::SetDescriptor (int fd, bool transfer_ownership)
if (IsValid())
Close();
m_descriptor = fd;
- m_owned = transfer_ownership;
+ m_own_descriptor = transfer_ownership;
}
@@ -155,10 +168,31 @@ File::GetStream ()
const char *mode = GetStreamOpenModeFromOptions (m_options);
if (mode)
{
+ if (!m_own_descriptor)
+ {
+ // We must duplicate the file descriptor if we don't own it because
+ // when you call fdopen, the stream will own the fd
+#ifdef _WIN32
+ m_descriptor = ::_dup(GetDescriptor());
+#else
+ m_descriptor = ::fcntl(GetDescriptor(), F_DUPFD);
+#endif
+ m_own_descriptor = true;
+ }
+
do
{
m_stream = ::fdopen (m_descriptor, mode);
} while (m_stream == NULL && errno == EINTR);
+
+ // If we got a stream, then we own the stream and should no
+ // longer own the descriptor because fclose() will close it for us
+
+ if (m_stream)
+ {
+ m_own_stream = true;
+ m_own_descriptor = false;
+ }
}
}
}
@@ -172,7 +206,7 @@ File::SetStream (FILE *fh, bool transfer_ownership)
if (IsValid())
Close();
m_stream = fh;
- m_owned = transfer_ownership;
+ m_own_stream = transfer_ownership;
}
Error
@@ -194,7 +228,7 @@ File::Duplicate (const File &rhs)
else
{
m_options = rhs.m_options;
- m_owned = true;
+ m_own_descriptor = true;
}
}
else
@@ -272,7 +306,10 @@ File::Open (const char *path, uint32_t options, uint32_t permissions)
if (!DescriptorIsValid())
error.SetErrorToErrno();
else
- m_owned = true;
+ {
+ m_own_descriptor = true;
+ m_options = options;
+ }
return error;
}
@@ -328,27 +365,24 @@ Error
File::Close ()
{
Error error;
- if (IsValid ())
+ if (StreamIsValid() && m_own_stream)
{
- if (m_owned)
- {
- if (StreamIsValid())
- {
- if (::fclose (m_stream) == EOF)
- error.SetErrorToErrno();
- }
-
- if (DescriptorIsValid())
- {
- if (::close (m_descriptor) != 0)
- error.SetErrorToErrno();
- }
- }
- m_descriptor = kInvalidDescriptor;
- m_stream = kInvalidStream;
- m_options = 0;
- m_owned = false;
+ if (::fclose (m_stream) == EOF)
+ error.SetErrorToErrno();
}
+
+ if (DescriptorIsValid() && m_own_descriptor)
+ {
+ if (::close (m_descriptor) != 0)
+ error.SetErrorToErrno();
+ }
+ m_descriptor = kInvalidDescriptor;
+ m_stream = kInvalidStream;
+ m_options = 0;
+ m_own_stream = false;
+ m_own_descriptor = false;
+ m_is_interactive = eLazyBoolCalculate;
+ m_is_real_terminal = eLazyBoolCalculate;
return error;
}
@@ -844,3 +878,43 @@ File::ConvertOpenOptionsForPOSIXOpen (uint32_t open_options)
return mode;
}
+
+void
+File::CalculateInteractiveAndTerminal ()
+{
+ const int fd = GetDescriptor();
+ if (fd >= 0)
+ {
+ m_is_interactive = eLazyBoolNo;
+ m_is_real_terminal = eLazyBoolNo;
+#ifndef _MSC_VER
+ if (isatty(fd))
+ {
+ m_is_interactive = eLazyBoolYes;
+ struct winsize window_size;
+ if (::ioctl (fd, TIOCGWINSZ, &window_size) == 0)
+ {
+ if (window_size.ws_col > 0)
+ m_is_real_terminal = eLazyBoolYes;
+ }
+ }
+#endif
+ }
+}
+
+bool
+File::GetIsInteractive ()
+{
+ if (m_is_interactive == eLazyBoolCalculate)
+ CalculateInteractiveAndTerminal ();
+ return m_is_interactive == eLazyBoolYes;
+}
+
+bool
+File::GetIsRealTerminal ()
+{
+ if (m_is_real_terminal == eLazyBoolCalculate)
+ CalculateInteractiveAndTerminal();
+ return m_is_real_terminal == eLazyBoolYes;
+}
+
diff --git a/source/Host/common/Host.cpp b/source/Host/common/Host.cpp
index 262776f6c7190..9e5e4cbdb04aa 100644
--- a/source/Host/common/Host.cpp
+++ b/source/Host/common/Host.cpp
@@ -18,6 +18,7 @@
#include <winsock2.h>
#include <WS2tcpip.h>
#else
+#include <unistd.h>
#include <dlfcn.h>
#include <grp.h>
#include <netdb.h>
@@ -37,7 +38,7 @@
#include <AvailabilityMacros.h>
#endif
-#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
+#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__)
#include <spawn.h>
#include <sys/wait.h>
#include <sys/syscall.h>
@@ -68,6 +69,18 @@
#include "llvm/Support/Host.h"
#include "llvm/Support/raw_ostream.h"
+#if defined (__APPLE__)
+#ifndef _POSIX_SPAWN_DISABLE_ASLR
+#define _POSIX_SPAWN_DISABLE_ASLR 0x0100
+#endif
+
+extern "C"
+{
+ int __pthread_chdir(const char *path);
+ int __pthread_fchdir (int fildes);
+}
+
+#endif
using namespace lldb;
using namespace lldb_private;
@@ -367,25 +380,31 @@ Host::GetArchitecture (SystemDefaultArchitecture arch_kind)
// If the OS is Linux, "unknown" in the vendor slot isn't what we want
// for the default triple. It's probably an artifact of config.guess.
if (triple.getOS() == llvm::Triple::Linux && triple.getVendor() == llvm::Triple::UnknownVendor)
- triple.setVendorName("");
+ triple.setVendorName ("");
+
+ const char* distribution_id = GetDistributionId ().AsCString();
switch (triple.getArch())
{
default:
g_host_arch_32.SetTriple(triple);
+ g_host_arch_32.SetDistributionId (distribution_id);
g_supports_32 = true;
break;
case llvm::Triple::x86_64:
g_host_arch_64.SetTriple(triple);
+ g_host_arch_64.SetDistributionId (distribution_id);
g_supports_64 = true;
g_host_arch_32.SetTriple(triple.get32BitArchVariant());
+ g_host_arch_32.SetDistributionId (distribution_id);
g_supports_32 = true;
break;
case llvm::Triple::sparcv9:
case llvm::Triple::ppc64:
g_host_arch_64.SetTriple(triple);
+ g_host_arch_64.SetDistributionId (distribution_id);
g_supports_64 = true;
break;
}
@@ -445,6 +464,19 @@ Host::GetTargetTriple()
return g_host_triple;
}
+// See linux/Host.cpp for Linux-based implementations of this.
+// Add your platform-specific implementation to the appropriate host file.
+#if !defined(__linux__)
+
+const ConstString &
+ Host::GetDistributionId ()
+{
+ static ConstString s_distribution_id;
+ return s_distribution_id;
+}
+
+#endif // #if !defined(__linux__)
+
lldb::pid_t
Host::GetCurrentProcessID()
{
@@ -457,7 +489,7 @@ lldb::tid_t
Host::GetCurrentThreadID()
{
#if defined (__APPLE__)
- // Calling "mach_port_deallocate()" bumps the reference count on the thread
+ // Calling "mach_thread_self()" bumps the reference count on the thread
// port, so we need to deallocate it. mach_task_self() doesn't bump the ref
// count.
thread_port_t thread_self = mach_thread_self();
@@ -489,10 +521,14 @@ Host::GetSignalAsCString (int signo)
case SIGILL: return "SIGILL"; // 4 illegal instruction (not reset when caught)
case SIGTRAP: return "SIGTRAP"; // 5 trace trap (not reset when caught)
case SIGABRT: return "SIGABRT"; // 6 abort()
-#if (defined(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE))
+#if defined(SIGPOLL)
+#if !defined(SIGIO) || (SIGPOLL != SIGIO)
+// Under some GNU/Linux, SIGPOLL and SIGIO are the same. Causing the build to
+// fail with 'multiple define cases with same value'
case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported)
#endif
-#if !defined(_POSIX_C_SOURCE)
+#endif
+#if defined(SIGEMT)
case SIGEMT: return "SIGEMT"; // 7 EMT instruction
#endif
case SIGFPE: return "SIGFPE"; // 8 floating point exception
@@ -510,15 +546,17 @@ Host::GetSignalAsCString (int signo)
case SIGCHLD: return "SIGCHLD"; // 20 to parent on child stop or exit
case SIGTTIN: return "SIGTTIN"; // 21 to readers pgrp upon background tty read
case SIGTTOU: return "SIGTTOU"; // 22 like TTIN for output if (tp->t_local&LTOSTOP)
-#if !defined(_POSIX_C_SOURCE)
+#if defined(SIGIO)
case SIGIO: return "SIGIO"; // 23 input/output possible signal
#endif
case SIGXCPU: return "SIGXCPU"; // 24 exceeded CPU time limit
case SIGXFSZ: return "SIGXFSZ"; // 25 exceeded file size limit
case SIGVTALRM: return "SIGVTALRM"; // 26 virtual time alarm
case SIGPROF: return "SIGPROF"; // 27 profiling time alarm
-#if !defined(_POSIX_C_SOURCE)
+#if defined(SIGWINCH)
case SIGWINCH: return "SIGWINCH"; // 28 window size changes
+#endif
+#if defined(SIGINFO)
case SIGINFO: return "SIGINFO"; // 29 information request
#endif
case SIGUSR1: return "SIGUSR1"; // 30 user defined signal 1
@@ -739,8 +777,8 @@ bool
Host::SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid,
const char *thread_name, size_t len)
{
- char *namebuf = (char *)::malloc (len + 1);
-
+ std::unique_ptr<char[]> namebuf(new char[len+1]);
+
// Thread names are coming in like '<lldb.comm.debugger.edit>' and
// '<lldb.comm.debugger.editline>'. So just chopping the end of the string
// off leads to a lot of similar named threads. Go through the thread name
@@ -749,10 +787,10 @@ Host::SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid,
if (lastdot && lastdot != thread_name)
thread_name = lastdot + 1;
- ::strncpy (namebuf, thread_name, len);
+ ::strncpy (namebuf.get(), thread_name, len);
namebuf[len] = 0;
- int namebuflen = strlen(namebuf);
+ int namebuflen = strlen(namebuf.get());
if (namebuflen > 0)
{
if (namebuf[namebuflen - 1] == '(' || namebuf[namebuflen - 1] == '>')
@@ -761,10 +799,8 @@ Host::SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid,
namebuflen--;
namebuf[namebuflen] = 0;
}
- return Host::SetThreadName (pid, tid, namebuf);
+ return Host::SetThreadName (pid, tid, namebuf.get());
}
-
- ::free(namebuf);
return false;
}
@@ -1092,19 +1128,23 @@ Host::GetLLDBPath (PathType path_type, FileSpec &file_spec)
{
framework_pos += strlen("LLDB.framework");
::strncpy (framework_pos, "/Resources/Python", PATH_MAX - (framework_pos - raw_path));
- }
-#else
- llvm::SmallString<256> python_version_dir;
- llvm::raw_svector_ostream os(python_version_dir);
- os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages";
- os.flush();
+ }
+ else
+ {
+#endif
+ llvm::SmallString<256> python_version_dir;
+ llvm::raw_svector_ostream os(python_version_dir);
+ os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages";
+ os.flush();
- // We may get our string truncated. Should we protect
- // this with an assert?
+ // We may get our string truncated. Should we protect
+ // this with an assert?
- ::strncat(raw_path, python_version_dir.c_str(),
- sizeof(raw_path) - strlen(raw_path) - 1);
+ ::strncat(raw_path, python_version_dir.c_str(),
+ sizeof(raw_path) - strlen(raw_path) - 1);
+#if defined (__APPLE__)
+ }
#endif
FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path));
g_lldb_python_dir.SetCString(resolved_path);
@@ -1224,6 +1264,29 @@ Host::GetLLDBPath (PathType path_type, FileSpec &file_spec)
// TODO: where would user LLDB plug-ins be located on other systems?
return false;
}
+
+ case ePathTypeLLDBTempSystemDir:
+ {
+ static ConstString g_lldb_tmp_dir;
+ if (!g_lldb_tmp_dir)
+ {
+ const char *tmpdir_cstr = getenv("TMPDIR");
+ if (tmpdir_cstr == NULL)
+ {
+ tmpdir_cstr = getenv("TMP");
+ if (tmpdir_cstr == NULL)
+ tmpdir_cstr = getenv("TEMP");
+ }
+ if (tmpdir_cstr)
+ {
+ g_lldb_tmp_dir.SetCString(tmpdir_cstr);
+ if (log)
+ log->Printf("Host::GetLLDBPath(ePathTypeLLDBTempSystemDir) => '%s'", g_lldb_tmp_dir.GetCString());
+ }
+ }
+ file_spec.GetDirectory() = g_lldb_tmp_dir;
+ return (bool)file_spec.GetDirectory();
+ }
}
return false;
@@ -1473,21 +1536,36 @@ Host::RunShellCommand (const char *command,
if (working_dir)
launch_info.SetWorkingDirectory(working_dir);
- char output_file_path_buffer[L_tmpnam];
+ char output_file_path_buffer[PATH_MAX];
const char *output_file_path = NULL;
+
if (command_output_ptr)
{
// Create a temporary file to get the stdout/stderr and redirect the
// output of the command into this file. We will later read this file
// if all goes well and fill the data into "command_output_ptr"
- output_file_path = ::tmpnam(output_file_path_buffer);
- launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false);
+ FileSpec tmpdir_file_spec;
+ if (Host::GetLLDBPath (ePathTypeLLDBTempSystemDir, tmpdir_file_spec))
+ {
+ tmpdir_file_spec.GetFilename().SetCString("lldb-shell-output.XXXXXX");
+ strncpy(output_file_path_buffer, tmpdir_file_spec.GetPath().c_str(), sizeof(output_file_path_buffer));
+ }
+ else
+ {
+ strncpy(output_file_path_buffer, "/tmp/lldb-shell-output.XXXXXX", sizeof(output_file_path_buffer));
+ }
+
+ output_file_path = ::mktemp(output_file_path_buffer);
+ }
+
+ launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false);
+ if (output_file_path)
+ {
launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path, false, true);
launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO);
}
else
{
- launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false);
launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true);
launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true);
}
@@ -1569,24 +1647,73 @@ Host::RunShellCommand (const char *command,
return error;
}
-#if defined(__linux__) || defined(__FreeBSD__)
-// The functions below implement process launching via posix_spawn() for Linux
-// and FreeBSD.
-// The posix_spawn() and posix_spawnp() functions first appeared in FreeBSD 8.0,
-static Error
-LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid)
+// LaunchProcessPosixSpawn for Apple, Linux, FreeBSD and other GLIBC
+// systems
+
+#if defined (__APPLE__) || defined (__linux__) || defined (__FreeBSD__) || defined (__GLIBC__)
+
+// this method needs to be visible to macosx/Host.cpp and
+// common/Host.cpp.
+
+short
+Host::GetPosixspawnFlags (ProcessLaunchInfo &launch_info)
+{
+ short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
+
+#if defined (__APPLE__)
+ if (launch_info.GetFlags().Test (eLaunchFlagExec))
+ flags |= POSIX_SPAWN_SETEXEC; // Darwin specific posix_spawn flag
+
+ if (launch_info.GetFlags().Test (eLaunchFlagDebug))
+ flags |= POSIX_SPAWN_START_SUSPENDED; // Darwin specific posix_spawn flag
+
+ if (launch_info.GetFlags().Test (eLaunchFlagDisableASLR))
+ flags |= _POSIX_SPAWN_DISABLE_ASLR; // Darwin specific posix_spawn flag
+
+ if (launch_info.GetLaunchInSeparateProcessGroup())
+ flags |= POSIX_SPAWN_SETPGROUP;
+
+#ifdef POSIX_SPAWN_CLOEXEC_DEFAULT
+#if defined (__APPLE__) && (defined (__x86_64__) || defined (__i386__))
+ static LazyBool g_use_close_on_exec_flag = eLazyBoolCalculate;
+ if (g_use_close_on_exec_flag == eLazyBoolCalculate)
+ {
+ g_use_close_on_exec_flag = eLazyBoolNo;
+
+ uint32_t major, minor, update;
+ if (Host::GetOSVersion(major, minor, update))
+ {
+ // Kernel panic if we use the POSIX_SPAWN_CLOEXEC_DEFAULT on 10.7 or earlier
+ if (major > 10 || (major == 10 && minor > 7))
+ {
+ // Only enable for 10.8 and later OS versions
+ g_use_close_on_exec_flag = eLazyBoolYes;
+ }
+ }
+ }
+#else
+ static LazyBool g_use_close_on_exec_flag = eLazyBoolYes;
+#endif
+ // Close all files exception those with file actions if this is supported.
+ if (g_use_close_on_exec_flag == eLazyBoolYes)
+ flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
+#endif
+#endif // #if defined (__APPLE__)
+ return flags;
+}
+
+Error
+Host::LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, ::pid_t &pid)
{
Error error;
Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_HOST | LIBLLDB_LOG_PROCESS));
- assert(exe_path);
- assert(!launch_info.GetFlags().Test (eLaunchFlagDebug));
-
posix_spawnattr_t attr;
-
error.SetError( ::posix_spawnattr_init (&attr), eErrorTypePOSIX);
- error.LogIfError(log, "::posix_spawnattr_init ( &attr )");
+
+ if (error.Fail() || log)
+ error.PutToLog(log, "::posix_spawnattr_init ( &attr )");
if (error.Fail())
return error;
@@ -1598,52 +1725,82 @@ LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, :
sigset_t all_signals;
sigemptyset (&no_signals);
sigfillset (&all_signals);
- ::posix_spawnattr_setsigmask(&attr, &all_signals);
+ ::posix_spawnattr_setsigmask(&attr, &no_signals);
+#if defined (__linux__) || defined (__FreeBSD__)
::posix_spawnattr_setsigdefault(&attr, &no_signals);
+#else
+ ::posix_spawnattr_setsigdefault(&attr, &all_signals);
+#endif
- short flags = POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK;
+ short flags = GetPosixspawnFlags(launch_info);
error.SetError( ::posix_spawnattr_setflags (&attr, flags), eErrorTypePOSIX);
- error.LogIfError(log, "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags);
+ if (error.Fail() || log)
+ error.PutToLog(log, "::posix_spawnattr_setflags ( &attr, flags=0x%8.8x )", flags);
if (error.Fail())
return error;
- const size_t num_file_actions = launch_info.GetNumFileActions ();
- posix_spawn_file_actions_t file_actions, *file_action_ptr = NULL;
- // Make a quick class that will cleanup the posix spawn attributes in case
- // we return in the middle of this function.
- lldb_utility::CleanUp <posix_spawn_file_actions_t *, int>
- posix_spawn_file_actions_cleanup (file_action_ptr, NULL, posix_spawn_file_actions_destroy);
+ // posix_spawnattr_setbinpref_np appears to be an Apple extension per:
+ // http://www.unix.com/man-page/OSX/3/posix_spawnattr_setbinpref_np/
+#if defined (__APPLE__) && !defined (__arm__)
- if (num_file_actions > 0)
+ // We don't need to do this for ARM, and we really shouldn't now that we
+ // have multiple CPU subtypes and no posix_spawnattr call that allows us
+ // to set which CPU subtype to launch...
+ const ArchSpec &arch_spec = launch_info.GetArchitecture();
+ cpu_type_t cpu = arch_spec.GetMachOCPUType();
+ cpu_type_t sub = arch_spec.GetMachOCPUSubType();
+ if (cpu != 0 &&
+ cpu != UINT32_MAX &&
+ cpu != LLDB_INVALID_CPUTYPE &&
+ !(cpu == 0x01000007 && sub == 8)) // If haswell is specified, don't try to set the CPU type or we will fail
{
- error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX);
- error.LogIfError(log, "::posix_spawn_file_actions_init ( &file_actions )");
- if (error.Fail())
+ size_t ocount = 0;
+ error.SetError( ::posix_spawnattr_setbinpref_np (&attr, 1, &cpu, &ocount), eErrorTypePOSIX);
+ if (error.Fail() || log)
+ error.PutToLog(log, "::posix_spawnattr_setbinpref_np ( &attr, 1, cpu_type = 0x%8.8x, count => %llu )", cpu, (uint64_t)ocount);
+
+ if (error.Fail() || ocount != 1)
return error;
+ }
- file_action_ptr = &file_actions;
- posix_spawn_file_actions_cleanup.set(file_action_ptr);
+#endif
- for (size_t i = 0; i < num_file_actions; ++i)
- {
- const ProcessLaunchInfo::FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i);
- if (launch_file_action &&
- !ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (&file_actions,
- launch_file_action,
- log,
- error))
- return error;
- }
+ const char *tmp_argv[2];
+ char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector();
+ char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector();
+ if (argv == NULL)
+ {
+ // posix_spawn gets very unhappy if it doesn't have at least the program
+ // name in argv[0]. One of the side affects I have noticed is the environment
+ // variables don't make it into the child process if "argv == NULL"!!!
+ tmp_argv[0] = exe_path;
+ tmp_argv[1] = NULL;
+ argv = (char * const*)tmp_argv;
}
- // Change working directory if neccessary.
+#if !defined (__APPLE__)
+ // manage the working directory
char current_dir[PATH_MAX];
current_dir[0] = '\0';
+#endif
const char *working_dir = launch_info.GetWorkingDirectory();
- if (working_dir != NULL)
+ if (working_dir)
{
+#if defined (__APPLE__)
+ // Set the working directory on this thread only
+ if (__pthread_chdir (working_dir) < 0) {
+ if (errno == ENOENT) {
+ error.SetErrorStringWithFormat("No such file or directory: %s", working_dir);
+ } else if (errno == ENOTDIR) {
+ error.SetErrorStringWithFormat("Path doesn't name a directory: %s", working_dir);
+ } else {
+ error.SetErrorStringWithFormat("An unknown error occurred when changing directory for process execution.");
+ }
+ return error;
+ }
+#else
if (::getcwd(current_dir, sizeof(current_dir)) == NULL)
{
error.SetError(errno, eErrorTypePOSIX);
@@ -1657,45 +1814,111 @@ LaunchProcessPosixSpawn (const char *exe_path, ProcessLaunchInfo &launch_info, :
error.LogIfError(log, "unable to change working directory to %s", working_dir);
return error;
}
+#endif
}
- const char *tmp_argv[2];
- char * const *argv = (char * const*)launch_info.GetArguments().GetConstArgumentVector();
- char * const *envp = (char * const*)launch_info.GetEnvironmentEntries().GetConstArgumentVector();
-
- // Prepare minimal argument list if we didn't get it from the launch_info structure.
- // We must pass argv into posix_spawnp and it must contain at least two items -
- // pointer to an executable and NULL.
- if (argv == NULL)
+ const size_t num_file_actions = launch_info.GetNumFileActions ();
+ if (num_file_actions > 0)
{
- tmp_argv[0] = exe_path;
- tmp_argv[1] = NULL;
- argv = (char * const*)tmp_argv;
- }
+ posix_spawn_file_actions_t file_actions;
+ error.SetError( ::posix_spawn_file_actions_init (&file_actions), eErrorTypePOSIX);
+ if (error.Fail() || log)
+ error.PutToLog(log, "::posix_spawn_file_actions_init ( &file_actions )");
+ if (error.Fail())
+ return error;
+
+ // Make a quick class that will cleanup the posix spawn attributes in case
+ // we return in the middle of this function.
+ lldb_utility::CleanUp <posix_spawn_file_actions_t *, int> posix_spawn_file_actions_cleanup (&file_actions, posix_spawn_file_actions_destroy);
+
+ for (size_t i=0; i<num_file_actions; ++i)
+ {
+ const ProcessLaunchInfo::FileAction *launch_file_action = launch_info.GetFileActionAtIndex(i);
+ if (launch_file_action)
+ {
+ if (!ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (&file_actions,
+ launch_file_action,
+ log,
+ error))
+ return error;
+ }
+ }
- error.SetError (::posix_spawnp (&pid,
- exe_path,
- (num_file_actions > 0) ? &file_actions : NULL,
- &attr,
- argv,
- envp),
- eErrorTypePOSIX);
+ error.SetError (::posix_spawnp (&pid,
+ exe_path,
+ &file_actions,
+ &attr,
+ argv,
+ envp),
+ eErrorTypePOSIX);
- error.LogIfError(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )",
- pid, exe_path, file_action_ptr, &attr, argv, envp);
+ if (error.Fail() || log)
+ {
+ error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = %p, attr = %p, argv = %p, envp = %p )",
+ pid,
+ exe_path,
+ &file_actions,
+ &attr,
+ argv,
+ envp);
+ if (log)
+ {
+ for (int ii=0; argv[ii]; ++ii)
+ log->Printf("argv[%i] = '%s'", ii, argv[ii]);
+ }
+ }
- // Change back the current directory.
- // NOTE: do not override previously established error from posix_spawnp.
- if (working_dir != NULL && ::chdir(current_dir) == -1 && error.Success())
+ }
+ else
{
- error.SetError(errno, eErrorTypePOSIX);
- error.LogIfError(log, "unable to change current directory back to %s",
- current_dir);
+ error.SetError (::posix_spawnp (&pid,
+ exe_path,
+ NULL,
+ &attr,
+ argv,
+ envp),
+ eErrorTypePOSIX);
+
+ if (error.Fail() || log)
+ {
+ error.PutToLog(log, "::posix_spawnp ( pid => %i, path = '%s', file_actions = NULL, attr = %p, argv = %p, envp = %p )",
+ pid,
+ exe_path,
+ &attr,
+ argv,
+ envp);
+ if (log)
+ {
+ for (int ii=0; argv[ii]; ++ii)
+ log->Printf("argv[%i] = '%s'", ii, argv[ii]);
+ }
+ }
+ }
+
+ if (working_dir)
+ {
+#if defined (__APPLE__)
+ // No more thread specific current working directory
+ __pthread_fchdir (-1);
+#else
+ if (::chdir(current_dir) == -1 && error.Success())
+ {
+ error.SetError(errno, eErrorTypePOSIX);
+ error.LogIfError(log, "unable to change current directory back to %s",
+ current_dir);
+ }
+#endif
}
return error;
}
+#endif // LaunchProcedssPosixSpawn: Apple, Linux, FreeBSD and other GLIBC systems
+
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__GLIBC__)
+// The functions below implement process launching via posix_spawn() for Linux
+// and FreeBSD.
Error
Host::LaunchProcess (ProcessLaunchInfo &launch_info)
@@ -1747,6 +1970,8 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info)
// If all went well, then set the process ID into the launch info
launch_info.SetProcessID(pid);
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
// Make sure we reap any processes we spawn or we will have zombies.
if (!launch_info.MonitorProcess())
{
@@ -1755,6 +1980,13 @@ Host::LaunchProcess (ProcessLaunchInfo &launch_info)
NULL,
pid,
monitor_signals);
+ if (log)
+ log->PutCString ("monitored child process with default Process::SetProcessExitStatus.");
+ }
+ else
+ {
+ if (log)
+ log->PutCString ("monitored child process with user-specified process monitor.");
}
}
else
diff --git a/source/Host/common/OptionParser.cpp b/source/Host/common/OptionParser.cpp
index ead044f53cf17..cf133597cb845 100644
--- a/source/Host/common/OptionParser.cpp
+++ b/source/Host/common/OptionParser.cpp
@@ -9,14 +9,10 @@
#include "lldb/Host/OptionParser.h"
-#ifdef _MSC_VER
-#include "../windows/msvc/getopt.inc"
-#else
-#ifdef _WIN32
+#if (!defined( _MSC_VER ) && defined( _WIN32 ))
#define _BSD_SOURCE // Required so that getopt.h defines optreset
#endif
-#include <getopt.h>
-#endif
+#include "lldb/Host/HostGetOpt.h"
using namespace lldb_private;
diff --git a/source/Host/common/SocketAddress.cpp b/source/Host/common/SocketAddress.cpp
index 1fa7531af9dc5..75f3cd13f5860 100644
--- a/source/Host/common/SocketAddress.cpp
+++ b/source/Host/common/SocketAddress.cpp
@@ -95,7 +95,7 @@ GetFamilyLength (sa_family_t family)
socklen_t
SocketAddress::GetLength () const
{
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
return m_socket_addr.sa.sa_len;
#else
return GetFamilyLength (GetFamily());
@@ -118,24 +118,24 @@ void
SocketAddress::SetFamily (sa_family_t family)
{
m_socket_addr.sa.sa_family = family;
-#if defined(__APPLE__)
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__)
m_socket_addr.sa.sa_len = GetFamilyLength (family);
#endif
}
-in_port_t
+uint16_t
SocketAddress::GetPort () const
{
switch (GetFamily())
{
- case AF_INET: return m_socket_addr.sa_ipv4.sin_port;
- case AF_INET6: return m_socket_addr.sa_ipv6.sin6_port;
+ case AF_INET: return ntohs(m_socket_addr.sa_ipv4.sin_port);
+ case AF_INET6: return ntohs(m_socket_addr.sa_ipv6.sin6_port);
}
return 0;
}
bool
-SocketAddress::SetPort (in_port_t port)
+SocketAddress::SetPort (uint16_t port)
{
switch (GetFamily())
{
@@ -206,36 +206,34 @@ SocketAddress::operator=(const struct sockaddr_storage &s)
}
bool
-SocketAddress::SetAddress (const struct addrinfo *hints_ptr,
- const char *host,
- const char *service,
- struct addrinfo *addr_info_ptr)
+SocketAddress::getaddrinfo (const char *host,
+ const char *service,
+ int ai_family,
+ int ai_socktype,
+ int ai_protocol,
+ int ai_flags)
{
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = ai_family;
+ hints.ai_socktype = ai_socktype;
+ hints.ai_protocol = ai_protocol;
+ hints.ai_flags = ai_flags;
+
struct addrinfo *service_info_list = NULL;
- int err = ::getaddrinfo (host, service, hints_ptr, &service_info_list);
+ int err = ::getaddrinfo (host, service, &hints, &service_info_list);
if (err == 0 && service_info_list)
- {
- if (addr_info_ptr)
- *addr_info_ptr = *service_info_list;
*this = service_info_list;
- }
else
Clear();
:: freeaddrinfo (service_info_list);
-
- const bool is_valid = IsValid();
- if (!is_valid)
- {
- if (addr_info_ptr)
- ::memset (addr_info_ptr, 0, sizeof(struct addrinfo));
- }
- return is_valid;
+ return IsValid();
}
bool
-SocketAddress::SetToLocalhost (sa_family_t family, in_port_t port)
+SocketAddress::SetToLocalhost (sa_family_t family, uint16_t port)
{
switch (family)
{
@@ -243,7 +241,7 @@ SocketAddress::SetToLocalhost (sa_family_t family, in_port_t port)
SetFamily (AF_INET);
if (SetPort (port))
{
- m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_ANY);
+ m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
return true;
}
break;
@@ -252,7 +250,7 @@ SocketAddress::SetToLocalhost (sa_family_t family, in_port_t port)
SetFamily (AF_INET6);
if (SetPort (port))
{
- m_socket_addr.sa_ipv6.sin6_addr = in6addr_any;
+ m_socket_addr.sa_ipv6.sin6_addr = in6addr_loopback;
return true;
}
break;
@@ -261,3 +259,31 @@ SocketAddress::SetToLocalhost (sa_family_t family, in_port_t port)
Clear();
return false;
}
+
+bool
+SocketAddress::SetToAnyAddress (sa_family_t family, uint16_t port)
+{
+ switch (family)
+ {
+ case AF_INET:
+ SetFamily (AF_INET);
+ if (SetPort (port))
+ {
+ m_socket_addr.sa_ipv4.sin_addr.s_addr = htonl (INADDR_ANY);
+ return true;
+ }
+ break;
+
+ case AF_INET6:
+ SetFamily (AF_INET6);
+ if (SetPort (port))
+ {
+ m_socket_addr.sa_ipv6.sin6_addr = in6addr_any;
+ return true;
+ }
+ break;
+
+ }
+ Clear();
+ return false;
+}