Line data Source code
1 : // Snap Communicator -- classes to ease handling communication between processes
2 : // Copyright (c) 2012-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 : // to get the POLLRDHUP definition
19 : #ifndef _GNU_SOURCE
20 : #define _GNU_SOURCE
21 : #endif
22 :
23 :
24 : // self
25 : //
26 : #include "eventdispatcher/message.h"
27 :
28 : #include "eventdispatcher/exception.h"
29 : //#include "eventdispatcher/snap_communicator_dispatcher.h"
30 :
31 :
32 : // snaplogger lib
33 : //
34 : #include "snaplogger/message.h"
35 :
36 :
37 : // snapdev lib
38 : //
39 : //#include "snapdev/not_reached.h"
40 : //#include "snapdev/not_used.h"
41 : #include <snapdev/string_replace_many.h>
42 :
43 :
44 : // boost lib
45 : //
46 : #include <boost/algorithm/string.hpp>
47 :
48 :
49 : //// libaddr lib
50 : ////
51 : //#include "libaddr/addr_parser.h"
52 : //
53 : //
54 : //// C++ lib
55 : ////
56 : //#include <sstream>
57 : //#include <limits>
58 : //#include <atomic>
59 : //
60 : //
61 : //// C lib
62 : ////
63 : //#include <fcntl.h>
64 : //#include <poll.h>
65 : //#include <unistd.h>
66 : //#include <sys/eventfd.h>
67 : //#include <sys/inotify.h>
68 : //#include <sys/ioctl.h>
69 : //#include <sys/resource.h>
70 : //#include <sys/syscall.h>
71 : //#include <sys/time.h>
72 :
73 :
74 : // last include
75 : //
76 : #include <snapdev/poison.h>
77 :
78 :
79 :
80 : /** \file
81 : * \brief Implementation of the Snap Communicator class.
82 : *
83 : * This class wraps the C poll() interface in a C++ object with many types
84 : * of objects:
85 : *
86 : * \li Server Connections; for software that want to offer a port to
87 : * which clients can connect to; the server will call accept()
88 : * once a new client connection is ready; this results in a
89 : * Server/Client connection object
90 : * \li Client Connections; for software that want to connect to
91 : * a server; these expect the IP address and port to connect to
92 : * \li Server/Client Connections; for the server when it accepts a new
93 : * connection; in this case the server gets a socket from accept()
94 : * and creates one of these objects to handle the connection
95 : *
96 : * Using the poll() function is the easiest and allows us to listen
97 : * on pretty much any number of sockets (on my server it is limited
98 : * at 16,768 and frankly over 1,000 we probably will start to have
99 : * real slowness issues on small VPN servers.)
100 : */
101 :
102 : namespace ed
103 : {
104 :
105 :
106 :
107 : /** \brief Parse a message from the specified paremeter.
108 : *
109 : * This function transformed the input string in a set of message
110 : * fields.
111 : *
112 : * The message format supported is:
113 : *
114 : * \code
115 : * ( '<' sent-from-server ':' sent-from-service ' ')? ( ( server ':' )? service '/' )? command ' ' ( parameter_name '=' value ';' )*
116 : * \endcode
117 : *
118 : * The sender "<sent-from-server:sent-from-service" names are added by
119 : * snapcommunicator when it receives a message which is destined for
120 : * another service (i.e. not itself). This can be used by the receiver
121 : * to reply back to the exact same process if it is a requirement for that
122 : * message (i.e. a process that sends a LOCK message, for example,
123 : * expects to receive the LOCKED message back as an answer.) Note that
124 : * it is assumed that there cannot be more than one service named
125 : * 'service' per server. This is enforced by the snapcommunicator
126 : * REGISTER function.
127 : *
128 : * \code
129 : * // to reply to the exact message sender, one can use the
130 : * // following two lines of code:
131 : * //
132 : * reply.set_server(message.get_sent_from_server());
133 : * reply.set_service(message.get_sent_from_service());
134 : *
135 : * // or use the reply_to() helper function
136 : * //
137 : * reply.reply_to(message);
138 : * \endcode
139 : *
140 : * The space after the command cannot be there unless parameters follow.
141 : * Parameters must be separated by semi-colons.
142 : *
143 : * The value of a parameter gets quoted when it includes a ';'. Within
144 : * the quotes, a Double Quote can be escaped inside by adding a backslash
145 : * in front of it (\"). Newline characters (as well as return carriage)
146 : * are also escaped using \n and \r respectively. Finally, we have to
147 : * escape backslashes themselves by doubling them, so \ becomes \\.
148 : *
149 : * Note that only parameter values support absolutely any character.
150 : * All the other parameters are limited to the latin alphabet, digits,
151 : * and underscores ([A-Za-z0-9_]+). Also all commands are limited
152 : * to uppercase letters only.
153 : *
154 : * \note
155 : * The input message is not saved as a cached version of the message
156 : * because we assume it may not be 100% optimized (canonicalized.)
157 : *
158 : * \param[in] message The string to convert into fields in this
159 : * message object.
160 : *
161 : * \return true if the message was succesfully parsed; false when an
162 : * error occurs and in that case no fields get modified.
163 : *
164 : * \sa to_message()
165 : * \sa get_sent_from_server()
166 : * \sa get_sent_from_service()
167 : * \sa reply_to()
168 : */
169 0 : bool message::from_message(std::string const & original_message)
170 : {
171 0 : std::string sent_from_server;
172 0 : std::string sent_from_service;
173 0 : std::string server;
174 0 : std::string service;
175 0 : std::string command;
176 0 : parameters_t parameters;
177 :
178 : // someone using telnet to test sending messages will include a '\r'
179 : // so run a trim on the message in case it is there
180 : //
181 0 : std::string msg(original_message);
182 0 : boost::trim(msg);
183 0 : char const * m(msg.c_str());
184 :
185 : // sent-from indicated?
186 : //
187 0 : if(*m != '\0' && *m == '<')
188 : {
189 : // the name of the server and server sending this message
190 : //
191 : // First ++m to skip the '<'
192 : //
193 0 : for(++m; *m != '\0' && *m != ':'; ++m)
194 : {
195 0 : if(*m == ' ')
196 : {
197 : // invalid syntax from input message
198 : //
199 : SNAP_LOG_ERROR
200 0 : << "a message with sent_from_server must not include a space in the server name ("
201 0 : << original_message
202 0 : << ").";
203 0 : return false;
204 : }
205 :
206 0 : sent_from_server += *m;
207 : }
208 0 : if(*m != '\0')
209 : {
210 : // First ++m to skip the ':'
211 0 : for(++m; *m != '\0' && *m != ' '; ++m)
212 : {
213 0 : sent_from_service += *m;
214 : }
215 : }
216 0 : if(m == '\0')
217 : {
218 : // invalid syntax from input message
219 : //
220 : SNAP_LOG_ERROR
221 0 : << "a message cannot only include a 'sent from service' definition.";
222 0 : return false;
223 : }
224 : // Skip the ' '
225 0 : ++m;
226 : }
227 :
228 0 : bool has_server(false);
229 0 : bool has_service(false);
230 0 : for(; *m != '\0' && *m != ' '; ++m)
231 : {
232 0 : if(*m == ':')
233 : {
234 0 : if(has_server
235 0 : || has_service
236 0 : || command.empty())
237 : {
238 : // we cannot have more than one ':'
239 : // and the name cannot be empty if ':' is used
240 : // we also cannot have a ':' after the '/'
241 : //
242 : SNAP_LOG_ERROR
243 0 : << "a server name cannot be empty when specified, also it cannot include two server names and a server name after a service name was specified.";
244 0 : return false;
245 : }
246 0 : has_server = true;
247 0 : server = command;
248 0 : command.clear();
249 : }
250 0 : else if(*m == '/')
251 : {
252 0 : if(has_service
253 0 : || command.empty())
254 : {
255 : // we cannot have more than one '/'
256 : // and the name cannot be empty if '/' is used
257 : //
258 : SNAP_LOG_ERROR
259 0 : << "a service name is mandatory when the message includes a slash (/), also it cannot include two service names.";
260 0 : return false;
261 : }
262 0 : has_service = true;
263 0 : service = command;
264 0 : command.clear();
265 : }
266 : else
267 : {
268 0 : command += *m;
269 : }
270 : }
271 :
272 0 : if(command.empty())
273 : {
274 : // command is mandatory
275 : //
276 : SNAP_LOG_ERROR
277 0 : << "a command is mandatory in a message.";
278 0 : return false;
279 : }
280 :
281 : // if we have a space, we expect one or more parameters
282 : //
283 0 : if(*m == ' ')
284 : {
285 0 : for(++m; *m != '\0';)
286 : {
287 : // first we have to read the parameter name (up to the '=')
288 : //
289 0 : std::string param_name;
290 0 : for(; *m != '\0' && *m != '='; ++m)
291 : {
292 0 : param_name += *m;
293 : }
294 0 : if(param_name.empty())
295 : {
296 : // parameters must have a name
297 : //
298 : SNAP_LOG_ERROR
299 0 : << "could not accept message because an empty parameter name is not valid.";
300 0 : return false;
301 : }
302 : try
303 : {
304 0 : verify_message_name(param_name);
305 : }
306 0 : catch(event_dispatcher_invalid_message const & e)
307 : {
308 : // name is not empty, but it has invalid characters in it
309 : //
310 : SNAP_LOG_ERROR
311 0 : << "could not accept message because parameter name \""
312 0 : << param_name
313 0 : << "\" is not considered valid: "
314 0 : << e.what();
315 0 : return false;
316 : }
317 :
318 0 : if(*m == '\0'
319 0 : || *m != '=')
320 : {
321 : // ?!?
322 : //
323 : SNAP_LOG_ERROR
324 0 : << "message parameters must be followed by an equal (=) character.";
325 0 : return false;
326 : }
327 0 : ++m; // skip '='
328 :
329 : // retrieve the parameter name at first
330 : //
331 0 : std::string param_value;
332 0 : if(*m != '\0' && *m == '"')
333 : {
334 : // quoted parameter
335 : //
336 0 : for(++m; *m != '"'; ++m)
337 : {
338 0 : if(*m == '\0')
339 : {
340 : // closing quote (") is missing
341 : //
342 : SNAP_LOG_ERROR
343 0 : << "a quoted message parameter must end with a quote (\").";
344 0 : return false;
345 : }
346 :
347 : // restored escaped double quotes
348 : // (note that we do not yet restore other backslashed
349 : // characters, that's done below)
350 : //
351 0 : if(*m == '\\' && m[1] != '\0' && m[1] == '"')
352 : {
353 0 : ++m;
354 : }
355 : // here the character may be ';'
356 : //
357 0 : param_value += *m;
358 : }
359 :
360 : // skip the quote
361 : //
362 0 : ++m;
363 : }
364 : else
365 : {
366 : // parameter value is found as is
367 : //
368 0 : for(; *m != '\0' && *m != ';'; ++m)
369 : {
370 0 : param_value += *m;
371 : }
372 : }
373 :
374 0 : if(*m != '\0')
375 : {
376 0 : if(*m != ';')
377 : {
378 : // this should never happend
379 : //
380 : SNAP_LOG_ERROR
381 0 : << "two parameters must be separated by a semicolon (;).";
382 0 : return false;
383 : }
384 :
385 : // skip the ';'
386 : //
387 0 : ++m;
388 : }
389 :
390 : // also restore new lines and blackslashes if any
391 : //
392 : std::string const unsafe_value(snap::string_replace_many(
393 : param_value,
394 : {
395 : { "\\\\", "\\" },
396 : { "\\n", "\n" },
397 : { "\\r", "\r" }
398 0 : }));
399 :
400 : // we got a valid parameter, add it
401 : //
402 0 : parameters[param_name] = unsafe_value;
403 : }
404 : }
405 :
406 0 : f_sent_from_server = sent_from_server;
407 0 : f_sent_from_service = sent_from_service;
408 0 : f_server = server;
409 0 : f_service = service;
410 0 : f_command = command;
411 0 : f_parameters.swap(parameters);
412 0 : f_cached_message.clear();
413 :
414 0 : return true;
415 : }
416 :
417 :
418 : /** \brief Transform all the message parameters in a string.
419 : *
420 : * This function transforms all the message parameters in a string
421 : * and returns the result. The string is a message we can send over
422 : * TCP/IP (if you make sure to add a "\n", note that the
423 : * send_message() does that automatically) or over UDP/IP.
424 : *
425 : * \note
426 : * The function caches the result so calling the function many times
427 : * will return the same string and thus the function is very fast
428 : * after the first call (assuming you do not modify the message on
429 : * each call to to_message().)
430 : *
431 : * \note
432 : * The sent-from information gets saved in the message only if both,
433 : * the server name and service name it was sent from are defined.
434 : *
435 : * \exception snap_communicator_invalid_message
436 : * This function raises an exception if the message command was not
437 : * defined since a command is always mandatory.
438 : *
439 : * \return The converted message as a string.
440 : *
441 : * \sa get_sent_from_server()
442 : * \sa get_sent_from_service()
443 : * \sa set_reply_to_server()
444 : * \sa set_reply_to_service()
445 : */
446 0 : std::string message::to_message() const
447 : {
448 0 : if(f_cached_message.empty())
449 : {
450 0 : if(f_command.empty())
451 : {
452 0 : throw event_dispatcher_invalid_message("message::to_message(): cannot build a valid message without at least a command.");
453 : }
454 :
455 : // add info about the sender
456 : //
457 : // ['<' <sent-from-server> ':' <sent-from-service> ' ']
458 : //
459 0 : if(!f_sent_from_server.empty()
460 0 : || !f_sent_from_service.empty())
461 : {
462 0 : f_cached_message += '<';
463 0 : f_cached_message += f_sent_from_server;
464 0 : f_cached_message += ':';
465 0 : f_cached_message += f_sent_from_service;
466 0 : f_cached_message += ' ';
467 : }
468 :
469 : // add server and optionally the destination server name if both are defined
470 : //
471 : // ['<' <sent-from-server> ':' <sent-from-service> ' ']
472 : // [[<server> ':'] <name> '/']
473 : //
474 0 : if(!f_service.empty())
475 : {
476 0 : if(!f_server.empty())
477 : {
478 0 : f_cached_message += f_server;
479 0 : f_cached_message += ':';
480 : }
481 0 : f_cached_message += f_service;
482 0 : f_cached_message += '/';
483 : }
484 :
485 : // ['<' <sent-from-server> ':' <sent-from-service> ' ']
486 : // [[<server> ':'] <name> '/'] <command>
487 : //
488 0 : f_cached_message += f_command;
489 :
490 : // add parameters if any
491 : //
492 : // ['<' <sent-from-server> ':' <sent-from-service> ' ']
493 : // [[<server> ':'] <name> '/'] <command>
494 : // [' ' <param1> '=' <value1>][';' <param2> '=' <value2>]...
495 : //
496 0 : char sep(' ');
497 0 : for(auto p : f_parameters)
498 : {
499 0 : f_cached_message += sep;
500 0 : f_cached_message += p.first;
501 :
502 : std::string safe_value(snap::string_replace_many(
503 : p.second,
504 : {
505 : { "\\", "\\\\" },
506 : { "\n", "\\n" },
507 : { "\r", "\\r" }
508 0 : }));
509 :
510 0 : if(safe_value.find(';') != std::string::npos
511 0 : || (!safe_value.empty() && safe_value[0] == '\"'))
512 : {
513 : // escape the double quotes
514 : //
515 0 : boost::algorithm::replace_all(safe_value, "\"", "\\\"");
516 :
517 : // quote the resulting parameter and save in f_cached_message
518 : //
519 0 : f_cached_message += '"';
520 0 : f_cached_message += safe_value;
521 0 : f_cached_message += '"';
522 : }
523 : else
524 : {
525 : // no special handling necessary
526 : //
527 0 : f_cached_message += safe_value;
528 : }
529 :
530 0 : sep = ';';
531 : }
532 : }
533 :
534 0 : return f_cached_message;
535 : }
536 :
537 :
538 : /** \brief Where this message came from.
539 : *
540 : * Some services send a message expecting an answer directly sent back
541 : * to them. Yet, those services may have multiple instances in your cluster
542 : * (i.e. snapcommunicator runs on all computers, snapwatchdog, snapfirewall,
543 : * snaplock, snapdbproxy are likely to run on most computers, etc.)
544 : * This parameter defines which computer the message came from. Thus,
545 : * you can use that information to send the message back to that
546 : * specific computer. The snapcommunicator on that computer will
547 : * then forward the message to the specified service instance.
548 : *
549 : * If empty (the default,) then the normal snapcommunicator behavior is
550 : * used (i.e. send to any instance of the service that is available.)
551 : *
552 : * \return The address and port of the specific service this message has to
553 : * be sent to.
554 : *
555 : * \sa set_sent_from_server()
556 : * \sa set_sent_from_service()
557 : * \sa get_sent_from_service()
558 : */
559 5 : std::string const & message::get_sent_from_server() const
560 : {
561 5 : return f_sent_from_server;
562 : }
563 :
564 :
565 : /** \brief Set the name of the server that sent this message.
566 : *
567 : * This function saves the name of the server that was used to
568 : * generate the message. This can be used later to send a reply
569 : * to the service that sent this message.
570 : *
571 : * The snapcommunicator tool is actually in charge of setting this
572 : * parameter and you should never have to do so from your tool.
573 : * The set happens whenever the snapcommunicator receives a
574 : * message from a client. If you are not using the snapcommunicator
575 : * then you are welcome to use this function for your own needs.
576 : *
577 : * \param[in] sent_from_server The name of the source server.
578 : *
579 : * \sa get_sent_from_server()
580 : * \sa get_sent_from_service()
581 : * \sa set_sent_from_service()
582 : */
583 1 : void message::set_sent_from_server(std::string const & sent_from_server)
584 : {
585 1 : if(f_sent_from_server != sent_from_server)
586 : {
587 : // this name can be empty and it supports lowercase
588 : //
589 1 : verify_message_name(sent_from_server, true);
590 :
591 1 : f_sent_from_server = sent_from_server;
592 1 : f_cached_message.clear();
593 : }
594 1 : }
595 :
596 :
597 : /** \brief Who sent this message.
598 : *
599 : * Some services send messages expecting an answer sent right back to
600 : * them. For example, the snaplock tool sends the message LOCKENTERING
601 : * and expects the LOCKENTERED as a reply. The reply has to be sent
602 : * to the exact same instance t hat sent the LOCKENTERING message.
603 : *
604 : * In order to do so, the system makes use of the server and service
605 : * name the data was sent from. Since the name of each service
606 : * registering with snapcommunicator must be unique, it 100% defines
607 : * the sender of the that message.
608 : *
609 : * If empty (the default,) then the normal snapcommunicator behavior is
610 : * used (i.e. send to any instance of the service that is available locally,
611 : * if not available locally, try to send it to another snapcommunicator
612 : * that knows about it.)
613 : *
614 : * \return The address and port of the specific service this message has to
615 : * be sent to.
616 : *
617 : * \sa get_sent_from_server()
618 : * \sa set_sent_from_server()
619 : * \sa set_sent_from_service()
620 : */
621 5 : std::string const & message::get_sent_from_service() const
622 : {
623 5 : return f_sent_from_service;
624 : }
625 :
626 :
627 : /** \brief Set the name of the server that sent this message.
628 : *
629 : * This function saves the name of the service that sent this message
630 : * to snapcommuncator. It is set by snapcommunicator whenever it receives
631 : * a message from a service it manages so you do not have to specify this
632 : * parameter yourselves.
633 : *
634 : * This can be used to provide the name of the service to reply to. This
635 : * is useful when the receiver does not already know exactly who sends it
636 : * certain messages.
637 : *
638 : * \param[in] sent_from_service The name of the service that sent this message.
639 : *
640 : * \sa get_sent_from_server()
641 : * \sa set_sent_from_server()
642 : * \sa get_sent_from_service()
643 : */
644 1 : void message::set_sent_from_service(std::string const & sent_from_service)
645 : {
646 1 : if(f_sent_from_service != sent_from_service)
647 : {
648 : // this name can be empty and it supports lowercase
649 : //
650 1 : verify_message_name(sent_from_service, true);
651 :
652 1 : f_sent_from_service = sent_from_service;
653 1 : f_cached_message.clear();
654 : }
655 1 : }
656 :
657 :
658 : /** \brief The server where this message has to be delivered.
659 : *
660 : * Some services need their messages to be delivered to a service
661 : * running on a specific computer. This function returns the name
662 : * of that server.
663 : *
664 : * If the function returns an empty string, then snapcommunicator is
665 : * free to send the message to any server.
666 : *
667 : * \return The name of the server to send this message to or an empty string.
668 : *
669 : * \sa set_server()
670 : * \sa get_service()
671 : * \sa set_service()
672 : */
673 4 : std::string const & message::get_server() const
674 : {
675 4 : return f_server;
676 : }
677 :
678 :
679 : /** \brief Set the name of a specific server where to send this message.
680 : *
681 : * In some cases you may want to send a message to a service running
682 : * on a specific server. This function can be used to specify the exact
683 : * server where the message has to be delivered.
684 : *
685 : * This is particularly useful when you need to send a reply to a
686 : * specific daemon that sent you a message.
687 : *
688 : * The name can be set to ".", which means send to a local service
689 : * only, whether it is available or not. This option can be used
690 : * to avoid/prevent sending a message to other computers.
691 : *
692 : * The name can be set to "*", which is useful to broadcast the message
693 : * to all servers even if the destination service name is
694 : * "snapcommunicator".
695 : *
696 : * \param[in] server The name of the server to send this message to.
697 : *
698 : * \sa get_server()
699 : * \sa get_service()
700 : * \sa set_service()
701 : */
702 2 : void message::set_server(std::string const & server)
703 : {
704 2 : if(f_server != server)
705 : {
706 : // this name can be empty and it supports lowercase
707 : //
708 4 : if(server != "."
709 2 : && server != "*")
710 : {
711 2 : verify_message_name(server, true);
712 : }
713 :
714 2 : f_server = server;
715 2 : f_cached_message.clear();
716 : }
717 2 : }
718 :
719 :
720 : /** \brief Retrieve the name of the service the message is for.
721 : *
722 : * This function returns the name of the service this message is being
723 : * sent to.
724 : *
725 : * \return Destination service.
726 : *
727 : * \sa get_server()
728 : * \sa set_server()
729 : * \sa set_service()
730 : */
731 4 : std::string const & message::get_service() const
732 : {
733 4 : return f_service;
734 : }
735 :
736 :
737 : /** \brief Set the name of the service this message is being sent to.
738 : *
739 : * This function specifies the name of the server this message is expected
740 : * to be sent to.
741 : *
742 : * When a service wants to send a message to snapcommunicator, no service
743 : * name is required.
744 : *
745 : * \param[in] service The name of the destination service.
746 : *
747 : * \sa get_server()
748 : * \sa set_server()
749 : * \sa get_service()
750 : */
751 2 : void message::set_service(std::string const & service)
752 : {
753 2 : if(f_service != service)
754 : {
755 : // broadcast is a special case that the verify_message_name() does not
756 : // support
757 : //
758 4 : if(service != "*"
759 2 : && service != "?"
760 4 : && service != ".")
761 : {
762 : // this name can be empty and it supports lowercase
763 : //
764 2 : verify_message_name(service, true);
765 : }
766 :
767 2 : f_service = service;
768 2 : f_cached_message.clear();
769 : }
770 2 : }
771 :
772 :
773 : /** \brief Copy sent information to this message.
774 : *
775 : * This function copies the sent information found in message
776 : * to this message server and service names.
777 : *
778 : * This is an equivalent to the following two lines of code:
779 : *
780 : * \code
781 : * reply.set_server(message.get_sent_from_server());
782 : * reply.set_service(message.get_sent_from_service());
783 : * \endcode
784 : *
785 : * \param[in] original_message The source message you want to reply to.
786 : */
787 1 : void message::reply_to(message const & original_message)
788 : {
789 1 : set_server(original_message.get_sent_from_server());
790 1 : set_service(original_message.get_sent_from_service());
791 1 : }
792 :
793 :
794 : /** \brief Get the command being sent.
795 : *
796 : * Each message is an equivalent to an RPC command being send between
797 : * services.
798 : *
799 : * The command is a string of text, generally one or more words
800 : * concatenated (no space allowed) such as STOP and LOCKENTERING.
801 : *
802 : * \note
803 : * The command string may still be empty if it was not yet assigned.
804 : *
805 : * \return The command of this message.
806 : */
807 3 : std::string const & message::get_command() const
808 : {
809 3 : return f_command;
810 : }
811 :
812 :
813 : /** \brief Set the message command.
814 : *
815 : * This function is used to define the RPC-like command of this message.
816 : *
817 : * The name of the command gets verified using the verify_message_name() function.
818 : * It cannot be empty and all letters have to be uppercase.
819 : *
820 : * \param[in] command The command to send to a connection.
821 : *
822 : * \sa verify_message_name()
823 : */
824 1 : void message::set_command(std::string const & command)
825 : {
826 : // this name cannot be empty and it does not support lowercase
827 : // characters either
828 : //
829 1 : verify_message_name(command, false, false);
830 :
831 1 : if(f_command != command)
832 : {
833 1 : f_command = command;
834 1 : f_cached_message.clear();
835 : }
836 1 : }
837 :
838 :
839 : /** \brief Retrieve the message version this library was compiled with.
840 : *
841 : * This function returns the MESSAGE_VERSION that this library was
842 : * compiled with. Since we offer a shared object (.so) library, it
843 : * could be different from the version your application was compiled
844 : * with. If that's the case, your application may want to at least
845 : * warn the user about the dicrepancy.
846 : *
847 : * \return The MESSAGE_VERSION at the time this library was compiled.
848 : */
849 2 : message_version_t message::get_message_version() const
850 : {
851 2 : return MESSAGE_VERSION;
852 : }
853 :
854 :
855 : /** \brief Check the version parameter.
856 : *
857 : * This function retrieves the version parameter which has to exist and
858 : * be an integer. If this is not the case, then an exception is raised.
859 : *
860 : * If the version is defined, it gets checked against this library's
861 : * compile time MESSAGE_VERSION variable. If equal, then the function
862 : * returns true. Otherwise, it returns false.
863 : *
864 : * In most cases, the very first message that you send with your service
865 : * should be such that it includes the version the libeventdispatcher
866 : * your program is linked against. In other words, you want to call
867 : * the add_version_parameter() in your sender and call this function
868 : * in your receiver.
869 : *
870 : * Make sure to design a way to disconnect cleanly so the other
871 : * party knows that the communication is interrupted because the
872 : * versions do not match. This allows your application to prevent
873 : * re-connecting over and over again when it knows it will fail
874 : * each time.
875 : *
876 : * \exception event_dispatcher_invalid_message
877 : * If you call this function and no version parameter was added to
878 : * the message, then this exception is raised.
879 : *
880 : * \return true if the version is present and valid.
881 : */
882 1 : bool message::check_version_parameter() const
883 : {
884 1 : return get_integer_parameter(MESSAGE_VERSION_NAME) == MESSAGE_VERSION;
885 : }
886 :
887 :
888 : /** \brief Add version parameter.
889 : *
890 : * Add a parameter named `"version"` with the current version of the
891 : * message protocol.
892 : *
893 : * In the snapcommunicator tool, this is sent over with the CONNECT
894 : * message. It allows the snapcommunicators to make sure they will
895 : * properly understand each others.
896 : */
897 1 : void message::add_version_parameter()
898 : {
899 1 : add_parameter(MESSAGE_VERSION_NAME, MESSAGE_VERSION);
900 1 : }
901 :
902 :
903 : /** \brief Add a string parameter to the message.
904 : *
905 : * Messages can include parameters (variables) such as a URI or a word.
906 : *
907 : * The value is not limited, although it probably should be limited to
908 : * standard text as these messages are sent as text. Especially, we
909 : * manage the '\0' character as the end of the message.
910 : *
911 : * The parameter name is verified by the verify_message_name() function.
912 : *
913 : * \param[in] name The name of the parameter.
914 : * \param[in] value The value of this parameter.
915 : *
916 : * \sa verify_message_name()
917 : */
918 1 : void message::add_parameter(std::string const & name, std::string const & value)
919 : {
920 1 : verify_message_name(name);
921 :
922 1 : f_parameters[name] = value;
923 1 : f_cached_message.clear();
924 1 : }
925 :
926 :
927 : /** \brief Add an integer parameter to the message.
928 : *
929 : * Messages can include parameters (variables) such as a URI or a word.
930 : *
931 : * The value is not limited, although it probably should be limited to
932 : * standard text as these messages are sent as text.
933 : *
934 : * The parameter name is verified by the verify_message_name() function.
935 : *
936 : * \param[in] name The name of the parameter.
937 : * \param[in] value The value of this parameter.
938 : *
939 : * \sa verify_message_name()
940 : */
941 2 : void message::add_parameter(std::string const & name, int32_t value)
942 : {
943 2 : verify_message_name(name);
944 :
945 2 : f_parameters[name] = std::to_string(value);
946 2 : f_cached_message.clear();
947 2 : }
948 :
949 :
950 : /** \brief Add an integer parameter to the message.
951 : *
952 : * Messages can include parameters (variables) such as a URI or a word.
953 : *
954 : * The value is not limited, although it probably should be limited to
955 : * standard text as these messages are sent as text.
956 : *
957 : * The parameter name is verified by the verify_message_name() function.
958 : *
959 : * \param[in] name The name of the parameter.
960 : * \param[in] value The value of this parameter.
961 : *
962 : * \sa verify_message_name()
963 : */
964 1 : void message::add_parameter(std::string const & name, uint32_t value)
965 : {
966 1 : verify_message_name(name);
967 :
968 1 : f_parameters[name] = std::to_string(value);
969 1 : f_cached_message.clear();
970 1 : }
971 :
972 :
973 : /** \brief Add an integer parameter to the message.
974 : *
975 : * Messages can include parameters (variables) such as a URI or a word.
976 : *
977 : * The value is not limited, although it probably should be limited to
978 : * standard text as these messages are sent as text.
979 : *
980 : * The parameter name is verified by the verify_message_name() function.
981 : *
982 : * \param[in] name The name of the parameter.
983 : * \param[in] value The value of this parameter.
984 : *
985 : * \sa verify_message_name()
986 : */
987 1 : void message::add_parameter(std::string const & name, long long value)
988 : {
989 1 : verify_message_name(name);
990 :
991 1 : f_parameters[name] = std::to_string(value);
992 1 : f_cached_message.clear();
993 1 : }
994 :
995 :
996 : /** \brief Add an integer parameter to the message.
997 : *
998 : * Messages can include parameters (variables) such as a URI or a word.
999 : *
1000 : * The value is not limited, although it probably should be limited to
1001 : * standard text as these messages are sent as text.
1002 : *
1003 : * The parameter name is verified by the verify_message_name() function.
1004 : *
1005 : * \param[in] name The name of the parameter.
1006 : * \param[in] value The value of this parameter.
1007 : *
1008 : * \sa verify_message_name()
1009 : */
1010 1 : void message::add_parameter(std::string const & name, unsigned long long value)
1011 : {
1012 1 : verify_message_name(name);
1013 :
1014 1 : f_parameters[name] = std::to_string(value);
1015 1 : f_cached_message.clear();
1016 1 : }
1017 :
1018 :
1019 : /** \brief Add an integer parameter to the message.
1020 : *
1021 : * Messages can include parameters (variables) such as a URI or a word.
1022 : *
1023 : * The value is not limited, although it probably should be limited to
1024 : * standard text as these messages are sent as text.
1025 : *
1026 : * The parameter name is verified by the verify_message_name() function.
1027 : *
1028 : * \param[in] name The name of the parameter.
1029 : * \param[in] value The value of this parameter.
1030 : *
1031 : * \sa verify_message_name()
1032 : */
1033 1 : void message::add_parameter(std::string const & name, int64_t value)
1034 : {
1035 1 : verify_message_name(name);
1036 :
1037 1 : f_parameters[name] = std::to_string(value);
1038 1 : f_cached_message.clear();
1039 1 : }
1040 :
1041 :
1042 : /** \brief Add an integer parameter to the message.
1043 : *
1044 : * Messages can include parameters (variables) such as a URI or a word.
1045 : *
1046 : * The value is not limited, although it probably should be limited to
1047 : * standard text as these messages are sent as text.
1048 : *
1049 : * The parameter name is verified by the verify_message_name() function.
1050 : *
1051 : * \param[in] name The name of the parameter.
1052 : * \param[in] value The value of this parameter.
1053 : *
1054 : * \sa verify_message_name()
1055 : */
1056 1 : void message::add_parameter(std::string const & name, uint64_t value)
1057 : {
1058 1 : verify_message_name(name);
1059 :
1060 1 : f_parameters[name] = std::to_string(value);
1061 1 : f_cached_message.clear();
1062 1 : }
1063 :
1064 :
1065 : /** \brief Check whether a parameter is defined in this message.
1066 : *
1067 : * This function checks whether a parameter is defined in a message. If
1068 : * so it returns true. This is important because the get_parameter()
1069 : * functions throw if the parameter is not available (i.e. which is
1070 : * what is used for mandatory parameters.)
1071 : *
1072 : * The parameter name is verified by the verify_message_name() function.
1073 : *
1074 : * \param[in] name The name of the parameter.
1075 : *
1076 : * \return true if that parameter exists.
1077 : *
1078 : * \sa verify_message_name()
1079 : */
1080 21 : bool message::has_parameter(std::string const & name) const
1081 : {
1082 21 : verify_message_name(name);
1083 :
1084 21 : return f_parameters.find(name) != f_parameters.end();
1085 : }
1086 :
1087 :
1088 : /** \brief Retrieve a parameter as a string from this message.
1089 : *
1090 : * This function retrieves the named parameter from this message as a string,
1091 : * which is the default.
1092 : *
1093 : * The name must be valid as defined by the verify_message_name() function.
1094 : *
1095 : * \note
1096 : * This function returns a copy of the parameter so if you later change
1097 : * the value of that parameter, what has been returned does not change
1098 : * under your feet.
1099 : *
1100 : * \exception snap_communicator_invalid_message
1101 : * This exception is raised whenever the parameter is not defined or
1102 : * if the parameter \p name is not considered valid.
1103 : *
1104 : * \param[in] name The name of the parameter.
1105 : *
1106 : * \return A copy of the parameter value.
1107 : *
1108 : * \sa verify_message_name()
1109 : */
1110 7 : std::string message::get_parameter(std::string const & name) const
1111 : {
1112 7 : verify_message_name(name);
1113 :
1114 7 : auto const it(f_parameters.find(name));
1115 7 : if(it != f_parameters.end())
1116 : {
1117 14 : return it->second;
1118 : }
1119 :
1120 : throw event_dispatcher_invalid_message(
1121 : "message::get_parameter(): parameter \""
1122 0 : + name
1123 0 : + "\" of command \""
1124 0 : + f_command
1125 0 : + "\" not defined, try has_parameter() before calling"
1126 0 : " the get_parameter() function.");
1127 : }
1128 :
1129 :
1130 : /** \brief Retrieve a parameter as an integer from this message.
1131 : *
1132 : * This function retrieves the named parameter from this message as a string,
1133 : * which is the default.
1134 : *
1135 : * The name must be valid as defined by the verify_message_name() function.
1136 : *
1137 : * \exception snap_communicator_invalid_message
1138 : * This exception is raised whenever the parameter is not a valid integer,
1139 : * it is not set, or the parameter name is not considered valid.
1140 : *
1141 : * \param[in] name The name of the parameter.
1142 : *
1143 : * \return The parameter converted to an integer.
1144 : *
1145 : * \sa verify_message_name()
1146 : */
1147 7 : std::int64_t message::get_integer_parameter(std::string const & name) const
1148 : {
1149 7 : verify_message_name(name);
1150 :
1151 7 : auto const it(f_parameters.find(name));
1152 7 : if(it != f_parameters.end())
1153 : {
1154 : std::int64_t r;
1155 7 : if(!advgetopt::validator_integer::convert_string(it->second, r))
1156 : {
1157 : throw event_dispatcher_invalid_message(
1158 : "message::get_integer_parameter(): command \""
1159 0 : + f_command
1160 0 : + "\" expected integer for \""
1161 0 : + name
1162 0 : + "\" but \""
1163 0 : + it->second
1164 0 : + "\" could not be converted.");
1165 : }
1166 14 : return r;
1167 : }
1168 :
1169 : throw event_dispatcher_invalid_message(
1170 : "message::get_integer_parameter(): parameter \""
1171 0 : + name
1172 0 : + "\" of command \""
1173 0 : + f_command
1174 0 : + "\" not defined, try has_parameter() before calling"
1175 0 : " the get_integer_parameter() function.");
1176 : }
1177 :
1178 :
1179 : /** \brief Retrieve the list of parameters from this message.
1180 : *
1181 : * This function returns a constant reference to the list of parameters
1182 : * defined in this message.
1183 : *
1184 : * This can be useful if you allow for variable lists of parameters, but
1185 : * generally the get_parameter() and get_integer_parameter() are prefered.
1186 : *
1187 : * \warning
1188 : * This is a direct reference to the list of parameter. If you call the
1189 : * add_parameter() function, the new parameter will be visible in that
1190 : * new list and an iterator is likely not going to be valid on return
1191 : * from that call.
1192 : *
1193 : * \return A constant reference to the list of message parameters.
1194 : *
1195 : * \sa get_parameter()
1196 : * \sa get_integer_parameter()
1197 : */
1198 3 : message::parameters_t const & message::get_all_parameters() const
1199 : {
1200 3 : return f_parameters;
1201 : }
1202 :
1203 :
1204 : /** \brief Verify various names used with messages.
1205 : *
1206 : * The messages use names for:
1207 : *
1208 : * \li commands
1209 : * \li services
1210 : * \li parameters
1211 : *
1212 : * All those names must be valid as per this function. They are checked
1213 : * on read and on write (i.e. add_parameter() and get_paramter() both
1214 : * check the parameter name to make sure you did not mistype it.)
1215 : *
1216 : * A valid name must start with a letter or an underscore (although
1217 : * we suggest you do not start names with underscores; we want to
1218 : * have those reserved for low level system like messages,) and
1219 : * it can only include letters, digits, and underscores.
1220 : *
1221 : * The letters are limited to uppercase for commands. Also certain
1222 : * names may be empty (See concerned functions for details on that one.)
1223 : *
1224 : * \note
1225 : * The allowed letters are 'a' to 'z' and 'A' to 'Z' only. The allowed
1226 : * digits are '0' to '9' only. The underscore is '_' only.
1227 : *
1228 : * A few valid names:
1229 : *
1230 : * \li commands: PING, STOP, LOCK, LOCKED, QUITTING, UNKNOWN, LOCKEXITING
1231 : * \li services: snapcommunicator, snapserver, snaplock, MyOwnService
1232 : * \li parameters: URI, name, IP, TimeOut
1233 : *
1234 : * At this point all our services use lowercase, but this is not enforced.
1235 : * Actually, mixed case or uppercase service names are allowed.
1236 : *
1237 : * \exception event_distpatcher_invalid_message
1238 : * This exception is raised if the name includes characters considered
1239 : * invalid.
1240 : *
1241 : * \param[in] name The name of the parameter.
1242 : * \param[in] can_be_empty Whether the name can be empty.
1243 : * \param[in] can_be_lowercase Whether the name can include lowercase letters.
1244 : */
1245 50 : void verify_message_name(std::string const & name, bool can_be_empty, bool can_be_lowercase)
1246 : {
1247 100 : if(!can_be_empty
1248 50 : && name.empty())
1249 : {
1250 0 : std::string err("a message name cannot be empty.");
1251 0 : SNAP_LOG_FATAL << err;
1252 0 : throw event_dispatcher_invalid_message(err);
1253 : }
1254 :
1255 325 : for(auto const & c : name)
1256 : {
1257 275 : if((c < 'a' || c > 'z' || !can_be_lowercase)
1258 37 : && (c < 'A' || c > 'Z')
1259 30 : && (c < '0' || c > '9')
1260 0 : && c != '_')
1261 : {
1262 : std::string err("a message name must be composed of ASCII"
1263 : " 'a'..'z', 'A'..'Z', '0'..'9', or '_'"
1264 0 : " only (also a command must be uppercase only,) \"");
1265 0 : err += name;
1266 0 : err += "\" is not valid.";
1267 0 : SNAP_LOG_FATAL << err;
1268 0 : throw event_dispatcher_invalid_message(err);
1269 : }
1270 : }
1271 :
1272 100 : if(!name.empty()
1273 50 : && name[0] >= '0' && name[0] <= '9')
1274 : {
1275 0 : std::string err("parameter name cannot start with a digit, \"");
1276 0 : err += name;
1277 0 : err += "\" is not valid.";
1278 0 : SNAP_LOG_FATAL << err;
1279 0 : throw event_dispatcher_invalid_message(err);
1280 : }
1281 50 : }
1282 :
1283 :
1284 :
1285 :
1286 6 : } // namespace ed
1287 : // vim: ts=4 sw=4 et
|