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