xref: /freebsd/contrib/llvm-project/llvm/include/llvm/Support/raw_socket_stream.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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