diff options
Diffstat (limited to 'source/Host/common')
-rw-r--r-- | source/Host/common/Editline.cpp | 696 | ||||
-rw-r--r-- | source/Host/common/File.cpp | 128 | ||||
-rw-r--r-- | source/Host/common/Host.cpp | 418 | ||||
-rw-r--r-- | source/Host/common/OptionParser.cpp | 8 | ||||
-rw-r--r-- | source/Host/common/SocketAddress.cpp | 78 |
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<OSTOP) -#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; +} |