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
24namespace llvm {
25
26class 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.
32class WSABalancer {
33public:
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///
60class 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
77public:
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
121class 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
127public:
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