1 | //===-- llvm/Support/raw_socket_stream.cpp - 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 | #include "llvm/Support/raw_socket_stream.h" |
15 | #include "llvm/Config/config.h" |
16 | #include "llvm/Support/Error.h" |
17 | #include "llvm/Support/FileSystem.h" |
18 | |
19 | #include <atomic> |
20 | #include <fcntl.h> |
21 | #include <thread> |
22 | |
23 | #ifndef _WIN32 |
24 | #include <poll.h> |
25 | #include <sys/socket.h> |
26 | #include <sys/un.h> |
27 | #else |
28 | #include "llvm/Support/Windows/WindowsSupport.h" |
29 | // winsock2.h must be included before afunix.h. Briefly turn off clang-format to |
30 | // avoid error. |
31 | // clang-format off |
32 | #include <winsock2.h> |
33 | #include <afunix.h> |
34 | // clang-format on |
35 | #include <io.h> |
36 | #endif // _WIN32 |
37 | |
38 | #if defined(HAVE_UNISTD_H) |
39 | #include <unistd.h> |
40 | #endif |
41 | |
42 | using namespace llvm; |
43 | |
44 | #ifdef _WIN32 |
45 | WSABalancer::WSABalancer() { |
46 | WSADATA WsaData; |
47 | ::memset(&WsaData, 0, sizeof(WsaData)); |
48 | if (WSAStartup(MAKEWORD(2, 2), &WsaData) != 0) { |
49 | llvm::report_fatal_error("WSAStartup failed" ); |
50 | } |
51 | } |
52 | |
53 | WSABalancer::~WSABalancer() { WSACleanup(); } |
54 | #endif // _WIN32 |
55 | |
56 | static std::error_code getLastSocketErrorCode() { |
57 | #ifdef _WIN32 |
58 | return std::error_code(::WSAGetLastError(), std::system_category()); |
59 | #else |
60 | return errnoAsErrorCode(); |
61 | #endif |
62 | } |
63 | |
64 | static sockaddr_un setSocketAddr(StringRef SocketPath) { |
65 | struct sockaddr_un Addr; |
66 | memset(s: &Addr, c: 0, n: sizeof(Addr)); |
67 | Addr.sun_family = AF_UNIX; |
68 | strncpy(dest: Addr.sun_path, src: SocketPath.str().c_str(), n: sizeof(Addr.sun_path) - 1); |
69 | return Addr; |
70 | } |
71 | |
72 | static Expected<int> getSocketFD(StringRef SocketPath) { |
73 | #ifdef _WIN32 |
74 | SOCKET Socket = socket(AF_UNIX, SOCK_STREAM, 0); |
75 | if (Socket == INVALID_SOCKET) { |
76 | #else |
77 | int Socket = socket(AF_UNIX, SOCK_STREAM, protocol: 0); |
78 | if (Socket == -1) { |
79 | #endif // _WIN32 |
80 | return llvm::make_error<StringError>(Args: getLastSocketErrorCode(), |
81 | Args: "Create socket failed" ); |
82 | } |
83 | |
84 | struct sockaddr_un Addr = setSocketAddr(SocketPath); |
85 | if (::connect(fd: Socket, addr: (struct sockaddr *)&Addr, len: sizeof(Addr)) == -1) |
86 | return llvm::make_error<StringError>(Args: getLastSocketErrorCode(), |
87 | Args: "Connect socket failed" ); |
88 | |
89 | #ifdef _WIN32 |
90 | return _open_osfhandle(Socket, 0); |
91 | #else |
92 | return Socket; |
93 | #endif // _WIN32 |
94 | } |
95 | |
96 | ListeningSocket::ListeningSocket(int SocketFD, StringRef SocketPath, |
97 | int PipeFD[2]) |
98 | : FD(SocketFD), SocketPath(SocketPath), PipeFD{PipeFD[0], PipeFD[1]} {} |
99 | |
100 | ListeningSocket::ListeningSocket(ListeningSocket &&LS) |
101 | : FD(LS.FD.load()), SocketPath(LS.SocketPath), |
102 | PipeFD{LS.PipeFD[0], LS.PipeFD[1]} { |
103 | |
104 | LS.FD = -1; |
105 | LS.SocketPath.clear(); |
106 | LS.PipeFD[0] = -1; |
107 | LS.PipeFD[1] = -1; |
108 | } |
109 | |
110 | Expected<ListeningSocket> ListeningSocket::createUnix(StringRef SocketPath, |
111 | int MaxBacklog) { |
112 | |
113 | // Handle instances where the target socket address already exists and |
114 | // differentiate between a preexisting file with and without a bound socket |
115 | // |
116 | // ::bind will return std::errc:address_in_use if a file at the socket address |
117 | // already exists (e.g., the file was not properly unlinked due to a crash) |
118 | // even if another socket has not yet binded to that address |
119 | if (llvm::sys::fs::exists(Path: SocketPath)) { |
120 | Expected<int> MaybeFD = getSocketFD(SocketPath); |
121 | if (!MaybeFD) { |
122 | |
123 | // Regardless of the error, notify the caller that a file already exists |
124 | // at the desired socket address and that there is no bound socket at that |
125 | // address. The file must be removed before ::bind can use the address |
126 | consumeError(Err: MaybeFD.takeError()); |
127 | return llvm::make_error<StringError>( |
128 | Args: std::make_error_code(e: std::errc::file_exists), |
129 | Args: "Socket address unavailable" ); |
130 | } |
131 | ::close(fd: std::move(*MaybeFD)); |
132 | |
133 | // Notify caller that the provided socket address already has a bound socket |
134 | return llvm::make_error<StringError>( |
135 | Args: std::make_error_code(e: std::errc::address_in_use), |
136 | Args: "Socket address unavailable" ); |
137 | } |
138 | |
139 | #ifdef _WIN32 |
140 | WSABalancer _; |
141 | SOCKET Socket = socket(AF_UNIX, SOCK_STREAM, 0); |
142 | if (Socket == INVALID_SOCKET) |
143 | #else |
144 | int Socket = socket(AF_UNIX, SOCK_STREAM, protocol: 0); |
145 | if (Socket == -1) |
146 | #endif |
147 | return llvm::make_error<StringError>(Args: getLastSocketErrorCode(), |
148 | Args: "socket create failed" ); |
149 | |
150 | struct sockaddr_un Addr = setSocketAddr(SocketPath); |
151 | if (::bind(fd: Socket, addr: (struct sockaddr *)&Addr, len: sizeof(Addr)) == -1) { |
152 | // Grab error code from call to ::bind before calling ::close |
153 | std::error_code EC = getLastSocketErrorCode(); |
154 | ::close(fd: Socket); |
155 | return llvm::make_error<StringError>(Args&: EC, Args: "Bind error" ); |
156 | } |
157 | |
158 | // Mark socket as passive so incoming connections can be accepted |
159 | if (::listen(fd: Socket, n: MaxBacklog) == -1) |
160 | return llvm::make_error<StringError>(Args: getLastSocketErrorCode(), |
161 | Args: "Listen error" ); |
162 | |
163 | int PipeFD[2]; |
164 | #ifdef _WIN32 |
165 | // Reserve 1 byte for the pipe and use default textmode |
166 | if (::_pipe(PipeFD, 1, 0) == -1) |
167 | #else |
168 | if (::pipe(pipedes: PipeFD) == -1) |
169 | #endif // _WIN32 |
170 | return llvm::make_error<StringError>(Args: getLastSocketErrorCode(), |
171 | Args: "pipe failed" ); |
172 | |
173 | #ifdef _WIN32 |
174 | return ListeningSocket{_open_osfhandle(Socket, 0), SocketPath, PipeFD}; |
175 | #else |
176 | return ListeningSocket{Socket, SocketPath, PipeFD}; |
177 | #endif // _WIN32 |
178 | } |
179 | |
180 | Expected<std::unique_ptr<raw_socket_stream>> |
181 | ListeningSocket::accept(std::chrono::milliseconds Timeout) { |
182 | |
183 | struct pollfd FDs[2]; |
184 | FDs[0].events = POLLIN; |
185 | #ifdef _WIN32 |
186 | SOCKET WinServerSock = _get_osfhandle(FD); |
187 | FDs[0].fd = WinServerSock; |
188 | #else |
189 | FDs[0].fd = FD; |
190 | #endif |
191 | FDs[1].events = POLLIN; |
192 | FDs[1].fd = PipeFD[0]; |
193 | |
194 | // Keep track of how much time has passed in case poll is interupted by a |
195 | // signal and needs to be recalled |
196 | int RemainingTime = Timeout.count(); |
197 | std::chrono::milliseconds ElapsedTime = std::chrono::milliseconds(0); |
198 | int PollStatus = -1; |
199 | |
200 | while (PollStatus == -1 && (Timeout.count() == -1 || ElapsedTime < Timeout)) { |
201 | if (Timeout.count() != -1) |
202 | RemainingTime -= ElapsedTime.count(); |
203 | |
204 | auto Start = std::chrono::steady_clock::now(); |
205 | #ifdef _WIN32 |
206 | PollStatus = WSAPoll(FDs, 2, RemainingTime); |
207 | if (PollStatus == SOCKET_ERROR) { |
208 | #else |
209 | PollStatus = ::poll(fds: FDs, nfds: 2, timeout: RemainingTime); |
210 | if (PollStatus == -1) { |
211 | #endif |
212 | // Ignore error if caused by interupting signal |
213 | std::error_code PollErrCode = getLastSocketErrorCode(); |
214 | if (PollErrCode != std::errc::interrupted) |
215 | return llvm::make_error<StringError>(Args&: PollErrCode, Args: "FD poll failed" ); |
216 | } |
217 | |
218 | if (PollStatus == 0) |
219 | return llvm::make_error<StringError>( |
220 | Args: std::make_error_code(e: std::errc::timed_out), |
221 | Args: "No client requests within timeout window" ); |
222 | |
223 | if (FDs[0].revents & POLLNVAL) |
224 | return llvm::make_error<StringError>( |
225 | Args: std::make_error_code(e: std::errc::bad_file_descriptor), |
226 | Args: "File descriptor closed by another thread" ); |
227 | |
228 | if (FDs[1].revents & POLLIN) |
229 | return llvm::make_error<StringError>( |
230 | Args: std::make_error_code(e: std::errc::operation_canceled), |
231 | Args: "Accept canceled" ); |
232 | |
233 | auto Stop = std::chrono::steady_clock::now(); |
234 | ElapsedTime += |
235 | std::chrono::duration_cast<std::chrono::milliseconds>(d: Stop - Start); |
236 | } |
237 | |
238 | int AcceptFD; |
239 | #ifdef _WIN32 |
240 | SOCKET WinAcceptSock = ::accept(WinServerSock, NULL, NULL); |
241 | AcceptFD = _open_osfhandle(WinAcceptSock, 0); |
242 | #else |
243 | AcceptFD = ::accept(fd: FD, NULL, NULL); |
244 | #endif |
245 | |
246 | if (AcceptFD == -1) |
247 | return llvm::make_error<StringError>(Args: getLastSocketErrorCode(), |
248 | Args: "Socket accept failed" ); |
249 | return std::make_unique<raw_socket_stream>(args&: AcceptFD); |
250 | } |
251 | |
252 | void ListeningSocket::shutdown() { |
253 | int ObservedFD = FD.load(); |
254 | |
255 | if (ObservedFD == -1) |
256 | return; |
257 | |
258 | // If FD equals ObservedFD set FD to -1; If FD doesn't equal ObservedFD then |
259 | // another thread is responsible for shutdown so return |
260 | if (!FD.compare_exchange_strong(i1&: ObservedFD, i2: -1)) |
261 | return; |
262 | |
263 | ::close(fd: ObservedFD); |
264 | ::unlink(name: SocketPath.c_str()); |
265 | |
266 | // Ensure ::poll returns if shutdown is called by a seperate thread |
267 | char Byte = 'A'; |
268 | ssize_t written = ::write(fd: PipeFD[1], buf: &Byte, n: 1); |
269 | |
270 | // Ignore any write() error |
271 | (void)written; |
272 | } |
273 | |
274 | ListeningSocket::~ListeningSocket() { |
275 | shutdown(); |
276 | |
277 | // Close the pipe's FDs in the destructor instead of within |
278 | // ListeningSocket::shutdown to avoid unnecessary synchronization issues that |
279 | // would occur as PipeFD's values would have to be changed to -1 |
280 | // |
281 | // The move constructor sets PipeFD to -1 |
282 | if (PipeFD[0] != -1) |
283 | ::close(fd: PipeFD[0]); |
284 | if (PipeFD[1] != -1) |
285 | ::close(fd: PipeFD[1]); |
286 | } |
287 | |
288 | //===----------------------------------------------------------------------===// |
289 | // raw_socket_stream |
290 | //===----------------------------------------------------------------------===// |
291 | |
292 | raw_socket_stream::raw_socket_stream(int SocketFD) |
293 | : raw_fd_stream(SocketFD, true) {} |
294 | |
295 | Expected<std::unique_ptr<raw_socket_stream>> |
296 | raw_socket_stream::createConnectedUnix(StringRef SocketPath) { |
297 | #ifdef _WIN32 |
298 | WSABalancer _; |
299 | #endif // _WIN32 |
300 | Expected<int> FD = getSocketFD(SocketPath); |
301 | if (!FD) |
302 | return FD.takeError(); |
303 | return std::make_unique<raw_socket_stream>(args&: *FD); |
304 | } |
305 | |
306 | raw_socket_stream::~raw_socket_stream() {} |
307 | |