Line data Source code
1 : // Copyright (c) 2012-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief Implementation of the Connection with Send Message class.
22 : *
23 : * This is a base class which ease the implementation of a connection
24 : * which is able to send and receive messages.
25 : */
26 :
27 :
28 : // self
29 : //
30 : #include "eventdispatcher/connection_with_send_message.h"
31 :
32 : #include "eventdispatcher/dispatcher_support.h"
33 : #include "eventdispatcher/exception.h"
34 :
35 :
36 : // snaplogger lib
37 : //
38 : #include <snaplogger/logger.h>
39 : #include <snaplogger/message.h>
40 :
41 :
42 : // snapdev lib
43 : //
44 : #include <snapdev/not_used.h>
45 :
46 :
47 : // boost lib
48 : //
49 : #include <boost/algorithm/string/join.hpp>
50 :
51 :
52 : // C lib
53 : //
54 : #ifdef __SANITIZE_ADDRESS__
55 : #include <sanitizer/lsan_interface.h>
56 : #endif
57 :
58 :
59 : // last include
60 : //
61 : #include <snapdev/poison.h>
62 :
63 :
64 :
65 : namespace ed
66 : {
67 :
68 :
69 :
70 : /** \brief Initialize a connection_with_send_message object.
71 : *
72 : * This constructor initializes a connection which supports a send_message()
73 : * function. This allows that object to send a certain number of default
74 : * messages such as the UNKNOWN message automatically.
75 : */
76 3 : connection_with_send_message::~connection_with_send_message()
77 : {
78 3 : }
79 :
80 :
81 :
82 : /** \brief Build the HELP reply and send it.
83 : *
84 : * When a daemon registers with the snapcommunicator, it sends a REGISTER
85 : * command. As a result, the daemon is sent a HELP command which must be
86 : * answered with a COMMAND and the list of commands that this connection
87 : * supports.
88 : *
89 : * \note
90 : * If the environment logger is not currently configured, this message
91 : * gets ignored.
92 : *
93 : * \param[in] message The HELP message.
94 : *
95 : * \sa help()
96 : */
97 0 : void connection_with_send_message::msg_help(message & msg)
98 : {
99 0 : snap::NOT_USED(msg);
100 :
101 0 : bool need_user_help(true);
102 0 : string_list_t commands;
103 :
104 : dispatcher_base * d;
105 0 : dispatcher_support * ds(dynamic_cast<dispatcher_support *>(this));
106 0 : if(ds != nullptr)
107 : {
108 : // we extract the bare pointer because in the other case
109 : // we only get a bare pointer... (which we can't safely
110 : // put in a shared pointer, although we could attempt to
111 : // use shared_from_this() but we could have a class without
112 : // it?)
113 : //
114 0 : d = ds->get_dispatcher().get();
115 : }
116 : else
117 : {
118 0 : d = dynamic_cast<dispatcher_base *>(this);
119 : }
120 0 : if(d != nullptr)
121 : {
122 0 : need_user_help = d->get_commands(commands);
123 : }
124 :
125 : // the user has "unknown" commands (as far as the dispatcher is concerned)
126 : // in his list of commands so we have to let him enter them "manually"
127 : //
128 : // this happens whenever there is an entry which is a regular expression
129 : // or something similar which we just cannot grab
130 : //
131 0 : if(need_user_help)
132 : {
133 0 : help(commands);
134 : }
135 :
136 : // the list of commands just cannot be empty
137 : //
138 0 : if(commands.empty())
139 : {
140 : throw event_dispatcher_implementation_error(
141 : "connection_with_send_message::msg_help()"
142 0 : " is not able to determine the commands this messenger supports");
143 : }
144 :
145 : // Now prepare the COMMAND message and send it
146 : //
147 : // Note: we turn off the caching on this message, it does not make sense
148 : // because if snapcommunicator is not running, then caching won't
149 : // happen work anyway (i.e. snapcommunicator has to send HELP first
150 : // and then we send the reply, if it has to restart, then just
151 : // sending COMMANDS will fail.)
152 : //
153 0 : message reply;
154 0 : reply.set_command("COMMANDS");
155 0 : reply.add_parameter("list", boost::algorithm::join(commands, ","));
156 0 : if(!send_message(reply, false))
157 : {
158 0 : SNAP_LOG_WARNING
159 0 : << "could not reply to \""
160 0 : << msg.get_command()
161 : << "\" with a COMMANDS message."
162 : << SNAP_LOG_SEND;
163 : }
164 0 : }
165 :
166 :
167 : /** \brief Reply to the watchdog message ALIVE.
168 : *
169 : * To check whether a service is alive, send the ALIVE message. This
170 : * function builds an ABSOLUTELY reply and attaches the "serial" parameter
171 : * as is if present. It will also include the original "timestamp" parameter
172 : * when present.
173 : *
174 : * The function also adds one field named "reply_timestamp" with the Unix
175 : * time when the reply is being sent.
176 : *
177 : * \note
178 : * The "serial" parameter is expected to be used to make sure that no messages
179 : * are lost, or if loss is expected, to see whether loss is heavy or not.
180 : *
181 : * \note
182 : * The "serial" and "timestamp" parameters do not get checked. If present
183 : * in the original message, they get copied verbatim to the destination.
184 : * This allows you to include anything you want in those parameters although
185 : * we suggest you use the "timestamp" only for a value representing time.
186 : *
187 : * \param[in] message The STOP message.
188 : */
189 0 : void connection_with_send_message::msg_alive(message & msg)
190 : {
191 0 : message absolutely;
192 0 : absolutely.reply_to(msg);
193 0 : absolutely.set_command("ABSOLUTELY");
194 0 : if(msg.has_parameter("serial"))
195 : {
196 0 : absolutely.add_parameter("serial", msg.get_parameter("serial"));
197 : }
198 0 : if(msg.has_parameter("timestamp"))
199 : {
200 0 : absolutely.add_parameter("timestamp", msg.get_parameter("timestamp"));
201 : }
202 0 : absolutely.add_parameter("reply_timestamp", time(nullptr));
203 0 : if(!send_message(absolutely, false))
204 : {
205 0 : SNAP_LOG_WARNING
206 0 : << "could not reply to \""
207 0 : << msg.get_command()
208 : << "\" with an ABSOLUTELY message."
209 : << SNAP_LOG_SEND;
210 : }
211 0 : }
212 :
213 :
214 : /** \brief Run the sanitizer leak checker.
215 : *
216 : * This function calls the function which prints out all the leaks found
217 : * at this time in this software (\em leaks as in any block of memory
218 : * currently allocated).
219 : *
220 : * The message does nothing if the library was not compiled with the
221 : * sanitizer feature turned on.
222 : *
223 : * The message can be sent any number of times.
224 : *
225 : * \param[in] msg The LEAK message, which is ignored.
226 : */
227 0 : void connection_with_send_message::msg_leak(ed::message & msg)
228 : {
229 0 : snap::NOT_USED(msg);
230 :
231 : #ifdef __SANITIZE_ADDRESS__
232 0 : __lsan_do_recoverable_leak_check();
233 : #else
234 : std::cerr << "error: leaks are not being tracked;"
235 : " use the --sanitize option to turn on this feature."
236 : << std::endl;
237 : #endif
238 0 : }
239 :
240 :
241 : /** \brief Reconfigure the logger.
242 : *
243 : * Whenever the logrotate runs or some changes are made to the log
244 : * definitions, the corresponding daemons need to reconfigure their
245 : * logger to make use of the new file and settings. This command is
246 : * used for this purpose.
247 : *
248 : * \note
249 : * If the environment logger is not currently configured, this message
250 : * is ignored.
251 : *
252 : * \param[in] message The STOP message.
253 : */
254 0 : void connection_with_send_message::msg_log(message & msg)
255 : {
256 0 : snap::NOT_USED(msg);
257 :
258 0 : if(snaplogger::is_configured())
259 : {
260 : // send log in the old file and format
261 : //
262 0 : SNAP_LOG_INFO
263 0 : << "-------------------- Logging reconfiguration request."
264 : << SNAP_LOG_SEND;
265 :
266 : // reconfigure
267 : //
268 0 : snaplogger::reopen();
269 :
270 : // send log to new file and format
271 : //
272 0 : SNAP_LOG_INFO
273 0 : << "-------------------- Logging reconfiguration done."
274 : << SNAP_LOG_SEND;
275 : }
276 0 : }
277 :
278 :
279 : /** \brief Call you stop() function with true.
280 : *
281 : * This command means that someone is asking your daemon to quit as soon as
282 : * possible because the Snap! environment is being asked to shutdown.
283 : *
284 : * The value 'true' means that all the daemons are being asked to stop and
285 : * not just you.
286 : *
287 : * \param[in] message The STOP message.
288 : *
289 : * \sa msg_stop()
290 : * \sa stop()
291 : */
292 0 : void connection_with_send_message::msg_quitting(message & msg)
293 : {
294 0 : snap::NOT_USED(msg);
295 :
296 0 : stop(true);
297 0 : }
298 :
299 :
300 : /** \brief Calls your ready() function with the message.
301 : *
302 : * All daemons using the snapcommunicator daemon have to have a ready()
303 : * function which gets called once the HELP and COMMAND message were
304 : * handled. This is when your daemon is expected to be ready to start
305 : * working. Some daemon, though, start working immediately no matter
306 : * what (i.e. snapwatchdog and snapfirewall do work either way.)
307 : *
308 : * \param[in] message The READY message.
309 : *
310 : * \sa ready()
311 : */
312 0 : void connection_with_send_message::msg_ready(message & msg)
313 : {
314 : // pass the message so any additional info can be accessed.
315 : //
316 0 : ready(msg);
317 0 : }
318 :
319 :
320 : /** \brief Calls your restart() function with the message.
321 : *
322 : * This message has no implementation by default at the moment. What we
323 : * want to do is find a clean way to restart any service instantly.
324 : *
325 : * The RESTART message is expected to be used whenever a modification
326 : * to some file or the system environment somehow affects your service
327 : * in such a way that it requires a restart. For example, after an
328 : * upgrade of eventdispatcher library, you should restart all the services
329 : * that make use of this library. For this reason, we have a RESTART
330 : * message.
331 : *
332 : * The message comes with one parameter named `reason` which describes
333 : * why the RESTART was sent:
334 : *
335 : * \li `reason=upgrade` -- something (library/tools) was upgraded
336 : * \li `reason=config` -- a configuration file was updated
337 : *
338 : * \note
339 : * There are currently some services that make use of a CONFIG message
340 : * whenever their configuration changes. Pretty much all services do
341 : * not support a live configuration change (because it initializes their
342 : * objects from the configuration data once on startup and in many cases
343 : * it would be very complicated to allow for changes to occur.)
344 : * \note
345 : * In those existing implementations, we really just do a restart anyway.
346 : *
347 : * \param[in] message The RESTART message.
348 : *
349 : * \sa restart()
350 : */
351 0 : void connection_with_send_message::msg_restart(message & msg)
352 : {
353 : // pass the message so any additional info can be accessed.
354 : //
355 0 : restart(msg);
356 0 : }
357 :
358 :
359 : /** \brief Call you stop() function with false.
360 : *
361 : * This command means that someone is asking your daemon to stop.
362 : *
363 : * The value 'false' means just your daemon was asked to stop and not the
364 : * entire system to shutdown (otherwise you would receive a QUITTING command
365 : * instead.)
366 : *
367 : * \param[in] message The STOP message.
368 : *
369 : * \sa msg_quitting()
370 : */
371 0 : void connection_with_send_message::msg_stop(message & msg)
372 : {
373 0 : snap::NOT_USED(msg);
374 :
375 0 : stop(false);
376 0 : }
377 :
378 :
379 : /** \brief Handle the UNKNOWN message.
380 : *
381 : * Whenever we send a command to another daemon, that command can be refused
382 : * by sending an UNKNOWN reply. This function handles the UNKNOWN command
383 : * by simply recording that as an error in the logs.
384 : *
385 : * \param[in] message The UNKNOWN message we just received.
386 : */
387 0 : void connection_with_send_message::msg_log_unknown(message & msg)
388 : {
389 : // we sent a command that the other end did not understand
390 : // and got an UNKNOWN reply
391 : //
392 0 : SNAP_LOG_ERROR
393 0 : << "we sent unknown command \""
394 0 : << msg.get_parameter("command")
395 : << "\" and probably did not get the expected result."
396 : << SNAP_LOG_SEND;
397 0 : }
398 :
399 :
400 : /** \brief Send the UNKNOWN message as a reply.
401 : *
402 : * This function replies to the \p message with the UNKNOWN message as
403 : * expected by all our connection objects when a service receives a
404 : * message it does not know how to handle.
405 : *
406 : * It is expected to be used in your dispatcher_match array.
407 : *
408 : * \note
409 : * This function is virtual which allows you to add it to your array of
410 : * of dispatcher_match items. The following shows an example of what that
411 : * can look like.
412 : *
413 : * \code
414 : * {
415 : * ...
416 : *
417 : * // ALWAYS LAST
418 : * {
419 : * nullptr
420 : * , &my_service_connection::msg_reply_with_unknown
421 : * , &ed::dispatcher<my_service_connection>::dispatcher_match::always_match
422 : * }
423 : * };
424 : * \endcode
425 : *
426 : * \param[in] message The message to reply to.
427 : */
428 0 : void connection_with_send_message::msg_reply_with_unknown(message & msg)
429 : {
430 0 : message unknown;
431 0 : unknown.reply_to(msg);
432 0 : unknown.set_command("UNKNOWN");
433 0 : unknown.add_parameter("command", msg.get_command());
434 0 : if(!send_message(unknown, false))
435 : {
436 0 : SNAP_LOG_WARNING
437 0 : << "could not reply to \""
438 0 : << msg.get_command()
439 : << "\" with UNKNOWN message."
440 : << SNAP_LOG_SEND;
441 : }
442 0 : }
443 :
444 :
445 : /** \brief The default help() function does nothing.
446 : *
447 : * This implementation does nothing. It is expected that you reimplement
448 : * this function depending on your daemon's need.
449 : *
450 : * The help() function gets called whenever the list of commands can't be
451 : * 100% defined automatically.
452 : *
453 : * Your function is expected to add commands to the \p commands parameter
454 : * as in:
455 : *
456 : * \code
457 : * commands << "MSG1";
458 : * commands << "MSG2";
459 : * commands << "MSG3";
460 : * \endcode
461 : *
462 : * This allows you to handle those three messages with a single entry in
463 : * your list of dispatcher_match objects with a regular expression such
464 : * as "MSG[1-3]".
465 : *
466 : * \param[in,out] commands List of commands to update.
467 : */
468 0 : void connection_with_send_message::help(string_list_t & commands)
469 : {
470 0 : snap::NOT_USED(commands);
471 :
472 : // do nothing by default -- user is expected to overload this function
473 0 : }
474 :
475 :
476 : /** \brief The default ready() function does nothing.
477 : *
478 : * This implementation does nothing. It is expected that you reimplement
479 : * this function depending on your daemon's need. Most often this function
480 : * is the one that really starts your daemons process.
481 : *
482 : * \param[in,out] message The READY message.
483 : */
484 0 : void connection_with_send_message::ready(message & msg)
485 : {
486 0 : snap::NOT_USED(msg);
487 :
488 : // do nothing by default -- user is expected to overload this function
489 : //
490 0 : SNAP_LOG_WARNING
491 0 : << "default ready() function was called."
492 : << SNAP_LOG_SEND;
493 0 : }
494 :
495 :
496 : /** \brief The default restart() function does nothing.
497 : *
498 : * This implementation does nothing. It is expected that you reimplement
499 : * this function depending on your daemon's need. Most often this function
500 : * calls the stop() function in order to restart the daemon. If only a
501 : * configuration file changed and your daemon is capable of reading the
502 : * new settings without a full restart, then just read that new config.
503 : *
504 : * \param[in,out] message The RESTART message.
505 : *
506 : * \sa msg_restart()
507 : */
508 0 : void connection_with_send_message::restart(message & msg)
509 : {
510 0 : snap::NOT_USED(msg);
511 :
512 : // do nothing by default -- user is expected to overload this function
513 : //
514 0 : SNAP_LOG_WARNING
515 0 : << "default restart() function was called."
516 : << SNAP_LOG_SEND;
517 0 : }
518 :
519 :
520 : /** \brief The default stop() function does nothing.
521 : *
522 : * This implementation does nothing. It is expected that you reimplement
523 : * this function depending on your daemon's need.
524 : *
525 : * \param[in] quitting Whether the QUITTING (true) or STOP (false) command
526 : * was received.
527 : */
528 0 : void connection_with_send_message::stop(bool quitting)
529 : {
530 0 : snap::NOT_USED(quitting);
531 :
532 : // do nothing by default -- user is expected to overload this function
533 : //
534 0 : SNAP_LOG_WARNING
535 0 : << "default stop() function was called."
536 : << SNAP_LOG_SEND;
537 0 : }
538 :
539 :
540 : /** \brief Check whether the last send_message() worked.
541 : *
542 : * This function returns true if the last send_message() to this connection
543 : * worked or not.
544 : *
545 : * \warning
546 : * This flag is used by the broadcast_message() function only.
547 : *
548 : * \return true if the last send_message() was successful.
549 : */
550 0 : bool connection_with_send_message::get_last_send_status() const
551 : {
552 0 : return f_last_send_status;
553 : }
554 :
555 :
556 :
557 6 : } // namespace ed
558 : // vim: ts=4 sw=4 et
|