180814287SRaphael Isemann //===-- ConnectionFileDescriptorPosix.cpp ---------------------------------===//
293a66fc1SZachary Turner //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
693a66fc1SZachary Turner //
793a66fc1SZachary Turner //===----------------------------------------------------------------------===//
893a66fc1SZachary Turner 
993a66fc1SZachary Turner #if defined(__APPLE__)
1093a66fc1SZachary Turner // Enable this special support for Apple builds where we can have unlimited
1193a66fc1SZachary Turner // select bounds. We tried switching to poll() and kqueue and we were panicing
1293a66fc1SZachary Turner // the kernel, so we have to stick with select for now.
1393a66fc1SZachary Turner #define _DARWIN_UNLIMITED_SELECT
1493a66fc1SZachary Turner #endif
1593a66fc1SZachary Turner 
16c34698a8SPavel Labath #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
1793a66fc1SZachary Turner #include "lldb/Host/Config.h"
1893a66fc1SZachary Turner #include "lldb/Host/Socket.h"
19b9c1b51eSKate Stone #include "lldb/Host/SocketAddress.h"
20c34698a8SPavel Labath #include "lldb/Utility/LLDBLog.h"
21ee1f578dSGreg Clayton #include "lldb/Utility/SelectHelper.h"
224ccd9954SPavel Labath #include "lldb/Utility/Timeout.h"
2393a66fc1SZachary Turner 
2476e47d48SRaphael Isemann #include <cerrno>
2576e47d48SRaphael Isemann #include <cstdlib>
2676e47d48SRaphael Isemann #include <cstring>
2793a66fc1SZachary Turner #include <fcntl.h>
2893a66fc1SZachary Turner #include <sys/types.h>
2993a66fc1SZachary Turner 
303011d55fSJonas Devlieghere #if LLDB_ENABLE_POSIX
3193a66fc1SZachary Turner #include <termios.h>
32b6dbe9a9SPavel Labath #include <unistd.h>
3393a66fc1SZachary Turner #endif
3493a66fc1SZachary Turner 
35796ac80bSJonas Devlieghere #include <memory>
3654971856SOleksiy Vyalov #include <sstream>
3754971856SOleksiy Vyalov 
3810c41f37SPavel Labath #include "llvm/Support/Errno.h"
3993a66fc1SZachary Turner #include "llvm/Support/ErrorHandling.h"
4093a66fc1SZachary Turner #if defined(__APPLE__)
4193a66fc1SZachary Turner #include "llvm/ADT/SmallVector.h"
4293a66fc1SZachary Turner #endif
4393a66fc1SZachary Turner #include "lldb/Host/Host.h"
4493a66fc1SZachary Turner #include "lldb/Host/Socket.h"
45e98628ceSOleksiy Vyalov #include "lldb/Host/common/TCPSocket.h"
46c9e6b701SPavel Labath #include "lldb/Host/common/UDPSocket.h"
476f9e6901SZachary Turner #include "lldb/Utility/Log.h"
48bf9a7730SZachary Turner #include "lldb/Utility/StreamString.h"
4938d0632eSPavel Labath #include "lldb/Utility/Timer.h"
5093a66fc1SZachary Turner 
5193a66fc1SZachary Turner using namespace lldb;
5293a66fc1SZachary Turner using namespace lldb_private;
5393a66fc1SZachary Turner 
ConnectionFileDescriptor(bool child_processes_inherit)54477e42a6SOleksiy Vyalov ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit)
55b9c1b51eSKate Stone     : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
569494c510SJonas Devlieghere 
57b9c1b51eSKate Stone       m_child_processes_inherit(child_processes_inherit) {
58a007a6d8SPavel Labath   Log *log(GetLog(LLDBLog::Connection | LLDBLog::Object));
5963e5fb76SJonas Devlieghere   LLDB_LOGF(log, "%p ConnectionFileDescriptor::ConnectionFileDescriptor ()",
60b9c1b51eSKate Stone             static_cast<void *>(this));
6193a66fc1SZachary Turner }
6293a66fc1SZachary Turner 
ConnectionFileDescriptor(int fd,bool owns_fd)6393a66fc1SZachary Turner ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd)
64b9c1b51eSKate Stone     : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
6558d28b93SMichał Górny       m_child_processes_inherit(false) {
66fee461b1SMichał Górny   m_io_sp =
67dcb0e687SMichał Górny       std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, owns_fd);
6893a66fc1SZachary Turner 
69a007a6d8SPavel Labath   Log *log(GetLog(LLDBLog::Connection | LLDBLog::Object));
7063e5fb76SJonas Devlieghere   LLDB_LOGF(log,
7163e5fb76SJonas Devlieghere             "%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = "
72b9c1b51eSKate Stone             "%i, owns_fd = %i)",
7316ff8604SSaleem Abdulrasool             static_cast<void *>(this), fd, owns_fd);
7493a66fc1SZachary Turner   OpenCommandPipe();
7593a66fc1SZachary Turner }
7693a66fc1SZachary Turner 
ConnectionFileDescriptor(Socket * socket)77a0e70cd4SRobert Flack ConnectionFileDescriptor::ConnectionFileDescriptor(Socket *socket)
78b9c1b51eSKate Stone     : Connection(), m_pipe(), m_mutex(), m_shutting_down(false),
7958d28b93SMichał Górny       m_child_processes_inherit(false) {
80a0e70cd4SRobert Flack   InitializeSocket(socket);
81a0e70cd4SRobert Flack }
82a0e70cd4SRobert Flack 
~ConnectionFileDescriptor()83b9c1b51eSKate Stone ConnectionFileDescriptor::~ConnectionFileDescriptor() {
84a007a6d8SPavel Labath   Log *log(GetLog(LLDBLog::Connection | LLDBLog::Object));
8563e5fb76SJonas Devlieghere   LLDB_LOGF(log, "%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()",
86b9c1b51eSKate Stone             static_cast<void *>(this));
87248a1305SKonrad Kleine   Disconnect(nullptr);
8893a66fc1SZachary Turner   CloseCommandPipe();
8993a66fc1SZachary Turner }
9093a66fc1SZachary Turner 
OpenCommandPipe()91b9c1b51eSKate Stone void ConnectionFileDescriptor::OpenCommandPipe() {
9293a66fc1SZachary Turner   CloseCommandPipe();
9393a66fc1SZachary Turner 
94a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Connection);
9593a66fc1SZachary Turner   // Make the command file descriptor here:
9697206d57SZachary Turner   Status result = m_pipe.CreateNew(m_child_processes_inherit);
97b9c1b51eSKate Stone   if (!result.Success()) {
9863e5fb76SJonas Devlieghere     LLDB_LOGF(log,
9963e5fb76SJonas Devlieghere               "%p ConnectionFileDescriptor::OpenCommandPipe () - could not "
100b9c1b51eSKate Stone               "make pipe: %s",
101b9c1b51eSKate Stone               static_cast<void *>(this), result.AsCString());
102b9c1b51eSKate Stone   } else {
10363e5fb76SJonas Devlieghere     LLDB_LOGF(log,
10463e5fb76SJonas Devlieghere               "%p ConnectionFileDescriptor::OpenCommandPipe() - success "
105b9c1b51eSKate Stone               "readfd=%d writefd=%d",
106b9c1b51eSKate Stone               static_cast<void *>(this), m_pipe.GetReadFileDescriptor(),
107b9c1b51eSKate Stone               m_pipe.GetWriteFileDescriptor());
10893a66fc1SZachary Turner   }
10993a66fc1SZachary Turner }
11093a66fc1SZachary Turner 
CloseCommandPipe()111b9c1b51eSKate Stone void ConnectionFileDescriptor::CloseCommandPipe() {
112a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Connection);
11363e5fb76SJonas Devlieghere   LLDB_LOGF(log, "%p ConnectionFileDescriptor::CloseCommandPipe()",
114b9c1b51eSKate Stone             static_cast<void *>(this));
11593a66fc1SZachary Turner 
11693a66fc1SZachary Turner   m_pipe.Close();
11793a66fc1SZachary Turner }
11893a66fc1SZachary Turner 
IsConnected() const119b9c1b51eSKate Stone bool ConnectionFileDescriptor::IsConnected() const {
120fee461b1SMichał Górny   return m_io_sp && m_io_sp->IsValid();
12193a66fc1SZachary Turner }
12293a66fc1SZachary Turner 
Connect(llvm::StringRef path,Status * error_ptr)1233165945aSZachary Turner ConnectionStatus ConnectionFileDescriptor::Connect(llvm::StringRef path,
12497206d57SZachary Turner                                                    Status *error_ptr) {
125*9aa6a479SJonas Devlieghere   return Connect(
126*9aa6a479SJonas Devlieghere       path, [](llvm::StringRef) {}, error_ptr);
12758d28b93SMichał Górny }
12858d28b93SMichał Górny 
12958d28b93SMichał Górny ConnectionStatus
Connect(llvm::StringRef path,socket_id_callback_type socket_id_callback,Status * error_ptr)13058d28b93SMichał Górny ConnectionFileDescriptor::Connect(llvm::StringRef path,
13158d28b93SMichał Górny                                   socket_id_callback_type socket_id_callback,
13258d28b93SMichał Górny                                   Status *error_ptr) {
13316ff8604SSaleem Abdulrasool   std::lock_guard<std::recursive_mutex> guard(m_mutex);
134a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Connection);
13563e5fb76SJonas Devlieghere   LLDB_LOGF(log, "%p ConnectionFileDescriptor::Connect (url = '%s')",
1363165945aSZachary Turner             static_cast<void *>(this), path.str().c_str());
13793a66fc1SZachary Turner 
13893a66fc1SZachary Turner   OpenCommandPipe();
13993a66fc1SZachary Turner 
1404b46a413SMichał Górny   if (path.empty()) {
1414b46a413SMichał Górny     if (error_ptr)
1424b46a413SMichał Górny       error_ptr->SetErrorString("invalid connect arguments");
1434b46a413SMichał Górny     return eConnectionStatusError;
1444b46a413SMichał Górny   }
1454b46a413SMichał Górny 
1464b46a413SMichał Górny   llvm::StringRef scheme;
1474b46a413SMichał Górny   std::tie(scheme, path) = path.split("://");
1484b46a413SMichał Górny 
1493165945aSZachary Turner   if (!path.empty()) {
1504b46a413SMichał Górny     auto method =
1514b46a413SMichał Górny         llvm::StringSwitch<ConnectionStatus (ConnectionFileDescriptor::*)(
15258d28b93SMichał Górny             llvm::StringRef, socket_id_callback_type, Status *)>(scheme)
153e50f02baSMichał Górny             .Case("listen", &ConnectionFileDescriptor::AcceptTCP)
1544b46a413SMichał Górny             .Cases("accept", "unix-accept",
155e50f02baSMichał Górny                    &ConnectionFileDescriptor::AcceptNamedSocket)
15658d28b93SMichał Górny             .Case("unix-abstract-accept",
157e50f02baSMichał Górny                   &ConnectionFileDescriptor::AcceptAbstractSocket)
1584b46a413SMichał Górny             .Cases("connect", "tcp-connect",
1594b46a413SMichał Górny                    &ConnectionFileDescriptor::ConnectTCP)
1604b46a413SMichał Górny             .Case("udp", &ConnectionFileDescriptor::ConnectUDP)
161e50f02baSMichał Górny             .Case("unix-connect", &ConnectionFileDescriptor::ConnectNamedSocket)
1624b46a413SMichał Górny             .Case("unix-abstract-connect",
163e50f02baSMichał Górny                   &ConnectionFileDescriptor::ConnectAbstractSocket)
1643011d55fSJonas Devlieghere #if LLDB_ENABLE_POSIX
1654b46a413SMichał Górny             .Case("fd", &ConnectionFileDescriptor::ConnectFD)
1664b46a413SMichał Górny             .Case("file", &ConnectionFileDescriptor::ConnectFile)
1674a7b4beaSMichał Górny             .Case("serial", &ConnectionFileDescriptor::ConnectSerialPort)
16893a66fc1SZachary Turner #endif
1694b46a413SMichał Górny             .Default(nullptr);
1704b46a413SMichał Górny 
171e50f02baSMichał Górny     if (method) {
172e50f02baSMichał Górny       if (error_ptr)
173e50f02baSMichał Górny         *error_ptr = Status();
17458d28b93SMichał Górny       return (this->*method)(path, socket_id_callback, error_ptr);
1754b46a413SMichał Górny     }
176e50f02baSMichał Górny   }
1774b46a413SMichał Górny 
17893a66fc1SZachary Turner   if (error_ptr)
179b9c1b51eSKate Stone     error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'",
1803165945aSZachary Turner                                         path.str().c_str());
18193a66fc1SZachary Turner   return eConnectionStatusError;
18293a66fc1SZachary Turner }
18393a66fc1SZachary Turner 
InterruptRead()184b9c1b51eSKate Stone bool ConnectionFileDescriptor::InterruptRead() {
1850b9d3eefSZachary Turner   size_t bytes_written = 0;
18697206d57SZachary Turner   Status result = m_pipe.Write("i", 1, bytes_written);
1870b9d3eefSZachary Turner   return result.Success();
18893a66fc1SZachary Turner }
18993a66fc1SZachary Turner 
Disconnect(Status * error_ptr)19097206d57SZachary Turner ConnectionStatus ConnectionFileDescriptor::Disconnect(Status *error_ptr) {
191a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Connection);
19263e5fb76SJonas Devlieghere   LLDB_LOGF(log, "%p ConnectionFileDescriptor::Disconnect ()",
193b9c1b51eSKate Stone             static_cast<void *>(this));
19493a66fc1SZachary Turner 
19593a66fc1SZachary Turner   ConnectionStatus status = eConnectionStatusSuccess;
19693a66fc1SZachary Turner 
197b9c1b51eSKate Stone   if (!IsConnected()) {
19863e5fb76SJonas Devlieghere     LLDB_LOGF(
19963e5fb76SJonas Devlieghere         log, "%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect",
200b9c1b51eSKate Stone         static_cast<void *>(this));
20193a66fc1SZachary Turner     return eConnectionStatusSuccess;
20293a66fc1SZachary Turner   }
20393a66fc1SZachary Turner 
204fee461b1SMichał Górny   if (m_io_sp->GetFdType() == IOObject::eFDTypeSocket)
205fee461b1SMichał Górny     static_cast<Socket &>(*m_io_sp).PreDisconnect();
20693a66fc1SZachary Turner 
20705097246SAdrian Prantl   // Try to get the ConnectionFileDescriptor's mutex.  If we fail, that is
20805097246SAdrian Prantl   // quite likely because somebody is doing a blocking read on our file
20905097246SAdrian Prantl   // descriptor.  If that's the case, then send the "q" char to the command
21005097246SAdrian Prantl   // file channel so the read will wake up and the connection will then know to
21105097246SAdrian Prantl   // shut down.
21216ff8604SSaleem Abdulrasool   std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock);
213b9c1b51eSKate Stone   if (!locker.try_lock()) {
214b9c1b51eSKate Stone     if (m_pipe.CanWrite()) {
2150b9d3eefSZachary Turner       size_t bytes_written = 0;
21697206d57SZachary Turner       Status result = m_pipe.Write("q", 1, bytes_written);
21763e5fb76SJonas Devlieghere       LLDB_LOGF(log,
21863e5fb76SJonas Devlieghere                 "%p ConnectionFileDescriptor::Disconnect(): Couldn't get "
219b9c1b51eSKate Stone                 "the lock, sent 'q' to %d, error = '%s'.",
220b9c1b51eSKate Stone                 static_cast<void *>(this), m_pipe.GetWriteFileDescriptor(),
221b9c1b51eSKate Stone                 result.AsCString());
222b9c1b51eSKate Stone     } else if (log) {
22363e5fb76SJonas Devlieghere       LLDB_LOGF(log,
22463e5fb76SJonas Devlieghere                 "%p ConnectionFileDescriptor::Disconnect(): Couldn't get the "
225b9c1b51eSKate Stone                 "lock, but no command pipe is available.",
22693a66fc1SZachary Turner                 static_cast<void *>(this));
22793a66fc1SZachary Turner     }
22816ff8604SSaleem Abdulrasool     locker.lock();
22993a66fc1SZachary Turner   }
23093a66fc1SZachary Turner 
231ece176e0SJonas Devlieghere   // Prevents reads and writes during shutdown.
232ece176e0SJonas Devlieghere   m_shutting_down = true;
233ece176e0SJonas Devlieghere 
234fee461b1SMichał Górny   Status error = m_io_sp->Close();
235fee461b1SMichał Górny   if (error.Fail())
23693a66fc1SZachary Turner     status = eConnectionStatusError;
23793a66fc1SZachary Turner   if (error_ptr)
238fee461b1SMichał Górny     *error_ptr = error;
23993a66fc1SZachary Turner 
2405df78fa3SGreg Clayton   // Close any pipes we were using for async interrupts
2415df78fa3SGreg Clayton   m_pipe.Close();
2425df78fa3SGreg Clayton 
243eb303ee5SVince Harron   m_uri.clear();
24493a66fc1SZachary Turner   m_shutting_down = false;
24593a66fc1SZachary Turner   return status;
24693a66fc1SZachary Turner }
24793a66fc1SZachary Turner 
Read(void * dst,size_t dst_len,const Timeout<std::micro> & timeout,ConnectionStatus & status,Status * error_ptr)248b9c1b51eSKate Stone size_t ConnectionFileDescriptor::Read(void *dst, size_t dst_len,
2492f159a5fSPavel Labath                                       const Timeout<std::micro> &timeout,
250b9c1b51eSKate Stone                                       ConnectionStatus &status,
25197206d57SZachary Turner                                       Status *error_ptr) {
252a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Connection);
25393a66fc1SZachary Turner 
25416ff8604SSaleem Abdulrasool   std::unique_lock<std::recursive_mutex> locker(m_mutex, std::defer_lock);
255b9c1b51eSKate Stone   if (!locker.try_lock()) {
25663e5fb76SJonas Devlieghere     LLDB_LOGF(log,
25763e5fb76SJonas Devlieghere               "%p ConnectionFileDescriptor::Read () failed to get the "
258b9c1b51eSKate Stone               "connection lock.",
259b9c1b51eSKate Stone               static_cast<void *>(this));
26093a66fc1SZachary Turner     if (error_ptr)
26193a66fc1SZachary Turner       error_ptr->SetErrorString("failed to get the connection lock for read.");
26293a66fc1SZachary Turner 
26393a66fc1SZachary Turner     status = eConnectionStatusTimedOut;
26493a66fc1SZachary Turner     return 0;
26593a66fc1SZachary Turner   }
26632c7265aSChaoren Lin 
267b9c1b51eSKate Stone   if (m_shutting_down) {
268ece176e0SJonas Devlieghere     if (error_ptr)
269ece176e0SJonas Devlieghere       error_ptr->SetErrorString("shutting down");
27032c7265aSChaoren Lin     status = eConnectionStatusError;
27132c7265aSChaoren Lin     return 0;
27232c7265aSChaoren Lin   }
27393a66fc1SZachary Turner 
2742f159a5fSPavel Labath   status = BytesAvailable(timeout, error_ptr);
27593a66fc1SZachary Turner   if (status != eConnectionStatusSuccess)
27693a66fc1SZachary Turner     return 0;
27793a66fc1SZachary Turner 
27897206d57SZachary Turner   Status error;
279ef984e7dSPavel Labath   size_t bytes_read = dst_len;
280fee461b1SMichał Górny   error = m_io_sp->Read(dst, bytes_read);
281ef984e7dSPavel Labath 
282b9c1b51eSKate Stone   if (log) {
28363e5fb76SJonas Devlieghere     LLDB_LOGF(log,
28463e5fb76SJonas Devlieghere               "%p ConnectionFileDescriptor::Read()  fd = %" PRIu64
285b9c1b51eSKate Stone               ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s",
286b9c1b51eSKate Stone               static_cast<void *>(this),
287fee461b1SMichał Górny               static_cast<uint64_t>(m_io_sp->GetWaitableHandle()),
288b9c1b51eSKate Stone               static_cast<void *>(dst), static_cast<uint64_t>(dst_len),
289b9c1b51eSKate Stone               static_cast<uint64_t>(bytes_read), error.AsCString());
29093a66fc1SZachary Turner   }
291ef984e7dSPavel Labath 
292b9c1b51eSKate Stone   if (bytes_read == 0) {
293b9c1b51eSKate Stone     error.Clear(); // End-of-file.  Do not automatically close; pass along for
294b9c1b51eSKate Stone                    // the end-of-file handlers.
29593a66fc1SZachary Turner     status = eConnectionStatusEndOfFile;
29693a66fc1SZachary Turner   }
29793a66fc1SZachary Turner 
29893a66fc1SZachary Turner   if (error_ptr)
29993a66fc1SZachary Turner     *error_ptr = error;
30093a66fc1SZachary Turner 
301b9c1b51eSKate Stone   if (error.Fail()) {
30293a66fc1SZachary Turner     uint32_t error_value = error.GetError();
303b9c1b51eSKate Stone     switch (error_value) {
304b9c1b51eSKate Stone     case EAGAIN: // The file was marked for non-blocking I/O, and no data were
305b9c1b51eSKate Stone                  // ready to be read.
306fee461b1SMichał Górny       if (m_io_sp->GetFdType() == IOObject::eFDTypeSocket)
30793a66fc1SZachary Turner         status = eConnectionStatusTimedOut;
30893a66fc1SZachary Turner       else
30993a66fc1SZachary Turner         status = eConnectionStatusSuccess;
31093a66fc1SZachary Turner       return 0;
31193a66fc1SZachary Turner 
31293a66fc1SZachary Turner     case EFAULT:  // Buf points outside the allocated address space.
313b9c1b51eSKate Stone     case EINTR:   // A read from a slow device was interrupted before any data
314b9c1b51eSKate Stone                   // arrived by the delivery of a signal.
31593a66fc1SZachary Turner     case EINVAL:  // The pointer associated with fildes was negative.
31693a66fc1SZachary Turner     case EIO:     // An I/O error occurred while reading from the file system.
31793a66fc1SZachary Turner                   // The process group is orphaned.
31805097246SAdrian Prantl                   // The file is a regular file, nbyte is greater than 0, the
31905097246SAdrian Prantl                   // starting position is before the end-of-file, and the
32005097246SAdrian Prantl                   // starting position is greater than or equal to the offset
32105097246SAdrian Prantl                   // maximum established for the open file descriptor
32205097246SAdrian Prantl                   // associated with fildes.
32393a66fc1SZachary Turner     case EISDIR:  // An attempt is made to read a directory.
32493a66fc1SZachary Turner     case ENOBUFS: // An attempt to allocate a memory buffer fails.
32593a66fc1SZachary Turner     case ENOMEM:  // Insufficient memory is available.
32693a66fc1SZachary Turner       status = eConnectionStatusError;
32793a66fc1SZachary Turner       break; // Break to close....
32893a66fc1SZachary Turner 
32993a66fc1SZachary Turner     case ENOENT:     // no such file or directory
330b9c1b51eSKate Stone     case EBADF:      // fildes is not a valid file or socket descriptor open for
331b9c1b51eSKate Stone                      // reading.
33293a66fc1SZachary Turner     case ENXIO:      // An action is requested of a device that does not exist..
33393a66fc1SZachary Turner                      // A requested action cannot be performed by the device.
334b9c1b51eSKate Stone     case ECONNRESET: // The connection is closed by the peer during a read
335b9c1b51eSKate Stone                      // attempt on a socket.
33693a66fc1SZachary Turner     case ENOTCONN:   // A read is attempted on an unconnected socket.
33793a66fc1SZachary Turner       status = eConnectionStatusLostConnection;
33893a66fc1SZachary Turner       break; // Break to close....
33993a66fc1SZachary Turner 
340b9c1b51eSKate Stone     case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a
341b9c1b51eSKate Stone                     // socket.
34293a66fc1SZachary Turner       status = eConnectionStatusTimedOut;
34393a66fc1SZachary Turner       return 0;
34493a66fc1SZachary Turner 
34593a66fc1SZachary Turner     default:
34610c41f37SPavel Labath       LLDB_LOG(log, "this = {0}, unexpected error: {1}", this,
34710c41f37SPavel Labath                llvm::sys::StrError(error_value));
34893a66fc1SZachary Turner       status = eConnectionStatusError;
34993a66fc1SZachary Turner       break; // Break to close....
35093a66fc1SZachary Turner     }
35193a66fc1SZachary Turner 
35293a66fc1SZachary Turner     return 0;
35393a66fc1SZachary Turner   }
354ef984e7dSPavel Labath   return bytes_read;
35593a66fc1SZachary Turner }
35693a66fc1SZachary Turner 
Write(const void * src,size_t src_len,ConnectionStatus & status,Status * error_ptr)357b9c1b51eSKate Stone size_t ConnectionFileDescriptor::Write(const void *src, size_t src_len,
358b9c1b51eSKate Stone                                        ConnectionStatus &status,
35997206d57SZachary Turner                                        Status *error_ptr) {
360a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Connection);
36163e5fb76SJonas Devlieghere   LLDB_LOGF(log,
36263e5fb76SJonas Devlieghere             "%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64
36363e5fb76SJonas Devlieghere             ")",
364b9c1b51eSKate Stone             static_cast<void *>(this), static_cast<const void *>(src),
365b9c1b51eSKate Stone             static_cast<uint64_t>(src_len));
36693a66fc1SZachary Turner 
367b9c1b51eSKate Stone   if (!IsConnected()) {
36893a66fc1SZachary Turner     if (error_ptr)
36993a66fc1SZachary Turner       error_ptr->SetErrorString("not connected");
37093a66fc1SZachary Turner     status = eConnectionStatusNoConnection;
37193a66fc1SZachary Turner     return 0;
37293a66fc1SZachary Turner   }
37393a66fc1SZachary Turner 
374ece176e0SJonas Devlieghere   if (m_shutting_down) {
375ece176e0SJonas Devlieghere     if (error_ptr)
376ece176e0SJonas Devlieghere       error_ptr->SetErrorString("shutting down");
377ece176e0SJonas Devlieghere     status = eConnectionStatusError;
378ece176e0SJonas Devlieghere     return 0;
379ece176e0SJonas Devlieghere   }
380ece176e0SJonas Devlieghere 
38197206d57SZachary Turner   Status error;
38293a66fc1SZachary Turner 
38393a66fc1SZachary Turner   size_t bytes_sent = src_len;
384fee461b1SMichał Górny   error = m_io_sp->Write(src, bytes_sent);
38593a66fc1SZachary Turner 
386b9c1b51eSKate Stone   if (log) {
38763e5fb76SJonas Devlieghere     LLDB_LOGF(log,
38863e5fb76SJonas Devlieghere               "%p ConnectionFileDescriptor::Write(fd = %" PRIu64
38963e5fb76SJonas Devlieghere               ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)",
390b9c1b51eSKate Stone               static_cast<void *>(this),
391fee461b1SMichał Górny               static_cast<uint64_t>(m_io_sp->GetWaitableHandle()),
392b9c1b51eSKate Stone               static_cast<const void *>(src), static_cast<uint64_t>(src_len),
393b9c1b51eSKate Stone               static_cast<uint64_t>(bytes_sent), error.AsCString());
39493a66fc1SZachary Turner   }
39593a66fc1SZachary Turner 
39693a66fc1SZachary Turner   if (error_ptr)
39793a66fc1SZachary Turner     *error_ptr = error;
39893a66fc1SZachary Turner 
399b9c1b51eSKate Stone   if (error.Fail()) {
400b9c1b51eSKate Stone     switch (error.GetError()) {
40193a66fc1SZachary Turner     case EAGAIN:
40293a66fc1SZachary Turner     case EINTR:
40393a66fc1SZachary Turner       status = eConnectionStatusSuccess;
40493a66fc1SZachary Turner       return 0;
40593a66fc1SZachary Turner 
406b9c1b51eSKate Stone     case ECONNRESET: // The connection is closed by the peer during a read
407b9c1b51eSKate Stone                      // attempt on a socket.
40893a66fc1SZachary Turner     case ENOTCONN:   // A read is attempted on an unconnected socket.
40993a66fc1SZachary Turner       status = eConnectionStatusLostConnection;
41093a66fc1SZachary Turner       break; // Break to close....
41193a66fc1SZachary Turner 
41293a66fc1SZachary Turner     default:
41393a66fc1SZachary Turner       status = eConnectionStatusError;
41493a66fc1SZachary Turner       break; // Break to close....
41593a66fc1SZachary Turner     }
41693a66fc1SZachary Turner 
41793a66fc1SZachary Turner     return 0;
41893a66fc1SZachary Turner   }
41993a66fc1SZachary Turner 
42093a66fc1SZachary Turner   status = eConnectionStatusSuccess;
42193a66fc1SZachary Turner   return bytes_sent;
42293a66fc1SZachary Turner }
42393a66fc1SZachary Turner 
GetURI()424b9c1b51eSKate Stone std::string ConnectionFileDescriptor::GetURI() { return m_uri; }
425eb303ee5SVince Harron 
426b9c1b51eSKate Stone // This ConnectionFileDescriptor::BytesAvailable() uses select() via
427b9c1b51eSKate Stone // SelectHelper
42893a66fc1SZachary Turner //
42993a66fc1SZachary Turner // PROS:
43093a66fc1SZachary Turner //  - select is consistent across most unix platforms
43193a66fc1SZachary Turner //  - The Apple specific version allows for unlimited fds in the fd_sets by
43293a66fc1SZachary Turner //    setting the _DARWIN_UNLIMITED_SELECT define prior to including the
43393a66fc1SZachary Turner //    required header files.
43493a66fc1SZachary Turner // CONS:
43593a66fc1SZachary Turner //  - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE.
43693a66fc1SZachary Turner //     This implementation  will assert if it runs into that hard limit to let
43793a66fc1SZachary Turner //     users know that another ConnectionFileDescriptor::BytesAvailable() should
43893a66fc1SZachary Turner //     be used or a new version of ConnectionFileDescriptor::BytesAvailable()
43993a66fc1SZachary Turner //     should be written for the system that is running into the limitations.
44093a66fc1SZachary Turner 
4412f159a5fSPavel Labath ConnectionStatus
BytesAvailable(const Timeout<std::micro> & timeout,Status * error_ptr)4422f159a5fSPavel Labath ConnectionFileDescriptor::BytesAvailable(const Timeout<std::micro> &timeout,
44397206d57SZachary Turner                                          Status *error_ptr) {
444b9c1b51eSKate Stone   // Don't need to take the mutex here separately since we are only called from
44505097246SAdrian Prantl   // Read.  If we ever get used more generally we will need to lock here as
44605097246SAdrian Prantl   // well.
44793a66fc1SZachary Turner 
448a007a6d8SPavel Labath   Log *log = GetLog(LLDBLog::Connection);
449d02b1c83SPavel Labath   LLDB_LOG(log, "this = {0}, timeout = {1}", this, timeout);
45093a66fc1SZachary Turner 
45105097246SAdrian Prantl   // Make a copy of the file descriptors to make sure we don't have another
45205097246SAdrian Prantl   // thread change these values out from under us and cause problems in the
45305097246SAdrian Prantl   // loop below where like in FS_SET()
454fee461b1SMichał Górny   const IOObject::WaitableHandle handle = m_io_sp->GetWaitableHandle();
45593a66fc1SZachary Turner   const int pipe_fd = m_pipe.GetReadFileDescriptor();
45693a66fc1SZachary Turner 
457b9c1b51eSKate Stone   if (handle != IOObject::kInvalidHandleValue) {
458ee1f578dSGreg Clayton     SelectHelper select_helper;
4592f159a5fSPavel Labath     if (timeout)
4602f159a5fSPavel Labath       select_helper.SetTimeout(*timeout);
461ee1f578dSGreg Clayton 
462ee1f578dSGreg Clayton     select_helper.FDSetRead(handle);
4638b98f12aSMartin Storsjo #if defined(_WIN32)
464b9c1b51eSKate Stone     // select() won't accept pipes on Windows.  The entire Windows codepath
46505097246SAdrian Prantl     // needs to be converted over to using WaitForMultipleObjects and event
46605097246SAdrian Prantl     // HANDLEs, but for now at least this will allow ::select() to not return
46705097246SAdrian Prantl     // an error.
46893a66fc1SZachary Turner     const bool have_pipe_fd = false;
46993a66fc1SZachary Turner #else
47093a66fc1SZachary Turner     const bool have_pipe_fd = pipe_fd >= 0;
471ee1f578dSGreg Clayton #endif
47293a66fc1SZachary Turner     if (have_pipe_fd)
473ee1f578dSGreg Clayton       select_helper.FDSetRead(pipe_fd);
474ee1f578dSGreg Clayton 
475fee461b1SMichał Górny     while (handle == m_io_sp->GetWaitableHandle()) {
47693a66fc1SZachary Turner 
47797206d57SZachary Turner       Status error = select_helper.Select();
47893a66fc1SZachary Turner 
47993a66fc1SZachary Turner       if (error_ptr)
48093a66fc1SZachary Turner         *error_ptr = error;
48193a66fc1SZachary Turner 
482b9c1b51eSKate Stone       if (error.Fail()) {
483b9c1b51eSKate Stone         switch (error.GetError()) {
484b9c1b51eSKate Stone         case EBADF: // One of the descriptor sets specified an invalid
485b9c1b51eSKate Stone                     // descriptor.
48693a66fc1SZachary Turner           return eConnectionStatusLostConnection;
48793a66fc1SZachary Turner 
488b9c1b51eSKate Stone         case EINVAL: // The specified time limit is invalid. One of its
489b9c1b51eSKate Stone                      // components is negative or too large.
49093a66fc1SZachary Turner         default:     // Other unknown error
49193a66fc1SZachary Turner           return eConnectionStatusError;
49293a66fc1SZachary Turner 
493ee1f578dSGreg Clayton         case ETIMEDOUT:
494ee1f578dSGreg Clayton           return eConnectionStatusTimedOut;
495ee1f578dSGreg Clayton 
49693a66fc1SZachary Turner         case EAGAIN: // The kernel was (perhaps temporarily) unable to
49705097246SAdrian Prantl                      // allocate the requested number of file descriptors, or
49805097246SAdrian Prantl                      // we have non-blocking IO
49993a66fc1SZachary Turner         case EINTR:  // A signal was delivered before the time limit
50005097246SAdrian Prantl           // expired and before any of the selected events occurred.
50193a66fc1SZachary Turner           break; // Lets keep reading to until we timeout
50293a66fc1SZachary Turner         }
503b9c1b51eSKate Stone       } else {
504ee1f578dSGreg Clayton         if (select_helper.FDIsSetRead(handle))
50593a66fc1SZachary Turner           return eConnectionStatusSuccess;
506ee1f578dSGreg Clayton 
507b9c1b51eSKate Stone         if (select_helper.FDIsSetRead(pipe_fd)) {
50805097246SAdrian Prantl           // There is an interrupt or exit command in the command pipe Read the
50905097246SAdrian Prantl           // data from that pipe:
510c1a6b128SPavel Labath           char c;
511b9bd6af2SVince Harron 
51258d28b93SMichał Górny           ssize_t bytes_read =
51358d28b93SMichał Górny               llvm::sys::RetryAfterSignal(-1, ::read, pipe_fd, &c, 1);
514c1a6b128SPavel Labath           assert(bytes_read == 1);
515c1a6b128SPavel Labath           (void)bytes_read;
516c1a6b128SPavel Labath           switch (c) {
51793a66fc1SZachary Turner           case 'q':
51863e5fb76SJonas Devlieghere             LLDB_LOGF(log,
51963e5fb76SJonas Devlieghere                       "%p ConnectionFileDescriptor::BytesAvailable() "
520b9bd6af2SVince Harron                       "got data: %c from the command channel.",
521c1a6b128SPavel Labath                       static_cast<void *>(this), c);
52293a66fc1SZachary Turner             return eConnectionStatusEndOfFile;
52393a66fc1SZachary Turner           case 'i':
52493a66fc1SZachary Turner             // Interrupt the current read
52593a66fc1SZachary Turner             return eConnectionStatusInterrupted;
52693a66fc1SZachary Turner           }
52793a66fc1SZachary Turner         }
52893a66fc1SZachary Turner       }
52993a66fc1SZachary Turner     }
53093a66fc1SZachary Turner   }
53193a66fc1SZachary Turner 
53293a66fc1SZachary Turner   if (error_ptr)
53393a66fc1SZachary Turner     error_ptr->SetErrorString("not connected");
53493a66fc1SZachary Turner   return eConnectionStatusLostConnection;
53593a66fc1SZachary Turner }
53693a66fc1SZachary Turner 
AcceptSocket(Socket::SocketProtocol socket_protocol,llvm::StringRef socket_name,llvm::function_ref<void (Socket &)> post_listen_callback,Status * error_ptr)537e50f02baSMichał Górny lldb::ConnectionStatus ConnectionFileDescriptor::AcceptSocket(
538e50f02baSMichał Górny     Socket::SocketProtocol socket_protocol, llvm::StringRef socket_name,
539e50f02baSMichał Górny     llvm::function_ref<void(Socket &)> post_listen_callback,
54097206d57SZachary Turner     Status *error_ptr) {
54158d28b93SMichał Górny   Status error;
542e50f02baSMichał Górny   std::unique_ptr<Socket> listening_socket =
543e50f02baSMichał Górny       Socket::Create(socket_protocol, m_child_processes_inherit, error);
5448dbbe335SMed Ismail Bennani   Socket *accepted_socket;
545e50f02baSMichał Górny 
546e50f02baSMichał Górny   if (!error.Fail())
547e50f02baSMichał Górny     error = listening_socket->Listen(socket_name, 5);
548e50f02baSMichał Górny 
549e50f02baSMichał Górny   if (!error.Fail()) {
550e50f02baSMichał Górny     post_listen_callback(*listening_socket);
551e50f02baSMichał Górny     error = listening_socket->Accept(accepted_socket);
552e50f02baSMichał Górny   }
553e50f02baSMichał Górny 
554e50f02baSMichał Górny   if (!error.Fail()) {
555e50f02baSMichał Górny     m_io_sp.reset(accepted_socket);
556e50f02baSMichał Górny     m_uri.assign(socket_name.str());
557e50f02baSMichał Górny     return eConnectionStatusSuccess;
558e50f02baSMichał Górny   }
559e50f02baSMichał Górny 
5608dbbe335SMed Ismail Bennani   if (error_ptr)
5618dbbe335SMed Ismail Bennani     *error_ptr = error;
5628dbbe335SMed Ismail Bennani   return eConnectionStatusError;
563e50f02baSMichał Górny }
5648dbbe335SMed Ismail Bennani 
565e50f02baSMichał Górny lldb::ConnectionStatus
ConnectSocket(Socket::SocketProtocol socket_protocol,llvm::StringRef socket_name,Status * error_ptr)566e50f02baSMichał Górny ConnectionFileDescriptor::ConnectSocket(Socket::SocketProtocol socket_protocol,
567e50f02baSMichał Górny                                         llvm::StringRef socket_name,
568e50f02baSMichał Górny                                         Status *error_ptr) {
569e50f02baSMichał Górny   Status error;
570e50f02baSMichał Górny   std::unique_ptr<Socket> socket =
571e50f02baSMichał Górny       Socket::Create(socket_protocol, m_child_processes_inherit, error);
572e50f02baSMichał Górny 
573e50f02baSMichał Górny   if (!error.Fail())
574e50f02baSMichał Górny     error = socket->Connect(socket_name);
575e50f02baSMichał Górny 
576e50f02baSMichał Górny   if (!error.Fail()) {
577e50f02baSMichał Górny     m_io_sp = std::move(socket);
578e50f02baSMichał Górny     m_uri.assign(socket_name.str());
5798dbbe335SMed Ismail Bennani     return eConnectionStatusSuccess;
580eb303ee5SVince Harron   }
581e1acadb6SMichał Górny 
582e50f02baSMichał Górny   if (error_ptr)
583e50f02baSMichał Górny     *error_ptr = error;
584e50f02baSMichał Górny   return eConnectionStatusError;
585e50f02baSMichał Górny }
586e50f02baSMichał Górny 
AcceptNamedSocket(llvm::StringRef socket_name,socket_id_callback_type socket_id_callback,Status * error_ptr)587e50f02baSMichał Górny ConnectionStatus ConnectionFileDescriptor::AcceptNamedSocket(
588e50f02baSMichał Górny     llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
589e50f02baSMichał Górny     Status *error_ptr) {
590e50f02baSMichał Górny   return AcceptSocket(
591e50f02baSMichał Górny       Socket::ProtocolUnixDomain, socket_name,
592e50f02baSMichał Górny       [socket_id_callback, socket_name](Socket &listening_socket) {
593e50f02baSMichał Górny         socket_id_callback(socket_name);
594e50f02baSMichał Górny       },
595e50f02baSMichał Górny       error_ptr);
596e50f02baSMichał Górny }
597e50f02baSMichał Górny 
ConnectNamedSocket(llvm::StringRef socket_name,socket_id_callback_type socket_id_callback,Status * error_ptr)598e50f02baSMichał Górny ConnectionStatus ConnectionFileDescriptor::ConnectNamedSocket(
599e50f02baSMichał Górny     llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
600e50f02baSMichał Górny     Status *error_ptr) {
601e50f02baSMichał Górny   return ConnectSocket(Socket::ProtocolUnixDomain, socket_name, error_ptr);
602e50f02baSMichał Górny }
603e50f02baSMichał Górny 
AcceptAbstractSocket(llvm::StringRef socket_name,socket_id_callback_type socket_id_callback,Status * error_ptr)604e50f02baSMichał Górny ConnectionStatus ConnectionFileDescriptor::AcceptAbstractSocket(
605e50f02baSMichał Górny     llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
606e50f02baSMichał Górny     Status *error_ptr) {
607e50f02baSMichał Górny   return AcceptSocket(
608e50f02baSMichał Górny       Socket::ProtocolUnixAbstract, socket_name,
609e50f02baSMichał Górny       [socket_id_callback, socket_name](Socket &listening_socket) {
610e50f02baSMichał Górny         socket_id_callback(socket_name);
611e50f02baSMichał Górny       },
612e50f02baSMichał Górny       error_ptr);
613e50f02baSMichał Górny }
614e50f02baSMichał Górny 
ConnectAbstractSocket(llvm::StringRef socket_name,socket_id_callback_type socket_id_callback,Status * error_ptr)615e50f02baSMichał Górny lldb::ConnectionStatus ConnectionFileDescriptor::ConnectAbstractSocket(
616e50f02baSMichał Górny     llvm::StringRef socket_name, socket_id_callback_type socket_id_callback,
617e50f02baSMichał Górny     Status *error_ptr) {
618e50f02baSMichał Górny   return ConnectSocket(Socket::ProtocolUnixAbstract, socket_name, error_ptr);
619e50f02baSMichał Górny }
620e50f02baSMichał Górny 
621e1acadb6SMichał Górny ConnectionStatus
AcceptTCP(llvm::StringRef socket_name,socket_id_callback_type socket_id_callback,Status * error_ptr)622e50f02baSMichał Górny ConnectionFileDescriptor::AcceptTCP(llvm::StringRef socket_name,
623e1acadb6SMichał Górny                                     socket_id_callback_type socket_id_callback,
624e1acadb6SMichał Górny                                     Status *error_ptr) {
625e50f02baSMichał Górny   ConnectionStatus ret = AcceptSocket(
626e50f02baSMichał Górny       Socket::ProtocolTcp, socket_name,
627e50f02baSMichał Górny       [socket_id_callback](Socket &listening_socket) {
628e50f02baSMichał Górny         uint16_t port =
629e50f02baSMichał Górny             static_cast<TCPSocket &>(listening_socket).GetLocalPortNumber();
630e50f02baSMichał Górny         socket_id_callback(std::to_string(port));
631e50f02baSMichał Górny       },
632e50f02baSMichał Górny       error_ptr);
633e50f02baSMichał Górny   if (ret == eConnectionStatusSuccess)
634e50f02baSMichał Górny     m_uri.assign(
635e50f02baSMichał Górny         static_cast<TCPSocket *>(m_io_sp.get())->GetRemoteConnectionURI());
636e50f02baSMichał Górny   return ret;
6378dbbe335SMed Ismail Bennani }
638e50f02baSMichał Górny 
639e50f02baSMichał Górny ConnectionStatus
ConnectTCP(llvm::StringRef socket_name,socket_id_callback_type socket_id_callback,Status * error_ptr)640e50f02baSMichał Górny ConnectionFileDescriptor::ConnectTCP(llvm::StringRef socket_name,
641e50f02baSMichał Górny                                      socket_id_callback_type socket_id_callback,
642e50f02baSMichał Górny                                      Status *error_ptr) {
643e50f02baSMichał Górny   return ConnectSocket(Socket::ProtocolTcp, socket_name, error_ptr);
64493a66fc1SZachary Turner }
64593a66fc1SZachary Turner 
64658d28b93SMichał Górny ConnectionStatus
ConnectUDP(llvm::StringRef s,socket_id_callback_type socket_id_callback,Status * error_ptr)64758d28b93SMichał Górny ConnectionFileDescriptor::ConnectUDP(llvm::StringRef s,
64858d28b93SMichał Górny                                      socket_id_callback_type socket_id_callback,
64997206d57SZachary Turner                                      Status *error_ptr) {
65093a66fc1SZachary Turner   if (error_ptr)
651c9e6b701SPavel Labath     *error_ptr = Status();
652c9e6b701SPavel Labath   llvm::Expected<std::unique_ptr<UDPSocket>> socket =
653c9e6b701SPavel Labath       Socket::UdpConnect(s, m_child_processes_inherit);
654c9e6b701SPavel Labath   if (!socket) {
655c9e6b701SPavel Labath     if (error_ptr)
656c9e6b701SPavel Labath       *error_ptr = socket.takeError();
657c9e6b701SPavel Labath     else
658a007a6d8SPavel Labath       LLDB_LOG_ERROR(GetLog(LLDBLog::Connection), socket.takeError(),
659a007a6d8SPavel Labath                      "tcp connect failed: {0}");
660eb303ee5SVince Harron     return eConnectionStatusError;
661eb303ee5SVince Harron   }
662fee461b1SMichał Górny   m_io_sp = std::move(*socket);
663adcd0268SBenjamin Kramer   m_uri.assign(std::string(s));
664eb303ee5SVince Harron   return eConnectionStatusSuccess;
66593a66fc1SZachary Turner }
66693a66fc1SZachary Turner 
66758d28b93SMichał Górny ConnectionStatus
ConnectFD(llvm::StringRef s,socket_id_callback_type socket_id_callback,Status * error_ptr)66858d28b93SMichał Górny ConnectionFileDescriptor::ConnectFD(llvm::StringRef s,
66958d28b93SMichał Górny                                     socket_id_callback_type socket_id_callback,
6704b46a413SMichał Górny                                     Status *error_ptr) {
6714b46a413SMichał Górny #if LLDB_ENABLE_POSIX
6724b46a413SMichał Górny   // Just passing a native file descriptor within this current process that
6734b46a413SMichał Górny   // is already opened (possibly from a service or other source).
6744b46a413SMichał Górny   int fd = -1;
6754b46a413SMichał Górny 
6764b46a413SMichał Górny   if (!s.getAsInteger(0, fd)) {
6774b46a413SMichał Górny     // We have what looks to be a valid file descriptor, but we should make
6784b46a413SMichał Górny     // sure it is. We currently are doing this by trying to get the flags
6794b46a413SMichał Górny     // from the file descriptor and making sure it isn't a bad fd.
6804b46a413SMichał Górny     errno = 0;
6814b46a413SMichał Górny     int flags = ::fcntl(fd, F_GETFL, 0);
6824b46a413SMichał Górny     if (flags == -1 || errno == EBADF) {
6834b46a413SMichał Górny       if (error_ptr)
6844b46a413SMichał Górny         error_ptr->SetErrorStringWithFormat("stale file descriptor: %s",
6854b46a413SMichał Górny                                             s.str().c_str());
686fee461b1SMichał Górny       m_io_sp.reset();
6874b46a413SMichał Górny       return eConnectionStatusError;
6884b46a413SMichał Górny     } else {
6894b46a413SMichał Górny       // Don't take ownership of a file descriptor that gets passed to us
6904b46a413SMichał Górny       // since someone else opened the file descriptor and handed it to us.
6914b46a413SMichał Górny       // TODO: Since are using a URL to open connection we should
6924b46a413SMichał Górny       // eventually parse options using the web standard where we have
6934b46a413SMichał Górny       // "fd://123?opt1=value;opt2=value" and we can have an option be
6944b46a413SMichał Górny       // "owns=1" or "owns=0" or something like this to allow us to specify
6954b46a413SMichał Górny       // this. For now, we assume we must assume we don't own it.
6964b46a413SMichał Górny 
6974b46a413SMichał Górny       std::unique_ptr<TCPSocket> tcp_socket;
6984b46a413SMichał Górny       tcp_socket = std::make_unique<TCPSocket>(fd, false, false);
6994b46a413SMichał Górny       // Try and get a socket option from this file descriptor to see if
7004b46a413SMichał Górny       // this is a socket and set m_is_socket accordingly.
7014b46a413SMichał Górny       int resuse;
7024b46a413SMichał Górny       bool is_socket =
7034b46a413SMichał Górny           !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse);
704fee461b1SMichał Górny       if (is_socket)
705fee461b1SMichał Górny         m_io_sp = std::move(tcp_socket);
706fee461b1SMichał Górny       else
707fee461b1SMichał Górny         m_io_sp =
7084b46a413SMichał Górny             std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, false);
7094b46a413SMichał Górny       m_uri = s.str();
7104b46a413SMichał Górny       return eConnectionStatusSuccess;
7114b46a413SMichał Górny     }
7124b46a413SMichał Górny   }
7134b46a413SMichał Górny 
7144b46a413SMichał Górny   if (error_ptr)
7154b46a413SMichał Górny     error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"%s\"",
7164b46a413SMichał Górny                                         s.str().c_str());
717fee461b1SMichał Górny   m_io_sp.reset();
7184b46a413SMichał Górny   return eConnectionStatusError;
7194b46a413SMichał Górny #endif // LLDB_ENABLE_POSIX
7204b46a413SMichał Górny   llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX");
7214b46a413SMichał Górny }
7224b46a413SMichał Górny 
ConnectFile(llvm::StringRef s,socket_id_callback_type socket_id_callback,Status * error_ptr)72358d28b93SMichał Górny ConnectionStatus ConnectionFileDescriptor::ConnectFile(
72458d28b93SMichał Górny     llvm::StringRef s, socket_id_callback_type socket_id_callback,
7254b46a413SMichał Górny     Status *error_ptr) {
7264b46a413SMichał Górny #if LLDB_ENABLE_POSIX
7274b46a413SMichał Górny   std::string addr_str = s.str();
7284b46a413SMichał Górny   // file:///PATH
7294b46a413SMichał Górny   int fd = llvm::sys::RetryAfterSignal(-1, ::open, addr_str.c_str(), O_RDWR);
7304b46a413SMichał Górny   if (fd == -1) {
7314b46a413SMichał Górny     if (error_ptr)
7324b46a413SMichał Górny       error_ptr->SetErrorToErrno();
7334b46a413SMichał Górny     return eConnectionStatusError;
7344b46a413SMichał Górny   }
7354b46a413SMichał Górny 
7364b46a413SMichał Górny   if (::isatty(fd)) {
7374b46a413SMichał Górny     // Set up serial terminal emulation
7384b46a413SMichał Górny     struct termios options;
7394b46a413SMichał Górny     ::tcgetattr(fd, &options);
7404b46a413SMichał Górny 
7414b46a413SMichał Górny     // Set port speed to maximum
7424b46a413SMichał Górny     ::cfsetospeed(&options, B115200);
7434b46a413SMichał Górny     ::cfsetispeed(&options, B115200);
7444b46a413SMichał Górny 
7454b46a413SMichał Górny     // Raw input, disable echo and signals
7464b46a413SMichał Górny     options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
7474b46a413SMichał Górny 
7484b46a413SMichał Górny     // Make sure only one character is needed to return from a read
7494b46a413SMichał Górny     options.c_cc[VMIN] = 1;
7504b46a413SMichał Górny     options.c_cc[VTIME] = 0;
7514b46a413SMichał Górny 
7524b46a413SMichał Górny     llvm::sys::RetryAfterSignal(-1, ::tcsetattr, fd, TCSANOW, &options);
7534b46a413SMichał Górny   }
7544b46a413SMichał Górny 
75558d28b93SMichał Górny   m_io_sp = std::make_shared<NativeFile>(fd, File::eOpenOptionReadWrite, true);
7564b46a413SMichał Górny   return eConnectionStatusSuccess;
7574b46a413SMichał Górny #endif // LLDB_ENABLE_POSIX
7584b46a413SMichał Górny   llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX");
7594b46a413SMichał Górny }
7604b46a413SMichał Górny 
ConnectSerialPort(llvm::StringRef s,socket_id_callback_type socket_id_callback,Status * error_ptr)76158d28b93SMichał Górny ConnectionStatus ConnectionFileDescriptor::ConnectSerialPort(
76258d28b93SMichał Górny     llvm::StringRef s, socket_id_callback_type socket_id_callback,
7634a7b4beaSMichał Górny     Status *error_ptr) {
7644a7b4beaSMichał Górny #if LLDB_ENABLE_POSIX
7654a7b4beaSMichał Górny   llvm::StringRef path, qs;
7664a7b4beaSMichał Górny   // serial:///PATH?k1=v1&k2=v2...
7674a7b4beaSMichał Górny   std::tie(path, qs) = s.split('?');
7684a7b4beaSMichał Górny 
7694a7b4beaSMichał Górny   llvm::Expected<SerialPort::Options> serial_options =
7704a7b4beaSMichał Górny       SerialPort::OptionsFromURL(qs);
7714a7b4beaSMichał Górny   if (!serial_options) {
7724a7b4beaSMichał Górny     if (error_ptr)
7734a7b4beaSMichał Górny       *error_ptr = serial_options.takeError();
7744a7b4beaSMichał Górny     else
7754a7b4beaSMichał Górny       llvm::consumeError(serial_options.takeError());
7764a7b4beaSMichał Górny     return eConnectionStatusError;
7774a7b4beaSMichał Górny   }
7784a7b4beaSMichał Górny 
7794a7b4beaSMichał Górny   int fd = llvm::sys::RetryAfterSignal(-1, ::open, path.str().c_str(), O_RDWR);
7804a7b4beaSMichał Górny   if (fd == -1) {
7814a7b4beaSMichał Górny     if (error_ptr)
7824a7b4beaSMichał Górny       error_ptr->SetErrorToErrno();
7834a7b4beaSMichał Górny     return eConnectionStatusError;
7844a7b4beaSMichał Górny   }
7854a7b4beaSMichał Górny 
7864a7b4beaSMichał Górny   llvm::Expected<std::unique_ptr<SerialPort>> serial_sp = SerialPort::Create(
7874a7b4beaSMichał Górny       fd, File::eOpenOptionReadWrite, serial_options.get(), true);
7884a7b4beaSMichał Górny   if (!serial_sp) {
7894a7b4beaSMichał Górny     if (error_ptr)
7904a7b4beaSMichał Górny       *error_ptr = serial_sp.takeError();
7914a7b4beaSMichał Górny     else
7924a7b4beaSMichał Górny       llvm::consumeError(serial_sp.takeError());
7934a7b4beaSMichał Górny     return eConnectionStatusError;
7944a7b4beaSMichał Górny   }
7954a7b4beaSMichał Górny   m_io_sp = std::move(serial_sp.get());
7964a7b4beaSMichał Górny 
7974a7b4beaSMichał Górny   return eConnectionStatusSuccess;
7984a7b4beaSMichał Górny #endif // LLDB_ENABLE_POSIX
7994a7b4beaSMichał Górny   llvm_unreachable("this function should be only called w/ LLDB_ENABLE_POSIX");
8004a7b4beaSMichał Górny }
8014a7b4beaSMichał Górny 
GetChildProcessesInherit() const802b9c1b51eSKate Stone bool ConnectionFileDescriptor::GetChildProcessesInherit() const {
803477e42a6SOleksiy Vyalov   return m_child_processes_inherit;
804477e42a6SOleksiy Vyalov }
805477e42a6SOleksiy Vyalov 
SetChildProcessesInherit(bool child_processes_inherit)806b9c1b51eSKate Stone void ConnectionFileDescriptor::SetChildProcessesInherit(
807b9c1b51eSKate Stone     bool child_processes_inherit) {
808477e42a6SOleksiy Vyalov   m_child_processes_inherit = child_processes_inherit;
809477e42a6SOleksiy Vyalov }
810a0e70cd4SRobert Flack 
InitializeSocket(Socket * socket)811b9c1b51eSKate Stone void ConnectionFileDescriptor::InitializeSocket(Socket *socket) {
812fee461b1SMichał Górny   m_io_sp.reset(socket);
813d5560951SAntonio Afonso   m_uri = socket->GetRemoteConnectionURI();
814a0e70cd4SRobert Flack }
815