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