Line data Source code
1 : /*
2 : * If not stated otherwise in this file or this component's LICENSE file the
3 : * following copyright and licenses apply:
4 : *
5 : * Copyright 2022 Sky UK
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : #ifndef FIREBOLT_RIALTO_IPC_I_IPC_SERVER_H_
21 : #define FIREBOLT_RIALTO_IPC_I_IPC_SERVER_H_
22 :
23 : #include <google/protobuf/message.h>
24 : #include <google/protobuf/service.h>
25 :
26 : #include <functional>
27 : #include <map>
28 : #include <memory>
29 : #include <string>
30 : #include <utility>
31 :
32 : namespace firebolt::rialto::ipc
33 : {
34 : /**
35 : * @brief Interface to object describing a client connected to the server.
36 : *
37 : * An object of this interface is provided in the callbacks for when a client
38 : * connects / disconnects. The interface can be used to query details above
39 : * the client and send it asynchronous events.
40 : */
41 : class IClient
42 : {
43 : public:
44 246 : IClient() = default;
45 246 : virtual ~IClient() = default;
46 :
47 : IClient(const IClient &) = delete;
48 : IClient &operator=(const IClient &) = delete;
49 : IClient(IClient &&) = delete;
50 : IClient &operator=(IClient &&) = delete;
51 :
52 : public:
53 : /**
54 : * @brief Gets the pid of the client that created the connection.
55 : *
56 : * \threadsafe
57 : *
58 : * @retval the pid.
59 : */
60 : virtual pid_t getClientPid() const = 0;
61 :
62 : /**
63 : * @brief Gets the userIdof the client that created the connection.
64 : *
65 : * \threadsafe
66 : *
67 : * @retval the userId.
68 : */
69 : virtual uid_t getClientUserId() const = 0;
70 :
71 : /**
72 : * @brief Gets the groupId of the client that created the connection.
73 : *
74 : * \threadsafe
75 : *
76 : * @retval the groupId.
77 : */
78 : virtual gid_t getClientGroupId() const = 0;
79 :
80 : /**
81 : * @brief Tells the server to disconnect the client.
82 : *
83 : * \threadsafe
84 : *
85 : * Typically you'd call this in the clientConnected method if you didn't want to accept the
86 : * client's connection.
87 : */
88 : virtual void disconnect() = 0;
89 :
90 : /**
91 : * @brief Adds a service implementation for the client.
92 : *
93 : * \threadsafe
94 : *
95 : * After this the client will be able to call methods on the service.
96 : *
97 : * @param[in] service : service to export.
98 : */
99 : virtual void exportService(const std::shared_ptr<google::protobuf::Service> &service) = 0;
100 :
101 : /**
102 : * @brief Sends a message out to the client connection.
103 : *
104 : * \threadsafe
105 : *
106 : * This method is thread safe
107 : * and may be called from any context; internally it will queue the message
108 : * on the client's message queue and the server process will send it
109 : *
110 : * @param[in] message : message to send.
111 : *
112 : * @retval true on success, false otherwise.
113 : */
114 : virtual bool sendEvent(const std::shared_ptr<google::protobuf::Message> &message) = 0;
115 :
116 : /**
117 : * @brief The server object.
118 : *
119 : * \threadsafe
120 : *
121 : * It is safe to hold a
122 : * shared_ptr to this object after the client has disconnected, but obviously
123 : * sendEvent will fail and data like pid and userId will be invalid.
124 : *
125 : * @retval true if the client is still conn
126 : */
127 : virtual bool isConnected() const = 0;
128 : };
129 :
130 : /**
131 : * @brief The server object.
132 : *
133 : * You must run the server as part of an external event loop implementation, you
134 : * can do that by setting up a loop that calls wait() and then process(). Or
135 : * you can get the fd() and add it to an external poll loop, and when woken call
136 : * process().
137 : */
138 : class IServer
139 : {
140 : public:
141 236 : IServer() = default;
142 236 : virtual ~IServer() = default;
143 :
144 : IServer(const IServer &) = delete;
145 : IServer &operator=(const IServer &) = delete;
146 : IServer(IServer &&) = delete;
147 : IServer &operator=(IServer &&) = delete;
148 :
149 : /**
150 : * @brief Creates the listening socket to accept new connections.
151 : *
152 : * \threadsafe
153 : *
154 : * This does not blockwaiting for connections, it just creates the socket and adds to the poll loop.
155 : *
156 : * @retval true on success, false otherwise.
157 : */
158 : inline bool addSocket(const std::string &socketPath) { return addSocket(socketPath, nullptr, nullptr); }
159 : inline bool addSocket(const std::string &socketPath,
160 : std::function<void(const std::shared_ptr<IClient> &)> clientConnectedCb)
161 : {
162 : return addSocket(socketPath, std::move(clientConnectedCb), nullptr);
163 : }
164 : virtual bool addSocket(const std::string &socketPath,
165 : std::function<void(const std::shared_ptr<IClient> &)> clientConnectedCb,
166 : std::function<void(const std::shared_ptr<IClient> &)> clientDisconnectedCb) = 0;
167 :
168 : /**
169 : * @brief Create a client.
170 : *
171 : * \threadsafe
172 : *
173 : * Given a file descriptor corresponding to one end of a socket, this function
174 : * will create a client (std::shared_ptr<Client>) and add the new client to
175 : * the internal clients list. At that point, the client is initialized and
176 : * ready to run, as if the client had connected to the servers listening socket.
177 : *
178 : * The other end of the socket can be passed to firebolt::rialto::ipc::Channel::connect(int sockFd)
179 : * function to create a client side IPC channel.
180 : *
181 : * When the other end of the socket is closed the clientDisconnectedCb will
182 : * be called.
183 : *
184 : * The socketFd must be a unix domain socket of type SOCK_SEQPACKET, if a different
185 : * socket type then this will fail and return a null std::shared_ptr.
186 : *
187 : * The server will dup the socketFd on success, so the caller can (and should)
188 : * close the socketFd after this call completes if it no longer needs the socket.
189 : *
190 : * @param[in] socketFd : The file descriptor for the socket.
191 : * @param[in] clientDisconnectedCb : Callback to be called on the closing of the socket.
192 : *
193 : * @retval client on success, nullptr otherwise.
194 : */
195 : inline std::shared_ptr<IClient> addClient(int socketFd) { return addClient(socketFd, nullptr); }
196 : virtual std::shared_ptr<IClient>
197 : addClient(int socketFd, std::function<void(const std::shared_ptr<IClient> &)> clientDisconnectedCb) = 0;
198 :
199 : /**
200 : * @brief Returns an fd that can be added to an external event loop. This is NOT the
201 : * server socket fd.
202 : *
203 : * @retval >= 0 on success, -1 otherwise.
204 : */
205 : virtual int fd() const = 0;
206 :
207 : /** Waits for any I/O to occur on the socket or timeout from a method call.
208 : * The call will block for a maximum of timeoutMSecs, to make the call wait
209 : * indefinitely pass -1.
210 : *
211 : * @param[in] timeoutMSecs : The time to wait for I/O.
212 : *
213 : * @retval false if there was an error , true otherwise
214 : */
215 : inline bool wait() { return this->wait(-1); }
216 : virtual bool wait(int timeoutMSecs) = 0;
217 :
218 : /**
219 : * @brief This is the heart of the server, it is where we wait for new incoming
220 : * connections or data from clients, and send data back to clients.
221 : *
222 : * @retval false if there was an error, true otherwise
223 : */
224 : virtual bool process() = 0;
225 : };
226 :
227 : } // namespace firebolt::rialto::ipc
228 :
229 : #endif // FIREBOLT_RIALTO_IPC_I_IPC_SERVER_H_
|