LCOV - code coverage report
Current view: top level - daemon - computer.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 114 114 100.0 %
Date: 2025-01-27 20:52:47 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2016-2024  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/cluck
       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             : // self
      20             : //
      21             : #include    "computer.h"
      22             : 
      23             : 
      24             : // cluck
      25             : //
      26             : #include    <cluck/exception.h>
      27             : 
      28             : 
      29             : // cppthread
      30             : //
      31             : #include    <cppthread/thread.h>
      32             : 
      33             : 
      34             : // snaplogger
      35             : //
      36             : #include    <snaplogger/message.h>
      37             : 
      38             : 
      39             : // libaddr
      40             : //
      41             : #include    <libaddr/addr_parser.h>
      42             : #include    <libaddr/exception.h>
      43             : 
      44             : 
      45             : // advgetopt
      46             : //
      47             : #include    <advgetopt/validator_integer.h>
      48             : 
      49             : 
      50             : // snapdev
      51             : //
      52             : #include    <snapdev/tokenize_string.h>
      53             : 
      54             : 
      55             : // C++
      56             : //
      57             : #include    <iomanip>
      58             : 
      59             : 
      60             : // openssl
      61             : //
      62             : #include    <openssl/rand.h>
      63             : 
      64             : 
      65             : // last include
      66             : //
      67             : #include    <snapdev/poison.h>
      68             : 
      69             : 
      70             : 
      71             : namespace cluck_daemon
      72             : {
      73             : 
      74             : 
      75             : 
      76         110 : computer::computer()
      77             : {
      78             :     // used for a remote computer, we'll eventually get a set_id() which
      79             :     // defines the necessary computer parameters
      80         110 : }
      81             : 
      82             : 
      83          44 : computer::computer(std::string const & name, priority_t priority, addr::addr ip_address)
      84          44 :     : f_self(true)
      85          44 :     , f_priority(priority)
      86          44 :     , f_ip_address(ip_address)
      87          44 :     , f_pid(getpid())
      88          88 :     , f_name(name)
      89             : {
      90             :     // verify the name becaue it can cause issues in the serializer if it
      91             :     // includes invalid characters are is empty
      92             :     //
      93          44 :     if(f_name.empty())
      94             :     {
      95           1 :         throw cluck::invalid_parameter("the computer name cannot be an empty string.");
      96             :     }
      97          86 :     std::string invalid_name_characters("|");
      98          43 :     invalid_name_characters += '\0';
      99          43 :     if(f_name.find_first_of(invalid_name_characters) != std::string::npos)
     100             :     {
     101           2 :         throw cluck::invalid_parameter("a computer name cannot include the '|' or null characters.");
     102             :     }
     103             : 
     104          41 :     if(f_priority < PRIORITY_USER_MIN
     105          40 :     || f_priority > PRIORITY_MAX)
     106             :     {
     107           4 :         throw cluck::invalid_parameter(
     108             :               "priority is limited to a number between "
     109           4 :             + std::to_string(static_cast<int>(PRIORITY_USER_MIN))
     110           6 :             + " and "
     111           8 :             + std::to_string(static_cast<int>(PRIORITY_MAX))
     112          10 :             + " inclusive.");
     113             :     }
     114             : 
     115          39 :     RAND_bytes(reinterpret_cast<unsigned char *>(&f_random_id), sizeof(f_random_id));
     116          97 : }
     117             : 
     118             : 
     119         904 : bool computer::is_self() const
     120             : {
     121         904 :     return f_self;
     122             : }
     123             : 
     124             : 
     125          28 : void computer::set_connected(bool connected)
     126             : {
     127          28 :     f_connected = connected;
     128          28 : }
     129             : 
     130             : 
     131         788 : bool computer::get_connected() const
     132             : {
     133         788 :     return f_connected;
     134             : }
     135             : 
     136             : 
     137             : /** \brief Initialize this computer object from \p id.
     138             :  *
     139             :  * \note
     140             :  * At the moment, the function is expected to work each time. This is
     141             :  * used internally to the cluckd, so the errors are due primarily to
     142             :  * a spurious message from a hacker. Otherwise, we would have to keep
     143             :  * all the new parameter on the stack and save them in the object
     144             :  * fields only if all are considered valid. (i.e. If the input string
     145             :  * is invalid, the computer object is likely left in an invalid state.)
     146             :  *
     147             :  * \exception logic_error
     148             :  * If the function was already called once successfully, then this
     149             :  * exception is raised when trying to do call it again.
     150             :  *
     151             :  * \return true if the id was considered 100% valid, false otherwise.
     152             :  */
     153         124 : bool computer::set_id(std::string const & id)
     154             : {
     155         124 :     if(f_priority != PRIORITY_UNDEFINED)
     156             :     {
     157          15 :         throw cluck::logic_error("computer::set_id() cannot be called more than once.");
     158             :     }
     159             : 
     160         109 :     std::vector<std::string> parts;
     161         109 :     snapdev::tokenize_string(parts, id, "|");
     162         109 :     if(parts.size() != 5)
     163             :     {
     164             :         // do not throw in case something changes we do not want snaplock to
     165             :         // "crash" over and over again
     166             :         //
     167           2 :         SNAP_LOG_ERROR
     168             :             << "received a computer id which does not have exactly 5 parts: \""
     169             :             << id
     170             :             << "\"."
     171             :             << SNAP_LOG_SEND;
     172           2 :         return false;
     173             :     }
     174             : 
     175             :     // base is VERY IMPORTANT for this one as we save priorities below ten
     176             :     // as 0n (01 to 09) so the sort works as expected
     177             :     //
     178         107 :     std::int64_t value(0);
     179         107 :     bool valid(advgetopt::validator_integer::convert_string(parts[0], value));
     180         107 :     if(!valid
     181         106 :     || value < PRIORITY_USER_MIN
     182         105 :     || value > PRIORITY_MAX)
     183             :     {
     184           6 :         SNAP_LOG_ERROR
     185           3 :             << "priority is limited to a number between "
     186             :             << static_cast<int>(PRIORITY_USER_MIN)
     187           3 :             << " and "
     188             :             << static_cast<int>(PRIORITY_MAX)
     189             :             << " inclusive."
     190             :             << SNAP_LOG_SEND;
     191           3 :         return false;
     192             :     }
     193         104 :     f_priority = value;
     194             : 
     195         104 :     if(!advgetopt::validator_integer::convert_string(parts[1], value))
     196             :     {
     197           2 :         SNAP_LOG_ERROR
     198             :             << "random value is expected to be a valid integer, not "
     199           1 :             << parts[1]
     200             :             << "."
     201             :             << SNAP_LOG_SEND;
     202           1 :         return false;
     203             :     }
     204         103 :     f_random_id = value;
     205             : 
     206         103 :     if(parts[2].empty())
     207             :     {
     208           1 :         SNAP_LOG_ERROR
     209             :             << "the process IP cannot be an empty string."
     210             :             << SNAP_LOG_SEND;
     211           1 :         return false;
     212             :     }
     213             :     try
     214             :     {
     215         105 :         f_ip_address = addr::string_to_addr(parts[2]);
     216         101 :         if(f_ip_address.is_default())
     217             :         {
     218           1 :             SNAP_LOG_ERROR
     219             :                 << "the IP address cannot be the default IP (0.0.0.0)."
     220             :                 << SNAP_LOG_SEND;
     221           1 :             return false;
     222             :         }
     223             :     }
     224           1 :     catch(addr::addr_invalid_argument const & e)
     225             :     {
     226             :         // we want to avoid "crashing" because any hacker could send us an
     227             :         // invalid message and get the service to stop on this one
     228             :         //
     229           3 :         SNAP_LOG_ERROR
     230             :             << "the process IP, \""
     231           1 :             << parts[2]
     232             :             << "\", is not valid: "
     233           1 :             << e.what()
     234             :             << SNAP_LOG_SEND;
     235           1 :         return false;
     236           1 :     }
     237             : 
     238         100 :     valid = advgetopt::validator_integer::convert_string(parts[3], value);
     239         200 :     if(!valid
     240          99 :     || value < 1
     241         199 :     || value > cppthread::get_pid_max())
     242             :     {
     243           8 :         SNAP_LOG_ERROR
     244             :             << "process identifier "
     245           4 :             << parts[3]
     246           4 :             << " is invalid ("
     247           4 :             << std::boolalpha << valid
     248           4 :             << ") or out of bounds: [1.."
     249             :             << cppthread::get_pid_max()
     250             :             << "]."
     251             :             << SNAP_LOG_SEND;
     252           4 :         return false;
     253             :     }
     254          96 :     f_pid = value;
     255             : 
     256          96 :     if(parts[4].empty())
     257             :     {
     258           1 :         SNAP_LOG_ERROR
     259             :             << "the server name in the lock identifier cannot be empty."
     260             :             << SNAP_LOG_SEND;
     261           1 :         return false;
     262             :     }
     263          95 :     f_name = parts[4];
     264             : 
     265          95 :     f_id = id;
     266             : 
     267          95 :     return true;
     268         109 : }
     269             : 
     270             : 
     271         125 : computer::priority_t computer::get_priority() const
     272             : {
     273         125 :     return f_priority;
     274             : }
     275             : 
     276             : 
     277          75 : void computer::set_start_time(snapdev::timespec_ex const & start_time)
     278             : {
     279          75 :     f_start_time = start_time;
     280          75 : }
     281             : 
     282             : 
     283          50 : snapdev::timespec_ex const & computer::get_start_time() const
     284             : {
     285          50 :     return f_start_time;
     286             : }
     287             : 
     288             : 
     289        1138 : std::string const & computer::get_name() const
     290             : {
     291        1138 :     return f_name;
     292             : }
     293             : 
     294             : 
     295        2515 : std::string const & computer::get_id() const
     296             : {
     297        2515 :     if(f_id.empty())
     298             :     {
     299          42 :         if(f_priority == PRIORITY_UNDEFINED)
     300             :         {
     301           1 :             throw cluck::invalid_parameter("computer::get_id() can't be called when the priority is not defined.");
     302             :         }
     303          41 :         if(f_ip_address.is_default())
     304             :         {
     305           1 :             throw cluck::invalid_parameter("computer::get_id() can't be called when the address is the default address.");
     306             :         }
     307          40 :         if(f_pid == 0)
     308             :         {
     309           1 :             throw cluck::invalid_parameter("computer::get_id() can't be called when the pid is not defined.");
     310             :         }
     311             : 
     312          39 :         std::stringstream ss;
     313             :         ss
     314          39 :             << std::setfill('0') << std::setw(2) << static_cast<int>(f_priority)
     315          39 :             << '|'
     316          39 :             << f_random_id
     317             :             << '|'
     318          78 :             << f_ip_address.to_ipv4or6_string(addr::STRING_IP_ADDRESS | addr::STRING_IP_BRACKET_ADDRESS)
     319          78 :             << '|'
     320          39 :             << f_pid
     321             :             << '|'
     322          78 :             << f_name;
     323          39 :         f_id = ss.str();
     324          39 :     }
     325             : 
     326        2512 :     return f_id;
     327             : }
     328             : 
     329             : 
     330         231 : addr::addr const & computer::get_ip_address() const
     331             : {
     332         231 :     return f_ip_address;
     333             : }
     334             : 
     335             : 
     336             : } // namespace cluck_daemon
     337             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.14

Snap C++ | List of projects | List of versions