30#include <cluck/names.h>
35#include <advgetopt/validator_integer.h>
40#include <snapdev/hexadecimal_string.h>
41#include <snapdev/string_replace_many.h>
42#include <snapdev/tokenize_string.h>
47#include <snaplogger/message.h>
52#include <snapdev/poison.h>
359 , std::string
const & object_name
360 , ed::dispatcher_match::tag_t tag
361 , std::string
const & entering_key
364 , std::string
const & server_name
365 , std::string
const & service_name)
368 , f_object_name(object_name)
370 , f_obtention_timeout(obtention_timeout)
371 , f_lock_duration(std::clamp(
373 ,
cluck::CLUCK_UNLOCK_MINIMUM_TIMEOUT
374 ,
cluck::CLUCK_MAXIMUM_TIMEOUT))
375 , f_server_name(server_name)
376 , f_service_name(service_name)
377 , f_owner(f_cluckd->get_server_name())
378 , f_entering_key(entering_key)
385 <<
"Attempting to lock \""
421 msg.set_service(cluck::g_name_cluck_service_name);
422 msg.add_parameter(cluck::g_name_cluck_param_object_name,
f_object_name);
423 msg.add_parameter(cluck::g_name_cluck_param_tag,
f_tag);
426 if(leader !=
nullptr)
431 msg.set_server(leader->get_name());
440 if(leader !=
nullptr)
442 msg.set_server(leader->get_name());
482 ed::message entering_message;
483 entering_message.set_command(cluck::g_name_cluck_cmd_lock_entering);
484 entering_message.add_parameter(cluck::g_name_cluck_param_key,
f_entering_key);
486 entering_message.add_parameter(cluck::g_name_cluck_param_duration,
f_lock_duration);
489 entering_message.add_parameter(cluck::g_name_cluck_param_unlock_duration,
f_unlock_duration);
492 entering_message.add_parameter(cluck::g_name_cluck_param_serial,
f_serial);
536 ed::message get_max_ticket_message;
537 get_max_ticket_message.set_command(cluck::g_name_cluck_cmd_get_max_ticket);
538 get_max_ticket_message.add_parameter(cluck::g_name_cluck_param_key,
f_entering_key);
584 throw cluck::out_of_range(
"ticket::max_ticket() tried to generate the next ticket and got a wrapping around number.");
607 throw cluck::logic_error(
"ticket::add_ticket() called more than once.");
630 ed::message add_ticket_message;
631 add_ticket_message.set_command(cluck::g_name_cluck_cmd_add_ticket);
632 add_ticket_message.add_parameter(cluck::g_name_cluck_param_key,
f_ticket_key);
672 ed::message exiting_message;
673 exiting_message.set_command(cluck::g_name_cluck_cmd_lock_exiting);
674 exiting_message.add_parameter(cluck::g_name_cluck_param_key,
f_entering_key);
710 if(key_entering->second->timed_out())
728 ed::message ticket_ready_message;
729 ticket_ready_message.set_command(cluck::g_name_cluck_cmd_ticket_ready);
730 ticket_ready_message.add_parameter(cluck::g_name_cluck_param_key,
f_ticket_key);
756 ed::message activate_lock_message;
757 activate_lock_message.set_command(cluck::g_name_cluck_cmd_activate_lock);
758 activate_lock_message.add_parameter(cluck::g_name_cluck_param_key,
f_ticket_key);
791 ed::message locked_message;
792 locked_message.set_command(cluck::g_name_cluck_cmd_locked);
795 locked_message.add_parameter(cluck::g_name_cluck_param_object_name,
f_object_name);
798 locked_message.add_parameter(cluck::g_name_cluck_param_tag,
f_tag);
825 ed::message drop_ticket_message;
826 drop_ticket_message.set_command(cluck::g_name_cluck_cmd_drop_ticket);
827 drop_ticket_message.add_parameter(
828 cluck::g_name_cluck_param_key
853 ed::message unlocked_message;
854 unlocked_message.set_command(cluck::g_name_cluck_cmd_unlocked);
857 unlocked_message.add_parameter(cluck::g_name_cluck_param_object_name,
f_object_name);
858 unlocked_message.add_parameter(cluck::g_name_cluck_param_unlocked_date, snapdev::now());
859 unlocked_message.add_parameter(cluck::g_name_cluck_param_tag,
f_tag);
907 send_msg_t send(SEND_MSG_NONE);
929 send = SEND_MSG_UNLOCKED;
933 send = SEND_MSG_UNLOCKING;
938 send = SEND_MSG_FAILED;
947 send = SEND_MSG_UNLOCKED;
970 case SEND_MSG_UNLOCKING:
983 <<
"\" timed out its lock allowed time."
986 ed::message lock_failed_message;
987 lock_failed_message.set_command(cluck::g_name_cluck_cmd_unlocking);
990 lock_failed_message.add_parameter(cluck::g_name_cluck_param_object_name,
f_object_name);
991 lock_failed_message.add_parameter(cluck::g_name_cluck_param_tag,
f_tag);
992 lock_failed_message.add_parameter(cluck::g_name_cluck_param_error, cluck::g_name_cluck_value_timedout);
997 case SEND_MSG_UNLOCKED:
1016 <<
"\" timed out its unlocking allowed time."
1019 ed::message lock_failed_message;
1020 lock_failed_message.set_command(cluck::g_name_cluck_cmd_unlocked);
1023 lock_failed_message.add_parameter(cluck::g_name_cluck_param_object_name,
f_object_name);
1024 lock_failed_message.add_parameter(cluck::g_name_cluck_param_tag,
f_tag);
1025 lock_failed_message.add_parameter(cluck::g_name_cluck_param_error, cluck::g_name_cluck_value_timedout);
1030 case SEND_MSG_FAILED:
1042 ed::message lock_failed_message;
1043 lock_failed_message.set_command(cluck::g_name_cluck_cmd_lock_failed);
1046 lock_failed_message.add_parameter(cluck::g_name_cluck_param_object_name,
f_object_name);
1047 lock_failed_message.add_parameter(cluck::g_name_cluck_param_tag,
f_tag);
1048 lock_failed_message.add_parameter(cluck::g_name_cluck_param_key,
f_entering_key);
1049 lock_failed_message.add_parameter(cluck::g_name_cluck_param_error, cluck::g_name_cluck_value_failed);
1050 lock_failed_message.add_parameter(cluck::g_name_cluck_param_description,
1051 "ticket failed before or after the lock was obtained ("
1116 std::vector<std::string> segments;
1119 throw cluck::invalid_parameter(
1120 "ticket::get_client_pid() split f_entering_key \""
1122 +
"\" and did not get exactly two segments.");
1125 advgetopt::validator_integer::convert_string(segments[1], value);
1126 return static_cast<pid_t
>(value);
1251 throw cluck::logic_error(
"ticket::set_ticket_number() called with "
1252 + std::to_string(number)
1253 +
" when f_our_ticket is already set to "
1585 std::map<std::string, std::string> data;
1588 data[
"tag"] = std::to_string(
static_cast<int>(
f_tag));
1598 data[
"serial"] = std::to_string(
f_serial);
1612 data[
"locked"] =
f_locked ?
"true" :
"false";
1618 data[
"lock_failed"] =
"none";
1622 data[
"lock_failed"] =
"lock";
1626 data[
"lock_failed"] =
"unlocking";
1632 for(
auto & it : data)
1637 result += snapdev::string_replace_many(it.second, {{
"|",
"%7C"}});
1659void ticket::unserialize(std::string
const & data)
1661 std::vector<std::string> vars;
1662 snapdev::NOT_USED(snapdev::tokenize_string(vars, data,
"|"));
1663 for(
auto const & d : vars)
1665 std::string::size_type
const pos(d.find(
'='));
1666 std::string
const name(d.substr(0, pos));
1667 std::string
const value(d.substr(pos + 1));
1671 if(name ==
"added_ticket")
1673 f_added_ticket = f_added_ticket || value ==
"true";
1675 else if(name ==
"added_ticket_quorum")
1677 f_added_ticket_quorum = f_added_ticket_quorum || value ==
"true";
1686 if(name ==
"entering_key")
1689 if(f_entering_key != value)
1692 throw cluck::logic_error(
1693 "ticket::unserialize() not unserializing entering key \""
1695 +
"\" over itself \""
1697 +
"\" (entering key mismatch).");
1701 f_entering_key = value;
1706 if(name ==
"get_max_ticket")
1708 f_get_max_ticket = f_get_max_ticket || value ==
"true";
1713 if(name ==
"lock_duration")
1717 else if(name ==
"locked")
1719 f_locked = f_locked || value ==
"true";
1721 else if(name ==
"lock_timeout_date")
1727 if(timeout_date > f_lock_timeout_date)
1729 f_lock_timeout_date = timeout_date;
1732 else if(name ==
"lock_failed")
1736 if(value ==
"unlocking")
1738 f_lock_failed = lock_failure_t::LOCK_FAILURE_UNLOCKING;
1740 else if(value ==
"lock" && f_lock_failed == lock_failure_t::LOCK_FAILURE_NONE)
1742 f_lock_failed = lock_failure_t::LOCK_FAILURE_LOCK;
1748 if(name ==
"object_name")
1751 if(f_object_name != value)
1754 throw cluck::logic_error(
1755 "ticket::unserialize() not unserializing object name \""
1757 +
"\" over itself \""
1759 +
"\" (object name mismatch).");
1763 f_object_name = value;
1765 else if(name ==
"obtention_timeout")
1769 else if(name ==
"owner")
1773 else if(name ==
"our_ticket")
1776 advgetopt::validator_integer::convert_string(value, v);
1782 if(name ==
"server_name")
1784 f_server_name = value;
1786 else if(name ==
"service_name")
1788 f_service_name = value;
1790 else if(name ==
"serial")
1793 advgetopt::validator_integer::convert_string(value, v);
1802 advgetopt::validator_integer::convert_string(value, v);
1805 else if(name ==
"ticket_key")
1807 f_ticket_key = value;
1809 else if(name ==
"ticket_ready")
1811 f_ticket_ready = f_ticket_ready || value ==
"true";
1816 if(name ==
"unlock_duration")
Class handling intercomputer locking.
std::string const & get_server_name() const
Get the name of the server we are running on.
void lock_exiting(ed::message &msg)
Used to simulate a LOCK_EXITING message.
computer::pointer_t get_leader_b() const
Get pointer to leader B.
ticket::ticket_id_t get_last_ticket(std::string const &lock_name)
Determine the last ticket defined in this cluck daemon.
int get_computer_count() const
Return the number of known computers running cluckd.
ticket::key_map_t const get_entering_tickets(std::string const &lock_name)
Get a reference to the list of entering tickets.
void set_ticket(std::string const &object_name, std::string const &key, ticket::pointer_t ticket)
Set the ticket.
computer::pointer_t get_leader_a() const
Get pointer to leader A.
std::shared_ptr< computer > pointer_t
Handle messages from the communicatord.
std::shared_ptr< messenger > pointer_t
void ticket_added(key_map_t const &entering)
Called whenever a TICKET_ADDED is received.
cluck::timeout_t f_unlock_duration
key_map_t f_still_entering
void set_owner(std::string const &owner)
Define whether this ticket is the owner of that lock.
void set_serial(serial_t owner)
Give the lock a serial number for some form of unicity.
std::string const & get_object_name() const
Retrieve the object name of this ticket.
void entering()
Enter the mode that lets us retrieve our ticket number.
cluck::timeout_t get_unlock_duration() const
Get unlock duration.
void set_alive_timeout(cluck::timeout_t timeout)
Define a time when the ticket times out while waiting.
std::string const & get_service_name() const
Retrieve the service name of this ticket.
cluck::timeout_t f_lock_timeout_date
static serial_t const NO_SERIAL
bool timed_out() const
Check whether this ticket timed out.
cluck::timeout_t get_lock_timeout_date() const
Get the lock timeout date.
cluck::timeout_t f_obtention_timeout
std::string const & get_owner() const
Return the name of this ticket's owner.
void entered()
Tell this entering that we received a LOCKENTERED message.
std::string const & get_entering_key() const
Retrieve a reference to the entering key of this ticket.
cluck::timeout_t get_obtention_timeout() const
Get the obtention timeout date.
static ticket_id_t const NO_TICKET
messenger::pointer_t f_messenger
cluck::timeout_t f_lock_duration
std::string serialize() const
Serialize a ticket to send it over to another leader.
ticket_id_t get_ticket_number() const
Return the ticket number of this ticket.
ed::dispatcher_match::tag_t get_tag() const
Retrieve the tag of this ticket.
ticket(cluckd *c, messenger::pointer_t messenger, std::string const &lock_name, ed::dispatcher_match::tag_t tag, std::string const &entering_key, cluck::timeout_t obtention_timeout, cluck::timeout_t lock_duration, std::string const &server_name, std::string const &service_name)
Initialize a ticket object.
void set_unlock_duration(cluck::timeout_t duration)
Change the unlock duration to the specified value.
bool f_added_ticket_quorum
void activate_lock()
Check whether this ticket can be activated and do so if so.
std::string f_object_name
cluck::timeout_t f_unlocked_timeout_date
ed::dispatcher_match::tag_t f_tag
std::string f_server_name
std::string const & get_server_name() const
Retrieve the server name of this ticket.
pid_t get_client_pid() const
Retrieve the client process identifier.
lock_failure_t f_lock_failed
std::uint32_t ticket_id_t
bool send_message_to_leaders(ed::message &msg)
Send a message to the other two leaders.
void drop_ticket()
We are done with the ticket.
bool is_locked() const
Check whether this ticket is locked or not.
serial_t get_serial() const
Return the serial number of this ticket.
void lock_failed(std::string const &reason)
Let the service that wanted this lock know that it failed.
void lock_activated()
Check whether this ticket can be activated and do so if so.
cluck::timeout_t get_current_timeout_date() const
Get the current lock timeout date.
std::string const & get_ticket_key() const
Retrieve a reference to the ticket key.
std::string f_service_name
void max_ticket(ticket_id_t new_max_ticket)
Called whenever a MAX_TICKET is received.
void set_ticket_number(ticket_id_t number)
Set the ticket number.
void remove_entering(std::string const &key)
Call any time time an entering flag is reset.
void add_ticket()
Send the ADD_TICKET message.
void set_ready()
Mark the ticket as being ready.
bool one_leader() const
Check whether the system only has one leader.
cluck::timeout_t f_alive_timeout
std::map< std::string, pointer_t > key_map_t
cluck::timeout_t get_lock_duration() const
Retrieve the lock duration.
std::string f_entering_key
Daemon handling inter-computer locking.
timeout_t CLUCK_DEFAULT_TIMEOUT
snapdev::timespec_ex timeout_t
A timeout delay.
timeout_t CLUCK_UNLOCK_MINIMUM_TIMEOUT
timeout_t CLUCK_MAXIMUM_TIMEOUT