Line data Source code
1 : // Copyright (c) 2012-2022 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/connection.h"
33 : #include "eventdispatcher/dispatcher_support.h"
34 : #include "eventdispatcher/exception.h"
35 :
36 :
37 : // snaplogger
38 : //
39 : #include <snaplogger/logger.h>
40 : #include <snaplogger/message.h>
41 :
42 :
43 : // snapdev
44 : //
45 : #include <snapdev/not_used.h>
46 :
47 :
48 : // boost
49 : //
50 : #include <boost/algorithm/string/join.hpp>
51 :
52 :
53 : // C
54 : //
55 : #ifdef __SANITIZE_ADDRESS__
56 : #include <sanitizer/lsan_interface.h>
57 : #endif
58 :
59 :
60 : // last include
61 : //
62 : #include <snapdev/poison.h>
63 :
64 :
65 :
66 : namespace ed
67 : {
68 :
69 :
70 :
71 : /** \brief Initialize the connection.
72 : *
73 : * This constructor initialize the connection with a send_message() function.
74 : * The function takes an optional \p service_name which is used in various
75 : * messages such as the REGISTER and UNREGISTER messages.
76 : *
77 : * If the \p service_name parameter is an empty string, then the functions
78 : * that require that name throw when reached.
79 : *
80 : * \param[in] service_name The name of your service.
81 : *
82 : * \sa register_service()
83 : * \sa unregister_service()
84 : */
85 3 : connection_with_send_message::connection_with_send_message(std::string const & service_name)
86 3 : : f_service_name(service_name)
87 : {
88 3 : }
89 :
90 :
91 : /** \brief Clean up.
92 : *
93 : * The destructor makes sure to clean up the connection with send_message()
94 : * objects.
95 : */
96 3 : connection_with_send_message::~connection_with_send_message()
97 : {
98 3 : }
99 :
100 :
101 :
102 : /** \brief Reply to the watchdog message ALIVE.
103 : *
104 : * To check whether a service is alive, send the ALIVE message. This
105 : * function builds an ABSOLUTELY reply and attaches the "serial" parameter
106 : * as is if present in the ALIVE message. It also includes the original
107 : * "timestamp" parameter.
108 : *
109 : * The function also adds one field named "reply_timestamp" with the Unix
110 : * time in seconds when the reply is being sent.
111 : *
112 : * \note
113 : * The "serial" parameter is expected to be used to make sure that no messages
114 : * are lost, or if loss is expected, to see whether loss is heavy or not.
115 : *
116 : * \note
117 : * The "serial" and "timestamp" parameters do not get checked. If present
118 : * in the original message, they get copied verbatim to the destination.
119 : * This allows you to include anything you want in those parameters although
120 : * we suggest you use the "timestamp" only for a value representing time.
121 : *
122 : * \param[in] msg The ALIVE message.
123 : */
124 0 : void connection_with_send_message::msg_alive(message & msg)
125 : {
126 0 : message absolutely;
127 0 : absolutely.user_data(msg.user_data<void>());
128 0 : absolutely.reply_to(msg);
129 0 : absolutely.set_command("ABSOLUTELY");
130 0 : if(msg.has_parameter("serial"))
131 : {
132 0 : absolutely.add_parameter("serial", msg.get_parameter("serial"));
133 : }
134 0 : if(msg.has_parameter("timestamp"))
135 : {
136 0 : absolutely.add_parameter("timestamp", msg.get_parameter("timestamp"));
137 : }
138 0 : absolutely.add_parameter("reply_timestamp", time(nullptr));
139 0 : if(!send_message(absolutely, false))
140 : {
141 0 : SNAP_LOG_WARNING
142 : << "could not reply to \""
143 0 : << msg.get_command()
144 : << "\" with an ABSOLUTELY message."
145 : << SNAP_LOG_SEND;
146 : }
147 0 : }
148 :
149 :
150 : /** \brief Build the HELP reply and send it.
151 : *
152 : * When a daemon registers with the snapcommunicator, it sends a REGISTER
153 : * command. As a result, your daemon is sent a HELP command which must be
154 : * answered with a COMMANDS message which includes the list of commands
155 : * (a.k.a. messages) that your daemon supports.
156 : *
157 : * The list of commands is built using the list of Expression() strings
158 : * found in the dispatcher of your daemon. If that list includes null
159 : * pointers or custom match functions, then the list is deemed to
160 : * include functions that the default loop cannot determine. As a result,
161 : * your help() function will be called and it must be overridden, otherwise
162 : * it will call the default which throws.
163 : *
164 : * \exception implementation_error
165 : * This exception is thrown if the resulting list of commands is empty.
166 : *
167 : * \param[in] msg The HELP message.
168 : *
169 : * \sa help()
170 : */
171 0 : void connection_with_send_message::msg_help(message & msg)
172 : {
173 0 : snapdev::NOT_USED(msg);
174 :
175 0 : bool need_user_help(true);
176 0 : string_list_t commands;
177 :
178 : dispatcher_base * d;
179 0 : dispatcher_support * ds(dynamic_cast<dispatcher_support *>(this));
180 0 : if(ds != nullptr)
181 : {
182 : // we extract the bare pointer because in the other case
183 : // we only get a bare pointer... (which we can't safely
184 : // put in a shared pointer, although we could attempt to
185 : // use shared_from_this() but we could have a class without
186 : // it?)
187 : //
188 0 : d = ds->get_dispatcher().get();
189 : }
190 : else
191 : {
192 0 : d = dynamic_cast<dispatcher_base *>(this);
193 : }
194 0 : if(d != nullptr)
195 : {
196 0 : need_user_help = d->get_commands(commands);
197 : }
198 :
199 : // the user has "unknown" commands (as far as the dispatcher is concerned)
200 : // in his list of commands so we have to let him enter them "manually"
201 : //
202 : // this happens whenever there is an entry which is a regular expression
203 : // or something similar which we just cannot grab
204 : //
205 0 : if(need_user_help)
206 : {
207 0 : help(commands);
208 : }
209 :
210 : // the list of commands just cannot be empty
211 : //
212 0 : if(commands.empty())
213 : {
214 : throw implementation_error(
215 : "connection_with_send_message::msg_help()"
216 0 : " is not able to determine the commands this messenger supports");
217 : }
218 :
219 : // Now prepare the COMMAND message and send it
220 : //
221 : // Note: we turn off the caching on this message, it does not make sense
222 : // because if snapcommunicator is not running, then caching won't
223 : // happen work anyway (i.e. snapcommunicator has to send HELP first
224 : // and then we send the reply, if it has to restart, then just
225 : // sending COMMANDS will fail.)
226 : //
227 0 : message reply;
228 0 : reply.user_data(msg.user_data<void>());
229 0 : reply.set_command("COMMANDS");
230 0 : reply.add_parameter("list", boost::algorithm::join(commands, ","));
231 0 : if(!send_message(reply, false))
232 : {
233 0 : SNAP_LOG_WARNING
234 : << "could not reply to \""
235 0 : << msg.get_command()
236 : << "\" with a COMMANDS message."
237 : << SNAP_LOG_SEND;
238 : }
239 0 : }
240 :
241 :
242 : /** \brief Run the sanitizer leak checker.
243 : *
244 : * This function calls the function printing out all the leaks found
245 : * at this time in this software (\em leaks as in any block of memory
246 : * currently allocated).
247 : *
248 : * The message does nothing if the library was not compiled with the
249 : * sanitizer feature turned on.
250 : *
251 : * The message can be sent any number of times.
252 : *
253 : * \note
254 : * This is a debug message only.
255 : *
256 : * \todo
257 : * Find a solution which actually works. The LSAN system doesn't offer
258 : * to (correctly) print the list of leaks at any given time.
259 : *
260 : * \param[in] msg The LEAK message, which is ignored.
261 : */
262 0 : void connection_with_send_message::msg_leak(ed::message & msg)
263 : {
264 0 : snapdev::NOT_USED(msg);
265 :
266 : #ifdef __SANITIZE_ADDRESS__
267 0 : __lsan_do_recoverable_leak_check();
268 : #else
269 : std::cerr << "error: leaks are not being tracked;"
270 : " use the --sanitize option to turn on this feature."
271 : << std::endl;
272 : #endif
273 0 : }
274 :
275 :
276 : /** \brief Reopen file-based logger appenders.
277 : *
278 : * Whenever logrotate runs or some changes are made to the log
279 : * definitions, the corresponding daemons need to reopen snaplogger
280 : * to make use of the new file and settings. This command is used
281 : * for that purpose.
282 : *
283 : * \note
284 : * If the snaplogger is not currently configured, this message is
285 : * ignored.
286 : *
287 : * \param[in] msg The LOG_ROTATE message.
288 : */
289 0 : void connection_with_send_message::msg_log_rotate(message & msg)
290 : {
291 0 : snapdev::NOT_USED(msg);
292 :
293 0 : if(snaplogger::is_configured())
294 : {
295 : // send log in the old file and format
296 : //
297 0 : SNAP_LOG_INFO
298 : << "-------------------- Logging reconfiguration request."
299 : << SNAP_LOG_SEND;
300 :
301 : // reconfigure
302 : //
303 0 : snaplogger::reopen();
304 :
305 : // send log to new file and format
306 : //
307 0 : SNAP_LOG_INFO
308 : << "-------------------- Logging reconfiguration done."
309 : << SNAP_LOG_SEND;
310 : }
311 0 : }
312 :
313 :
314 : /** \brief Call you stop() function with true.
315 : *
316 : * This command means that someone is asking your daemon to quit as soon as
317 : * possible because the Snap! environment is being asked to shutdown.
318 : *
319 : * The value 'true' means that all the daemons are being asked to stop and
320 : * not just you.
321 : *
322 : * \param[in] msg The QUITTING message.
323 : *
324 : * \sa msg_stop()
325 : * \sa stop()
326 : */
327 0 : void connection_with_send_message::msg_quitting(message & msg)
328 : {
329 0 : snapdev::NOT_USED(msg);
330 :
331 0 : stop(true);
332 0 : }
333 :
334 :
335 : /** \brief Calls your ready() function with the message.
336 : *
337 : * All daemons using the snapcommunicator daemon have to have a ready()
338 : * function which gets called once the HELP and COMMAND message were
339 : * handled. This is when your daemon is expected to be ready to start
340 : * working. Some daemons start working immediately no matter
341 : * what (i.e. snapwatchdog and snapfirewall do work either way), but
342 : * those are rare.
343 : *
344 : * \param[in] msg The READY message.
345 : *
346 : * \sa ready()
347 : */
348 0 : void connection_with_send_message::msg_ready(message & msg)
349 : {
350 : // pass the message so any additional info can be accessed.
351 : //
352 0 : ready(msg);
353 0 : }
354 :
355 :
356 : /** \brief Calls your restart() function with the message.
357 : *
358 : * This message has no implementation by default. What we want to
359 : * do is find a clean way to restart any service instantly.
360 : *
361 : * The RESTART message is expected to be used whenever a modification
362 : * to some file or the system environment somehow affects your service
363 : * in such a way that it requires a restart. For example, after an
364 : * upgrade of the eventdispatcher library, you should restart all the
365 : * services that make use of it. For this reason, we have a RESTART
366 : * message.
367 : *
368 : * The message comes with one parameter named `reason` which describes
369 : * why the RESTART was sent:
370 : *
371 : * \li `reason=upgrade` -- something (library/tools) was upgraded
372 : * \li `reason=config` -- a configuration file was updated
373 : *
374 : * \note
375 : * At the time we created this message, a live configuration was not
376 : * available. Now that we have the fluid-settings service, that changed
377 : * and in most cases, `reason=config` should not be necessary anymore.
378 : * (only for fluid-settings itself and for some parameters not found
379 : * in the fluid-settings).
380 : *
381 : * \note
382 : * There are currently some services that make use of a CONFIG message
383 : * whenever their configuration changes. Pretty much all services do
384 : * not support a live configuration change (because it initializes their
385 : * objects from the configuration data once on startup and in many cases
386 : * it would be very complicated to allow for changes to occur on the fly).
387 : * \note
388 : * In those existing implementations, we really just do a restart anyway.
389 : * \note
390 : * The new versions will be using fluid-settings. We can still support
391 : * a RESTART message, but a RELOADCONFIG (as we had in snapcommunicator)
392 : * should not be used.
393 : *
394 : * \param[in] msg The RESTART message.
395 : *
396 : * \sa restart()
397 : */
398 0 : void connection_with_send_message::msg_restart(message & msg)
399 : {
400 : // pass the message so any additional info can be accessed.
401 : //
402 0 : restart(msg);
403 0 : }
404 :
405 :
406 : /** \brief Call you stop() function with false.
407 : *
408 : * This command means that someone is asking your daemon to stop.
409 : *
410 : * The value 'false' means just your daemon was asked to stop and not the
411 : * entire system to shutdown (otherwise you would receive a QUITTING command
412 : * instead.)
413 : *
414 : * \param[in] msg The STOP message.
415 : *
416 : * \sa msg_quitting()
417 : */
418 0 : void connection_with_send_message::msg_stop(message & msg)
419 : {
420 0 : snapdev::NOT_USED(msg);
421 :
422 0 : stop(false);
423 0 : }
424 :
425 :
426 : /** \brief Handle the UNKNOWN message.
427 : *
428 : * Whenever we send a command to another daemon, that command can be refused
429 : * by sending an UNKNOWN reply. This function handles the UNKNOWN command
430 : * by simply recording that as an error in the logs.
431 : *
432 : * \param[in] msg The UNKNOWN message we just received.
433 : */
434 0 : void connection_with_send_message::msg_log_unknown(message & msg)
435 : {
436 : // we sent a command that the other end did not understand
437 : // and got an UNKNOWN reply
438 : //
439 0 : SNAP_LOG_ERROR
440 : << "we sent command \""
441 0 : << msg.get_parameter("command")
442 : << "\" and the destination replied with \"UNKNOWN\""
443 : " so we probably did not get the expected result."
444 : << SNAP_LOG_SEND;
445 0 : }
446 :
447 :
448 : /** \brief Send the UNKNOWN message as a reply.
449 : *
450 : * This function replies to the \p message with the UNKNOWN message as
451 : * expected by all our connection objects when a service receives a
452 : * message it does not know how to handle.
453 : *
454 : * It is expected to be used in your dispatcher_match array.
455 : *
456 : * \note
457 : * This function is virtual which allows you to add it to your array of
458 : * of dispatcher_match items. The following shows an example of what that
459 : * can look like.
460 : *
461 : * \code
462 : * {
463 : * ed::dispatcher<my_connection>::define_match(
464 : * ...
465 : * ),
466 : * ...
467 : *
468 : * // ALWAYS LAST
469 : * ed::dispatcher<my_connection>::define_catch_all()
470 : * };
471 : * \endcode
472 : *
473 : * \param[in] msg The message to reply to.
474 : */
475 0 : void connection_with_send_message::msg_reply_with_unknown(message & msg)
476 : {
477 0 : message unknown;
478 0 : unknown.user_data(msg.user_data<void>());
479 0 : unknown.reply_to(msg);
480 0 : unknown.set_command("UNKNOWN");
481 0 : unknown.add_parameter("command", msg.get_command());
482 0 : if(!send_message(unknown, false))
483 : {
484 0 : SNAP_LOG_WARNING
485 : << "could not reply to \""
486 0 : << msg.get_command()
487 : << "\" with UNKNOWN message."
488 : << SNAP_LOG_SEND;
489 : }
490 : else
491 : {
492 0 : SNAP_LOG_MINOR
493 : << "unknown command \""
494 0 : << msg.get_command()
495 : << "\"."
496 : << SNAP_LOG_SEND;
497 : }
498 0 : }
499 :
500 :
501 : /** \brief The default help() function does nothing.
502 : *
503 : * This implementation does nothing. It is expected that you reimplement
504 : * this function depending on your daemon's need.
505 : *
506 : * The help() function gets called whenever the list of commands can't be
507 : * 100% defined automatically.
508 : *
509 : * Your function is expected to add commands to the \p commands parameter
510 : * as in:
511 : *
512 : * \code
513 : * commands.push_back("MSG1");
514 : * commands.push_back("MSG2");
515 : * commands.push_back("MSG3");
516 : * \endcode
517 : *
518 : * This allows you to handle those three messages with a single entry in
519 : * your list of dispatcher_match objects with a regular expression such
520 : * as "MSG[1-3]".
521 : *
522 : * \param[in,out] commands List of commands to update.
523 : */
524 0 : void connection_with_send_message::help(string_list_t & commands)
525 : {
526 0 : snapdev::NOT_USED(commands);
527 :
528 : // do nothing by default -- user is expected to overload this function
529 0 : }
530 :
531 :
532 : /** \brief The default ready() function does nothing.
533 : *
534 : * This implementation does nothing. It is expected that you reimplement
535 : * this function depending on your daemon's need. Most often this function
536 : * is the one that really starts your daemons process.
537 : *
538 : * \param[in,out] msg The READY message.
539 : */
540 0 : void connection_with_send_message::ready(message & msg)
541 : {
542 0 : snapdev::NOT_USED(msg);
543 :
544 : // do nothing by default -- user is expected to overload this function
545 : //
546 0 : SNAP_LOG_WARNING
547 : << "default ready() function was called."
548 : << SNAP_LOG_SEND;
549 0 : }
550 :
551 :
552 : /** \brief The default restart() function does nothing.
553 : *
554 : * This implementation does nothing. It is expected that you reimplement
555 : * this function depending on your daemon's need. Most often this function
556 : * calls the stop() function in order to restart the daemon. If only a
557 : * configuration file changed and your daemon is capable of reading the
558 : * new settings without a full restart, then just read that new config.
559 : *
560 : * \param[in,out] msg The RESTART message.
561 : *
562 : * \sa msg_restart()
563 : */
564 0 : void connection_with_send_message::restart(message & msg)
565 : {
566 0 : snapdev::NOT_USED(msg);
567 :
568 : // do nothing by default -- user is expected to overload this function
569 : //
570 0 : SNAP_LOG_WARNING
571 : << "default restart() function was called."
572 : << SNAP_LOG_SEND;
573 0 : }
574 :
575 :
576 : /** \brief The default stop() function does nothing.
577 : *
578 : * This implementation does nothing. It is expected that you reimplement
579 : * this function depending on your daemon's need.
580 : *
581 : * \param[in] quitting Whether the QUITTING (true) or STOP (false) command
582 : * was received.
583 : */
584 0 : void connection_with_send_message::stop(bool quitting)
585 : {
586 0 : snapdev::NOT_USED(quitting);
587 :
588 : // do nothing by default -- user is expected to overload this function
589 : //
590 0 : SNAP_LOG_WARNING
591 : << "default stop() function was called."
592 : << SNAP_LOG_SEND;
593 0 : }
594 :
595 :
596 : /** \brief Retrieve the name of this service.
597 : *
598 : * A messenger used with the snapcommunicator is viewed as a service and
599 : * it needs to have a name. That name is specified at the time you create
600 : * your service (see constructor).
601 : *
602 : * This function returns that name.
603 : *
604 : * \exception name_undefined
605 : * When \p required is true, this function make raise a `name_undefined` if
606 : * the service name is empty. Fix this issue by adding the name in your
607 : * constructor.
608 : *
609 : * \param[in] required Set to true if you want to raise an error when the
610 : * service name is not defined.
611 : *
612 : * \return The service name of this connection.
613 : */
614 0 : std::string connection_with_send_message::get_service_name(bool required) const
615 : {
616 0 : if(required
617 0 : && f_service_name.empty())
618 : {
619 0 : throw name_undefined("service name is required but not available.");
620 : }
621 :
622 0 : return f_service_name;
623 : }
624 :
625 :
626 : /** \brief Register your snapcommunicator service.
627 : *
628 : * This function registers your snapcommunicator service by sending
629 : * the REGISTER command to it. The service name must have been defined
630 : * in your constructor.
631 : *
632 : * The function is expected to be called in your process_connected()
633 : * function.
634 : *
635 : * \code
636 : * void my_messenger::process_connected()
637 : * {
638 : * // make sure to call default function
639 : * snap_tcp_client_permanent_message_connection::process_connected();
640 : *
641 : * // then register
642 : * register_service();
643 : * }
644 : * \endcode
645 : *
646 : * \note
647 : * The function generates a fatal error if the send_message() fails.
648 : * However, you are responsible for quitting your service if the function
649 : * returns false. This very function does not attempt anything more.
650 : *
651 : * \exception name_undefined
652 : * The function makes use of the service name as specified in the
653 : * constructor. It cannot be empty.
654 : *
655 : * \return true if the send_message() succeeded.
656 : */
657 0 : bool connection_with_send_message::register_service()
658 : {
659 0 : message register_msg;
660 0 : register_msg.set_command("REGISTER");
661 0 : register_msg.add_parameter("service", get_service_name());
662 0 : register_msg.add_version_parameter();
663 0 : if(!send_message(register_msg, false))
664 : {
665 0 : SNAP_LOG_FATAL
666 : << "could not REGISTER with snapcommunicator."
667 : << SNAP_LOG_SEND;
668 0 : return false;
669 : }
670 :
671 0 : return true;
672 : }
673 :
674 :
675 : /** \brief Unregister from the snapcommunicator.
676 : *
677 : * The register_service() function registers your service with the
678 : * snapcommunicator. This function is the converse. It sends a message
679 : * to UNREGISTER you from the snapcommunicator. This means other services
680 : * will not be able to send you messages anymore.
681 : *
682 : * \exception name_undefined
683 : * The function makes use of the service name as specified in the
684 : * constructor. It cannot be empty.
685 : */
686 0 : void connection_with_send_message::unregister_service()
687 : {
688 : // mark ourself as done so once the last message(s) were sent,
689 : // we get automatically removed from the communicator
690 : //
691 0 : connection * c(dynamic_cast<connection *>(this));
692 0 : if(c == nullptr)
693 : {
694 0 : throw implementation_error("ed::connection_with_send_message must derive from ed::connection.");
695 : }
696 0 : c->mark_done();
697 :
698 : // unregister ourself from the snapcommunicator daemon
699 : //
700 0 : message unregister_msg;
701 0 : unregister_msg.set_command("UNREGISTER");
702 0 : unregister_msg.add_parameter("service", get_service_name(true));
703 0 : if(!send_message(unregister_msg, false))
704 : {
705 0 : SNAP_LOG_WARNING
706 : << "could not UNREGISTER from snapcommunicator."
707 : << SNAP_LOG_SEND;
708 : }
709 0 : }
710 :
711 :
712 :
713 6 : } // namespace ed
714 : // vim: ts=4 sw=4 et
|