1 //===-- ConnectionFileDescriptorPosix.cpp -----------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #if defined(__APPLE__)
11 // Enable this special support for Apple builds where we can have unlimited
12 // select bounds. We tried switching to poll() and kqueue and we were panicing
13 // the kernel, so we have to stick with select for now.
14 #define _DARWIN_UNLIMITED_SELECT
15 #endif
16 
17 #include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
18 #include "lldb/Host/Config.h"
19 #include "lldb/Host/IOObject.h"
20 #include "lldb/Host/SocketAddress.h"
21 #include "lldb/Host/Socket.h"
22 #include "lldb/Host/StringConvert.h"
23 
24 // C Includes
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 
31 #ifndef LLDB_DISABLE_POSIX
32 #include <termios.h>
33 #endif
34 
35 // C++ Includes
36 #include <sstream>
37 
38 // Other libraries and framework includes
39 #include "llvm/Support/ErrorHandling.h"
40 #if defined(__APPLE__)
41 #include "llvm/ADT/SmallVector.h"
42 #endif
43 // Project includes
44 #include "lldb/Core/Communication.h"
45 #include "lldb/Core/Log.h"
46 #include "lldb/Core/StreamString.h"
47 #include "lldb/Core/Timer.h"
48 #include "lldb/Host/Host.h"
49 #include "lldb/Host/Socket.h"
50 #include "lldb/Interpreter/Args.h"
51 
52 #include "Utility/UriParser.h"
53 
54 using namespace lldb;
55 using namespace lldb_private;
56 
57 ConnectionFileDescriptor::ConnectionFileDescriptor(bool child_processes_inherit)
58     : Connection()
59     , m_pipe()
60     , m_mutex(Mutex::eMutexTypeRecursive)
61     , m_shutting_down(false)
62     , m_waiting_for_accept(false)
63     , m_child_processes_inherit(child_processes_inherit)
64 {
65     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT));
66     if (log)
67         log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", static_cast<void *>(this));
68 }
69 
70 ConnectionFileDescriptor::ConnectionFileDescriptor(int fd, bool owns_fd)
71     : Connection()
72     , m_pipe()
73     , m_mutex(Mutex::eMutexTypeRecursive)
74     , m_shutting_down(false)
75     , m_waiting_for_accept(false)
76     , m_child_processes_inherit(false)
77 {
78     m_write_sp.reset(new File(fd, owns_fd));
79     m_read_sp.reset(new File(fd, false));
80 
81     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT));
82     if (log)
83         log->Printf("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", static_cast<void *>(this), fd,
84                     owns_fd);
85     OpenCommandPipe();
86 }
87 
88 ConnectionFileDescriptor::ConnectionFileDescriptor(Socket* socket)
89     : Connection()
90     , m_pipe()
91     , m_mutex(Mutex::eMutexTypeRecursive)
92     , m_shutting_down(false)
93     , m_waiting_for_accept(false)
94     , m_child_processes_inherit(false)
95 {
96     InitializeSocket(socket);
97 }
98 
99 ConnectionFileDescriptor::~ConnectionFileDescriptor()
100 {
101     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT));
102     if (log)
103         log->Printf("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", static_cast<void *>(this));
104     Disconnect(NULL);
105     CloseCommandPipe();
106 }
107 
108 void
109 ConnectionFileDescriptor::OpenCommandPipe()
110 {
111     CloseCommandPipe();
112 
113     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
114     // Make the command file descriptor here:
115     Error result = m_pipe.CreateNew(m_child_processes_inherit);
116     if (!result.Success())
117     {
118         if (log)
119             log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe () - could not make pipe: %s", static_cast<void *>(this),
120                         result.AsCString());
121     }
122     else
123     {
124         if (log)
125             log->Printf("%p ConnectionFileDescriptor::OpenCommandPipe() - success readfd=%d writefd=%d", static_cast<void *>(this),
126                         m_pipe.GetReadFileDescriptor(), m_pipe.GetWriteFileDescriptor());
127     }
128 }
129 
130 void
131 ConnectionFileDescriptor::CloseCommandPipe()
132 {
133     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
134     if (log)
135         log->Printf("%p ConnectionFileDescriptor::CloseCommandPipe()", static_cast<void *>(this));
136 
137     m_pipe.Close();
138 }
139 
140 bool
141 ConnectionFileDescriptor::IsConnected() const
142 {
143     return (m_read_sp && m_read_sp->IsValid()) || (m_write_sp && m_write_sp->IsValid());
144 }
145 
146 ConnectionStatus
147 ConnectionFileDescriptor::Connect(const char *s, Error *error_ptr)
148 {
149     Mutex::Locker locker(m_mutex);
150     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
151     if (log)
152         log->Printf("%p ConnectionFileDescriptor::Connect (url = '%s')", static_cast<void *>(this), s);
153 
154     OpenCommandPipe();
155 
156     if (s && s[0])
157     {
158         if (strstr(s, "listen://") == s)
159         {
160             // listen://HOST:PORT
161             return SocketListenAndAccept(s + strlen("listen://"), error_ptr);
162         }
163         else if (strstr(s, "accept://") == s)
164         {
165             // unix://SOCKNAME
166             return NamedSocketAccept(s + strlen("accept://"), error_ptr);
167         }
168         else if (strstr(s, "unix-accept://") == s)
169         {
170             // unix://SOCKNAME
171             return NamedSocketAccept(s + strlen("unix-accept://"), error_ptr);
172         }
173         else if (strstr(s, "adb://") == s)
174         {
175             int port = -1;
176             std::string scheme, host, path;
177             if (!UriParser::Parse(s, scheme, host, port, path))
178             {
179                 if (error_ptr)
180                     error_ptr->SetErrorStringWithFormat("Failed to parse URL '%s'", s);
181                 return eConnectionStatusError;
182             }
183             std::ostringstream host_and_port;
184             host_and_port << "localhost:" << port;
185             return ConnectTCP(host_and_port.str().c_str(), error_ptr);
186         }
187         else if (strstr(s, "connect://") == s)
188         {
189             return ConnectTCP(s + strlen("connect://"), error_ptr);
190         }
191         else if (strstr(s, "tcp-connect://") == s)
192         {
193             return ConnectTCP(s + strlen("tcp-connect://"), error_ptr);
194         }
195         else if (strstr(s, "udp://") == s)
196         {
197             return ConnectUDP(s + strlen("udp://"), error_ptr);
198         }
199 #ifndef LLDB_DISABLE_POSIX
200         else if (strstr(s, "fd://") == s)
201         {
202             // Just passing a native file descriptor within this current process
203             // that is already opened (possibly from a service or other source).
204             s += strlen("fd://");
205             bool success = false;
206             int fd = StringConvert::ToSInt32(s, -1, 0, &success);
207 
208             if (success)
209             {
210                 // We have what looks to be a valid file descriptor, but we
211                 // should make sure it is. We currently are doing this by trying to
212                 // get the flags from the file descriptor and making sure it
213                 // isn't a bad fd.
214                 errno = 0;
215                 int flags = ::fcntl(fd, F_GETFL, 0);
216                 if (flags == -1 || errno == EBADF)
217                 {
218                     if (error_ptr)
219                         error_ptr->SetErrorStringWithFormat("stale file descriptor: %s", s);
220                     m_read_sp.reset();
221                     m_write_sp.reset();
222                     return eConnectionStatusError;
223                 }
224                 else
225                 {
226                     // Don't take ownership of a file descriptor that gets passed
227                     // to us since someone else opened the file descriptor and
228                     // handed it to us.
229                     // TODO: Since are using a URL to open connection we should
230                     // eventually parse options using the web standard where we
231                     // have "fd://123?opt1=value;opt2=value" and we can have an
232                     // option be "owns=1" or "owns=0" or something like this to
233                     // allow us to specify this. For now, we assume we must
234                     // assume we don't own it.
235 
236                     std::unique_ptr<Socket> tcp_socket;
237                     tcp_socket.reset(new Socket(fd, Socket::ProtocolTcp, false));
238                     // Try and get a socket option from this file descriptor to
239                     // see if this is a socket and set m_is_socket accordingly.
240                     int resuse;
241                     bool is_socket = !!tcp_socket->GetOption(SOL_SOCKET, SO_REUSEADDR, resuse);
242                     if (is_socket)
243                     {
244                         m_read_sp = std::move(tcp_socket);
245                         m_write_sp = m_read_sp;
246                     }
247                     else
248                     {
249                         m_read_sp.reset(new File(fd, false));
250                         m_write_sp.reset(new File(fd, false));
251                     }
252                     m_uri.assign(s);
253                     return eConnectionStatusSuccess;
254                 }
255             }
256 
257             if (error_ptr)
258                 error_ptr->SetErrorStringWithFormat("invalid file descriptor: \"fd://%s\"", s);
259             m_read_sp.reset();
260             m_write_sp.reset();
261             return eConnectionStatusError;
262         }
263         else if (strstr(s, "file://") == s)
264         {
265             // file:///PATH
266             const char *path = s + strlen("file://");
267             int fd = -1;
268             do
269             {
270                 fd = ::open(path, O_RDWR);
271             } while (fd == -1 && errno == EINTR);
272 
273             if (fd == -1)
274             {
275                 if (error_ptr)
276                     error_ptr->SetErrorToErrno();
277                 return eConnectionStatusError;
278             }
279 
280             if (::isatty(fd))
281             {
282                 // Set up serial terminal emulation
283                 struct termios options;
284                 ::tcgetattr(fd, &options);
285 
286                 // Set port speed to maximum
287                 ::cfsetospeed(&options, B115200);
288                 ::cfsetispeed(&options, B115200);
289 
290                 // Raw input, disable echo and signals
291                 options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
292 
293                 // Make sure only one character is needed to return from a read
294                 options.c_cc[VMIN] = 1;
295                 options.c_cc[VTIME] = 0;
296 
297                 ::tcsetattr(fd, TCSANOW, &options);
298             }
299 
300             int flags = ::fcntl(fd, F_GETFL, 0);
301             if (flags >= 0)
302             {
303                 if ((flags & O_NONBLOCK) == 0)
304                 {
305                     flags |= O_NONBLOCK;
306                     ::fcntl(fd, F_SETFL, flags);
307                 }
308             }
309             m_read_sp.reset(new File(fd, true));
310             m_write_sp.reset(new File(fd, false));
311             return eConnectionStatusSuccess;
312         }
313 #endif
314         if (error_ptr)
315             error_ptr->SetErrorStringWithFormat("unsupported connection URL: '%s'", s);
316         return eConnectionStatusError;
317     }
318     if (error_ptr)
319         error_ptr->SetErrorString("invalid connect arguments");
320     return eConnectionStatusError;
321 }
322 
323 bool
324 ConnectionFileDescriptor::InterruptRead()
325 {
326     size_t bytes_written = 0;
327     Error result = m_pipe.Write("i", 1, bytes_written);
328     return result.Success();
329 }
330 
331 ConnectionStatus
332 ConnectionFileDescriptor::Disconnect(Error *error_ptr)
333 {
334     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
335     if (log)
336         log->Printf("%p ConnectionFileDescriptor::Disconnect ()", static_cast<void *>(this));
337 
338     ConnectionStatus status = eConnectionStatusSuccess;
339 
340     if (!IsConnected())
341     {
342         if (log)
343             log->Printf("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", static_cast<void *>(this));
344         return eConnectionStatusSuccess;
345     }
346 
347     if (m_read_sp && m_read_sp->IsValid() && m_read_sp->GetFdType() == IOObject::eFDTypeSocket)
348         static_cast<Socket &>(*m_read_sp).PreDisconnect();
349 
350     // Try to get the ConnectionFileDescriptor's mutex.  If we fail, that is quite likely
351     // because somebody is doing a blocking read on our file descriptor.  If that's the case,
352     // then send the "q" char to the command file channel so the read will wake up and the connection
353     // will then know to shut down.
354 
355     m_shutting_down = true;
356 
357     Mutex::Locker locker;
358     bool got_lock = locker.TryLock(m_mutex);
359 
360     if (!got_lock)
361     {
362         if (m_pipe.CanWrite())
363         {
364             size_t bytes_written = 0;
365             Error result = m_pipe.Write("q", 1, bytes_written);
366             if (log)
367                 log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, sent 'q' to %d, error = '%s'.",
368                             static_cast<void *>(this), m_pipe.GetWriteFileDescriptor(), result.AsCString());
369         }
370         else if (log)
371         {
372             log->Printf("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, but no command pipe is available.",
373                         static_cast<void *>(this));
374         }
375         locker.Lock(m_mutex);
376     }
377 
378     Error error = m_read_sp->Close();
379     Error error2 = m_write_sp->Close();
380     if (error.Fail() || error2.Fail())
381         status = eConnectionStatusError;
382     if (error_ptr)
383         *error_ptr = error.Fail() ? error : error2;
384 
385     // Close any pipes we were using for async interrupts
386     m_pipe.Close();
387 
388     m_uri.clear();
389     m_shutting_down = false;
390     return status;
391 }
392 
393 size_t
394 ConnectionFileDescriptor::Read(void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr)
395 {
396     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
397 
398     Mutex::Locker locker;
399     bool got_lock = locker.TryLock(m_mutex);
400     if (!got_lock)
401     {
402         if (log)
403             log->Printf("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", static_cast<void *>(this));
404         if (error_ptr)
405             error_ptr->SetErrorString("failed to get the connection lock for read.");
406 
407         status = eConnectionStatusTimedOut;
408         return 0;
409     }
410 
411     if (m_shutting_down)
412     {
413         status = eConnectionStatusError;
414         return 0;
415     }
416 
417     status = BytesAvailable(timeout_usec, error_ptr);
418     if (status != eConnectionStatusSuccess)
419         return 0;
420 
421     Error error;
422     size_t bytes_read = dst_len;
423     error = m_read_sp->Read(dst, bytes_read);
424 
425     if (log)
426     {
427         log->Printf("%p ConnectionFileDescriptor::Read()  fd = %" PRIu64 ", dst = %p, dst_len = %" PRIu64 ") => %" PRIu64 ", error = %s",
428                     static_cast<void *>(this), static_cast<uint64_t>(m_read_sp->GetWaitableHandle()), static_cast<void *>(dst),
429                     static_cast<uint64_t>(dst_len), static_cast<uint64_t>(bytes_read), error.AsCString());
430     }
431 
432     if (bytes_read == 0)
433     {
434         error.Clear(); // End-of-file.  Do not automatically close; pass along for the end-of-file handlers.
435         status = eConnectionStatusEndOfFile;
436     }
437 
438     if (error_ptr)
439         *error_ptr = error;
440 
441     if (error.Fail())
442     {
443         uint32_t error_value = error.GetError();
444         switch (error_value)
445         {
446             case EAGAIN: // The file was marked for non-blocking I/O, and no data were ready to be read.
447                 if (m_read_sp->GetFdType() == IOObject::eFDTypeSocket)
448                     status = eConnectionStatusTimedOut;
449                 else
450                     status = eConnectionStatusSuccess;
451                 return 0;
452 
453             case EFAULT:  // Buf points outside the allocated address space.
454             case EINTR:   // A read from a slow device was interrupted before any data arrived by the delivery of a signal.
455             case EINVAL:  // The pointer associated with fildes was negative.
456             case EIO:     // An I/O error occurred while reading from the file system.
457                           // The process group is orphaned.
458                           // The file is a regular file, nbyte is greater than 0,
459                           // the starting position is before the end-of-file, and
460                           // the starting position is greater than or equal to the
461                           // offset maximum established for the open file
462                           // descriptor associated with fildes.
463             case EISDIR:  // An attempt is made to read a directory.
464             case ENOBUFS: // An attempt to allocate a memory buffer fails.
465             case ENOMEM:  // Insufficient memory is available.
466                 status = eConnectionStatusError;
467                 break; // Break to close....
468 
469             case ENOENT:     // no such file or directory
470             case EBADF:      // fildes is not a valid file or socket descriptor open for reading.
471             case ENXIO:      // An action is requested of a device that does not exist..
472                              // A requested action cannot be performed by the device.
473             case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket.
474             case ENOTCONN:   // A read is attempted on an unconnected socket.
475                 status = eConnectionStatusLostConnection;
476                 break; // Break to close....
477 
478             case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket.
479                 status = eConnectionStatusTimedOut;
480                 return 0;
481 
482             default:
483                 if (log)
484                     log->Printf("%p ConnectionFileDescriptor::Read (), unexpected error: %s", static_cast<void *>(this),
485                                 strerror(error_value));
486                 status = eConnectionStatusError;
487                 break; // Break to close....
488         }
489 
490         return 0;
491     }
492     return bytes_read;
493 }
494 
495 size_t
496 ConnectionFileDescriptor::Write(const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr)
497 {
498     Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_CONNECTION));
499     if (log)
500         log->Printf("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", static_cast<void *>(this),
501                     static_cast<const void *>(src), static_cast<uint64_t>(src_len));
502 
503     if (!IsConnected())
504     {
505         if (error_ptr)
506             error_ptr->SetErrorString("not connected");
507         status = eConnectionStatusNoConnection;
508         return 0;
509     }
510 
511     Error error;
512 
513     size_t bytes_sent = src_len;
514     error = m_write_sp->Write(src, bytes_sent);
515 
516     if (log)
517     {
518         log->Printf("%p ConnectionFileDescriptor::Write(fd = %" PRIu64 ", src = %p, src_len = %" PRIu64 ") => %" PRIu64 " (error = %s)",
519                     static_cast<void *>(this), static_cast<uint64_t>(m_write_sp->GetWaitableHandle()), static_cast<const void *>(src),
520                     static_cast<uint64_t>(src_len), static_cast<uint64_t>(bytes_sent), error.AsCString());
521     }
522 
523     if (error_ptr)
524         *error_ptr = error;
525 
526     if (error.Fail())
527     {
528         switch (error.GetError())
529         {
530             case EAGAIN:
531             case EINTR:
532                 status = eConnectionStatusSuccess;
533                 return 0;
534 
535             case ECONNRESET: // The connection is closed by the peer during a read attempt on a socket.
536             case ENOTCONN:   // A read is attempted on an unconnected socket.
537                 status = eConnectionStatusLostConnection;
538                 break; // Break to close....
539 
540             default:
541                 status = eConnectionStatusError;
542                 break; // Break to close....
543         }
544 
545         return 0;
546     }
547 
548     status = eConnectionStatusSuccess;
549     return bytes_sent;
550 }
551 
552 std::string
553 ConnectionFileDescriptor::GetURI()
554 {
555     return m_uri;
556 }
557 
558 // This ConnectionFileDescriptor::BytesAvailable() uses select().
559 //
560 // PROS:
561 //  - select is consistent across most unix platforms
562 //  - The Apple specific version allows for unlimited fds in the fd_sets by
563 //    setting the _DARWIN_UNLIMITED_SELECT define prior to including the
564 //    required header files.
565 // CONS:
566 //  - on non-Apple platforms, only supports file descriptors up to FD_SETSIZE.
567 //     This implementation  will assert if it runs into that hard limit to let
568 //     users know that another ConnectionFileDescriptor::BytesAvailable() should
569 //     be used or a new version of ConnectionFileDescriptor::BytesAvailable()
570 //     should be written for the system that is running into the limitations.
571 
572 #if defined(__APPLE__)
573 #define FD_SET_DATA(fds) fds.data()
574 #else
575 #define FD_SET_DATA(fds) &fds
576 #endif
577 
578 ConnectionStatus
579 ConnectionFileDescriptor::BytesAvailable(uint32_t timeout_usec, Error *error_ptr)
580 {
581     // Don't need to take the mutex here separately since we are only called from Read.  If we
582     // ever get used more generally we will need to lock here as well.
583 
584     Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_CONNECTION));
585     if (log)
586         log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", static_cast<void *>(this), timeout_usec);
587 
588     struct timeval *tv_ptr;
589     struct timeval tv;
590     if (timeout_usec == UINT32_MAX)
591     {
592         // Inifinite wait...
593         tv_ptr = nullptr;
594     }
595     else
596     {
597         TimeValue time_value;
598         time_value.OffsetWithMicroSeconds(timeout_usec);
599         tv.tv_sec = time_value.seconds();
600         tv.tv_usec = time_value.microseconds();
601         tv_ptr = &tv;
602     }
603 
604     // Make a copy of the file descriptors to make sure we don't
605     // have another thread change these values out from under us
606     // and cause problems in the loop below where like in FS_SET()
607     const IOObject::WaitableHandle handle = m_read_sp->GetWaitableHandle();
608     const int pipe_fd = m_pipe.GetReadFileDescriptor();
609 
610     if (handle != IOObject::kInvalidHandleValue)
611     {
612 #if defined(_MSC_VER)
613         // select() won't accept pipes on Windows.  The entire Windows codepath needs to be
614         // converted over to using WaitForMultipleObjects and event HANDLEs, but for now at least
615         // this will allow ::select() to not return an error.
616         const bool have_pipe_fd = false;
617 #else
618         const bool have_pipe_fd = pipe_fd >= 0;
619 #if !defined(__APPLE__)
620         assert(handle < FD_SETSIZE);
621         if (have_pipe_fd)
622             assert(pipe_fd < FD_SETSIZE);
623 #endif
624 #endif
625         while (handle == m_read_sp->GetWaitableHandle())
626         {
627             const int nfds = std::max<int>(handle, pipe_fd) + 1;
628 #if defined(__APPLE__)
629             llvm::SmallVector<fd_set, 1> read_fds;
630             read_fds.resize((nfds / FD_SETSIZE) + 1);
631             for (size_t i = 0; i < read_fds.size(); ++i)
632                 FD_ZERO(&read_fds[i]);
633 // FD_SET doesn't bounds check, it just happily walks off the end
634 // but we have taken care of making the extra storage with our
635 // SmallVector of fd_set objects
636 #else
637             fd_set read_fds;
638             FD_ZERO(&read_fds);
639 #endif
640             FD_SET(handle, FD_SET_DATA(read_fds));
641             if (have_pipe_fd)
642                 FD_SET(pipe_fd, FD_SET_DATA(read_fds));
643 
644             Error error;
645 
646             if (log)
647             {
648                 if (have_pipe_fd)
649                     log->Printf(
650                         "%p ConnectionFileDescriptor::BytesAvailable()  ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...",
651                         static_cast<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr));
652                 else
653                     log->Printf("%p ConnectionFileDescriptor::BytesAvailable()  ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...",
654                                 static_cast<void *>(this), nfds, handle, static_cast<void *>(tv_ptr));
655             }
656 
657             const int num_set_fds = ::select(nfds, FD_SET_DATA(read_fds), NULL, NULL, tv_ptr);
658             if (num_set_fds < 0)
659                 error.SetErrorToErrno();
660             else
661                 error.Clear();
662 
663             if (log)
664             {
665                 if (have_pipe_fd)
666                     log->Printf("%p ConnectionFileDescriptor::BytesAvailable()  ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) "
667                                 "=> %d, error = %s",
668                                 static_cast<void *>(this), nfds, handle, pipe_fd, static_cast<void *>(tv_ptr), num_set_fds,
669                                 error.AsCString());
670                 else
671                     log->Printf("%p ConnectionFileDescriptor::BytesAvailable()  ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => "
672                                 "%d, error = %s",
673                                 static_cast<void *>(this), nfds, handle, static_cast<void *>(tv_ptr), num_set_fds, error.AsCString());
674             }
675 
676             if (error_ptr)
677                 *error_ptr = error;
678 
679             if (error.Fail())
680             {
681                 switch (error.GetError())
682                 {
683                     case EBADF: // One of the descriptor sets specified an invalid descriptor.
684                         return eConnectionStatusLostConnection;
685 
686                     case EINVAL: // The specified time limit is invalid. One of its components is negative or too large.
687                     default:     // Other unknown error
688                         return eConnectionStatusError;
689 
690                     case EAGAIN: // The kernel was (perhaps temporarily) unable to
691                                  // allocate the requested number of file descriptors,
692                                  // or we have non-blocking IO
693                     case EINTR:  // A signal was delivered before the time limit
694                         // expired and before any of the selected events
695                         // occurred.
696                         break; // Lets keep reading to until we timeout
697                 }
698             }
699             else if (num_set_fds == 0)
700             {
701                 return eConnectionStatusTimedOut;
702             }
703             else if (num_set_fds > 0)
704             {
705                 if (FD_ISSET(handle, FD_SET_DATA(read_fds)))
706                     return eConnectionStatusSuccess;
707                 if (have_pipe_fd && FD_ISSET(pipe_fd, FD_SET_DATA(read_fds)))
708                 {
709                     // There is an interrupt or exit command in the command pipe
710                     // Read the data from that pipe:
711                     char buffer[1];
712 
713                     ssize_t bytes_read;
714 
715                     do
716                     {
717                         bytes_read = ::read(pipe_fd, buffer, sizeof(buffer));
718                     } while (bytes_read < 0 && errno == EINTR);
719 
720                     switch (buffer[0])
721                     {
722                         case 'q':
723                             if (log)
724                                 log->Printf("%p ConnectionFileDescriptor::BytesAvailable() "
725                                             "got data: %c from the command channel.",
726                                             static_cast<void *>(this), buffer[0]);
727                             return eConnectionStatusEndOfFile;
728                         case 'i':
729                             // Interrupt the current read
730                             return eConnectionStatusInterrupted;
731                     }
732                 }
733             }
734         }
735     }
736 
737     if (error_ptr)
738         error_ptr->SetErrorString("not connected");
739     return eConnectionStatusLostConnection;
740 }
741 
742 ConnectionStatus
743 ConnectionFileDescriptor::NamedSocketAccept(const char *socket_name, Error *error_ptr)
744 {
745     Socket *socket = nullptr;
746     Error error = Socket::UnixDomainAccept(socket_name, m_child_processes_inherit, socket);
747     if (error_ptr)
748         *error_ptr = error;
749     m_write_sp.reset(socket);
750     m_read_sp = m_write_sp;
751     if (error.Fail())
752     {
753         return eConnectionStatusError;
754     }
755     m_uri.assign(socket_name);
756     return eConnectionStatusSuccess;
757 }
758 
759 ConnectionStatus
760 ConnectionFileDescriptor::NamedSocketConnect(const char *socket_name, Error *error_ptr)
761 {
762     Socket *socket = nullptr;
763     Error error = Socket::UnixDomainConnect(socket_name, m_child_processes_inherit, socket);
764     if (error_ptr)
765         *error_ptr = error;
766     m_write_sp.reset(socket);
767     m_read_sp = m_write_sp;
768     if (error.Fail())
769     {
770         return eConnectionStatusError;
771     }
772     m_uri.assign(socket_name);
773     return eConnectionStatusSuccess;
774 }
775 
776 ConnectionStatus
777 ConnectionFileDescriptor::SocketListenAndAccept(const char *s, Error *error_ptr)
778 {
779     m_port_predicate.SetValue(0, eBroadcastNever);
780 
781     Socket *socket = nullptr;
782     m_waiting_for_accept = true;
783     Error error = Socket::TcpListen(s, m_child_processes_inherit, socket, &m_port_predicate);
784     if (error_ptr)
785         *error_ptr = error;
786     if (error.Fail())
787         return eConnectionStatusError;
788 
789     std::unique_ptr<Socket> listening_socket_up;
790 
791     listening_socket_up.reset(socket);
792     socket = nullptr;
793     error = listening_socket_up->BlockingAccept(s, m_child_processes_inherit, socket);
794     listening_socket_up.reset();
795     if (error_ptr)
796         *error_ptr = error;
797     if (error.Fail())
798         return eConnectionStatusError;
799 
800     InitializeSocket(socket);
801     return eConnectionStatusSuccess;
802 }
803 
804 ConnectionStatus
805 ConnectionFileDescriptor::ConnectTCP(const char *s, Error *error_ptr)
806 {
807     Socket *socket = nullptr;
808     Error error = Socket::TcpConnect(s, m_child_processes_inherit, socket);
809     if (error_ptr)
810         *error_ptr = error;
811     m_write_sp.reset(socket);
812     m_read_sp = m_write_sp;
813     if (error.Fail())
814     {
815         return eConnectionStatusError;
816     }
817     m_uri.assign(s);
818     return eConnectionStatusSuccess;
819 }
820 
821 ConnectionStatus
822 ConnectionFileDescriptor::ConnectUDP(const char *s, Error *error_ptr)
823 {
824     Socket *send_socket = nullptr;
825     Socket *recv_socket = nullptr;
826     Error error = Socket::UdpConnect(s, m_child_processes_inherit, send_socket, recv_socket);
827     if (error_ptr)
828         *error_ptr = error;
829     m_write_sp.reset(send_socket);
830     m_read_sp.reset(recv_socket);
831     if (error.Fail())
832     {
833         return eConnectionStatusError;
834     }
835     m_uri.assign(s);
836     return eConnectionStatusSuccess;
837 }
838 
839 uint16_t
840 ConnectionFileDescriptor::GetListeningPort(uint32_t timeout_sec)
841 {
842     uint16_t bound_port = 0;
843     if (timeout_sec == UINT32_MAX)
844         m_port_predicate.WaitForValueNotEqualTo(0, bound_port);
845     else
846     {
847         TimeValue timeout = TimeValue::Now();
848         timeout.OffsetWithSeconds(timeout_sec);
849         m_port_predicate.WaitForValueNotEqualTo(0, bound_port, &timeout);
850     }
851     return bound_port;
852 }
853 
854 bool
855 ConnectionFileDescriptor::GetChildProcessesInherit() const
856 {
857     return m_child_processes_inherit;
858 }
859 
860 void
861 ConnectionFileDescriptor::SetChildProcessesInherit(bool child_processes_inherit)
862 {
863     m_child_processes_inherit = child_processes_inherit;
864 }
865 
866 void
867 ConnectionFileDescriptor::InitializeSocket(Socket* socket)
868 {
869     m_write_sp.reset(socket);
870     m_read_sp = m_write_sp;
871     StreamString strm;
872     strm.Printf("connect://%s:%u",socket->GetRemoteIPAddress().c_str(), socket->GetRemotePortNumber());
873     m_uri.swap(strm.GetString());
874 }
875