1 //===-- llvm/Support/raw_socket_stream.h - Socket streams --*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains raw_ostream implementations for streams to communicate 10 // via UNIX sockets 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef LLVM_SUPPORT_RAW_SOCKET_STREAM_H 15 #define LLVM_SUPPORT_RAW_SOCKET_STREAM_H 16 17 #include "llvm/Support/Threading.h" 18 #include "llvm/Support/raw_ostream.h" 19 20 #include <atomic> 21 #include <chrono> 22 23 namespace llvm { 24 25 class raw_socket_stream; 26 27 #ifdef _WIN32 28 /// Ensures proper initialization and cleanup of winsock resources 29 /// 30 /// Make sure that calls to WSAStartup and WSACleanup are balanced. 31 class WSABalancer { 32 public: 33 WSABalancer(); 34 ~WSABalancer(); 35 }; 36 #endif // _WIN32 37 38 /// Manages a passive (i.e., listening) UNIX domain socket 39 /// 40 /// The ListeningSocket class encapsulates a UNIX domain socket that can listen 41 /// and accept incoming connections. ListeningSocket is portable and supports 42 /// Windows builds begining with Insider Build 17063. ListeningSocket is 43 /// designed for server-side operations, working alongside \p raw_socket_streams 44 /// that function as client connections. 45 /// 46 /// Usage example: 47 /// \code{.cpp} 48 /// std::string Path = "/path/to/socket" 49 /// Expected<ListeningSocket> S = ListeningSocket::createUnix(Path); 50 /// 51 /// if (S) { 52 /// Expected<std::unique_ptr<raw_socket_stream>> connection = S->accept(); 53 /// if (connection) { 54 /// // Use the accepted raw_socket_stream for communication. 55 /// } 56 /// } 57 /// \endcode 58 /// 59 class ListeningSocket { 60 61 std::atomic<int> FD; 62 std::string SocketPath; // Not modified after construction 63 64 /// If a separate thread calls ListeningSocket::shutdown, the ListeningSocket 65 /// file descriptor (FD) could be closed while ::poll is waiting for it to be 66 /// ready to perform a I/O operations. ::poll will continue to block even 67 /// after FD is closed so use a self-pipe mechanism to get ::poll to return 68 int PipeFD[2]; // Not modified after construction other then move constructor 69 70 ListeningSocket(int SocketFD, StringRef SocketPath, int PipeFD[2]); 71 72 #ifdef _WIN32 73 WSABalancer _; 74 #endif // _WIN32 75 76 public: 77 ~ListeningSocket(); 78 ListeningSocket(ListeningSocket &&LS); 79 ListeningSocket(const ListeningSocket &LS) = delete; 80 ListeningSocket &operator=(const ListeningSocket &) = delete; 81 82 /// Closes the FD, unlinks the socket file, and writes to PipeFD. 83 /// 84 /// After the construction of the ListeningSocket, shutdown is signal safe if 85 /// it is called during the lifetime of the object. shutdown can be called 86 /// concurrently with ListeningSocket::accept as writing to PipeFD will cause 87 /// a blocking call to ::poll to return. 88 /// 89 /// Once shutdown is called there is no way to reinitialize ListeningSocket. 90 void shutdown(); 91 92 /// Accepts an incoming connection on the listening socket. This method can 93 /// optionally either block until a connection is available or timeout after a 94 /// specified amount of time has passed. By default the method will block 95 /// until the socket has recieved a connection. If the accept timesout this 96 /// method will return std::errc:timed_out 97 /// 98 /// \param Timeout An optional timeout duration in milliseconds. Setting 99 /// Timeout to a negative number causes ::accept to block indefinitely 100 /// 101 Expected<std::unique_ptr<raw_socket_stream>> accept( 102 const std::chrono::milliseconds &Timeout = std::chrono::milliseconds(-1)); 103 104 /// Creates a listening socket bound to the specified file system path. 105 /// Handles the socket creation, binding, and immediately starts listening for 106 /// incoming connections. 107 /// 108 /// \param SocketPath The file system path where the socket will be created 109 /// \param MaxBacklog The max number of connections in a socket's backlog 110 /// 111 static Expected<ListeningSocket> createUnix( 112 StringRef SocketPath, 113 int MaxBacklog = llvm::hardware_concurrency().compute_thread_count()); 114 }; 115 116 //===----------------------------------------------------------------------===// 117 // raw_socket_stream 118 //===----------------------------------------------------------------------===// 119 120 class raw_socket_stream : public raw_fd_stream { current_pos()121 uint64_t current_pos() const override { return 0; } 122 #ifdef _WIN32 123 WSABalancer _; 124 #endif // _WIN32 125 126 public: 127 raw_socket_stream(int SocketFD); 128 ~raw_socket_stream(); 129 130 /// Create a \p raw_socket_stream connected to the UNIX domain socket at \p 131 /// SocketPath. 132 static Expected<std::unique_ptr<raw_socket_stream>> 133 createConnectedUnix(StringRef SocketPath); 134 135 /// Attempt to read from the raw_socket_stream's file descriptor. 136 /// 137 /// This method can optionally either block until data is read or an error has 138 /// occurred or timeout after a specified amount of time has passed. By 139 /// default the method will block until the socket has read data or 140 /// encountered an error. If the read times out this method will return 141 /// std::errc:timed_out 142 /// 143 /// \param Ptr The start of the buffer that will hold any read data 144 /// \param Size The number of bytes to be read 145 /// \param Timeout An optional timeout duration in milliseconds 146 /// 147 ssize_t read( 148 char *Ptr, size_t Size, 149 const std::chrono::milliseconds &Timeout = std::chrono::milliseconds(-1)); 150 }; 151 152 } // end namespace llvm 153 154 #endif 155