Line data Source code
1 : // Copyright (c) 2012-2019 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
17 : // along with this program; if not, write to the Free Software
18 : // Foundation, Inc., 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 connectopm 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 0 : connection_with_send_message::~connection_with_send_message()
70 : {
71 0 : }
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 msg_ready()
89 : */
90 0 : void connection_with_send_message::msg_help(message & msg)
91 : {
92 0 : snap::NOTUSED(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 : SNAP_LOG_WARNING
152 0 : << "could not reply to \""
153 0 : << msg.get_command()
154 0 : << "\" with a COMMANDS message.";
155 : }
156 0 : }
157 :
158 :
159 : /** \brief Reply to the watchdog message ALIVE.
160 : *
161 : * To check whether a service is alive, send the ALIVE message. This
162 : * function builds a ABSOLUTELY reply and attaches the "serial" parameter
163 : * as is if present. It will also include the original "timestamp" parameter
164 : * when present.
165 : *
166 : * The function also adds one field named "reply_timestamp" with the Unix
167 : * time when the reply is being sent.
168 : *
169 : * \note
170 : * The "serial" parameter is expected to be used to make sure that no messages
171 : * are lost, or if loss is expected, to see whether loss is heavy or not.
172 : *
173 : * \note
174 : * The "serial" and "timestamp" parameters do not get checked. If present
175 : * in the original message, they get copied verbatim to the destination.
176 : * This allows you to include anything you want in those parameters although
177 : * we suggest you use the "timestamp" only for a value representing time.
178 : *
179 : * \param[in] message The STOP message.
180 : */
181 0 : void connection_with_send_message::msg_alive(message & msg)
182 : {
183 0 : message absolutely;
184 0 : absolutely.reply_to(msg);
185 0 : absolutely.set_command("ABSOLUTELY");
186 0 : if(msg.has_parameter("serial"))
187 : {
188 0 : absolutely.add_parameter("serial", msg.get_parameter("serial"));
189 : }
190 0 : if(msg.has_parameter("timestamp"))
191 : {
192 0 : absolutely.add_parameter("timestamp", msg.get_parameter("timestamp"));
193 : }
194 0 : absolutely.add_parameter("reply_timestamp", time(nullptr));
195 0 : if(!send_message(absolutely, false))
196 : {
197 : SNAP_LOG_WARNING
198 0 : << "could not reply to \""
199 0 : << msg.get_command()
200 0 : << "\" with an ABSOLULTELY message.";
201 : }
202 0 : }
203 :
204 :
205 : /** \brief Reconfigure the logger.
206 : *
207 : * Whenever the logrotate runs or some changes are maed to the log
208 : * definitions, the corresponding daemons need to reconfigure their
209 : * logger to make use of the new file and settings. This command is
210 : * used for the purpose.
211 : *
212 : * \note
213 : * If the environment logger is not currently configured, this message
214 : * gets ignored.
215 : *
216 : * \param[in] message The STOP message.
217 : */
218 0 : void connection_with_send_message::msg_log(message & msg)
219 : {
220 0 : snap::NOTUSED(msg);
221 :
222 0 : if(snaplogger::is_configured())
223 : {
224 : // send log in the old file and format
225 : //
226 : SNAP_LOG_INFO
227 0 : << "-------------------- Logging reconfiguration request.";
228 :
229 : // reconfigure
230 : //
231 0 : snaplogger::reopen();
232 :
233 : // send log to new file and format
234 : //
235 : SNAP_LOG_INFO
236 0 : << "-------------------- Logging reconfiguration done.";
237 : }
238 0 : }
239 :
240 :
241 : /** \brief Call you stop() function with true.
242 : *
243 : * This command means that someone is asking your daemon to quit as soon as
244 : * possible because the Snap! environment is being asked to shutdown.
245 : *
246 : * The value 'true' means that all the daemons are being asked to stop and
247 : * not just you.
248 : *
249 : * \param[in] message The STOP message.
250 : *
251 : * \sa msg_stop()
252 : */
253 0 : void connection_with_send_message::msg_quitting(message & msg)
254 : {
255 0 : snap::NOTUSED(msg);
256 :
257 0 : stop(true);
258 0 : }
259 :
260 :
261 : /** \brief Call you ready() function with the message.
262 : *
263 : * All daemons using the snapcommunicator daemon have to have a ready()
264 : * function which gets called once the HELP and COMMAND message were
265 : * handled. This is why your daemon is expected to be ready to start
266 : * working. Some daemon, though, start working immediately no matter
267 : * what (i.e. snapwatchdog and snapfirewall do work either way.)
268 : *
269 : * \param[in] message The READY message.
270 : *
271 : * \sa msg_help()
272 : */
273 0 : void connection_with_send_message::msg_ready(message & msg)
274 : {
275 : // pass the message so any additional info can be accessed.
276 : //
277 0 : ready(msg);
278 0 : }
279 :
280 :
281 : /** \brief Do nothing at the moment.
282 : *
283 : * This message has no implementation by default at the moment. What we
284 : * want to do is find a clean way to restart any service instantly.
285 : *
286 : * The RESTART message is expected to be used whenever a modification
287 : * to some file or the system environment somehow affects your service
288 : * in such a way that it requires a restart. For example, after an
289 : * upgrade of eventdispatcher library, you should restart all the services
290 : * that make use of this library. For this reason, we have a RESTART
291 : * message.
292 : *
293 : * The messages comes with one parameter named `reason` which describes
294 : * why the RESTART was sent:
295 : *
296 : * \li upgrade -- something (library/tools) was upgraded
297 : * \li config -- a congiguration file was updated
298 : *
299 : * \note
300 : * There is currently some services that make use of a CONFIG message
301 : * whenever their configuration changes. Pretty much all services do
302 : * not support a live configuration change (because it initializes their
303 : * objects from the configuration data once on startup and in many cases
304 : * it would be very complicated to allow for changes to occur.)
305 : * \note
306 : * In those existing implementation, we really just do a restart anyway.
307 : *
308 : * \param[in] message The READY message.
309 : *
310 : * \sa msg_help()
311 : */
312 0 : void connection_with_send_message::msg_restart(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 Call you stop() function with false.
321 : *
322 : * This command means that someone is asking your daemon to stop.
323 : *
324 : * The value 'false' means just your daemon was asked to stop and not the
325 : * entire system to shutdown (otherwise you would receive a QUITTING command
326 : * instead.)
327 : *
328 : * \param[in] message The STOP message.
329 : *
330 : * \sa msg_quitting()
331 : */
332 0 : void connection_with_send_message::msg_stop(message & msg)
333 : {
334 0 : snap::NOTUSED(msg);
335 :
336 0 : stop(false);
337 0 : }
338 :
339 :
340 : /** \brief Handle the UNKNOWN message.
341 : *
342 : * Whenever we send a command to another daemon, that command can be refused
343 : * by sending an UNKNOWN reply. This function handles the UNKNOWN command
344 : * by simply recording that as an error in the logs.
345 : *
346 : * \param[in] message The UNKNOWN message we just received.
347 : */
348 0 : void connection_with_send_message::msg_log_unknown(message & msg)
349 : {
350 : // we sent a command that the other end did not understand
351 : // and got an UNKNOWN reply
352 : //
353 : SNAP_LOG_ERROR
354 0 : << "we sent unknown command \""
355 0 : << msg.get_parameter("command")
356 0 : << "\" and probably did not get the expected result.";
357 0 : }
358 :
359 :
360 : /** \brief Send the UNKNOWN message as a reply.
361 : *
362 : * This function replies to the \p message with the UNKNOWN message as
363 : * expected by all our `snap_connection`'s when a service receives a
364 : * message it does not know how to handle.
365 : *
366 : * It is expected to be used in your dispatcher_match array.
367 : *
368 : * \note
369 : * This function is virtual which allows you to add it to your array of
370 : * of dispatcher_match items. The following shows an example of what that
371 : * can look like.
372 : *
373 : * \code
374 : * {
375 : * ...
376 : *
377 : * // ALWAYS LAST
378 : * {
379 : * nullptr
380 : * , &my_service_connection::msg_reply_with_unknown
381 : * , &snap::dispatcher<my_service_connection>::dispatcher_match::always_match
382 : * }
383 : * };
384 : * \endcode
385 : *
386 : * \param[in] message The messageto reply to.
387 : */
388 0 : void connection_with_send_message::msg_reply_with_unknown(message & msg)
389 : {
390 0 : message unknown;
391 0 : unknown.reply_to(msg);
392 0 : unknown.set_command("UNKNOWN");
393 0 : unknown.add_parameter("command", msg.get_command());
394 0 : if(!send_message(unknown, false))
395 : {
396 : SNAP_LOG_WARNING
397 0 : << "could not reply to \""
398 0 : << msg.get_command()
399 0 : << "\" with UNKNOWN message.";
400 : }
401 0 : }
402 :
403 :
404 : /** \brief The default help() function does nothing.
405 : *
406 : * This implementation does nothing. It is expected that you reimplement
407 : * this function depending on your daemon's need.
408 : *
409 : * The help() function gets called whenever the list of commands can't be
410 : * 100% defined automatically.
411 : *
412 : * Your function is expected to add commands to the \p commands parameter
413 : * as in:
414 : *
415 : * \code
416 : * commands << "MSG1";
417 : * commands << "MSG2";
418 : * commands << "MSG3";
419 : * \endcode
420 : *
421 : * This allows you to handle those three messages with a single entry in
422 : * your list of dispatcher_match objects with a regular expression such
423 : * as "MSG[1-3]".
424 : *
425 : * \param[in,out] commands List of commands to update.
426 : */
427 0 : void connection_with_send_message::help(string_list_t & commands)
428 : {
429 0 : snap::NOTUSED(commands);
430 :
431 : // do nothing by default -- user is expected to overload this function
432 0 : }
433 :
434 :
435 : /** \brief The default ready() function does nothing.
436 : *
437 : * This implementation does nothing. It is expected that you reimplement
438 : * this function depending on your daemon's need. Most often this function
439 : * is the one that really starts your daemons process.
440 : *
441 : * \param[in,out] message The READY message.
442 : */
443 0 : void connection_with_send_message::ready(message & msg)
444 : {
445 0 : snap::NOTUSED(msg);
446 :
447 : // do nothing by default -- user is expected to overload this function
448 : //
449 : SNAP_LOG_WARNING
450 0 : << "default ready() function was called.";
451 0 : }
452 :
453 :
454 : /** \brief The default stop() function does nothing.
455 : *
456 : * This implementation does nothing. It is expected that you reimplement
457 : * this function depending on your daemon's need.
458 : *
459 : * \param[in] quitting Whether the QUITTING (true) or STOP (false) command
460 : * was received.
461 : */
462 0 : void connection_with_send_message::stop(bool quitting)
463 : {
464 0 : snap::NOTUSED(quitting);
465 :
466 : // do nothing by default -- user is expected to overload this function
467 : //
468 : SNAP_LOG_WARNING
469 0 : << "default stop() function was called.";
470 0 : }
471 :
472 :
473 :
474 6 : } // namespace ed
475 : // vim: ts=4 sw=4 et
|