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