Line data Source code
1 : // Copyright (c) 2011-2024 Made to Order Software Corp. All Rights Reserved 2 : // 3 : // https://snapwebsites.org/project/communicatord 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 3 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, see <https://www.gnu.org/licenses/>. 18 : 19 : /** \file 20 : * \brief Base declaration for all the connections. 21 : * 22 : * The communicatord daemon has to deal with many connections. This 23 : * base handles some basic aspects of each connection so we do not have 24 : * to specialize the code everywhere. 25 : * 26 : * The several types of connections supported: 27 : * 28 : * * Connection from this server to another communicatord server 29 : * (see remote_connection) 30 : * 31 : * * Connection from another communicatord server to this server 32 : * (see remote_connection) 33 : * 34 : * * Connection from a communicatord to another which is expected to 35 : * connect to us (see gossip_connection) 36 : * 37 : * * Connection from a local server to the communicatord 38 : * (see service_connection and unix_connection) 39 : * 40 : * * Messages from a local server via UDP 41 : * (see ping) 42 : * 43 : * The connections between communicators are 100% managed by each 44 : * communicatord. This creates the RPC functionality between all your 45 : * tools. 46 : * 47 : * The other type of connections happen from the various local or remote 48 : * services to the communicatord. 49 : */ 50 : 51 : 52 : 53 : // self 54 : // 55 : #include "base_connection.h" 56 : 57 : 58 : // communicatord 59 : // 60 : #include <communicatord/exception.h> 61 : 62 : 63 : // snapdev 64 : // 65 : #include <snapdev/tokenize_string.h> 66 : 67 : 68 : // last include 69 : // 70 : #include <snapdev/poison.h> 71 : 72 : 73 : 74 : namespace communicator_daemon 75 : { 76 : 77 : 78 : 79 : 80 : 81 : /** \brief Initialize the base_connection() object. 82 : * 83 : * The constructor saves the communicator server pointer 84 : * so one can access it from any derived version. 85 : */ 86 1 : base_connection::base_connection( 87 : server::pointer_t cs 88 1 : , bool is_udp) 89 1 : : f_server(cs) 90 1 : , f_is_udp(is_udp) 91 : { 92 1 : } 93 : 94 : 95 : /** \brief We have a destructor to make it virtual. 96 : * 97 : * Everything is otherwise automatically released. 98 : */ 99 1 : base_connection::~base_connection() 100 : { 101 1 : } 102 : 103 : 104 : /** \brief Save when the connection started. 105 : * 106 : * This function is called whenever a CONNECT or REGISTER message 107 : * is received since those mark the time when a connection starts. 108 : * 109 : * You can later retrieve when the connection started with the 110 : * get_connection_started() function. 111 : * 112 : * This call also resets the f_ended_on in case we were able to 113 : * reuse the same connection multiple times (reconnecting means 114 : * a new socket and thus a brand new connection object...) 115 : */ 116 0 : void base_connection::connection_started() 117 : { 118 0 : f_started_on = time(nullptr); 119 0 : f_ended_on = -1; 120 0 : } 121 : 122 : 123 : /** \brief Return information on when the connection started. 124 : * 125 : * This function gives you the date and time when the connection 126 : * started, meaning when the connection received a CONNECT or 127 : * REGISTER event. 128 : * 129 : * If the events have no yet occured, then the connection returns 130 : * -1 instead. 131 : * 132 : * \return The date and time when the connection started in microseconds. 133 : */ 134 1 : int64_t base_connection::get_connection_started() const 135 : { 136 1 : return f_started_on; 137 : } 138 : 139 : 140 : /** \brief Connection ended, save the date and time of the event. 141 : * 142 : * Whenever we receive a DISCONNECT or UNREGISTER we call this 143 : * function. It also gets called in the event a connection 144 : * is deleted without first receiving a graceful DISCONNECT 145 : * or UNREGISTER event. 146 : */ 147 0 : void base_connection::connection_ended() 148 : { 149 : // save the current date only if the connection really started 150 : // before and also only once (do not update the end time again 151 : // until a connection_started() call happens) 152 : // 153 0 : if(f_started_on != -1 154 0 : && f_ended_on == -1) 155 : { 156 0 : f_ended_on = time(nullptr); 157 : } 158 0 : } 159 : 160 : 161 : /** \brief Timestamp when the connection was ended. 162 : * 163 : * This value represents the time when the UNREGISTER, DISCONNECT, 164 : * or the destruction of the service_connection object occurred. It 165 : * represents the time when the specific service was shutdown. 166 : * 167 : * \return The approximative date when the connection ended in microseconds. 168 : */ 169 1 : int64_t base_connection::get_connection_ended() const 170 : { 171 1 : return f_ended_on; 172 : } 173 : 174 : 175 : /** \brief Save the name of the server. 176 : * 177 : * \param[in] server_name The name of the server that is on the other 178 : * side of this connection. 179 : */ 180 0 : void base_connection::set_server_name(std::string const & server_name) 181 : { 182 0 : f_server_name = server_name; 183 0 : } 184 : 185 : 186 : /** \brief Get the name of the server. 187 : * 188 : * \return The name of the server that is on the other 189 : * side of this connection. 190 : */ 191 1 : std::string base_connection::get_server_name() const 192 : { 193 1 : return f_server_name; 194 : } 195 : 196 : 197 : /** \brief Save the address of that connection. 198 : * 199 : * This is only used for remote connections on either the CONNECT 200 : * or ACCEPT message. 201 : * 202 : * \param[in] my_address The address of the server that is on the 203 : * other side of this connection. 204 : */ 205 0 : void base_connection::set_connection_address(addr::addr const & connection_address) 206 : { 207 0 : f_connection_address = connection_address; 208 0 : } 209 : 210 : 211 : /** \brief Get the address of that connection. 212 : * 213 : * This function returns a valid address only after the CONNECT 214 : * or ACCEPT message were received for this connection. 215 : * 216 : * \return The address of the server that is on the 217 : * other side of this connection. 218 : */ 219 0 : addr::addr base_connection::get_connection_address() const 220 : { 221 0 : return f_connection_address; 222 : } 223 : 224 : 225 : /** \brief Define the type of communicatord server. 226 : * 227 : * This function is called whenever a CONNECT or an ACCEPT is received. 228 : * It saves the type=... parameter. By default the type is empty meaning 229 : * that the connection was not yet fully initialized. 230 : * 231 : * When a REGISTER is received instead of a CONNECT or an ACCEPT, then 232 : * the type is set to "client". 233 : * 234 : * \param[in] type The type of connection. 235 : */ 236 0 : void base_connection::set_connection_type(connection_type_t type) 237 : { 238 0 : f_type = type; 239 0 : } 240 : 241 : 242 : /** \brief Retrieve the current type of this connection. 243 : * 244 : * By default a connection is given the type CONNECTION_TYPE_DOWN, 245 : * which means that it is not currently connected. To initialize 246 : * a connection one has to either CONNECT (between communicatord 247 : * servers) or REGISTER (a service such as snapbackend, snapserver, 248 : * sitter, and others.) 249 : * 250 : * The type is set to CONNECTION_TYPE_LOCAL for local services and 251 : * CONNECTION_TYPE_REMOTE when representing another snapserver. 252 : * 253 : * \return The type of server this connection represents. 254 : */ 255 0 : connection_type_t base_connection::get_connection_type() const 256 : { 257 0 : return f_type; 258 : } 259 : 260 : 261 : /** \brief Set the username required to connect on this TCP connection. 262 : * 263 : * When accepting connections from remote communicatord, it is best to 264 : * assign a user name and password to that connection. This protects 265 : * your connection from hackers without such credentials. 266 : * 267 : * \param[in] username The name of the user that can connect to this listener. 268 : */ 269 0 : void base_connection::set_username(std::string const & username) 270 : { 271 0 : f_username = username; 272 0 : } 273 : 274 : 275 : /** \brief Retrieve the user name of this connection. 276 : * 277 : */ 278 0 : std::string base_connection::get_username() const 279 : { 280 0 : return f_username; 281 : } 282 : 283 : 284 0 : void base_connection::set_password(std::string const & password) 285 : { 286 0 : f_password = password; 287 0 : } 288 : 289 : 290 : /** \brief Return the password assigned to this connection. 291 : * 292 : * Each listener may include a password to prevent unwanted connections 293 : * from hackers on public facing connections. 294 : */ 295 0 : std::string base_connection::get_password() const 296 : { 297 0 : return f_password; 298 : } 299 : 300 : 301 : /** \brief Define the list of services supported by the communicatord. 302 : * 303 : * Whenever a communicatord connects to another one, either by 304 : * doing a CONNECT or replying to a CONNECT by an ACCEPT, it is 305 : * expected to list services that it supports (the list could be 306 : * empty as it usually is on a Cassandra node.) This function 307 : * saves that list. 308 : * 309 : * This defines the name of services and thus where to send various 310 : * messages such as a PING to request a service to start doing work. 311 : * 312 : * \param[in] services The list of services this server handles. 313 : */ 314 0 : void base_connection::set_services(std::string const & services) 315 : { 316 0 : snapdev::tokenize_string(f_services, services, { "," }); 317 0 : } 318 : 319 : 320 : /** \brief Retrieve the list of services offered by other communicators. 321 : * 322 : * This function saves in the input parameter \p services the list of 323 : * services that this very communicatord offers. 324 : * 325 : * \param[in,out] services The map where all the services are defined. 326 : */ 327 0 : void base_connection::get_services(advgetopt::string_set_t & services) 328 : { 329 0 : f_services.merge(services); 330 0 : } 331 : 332 : 333 : /** \brief Check whether the service is known by that connection. 334 : * 335 : * This function returns true if the service was defined as one 336 : * this connection supports. 337 : * 338 : * \param[in] name The name of the service to check for. 339 : * 340 : * \return true if the service is known. 341 : */ 342 0 : bool base_connection::has_service(std::string const & name) 343 : { 344 0 : return f_services.find(name) != f_services.end(); 345 : } 346 : 347 : 348 : /** \brief Define the list of services we heard of. 349 : * 350 : * This function saves the list of services that were heard of by 351 : * another communicatord server. This list may be updated later 352 : * with an ACCEPT event. 353 : * 354 : * This list is used to know where to forward a message if we do 355 : * not have a more direct link to those services (i.e. the same 356 : * service defined in our own list or in a communicatord 357 : * we are directly connected to.) 358 : * 359 : * \param[in] services The list of services heard of. 360 : */ 361 0 : void base_connection::set_services_heard_of(std::string const & services) 362 : { 363 0 : snapdev::tokenize_string(f_services_heard_of, services, { "," }); 364 0 : } 365 : 366 : 367 : /** \brief Retrieve the list of services heard of by another server. 368 : * 369 : * This function saves in the input parameter \p services the list of 370 : * services that this communicatord heard of. 371 : * 372 : * \param[in,out] services The map where all the services are defined. 373 : */ 374 0 : void base_connection::get_services_heard_of(advgetopt::string_set_t & services) 375 : { 376 0 : f_services_heard_of.merge(services); 377 0 : } 378 : 379 : 380 : /** \brief List of defined commands. 381 : * 382 : * This function saves the list of commands known by another process. 383 : * The \p commands parameter is broken up at each comma and the 384 : * resulting list saved in the f_understood_commands map for fast 385 : * retrieval. 386 : * 387 : * In general a process receives the COMMANDS event whenever it 388 : * sent the HELP event to request for this list. 389 : * 390 : * \param[in] commands The list of understood commands. 391 : */ 392 0 : void base_connection::add_commands(std::string const & commands) 393 : { 394 0 : snapdev::tokenize_string( 395 0 : f_understood_commands 396 : , commands 397 : , { "," }); 398 0 : } 399 : 400 : 401 : /** \brief Check whether a certain command is understood by this connection. 402 : * 403 : * This function checks whether this connection understands \p command. 404 : * 405 : * \param[in] command The command to check for. 406 : * 407 : * \return true if the command is supported, false otherwise. 408 : */ 409 0 : bool base_connection::understand_command(std::string const & command) 410 : { 411 0 : return f_understood_commands.find(command) != f_understood_commands.end(); 412 : } 413 : 414 : 415 : /** \brief Check whether this connection received the COMMANDS message. 416 : * 417 : * This function returns true if the list of understood commands is 418 : * defined. This means we do know whether a verification (i.e. a call 419 : * to the understand_command() function) will return false because the 420 : * list of commands is empty or because a command is not understood. 421 : * 422 : * \return true if one or more commands are understood. 423 : */ 424 0 : bool base_connection::has_commands() const 425 : { 426 0 : return !f_understood_commands.empty(); 427 : } 428 : 429 : 430 : /** \brief Remove a command. 431 : * 432 : * This function is used to make the system think that certain command 433 : * are actually not understood. 434 : * 435 : * At this time, it is only used when a connection goes away and we 436 : * want to send a STATUS message to various services interested in 437 : * such a message. 438 : * 439 : * \param[in] command The command to remove. 440 : */ 441 0 : void base_connection::remove_command(std::string const & command) 442 : { 443 0 : auto it(f_understood_commands.find(command)); 444 0 : if(it != f_understood_commands.end()) 445 : { 446 0 : f_understood_commands.erase(it); 447 : } 448 0 : } 449 : 450 : 451 : /** \brief Mark that connection as a remote connection. 452 : * 453 : * When we receive a connection from another communicatord, we call 454 : * this function so later we can very quickly determine whether the 455 : * connection is a remote connection. 456 : */ 457 0 : void base_connection::mark_as_remote() 458 : { 459 0 : f_remote_connection = true; 460 0 : } 461 : 462 : 463 : /** \brief Check whether this connection is a remote connection. 464 : * 465 : * The function returns false by default. If the mark_as_remote() 466 : * was called, this function returns true. 467 : * 468 : * \return true if the connection was marked as a remote connection. 469 : */ 470 0 : bool base_connection::is_remote() const 471 : { 472 0 : return f_remote_connection; 473 : } 474 : 475 : 476 : /** \brief The function returns true if this is a UDP connection. 477 : * 478 : * This function returns the f_is_udp flag which is true if the connection 479 : * represents a UDP (datagram based) connection. 480 : * 481 : * At this time, we only have one UDP connection recorded here. The connection 482 : * managed by the f_logrotate object is not added to the communicatord 483 : * system (it does not even derive from the base_connection; although we may 484 : * stop using that sub-object because the ping class is probably enough). 485 : * 486 : * \return true if the connection is a datagram based connection. 487 : */ 488 0 : bool base_connection::is_udp() const 489 : { 490 0 : return f_is_udp; 491 : } 492 : 493 : 494 : /** \brief Set whether this connection wants to receive LOADAVG messages. 495 : * 496 : * Whenever a frontend wants to know which backend to use for its 497 : * current client request, it can check a set of IP addresses for 498 : * the least loaded computer. Then it can use that IP address to 499 : * process the request. 500 : * 501 : * \param[in] wants_loadavg Whether this connection wants 502 : * (REGISTERFORLOADAVG) or does not want (UNREGISTERFORLOADAVG) 503 : * to receive LOADAVG messages from this communicatord. 504 : */ 505 0 : void base_connection::set_wants_loadavg(bool wants_loadavg) 506 : { 507 0 : f_wants_loadavg = wants_loadavg; 508 0 : } 509 : 510 : 511 : /** \brief Check whether this connection wants LOADAVG messages. 512 : * 513 : * This function returns true if the connection last sent us a 514 : * REGISTERFORLOADAVG message. 515 : * 516 : * \return true if the LOADAVG should be sent to this connection. 517 : */ 518 0 : bool base_connection::wants_loadavg() const 519 : { 520 0 : return f_wants_loadavg; 521 : } 522 : 523 : 524 0 : bool base_connection::send_message_to_connection(ed::message & msg, bool cache) 525 : { 526 0 : ed::connection * conn(dynamic_cast<ed::connection *>(this)); 527 0 : if(conn == nullptr) 528 : { 529 0 : throw communicatord::logic_error("somehow a dynamic_cast<ed::connection *> on our base_connection failed."); 530 : } 531 0 : ed::connection_with_send_message::pointer_t conn_msg(std::dynamic_pointer_cast<ed::connection_with_send_message>(conn->shared_from_this())); 532 0 : if(conn_msg == nullptr) 533 : { 534 0 : throw communicatord::logic_error("std::dynamic_pointer_cast<ed::connection_with_send_message>() on our ed::connection failed."); 535 : } 536 0 : return conn_msg->send_message(msg, cache); 537 0 : } 538 : 539 : 540 : 541 : } // namespace communicator_daemon 542 : // vim: ts=4 sw=4 et