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