LCOV - code coverage report
Current view: top level - daemon - computer.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 113 113
Test Date: 2025-08-17 08:58:50 Functions: 100.0 % 12 12
Legend: Lines: hit not hit

            Line data    Source code
       1              : // Copyright (c) 2016-2025  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            3 :         throw cluck::invalid_parameter("the computer name cannot be an empty string.");
      96              :     }
      97          129 :     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            6 :         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              :         throw cluck::invalid_parameter(
     108              :               "priority is limited to a number between "
     109            4 :             + std::to_string(static_cast<int>(PRIORITY_USER_MIN))
     110            8 :             + " and "
     111            8 :             + std::to_string(static_cast<int>(PRIORITY_MAX))
     112            6 :             + " 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           45 :         throw cluck::logic_error("computer::set_id() cannot be called more than once.");
     158              :     }
     159              : 
     160          109 :     std::vector<std::string> parts;
     161          327 :     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            4 :         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            9 :         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            3 :         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            2 :         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          106 :         f_ip_address = addr::string_to_addr(parts[2]);
     216          101 :         if(f_ip_address.is_default())
     217              :         {
     218            2 :             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            4 :         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           12 :         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            2 :         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            3 :             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            3 :             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            3 :             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 2.0-1

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