The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.


WGmeta::Wrapper::ConfigT - Class for interfacing the wireguard configuration supporting concurrent access


Specialized child class of Wireguard::WGmeta::Wrapper::Config which is capable of handling concurrent access.


The interface is almost identical with the exception of "commit([$is_hot_config = FALSE, $plain = FALSE, $ref_hash_integrity_keys = undef])"

 use Wireguard::WGmeta::Wrapper::ConfigT;
 my $wg_metaT = Wireguard::WGmeta::Wrapper::ConfigT->new('<path to wireguard configuration>');


To ensure that no inconsistent config files are generated, calls to a get_*() may result in a reload from disk - namely when the config file on disk is newer than the current (parsed) one. So keep in mind to commit() as soon as possible (this is obviously only true for environments where such situations are possible to occur)

 # thread/process A
 $wg_metaT->set('wg0', 'WG_0_PEER_A_PUBLIC_KEY', 'alias', 'A');

 # thread/process B
 $wg_metaT->set('wg0', 'WG_0_PEER_A_PUBLIC_KEY', 'alias', 'B');

 # thread/process A (alias 'A' is overwritten by 'B')
 wg_metaT->disable_by_alias('wg0', 'A'); # throws exception `invalid alias`!

For more details about the reloading behaviour please refer to "may_reload_from_disk([$interface = undef])".

Commit behaviour

        FUNCTION commit($integrity_hashes)
                FOR $interface IN $known_interfaces
                    IF has_changed($interface) THEN
                UNLESS my_config_is_latest THEN
                    $on_disk <- read_from_disk($interface)
                $contents <- create_wg_config($interface, $on_disk,$integrity_hashes)

        FUNCTION create_wg_config($interface, $on_disk, $integrity_hashes);
                $may_conflicting <- search_for_common_data($interface, $on_disk)
                FOR $section IN $may_conflicting
                        $sha_internal <- calculate_sha_from_internal()
                        $sha_disk <- calculate_sha_from_disk()
                        IF $sha_internal NE $sha_disk
                                IF $sha_disk EQ $integrity_hashes[$section]
                                        $section_data <- take_from_internal()
                                        $section_data <- take_from_disk()
                                $section_data <- take_from_disk()
                        $config_content .= create_section($section_data)
                $config_content .= create_non_conflicting()
                return $config_content


 use Wireguard::WGmeta::Wrapper::ConfigT;

 # thread A
 my $wg_metaT = Wireguard::WGmeta::Wrapper::ConfigT->new('<path to wireguard configuration>');
 $wg_metaT->set('wg0', 'WG_0_PEER_A_PUBLIC_KEY', 'name', 'set_in_thread_A');
 # Assumption: Our internal version is equal with the on-disk version at this point
 my $integrity_hash = $wg_metaT->calculate_sha_from_internal();

 # thread B
 my $wg_metaT = Wireguard::WGmeta::Wrapper::ConfigT->new('<path to wireguard configuration>');
 $wg_metaT->set('wg0', 'AN_OTHER_PUBLIC_KEY', 'name', 'set_in_thread_B');
 $wg_metaT->commit(1); # works fine (internal & on_disk have same version)

 # thread A (non conflicting changes -> same file, different section)
 $wg_metaT->commit(1); # "Your changes for `WG_0_PEER_A_PUBLIC_KEY` were not applied"
 $wg_metaT->commit(1, 0, {'WG_0_PEER_A_PUBLIC_KEY' => $integrity_hash}); # works fine -> non conflicting changes

 # Reload callbacks
 sub my_reload_callback($interface, $ref_list_args){
    my @args = @{$ref_list_args};
    print "$interface, reloaded and $args[0]!";

 # register our callback handler
 $wg_metaT->register_on_reload_listener(\&my_reload_callback, 'handler_id', [ 'hello from listener' ]);

 # Everytime an interface is reloaded, our handler is called until we uninstall our handler



"is_valid_interface($interface)" in Wireguard::WGmeta::Wrapper::Config

is_valid_identifier($interface, $identifier)

"is_valid_identifier($interface, $identifier)" in Wireguard::WGmeta::Wrapper::Config

try_translate_alias($interface, $may_alias)

"try_translate_alias($interface, $may_alias)" in Wireguard::WGmeta::Wrapper::Config

get_interface_section($interface, $identifier)

"get_interface_section($interface, $identifier)" in Wireguard::WGmeta::Wrapper::Config


"get_section_list($interface)" in Wireguard::WGmeta::Wrapper::Config


"get_peer_count([$interface = undef])" in Wireguard::WGmeta::Wrapper::Config


"get_interface_list()" in Wireguard::WGmeta::Wrapper::Config

commit([$is_hot_config = FALSE, $plain = FALSE, $ref_hash_integrity_keys = undef])

Writes down the parsed config to the wireguard configuration folder.


  • [$is_hot_config = FALSE]) If set to TRUE, the existing configuration is overwritten. Otherwise, the suffix '_not_applied' is appended to the filename

  • [$plain = FALSE]) If set to TRUE, no header is generated

  • [$ref_hash_integrity_keys = undef]) Reference to a hash of integrity keys. Expected structure:

            <identifier1> => 'integrity_hash_of_corresponding_section',
            <identifier2> => 'integrity_hash_of_corresponding_section'

    For a more detailed explanation when this information is needed please refer to "CONCURRENCY".


Exception if: Folder or file is not writeable



may_reload_from_disk([$interface = undef])

This method is called before any data is returned from one of the get_*() methods. It behaves as follows:

  • If the interface is not defined, it loops through the known interfaces and reloads them individually (if needed).

  • If the interface is defined (and known), the modify timestamps are compared an if the on-disk version is newer, a reload is triggered.

  • If the interface is defined (but not known -> this could be the case if a new interface has been added), first we check if there is actually a matching config file on disk and if yes, its loaded and parsed from disk.

Remark: This method is not meant for public access, there is just this extensive documentation block since its behaviour is crucial to the function of this class.


  • $interface A (possibly) invalid (or new) interface name



calculate_sha_from_internal($interface, $identifier)

Calculates the sha1 from a section (already parsed).


It is possible that this method does not return the most recent, on-disk version of this section! It returns your current parsed state! This method does NOT trigger a may_reload_from_disk()!


  • $interface A valid interface name

  • $identifier A valid identifier for this interface


The sha1 (in HEX) the requested section