GCC Code Coverage Report


Directory: ./
File: pdcom5/include/pdcom5/Process.h
Date: 2024-12-29 04:08:32
Exec Total Coverage
Lines: 1 9 11.1%
Branches: 0 2 0.0%

Line Branch Exec Source
1 /*****************************************************************************
2 * vim:tw=78
3 *
4 * Copyright (C) 2021 Richard Hacker (lerichi at gmx dot net),
5 * Florian Pose (fp at igh dot de),
6 * Bjarne von Horn (vh at igh dot de).
7 *
8 * This file is part of the PdCom library.
9 *
10 * The PdCom library is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or (at your
13 * option) any later version.
14 *
15 * The PdCom library is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
18 * License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with the PdCom library. If not, see <http://www.gnu.org/licenses/>.
22 *
23 *****************************************************************************/
24
25 /** @file */
26
27 #ifndef PDCOM5_PROCESS_H
28 #define PDCOM5_PROCESS_H
29
30 #include "ClientStatistics.h"
31 #include "Variable.h"
32
33 #include <chrono>
34 #include <memory>
35 #include <pdcom5_export.h>
36 #include <stddef.h>
37 #include <stdint.h>
38 #include <string>
39 #include <vector>
40
41 namespace PdCom {
42 namespace impl {
43 class Process;
44 }
45
46 class SecureProcess;
47 class Variable;
48 class Sasl;
49 class Subscription;
50 class Subscriber;
51 class MessageManagerBase;
52
53 /** Base class for PdCom protocol handler
54 *
55 * This is the base class to interact with real time process server. The
56 * PdCom protocol ist implemented using this class.
57 *
58 * For socket input and output, the library completely relies on a derived
59 * class where read(), write(), flush() and connected() methods are
60 * reimplemented.
61 *
62 * When data is available for reading, call asyncData() which in turn
63 * calls the reimplemented read() method.
64 *
65 * When the protocol is initialized, the reimplemented connected() method
66 * is called.
67 *
68 * After connected(), the following commands can be issued:
69 *
70 * * list(): list a directory; returns result in listReply()
71 * * find(): find a variable; returns result in findReply()
72 * * ping(): ping the server; returns result in pingReply()
73 *
74 * All these commands are non-blocking asynchronous calls and either
75 * return the result immediately with the corresponding reply methods or
76 * issue a command to the server using excessive (!) calls to write().
77 * Data should be written to a buffer to optimize network communication.
78 * To flush the buffer to wire, flush() is issued by the library when
79 * required.
80 *
81 * The server may query presence of the user by issuing an alive() call.
82 * Using this call, certain actions could be undertaken by the server if
83 * the user is not active any more.
84 *
85 */
86 158 class PDCOM5_PUBLIC Process
87 {
88 friend class impl::Process;
89 friend class SecureProcess;
90
91 public:
92 /** Constructor */
93 Process();
94 Process(Process &&) = delete;
95 Process(Process const &) = delete;
96 Process &operator=(Process &&) = delete;
97 Process &operator=(Process const &) = delete;
98
99 protected:
100 /** Destructor
101 *
102 * The destructor cleans up all internally allocated structures
103 */
104 virtual ~Process();
105
106 public:
107 /** Remote process name string */
108 std::string name() const;
109
110 /** Remote process version string */
111 std::string version() const;
112
113 /** Library entry point for new data.
114 *
115 * Calling this method tells the library that new data has arrived
116 * from the server and is waiting to be processed.
117 *
118 * The library prepares an input buffer and then calls the
119 * reimplemented read() virtual method to read incoming data.
120 *
121 * This method can throw many exceptions, especially protocol_error and all
122 * exceptions which are thrown in the callbacks.
123 */
124 void asyncData();
125
126 /** Call delayed callbacks.
127 *
128 * This method is used to call queued callbacks,
129 * for example Subscriber::stateChanged() after a Subscription has been
130 * created. It is also called twice by asyncData(), so usually you don't
131 * have to bother with it.
132 */
133 void callPendingCallbacks();
134
135 /** Send a broadcast message to the server and other clients.
136 *
137 * @param message Broadcast message.
138 * @param attr Xml tag name, can be text or action.
139 */
140 void
141 broadcast(const std::string &message, const std::string &attr = "text");
142
143 struct SubscriptionInfo
144 {
145 const PdCom::Subscription *subscription;
146 const PdCom::Subscriber *subscriber;
147 PdCom::Variable variable;
148
149 SubscriptionInfo(
150 const PdCom::Subscription *subscription,
151 const PdCom::Subscriber *subscriber,
152 PdCom::Variable variable) :
153 subscription(subscription),
154 subscriber(subscriber),
155 variable(variable)
156 {}
157 };
158
159 std::vector<SubscriptionInfo> getActiveSubscriptions() const;
160
161 protected:
162 /** Reset communications and clean up internal buffers */
163 void reset();
164
165 /** Name of application user application.
166 *
167 * The application name is transferred to the server to be able to
168 * identify the clients more easily.
169 *
170 * \return a descriptive name of your application.
171 */
172 virtual std::string applicationName() const { return "pdcom5"; }
173
174 /** Host name of remote server.
175 *
176 * Reimplement this method to return the remote server host name
177 * this library connects to. This is especially important in
178 * multi-hosted TLS environments, where multiple hosts resolv to
179 * the same IP address. TLS needs to know the original server host
180 * name.
181 *
182 * \return server host name
183 */
184 virtual std::string hostname() const { return {}; }
185
186 /** Read data from server
187 *
188 * Reimplement this method to transfer data from the server to
189 * the library. This method is called within the call to
190 * asyncData().
191 *
192 * Essentially this method is a little wrapper around your
193 * socket's `%read()` function:
194 * \code
195 * int MyProcess::read(char *buf, size_t count)
196 * {
197 * return ::read(this->socket_fd, buf, count);
198 * }
199 * \endcode
200 *
201 * The method must return the number of bytes read, which may of
202 * coarse be less than \p count or even 0. Return values of
203 * &lt;=&nbsp;0 are not interpreted by the protocol handler.
204 *
205 * @param buf data destination
206 * @param count buffer size
207 *
208 * \return number of bytes read, 0 on connection close, -EAGAIN on when no
209 * data is available or <0 on error.
210 */
211 virtual int read(char *buf, int count) = 0;
212
213 /** Write data to server
214 *
215 * Reimplement this method to transfer data from the library to
216 * the server. This method is called when any library
217 * operation requires data to be sent to the server.
218 *
219 * Note: the library makes many calls to write(), so use
220 * buffered output otherwise you're in for performance problems!
221 *
222 * Essentially this method is a little wrapper around your
223 * socket's `%write()` function:
224 * \code
225 * void MyProcess::write(const char *buf, size_t count)
226 * {
227 * if (count != ::fwrite(buf, 1, count, this->socket_file)) {
228 * // react to errors, set flags, etc
229 * }
230 * }
231 * \endcode
232 *
233 * Note: the library does not have an internal buffer and expects
234 * that all data is sent. If your implementation might send less
235 * than \p count, it is your responsibility to buffer the data and
236 * send it later.
237 *
238 * @param buf data to be sent
239 * @param count number of bytes to send
240 */
241 virtual void write(const char *buf, size_t count) = 0;
242
243 /** Flush unsent data in output buffer
244 *
245 * Reimplement this method to flush data in the output buffer.
246 *
247 * This method tells the user that it is time to flush the
248 * output buffer to the wire. The library only expects that data
249 * is sent to the server within this call.
250 *
251 * Essentially this method is a little wrapper around your
252 * socket's `fflush()` function:
253 * \code
254 * void MyProcess::flush()
255 * {
256 * if (::fflush(this->socket_file)) {
257 * // react to errors
258 * }
259 * }
260 * \endcode
261 */
262 virtual void flush() = 0;
263
264 /** Protocol initialization completed
265 *
266 * This is a signal emitted by the library to indicate that
267 * protocol initialization has been completed and that library
268 * operations can be performed thereafter.
269 *
270 * Reimplement this method to get the signal.
271 *
272 * Absolutely NO process operations other than asyncData(),
273 * and Sasl::login() (and then only due to a previous
274 * loginReply() are permitted before this signal has been
275 * emitted.
276 */
277 virtual void connected() = 0;
278
279 /** List a directory path
280 *
281 * A process command to return all variables and directories
282 * within a directory path. The \p path parameter has typical
283 * unix character, with forward slashes '/' separating
284 * directories.
285 *
286 * listReply() must be reimplemented to receive the reply
287 * to this call.
288 *
289 * If the directory is cached (for instance a previous call to
290 * a similar path, or an entire server listing has been performed),
291 * listReply() is called within the context of this call and no
292 * server query is performed.
293 *
294 * If uncached, the library sends a server query and returns
295 * immediately. Later on during asyncData(), the virtual method
296 * listReply(), is called when the server's reply is
297 * processed.
298 *
299 * As a special case, an empty string (std::string()) for \p path
300 * will let the server list all its variables in one go. This
301 * possibility must be used with caution, as it can cause heavy
302 * network traffic.
303 *
304 * @param path directory path
305 *
306 * \return
307 * true if the path was cached
308 */
309 bool list(const std::string &path = "");
310
311 /** Reply to list() call
312 *
313 * You must reimplement this method to receive replies to list()
314 * calls.
315 *
316 * Note that a variable can have the same path as a directory!
317 * An example is a vector variable with atomized elements.
318 *
319 * Replies are in strict order of list() calls.
320 *
321 * @param variables List of variables.
322 * @param dirs List of directories.
323 */
324 virtual void
325 listReply(std::vector<Variable> variables, std::vector<std::string> dirs);
326
327 /** Find a variable with a corresponding path
328 *
329 * If the path search is known (be it successful or unsuccessful),
330 * the variable is returned in the call to the reimplemented
331 * virtual findReply() method immediately and the method returns
332 * true;
333 *
334 * If unsuccessful, the command is sent to the server to and the
335 * call returns immediately with false. Later on during
336 * asyncData(), findReply() is called when the server's reply is
337 * processed.
338 *
339 * @param path path of variable to find
340 *
341 * \return true if path is found immediately (cached)
342 */
343 bool find(const std::string &path);
344
345 /** Reply to find()
346 *
347 * This virtual method is called within the context of asyncData()
348 * when the server's reply to a variable discovery is processed.
349 *
350 * findReply()ies are called in strict order of find()
351 *
352 * @param variable Variable, empty if variable was not found.
353 */
354 virtual void findReply(const Variable &variable);
355
356 /** @brief Request client statistics from the server.
357 *
358 * Override clientStatisticsReply() to process the reply.
359 */
360 void getClientStatistics();
361
362 /// @brief Reply for getClientStatistics().
363 /// @param statistics The stats for all connected clients.
364 virtual void
365 clientStatisticsReply(std::vector<ClientStatistics> statistics);
366
367 /** Ping server */
368 void ping();
369
370 /** Ping reply
371 *
372 * You must reimplement this method to receive the reply to a
373 * ping() call.
374 */
375 virtual void pingReply() {}
376
377 /** Test from process whether client is alive.
378 *
379 * In some cases the server may want to know whether the client
380 * is still alive. Default implementation is to return true.
381 * Reimplement this if you wish to control presence
382 *
383 * \return true to indicate user presence
384 */
385 virtual bool alive() { return true; }
386
387 /** Register a SASL handler.
388 *
389 * A previous registered handler will be unregistered.
390 * Note that the registered handler has to outlive the process.
391 * Passing a \c nullptr will unregister the handler.
392 */
393 void setAuthManager(Sasl *);
394
395 /** Get the current SASL handler.
396 *
397 * \return The current SASL handler, may be NULL.
398 */
399 Sasl *getAuthManager() const;
400
401 /** Register a Message handler.
402 *
403 * A previous registered handler will be unregistered.
404 * Note that the registered handler has to outlive the process.
405 * Passing a \c nullptr will unregister the handler.
406 */
407 void setMessageManager(MessageManagerBase *);
408
409 /** Recieve a broadcast message from other clients.
410 *
411 * \p time_ns and \p user will be filled in by the server.
412 * If the sending client is not authenticated, \p user will be \c anonymous.
413 *
414 * @param message Message
415 * @param attr Attribute name, can be action or text.
416 * @param time_ns Time since epoch, will be filled in by the server.
417 * @param user Client which sent the broadcast, will be filled in by the
418 * server.
419 */
420 virtual void broadcastReply(
421 const std::string &message,
422 const std::string &attr,
423 std::chrono::nanoseconds time_ns,
424 const std::string &user);
425
426 private:
427 std::shared_ptr<impl::Process> pimpl;
428 explicit Process(std::shared_ptr<impl::Process> impl);
429 };
430 } // namespace PdCom
431
432 #endif // PDCOM5_PROCESS_H
433