#!/usr/bin/perl
# 
# This daemon watches for network interface link change (unplugged or plugged in network cables).
# 
# At this point, the only thing this does is call 'scan-network' when a change is detected.
# 
# NOTE: This is designed to be minimal overhead, so there is no attempt to connect to the database. As such, 
#       be mindful of what this daemon is used for.
# 

use strict;
use warnings;
use Data::Dumper;
use Text::Diff;
use Anvil::Tools;

my $THIS_FILE           =  ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory   =  ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
	$running_directory =~ s/^\./$ENV{PWD}/;
}

# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;

my $anvil = Anvil::Tools->new();

# Read switches
$anvil->Get->switches({list => [], man => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => $anvil->data->{switches}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});

# Calculate my sum so that we can exit if it changes later.
$anvil->Storage->record_md5sums;
my $next_md5sum_check = time + 30;

my $directory = "/sys/class/net";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { directory => $directory }});

# Now go into the main loop
while(1)
{
	### NOTE: A lot of this logic comes from scan-network
	my $scan_time = time;
	my $trigger   = 0;
	
	# Look for interfaces.
	local(*DIRECTORY);
	$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0018", variables => { directory => $directory }});
	opendir(DIRECTORY, $directory);
	while(my $file = readdir(DIRECTORY))
	{
		next if $file eq ".";
		next if $file eq "..";
		next if $file eq "lo"; 
		my $full_path = $directory."/".$file;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { full_path => $full_path }});
		if (-d $full_path)
		{
			# Pull out the data I want. Note that some of these don't exist with virtio-net interfaces.
			my $interface   = $file;
			my $link_state  = -e $full_path."/carrier"         ? $anvil->Storage->read_file({file => $full_path."/carrier"})         : 0;
			   $link_state  =~ s/\n$//;
			my $mtu         = -e $full_path."/mtu"             ? $anvil->Storage->read_file({file => $full_path."/mtu"})             : 0;
			   $mtu         =~ s/\n$//;
			my $duplex      = -e $full_path."/duplex"          ? $anvil->Storage->read_file({file => $full_path."/duplex"})          : "unknown";	# full or half?
			   $duplex      =~ s/\n$//;
			my $operational = -e $full_path."/operstate"       ? $anvil->Storage->read_file({file => $full_path."/operstate"})       : "unknown";	# up or down
			   $operational =~ s/\n$//;
			my $modalias    = -e $full_path."/device/modalias" ? $anvil->Storage->read_file({file => $full_path."/device/modalias"}) : "unknown";
			   $modalias    =~ s/\n$//;
			my $speed       = $link_state ? $anvil->Storage->read_file({file => $full_path."/speed"}) : 0;	# Mbps (ie: 1000 = Gbps), gives a very high number for unplugged link
			   $speed       =~ s/\n$//;
			my $media       = "unknown";
			my $type        = "interface";
			my $driver      = "";
			my $tx_bytes    = 0;	# How many bytes transmitted
			my $rx_bytes    = 0;	# How many bytes received
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				interface   => $interface, 
				link_state  => $link_state, 
				mtu         => $mtu,
				duplex      => $duplex, 
				operational => $operational,
				speed       => $speed, 
				modalias    => $modalias, 
			}});
			
			# Get the MAC address
			my $mac_address = "";
			my $shell_call  = $anvil->data->{path}{exe}{ethtool}." -P ".$interface;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
			
			my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				output      => $output,
				return_code => $return_code, 
			}});
			if ($output =~ /(\w\w:\w\w:\w\w:\w\w:\w\w:\w\w)$/)
			{
				$mac_address = lc($1);
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }});
			}
			else
			{
				# Get it by reading the address file.
				if (-e $full_path."/bonding_slave/perm_hwaddr")
				{
					$mac_address = $anvil->Storage->read_file({file => $full_path."/bonding_slave/perm_hwaddr"});
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }});
				}
				elsif (-e $full_path."/address")
				{
					$mac_address =  $anvil->Storage->read_file({file => $full_path."/address"});
					$mac_address =~ s/\n//;
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }});
				}
			}
			
			# These are variables that will be needed if this is a bond interface.
			my $ip_address           = "";
			my $subnet_mask          = "";
			my $bond_mode            = "";
			my $primary_interface    = "";
			my $primary_reselect     = "";
			my $active_interface     = ""; 
			my $mii_polling_interval = "";
			my $up_delay             = "";
			my $down_delay           = "";
			my $bond_parent          = "";
			
			# These are variables that will be needed if this is a bridge interface
			my $bridge_id          = "";
			my $bridge_stp_enabled = "";
			
			# If this interface is already a bond slave, the real mac address will be in a 
			# sub-directory.
			my $mac_bond_file = $directory."/".$file."/bonding_slave/perm_hwaddr";
			if (-e $mac_bond_file)
			{
				# It's a slave.
				$mac_address =  $anvil->Storage->read_file({file => $mac_bond_file});
				$mac_address =~ s/\n$//;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mac_address => $mac_address }});
			}
			
			# Pick out our driver.
			if ($modalias =~ /^virtio:/)
			{
				$driver = "virtio";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { driver => $driver }});
			}
			
			# If this is a virtual interface, set some fake values that don't actually exist on 
			# the system for the sake of a cleaner display.
			if (($mac_address =~ /^52:54:00/) or ($driver eq "virtio"))
			{
				### Set some fake values.
				# Speed is "as fast as possible", so we'll record 100 Gbps, but that is really kind of arbitrary.
				if ((not $speed) or ($speed eq "-1"))
				{
					$speed = 10000;
				}
				if ((not $duplex) or ($duplex eq "unknown"))
				{
					$duplex = "full";
				}
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					speed  => $speed, 
					duplex => $duplex,
				}});
			}
			# If the state is 'down', set the speed to '0'.
			if (not $link_state)
			{
				$speed = 0;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { speed => $speed }});
			}
			
			# Is this a bond interface?
			if (-e "/proc/net/bonding/".$interface)
			{
				# Yup, we'll neet to dig into the bond proc files to get the proper slaved 
				# interface MAC addresses.
				$type = "bond";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }});
				
				# Read the bond mode.
				$bond_mode            =  $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/mode"});
				$bond_mode            =~ s/\s.*//;
				$bond_mode            =~ s/\n$//;
				$primary_interface    =  $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/primary"});
				$primary_interface    =~ s/\n$//;
				$primary_reselect     =  $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/primary_reselect"});
				$primary_reselect     =~ s/\s.*//;
				$primary_reselect     =~ s/\n$//;
				$active_interface     =  $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/active_slave"});
				$active_interface     =~ s/\n$//;
				$mii_polling_interval =  $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/miimon"});
				$mii_polling_interval =~ s/\n$//;
				$up_delay             =  $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/updelay"});
				$up_delay             =~ s/\n$//;
				$down_delay           =  $anvil->Storage->read_file({file => "/sys/devices/virtual/net/".$interface."/bonding/downdelay"});
				$down_delay           =~ s/\n$//;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					active_interface     => $active_interface, 
					bond_mode            => $bond_mode, 
					mii_polling_interval => $mii_polling_interval, 
					primary_reselect     => $primary_reselect, 
					primary_interface    => $primary_interface, 
					type                 => $type,
				}});
			}
			elsif ((-e $full_path."/master") && ($interface !~ /^vnet/))
			{
				# We're in a bond.
				my $target      = readlink($full_path."/master");
				   $bond_parent = ($target =~ /^.*\/(.*)$/)[0];
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					target      => $target, 
					bond_parent => $bond_parent, 
				}});
			}
			elsif (-d $full_path."/bridge")
			{
				# It's a bridge
				$type               =  "bridge";
				$bridge_id          =  $anvil->Storage->read_file({debug => 3, file => $full_path."/bridge/bridge_id"});
				$bridge_id          =~ s/\n$//;
				$bridge_stp_enabled =  $anvil->Storage->read_file({debug => 3, file => $full_path."/bridge/stp_state"});
				$bridge_stp_enabled =~ s/\n$//;
				$speed              = 0;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					type               => $type,
					bridge_id          => $bridge_id, 
					bridge_stp_enabled => $bridge_stp_enabled, 
				}});
				if ($bridge_stp_enabled eq "0")
				{
					$bridge_stp_enabled = "disabled";
				}
				elsif ($bridge_stp_enabled eq "1")
				{
					$bridge_stp_enabled = "enabled_kernel";
				}
				elsif ($bridge_stp_enabled eq "2")
				{
					$bridge_stp_enabled = "enabled_userland";
				}
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bridge_stp_enabled => $bridge_stp_enabled }});
			}
			
			# If this is a 'vnet' device, set 'operational' to up
			if ($interface =~ /^vnet/)
			{
				### TODO: We can't assume this, we need to detect virsh net up/down
				$operational = "up";
				$media       = "virtual";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					operational => $operational,
					media       => $media, 
				}});
			}
			
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				active_interface     => $active_interface, 
				bond_parent          => $bond_parent, 
				bond_mode            => $bond_mode, 
				bridge_id            => $bridge_id, 
				bridge_stp_enabled   => $bridge_stp_enabled, 
				down_delay           => $down_delay, 
				duplex               => $duplex, 
				interface            => $interface,
				mac_address          => $mac_address, 
				mii_polling_interval => $mii_polling_interval, 
				mtu                  => $mtu, 
				operational          => $operational, 
				primary_reselect     => $primary_reselect, 
				primary_interface    => $primary_interface, 
				speed                => $speed, 
				subnet_mask          => $subnet_mask, 
				type                 => $type, 
				up_delay             => $up_delay, 
			}});
			
			# Find the media, if possible.
			(my $ethtool, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{ethtool}." ".$interface});
			foreach my $line (split/\n/, $ethtool)
			{
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
				if ($line =~ /Supported ports: \[ (.*?) \]/i)
				{
					$media = lc($1);
					$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { media => $media }});
					
					# This can be 'tp	 mii', which breaks json.
					if ($media =~ /\t/)
					{
						$media =~ s/\t/,/g;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { media => $media }});
					}
					last;
				}
			}
			
			# Trigger?
			if (not exists $anvil->data->{last_scan}{$interface})
			{
				print "The interface: [".$interface."] has been found. We will now monitor it for changes.\n";
				$trigger = 1;
			}
			else
			{
				# Now look for differences.
				if ($anvil->data->{last_scan}{$interface}{active_interface} ne $active_interface)
				{
					print "The ".$type.": [".$interface."] has a different active interface: [".$anvil->data->{last_scan}{$interface}{active_interface}."] -> [".$active_interface."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{bond_mode} ne $bond_mode)
				{
					print "The ".$type.": [".$interface."] mode has changed from: [".$anvil->data->{last_scan}{$interface}{bond_mode}."] -> [".$bond_mode."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{bond_parent} ne $bond_parent)
				{
					print "The ".$type.": [".$interface."] bond parent has changed from: [".$anvil->data->{last_scan}{$interface}{bond_parent}."] -> [".$bond_parent."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{bridge_id} ne $bridge_id)
				{
					print "The ".$type.": [".$interface."] bridge ID has changed from: [".$anvil->data->{last_scan}{$interface}{bridge_id}."] -> [".$bridge_id."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{bridge_stp_enabled} ne $bridge_stp_enabled)
				{
					print "The ".$type.": [".$interface."] spanning tree protocol (STP) setting has changed from: [".$anvil->data->{last_scan}{$interface}{bridge_stp_enabled}."] -> [".$bridge_stp_enabled."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{down_delay} ne $down_delay)
				{
					print "The ".$type.": [".$interface."] down delay has changed from: [".$anvil->data->{last_scan}{$interface}{bridge_stp_enabled}."ms] -> [".$bridge_stp_enabled."ms].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{up_delay} ne $up_delay)
				{
					print "The ".$type.": [".$interface."] up delay has changed from: [".$anvil->data->{last_scan}{$interface}{up_delay}."ms] -> [".$up_delay."ms].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{duplex} ne $duplex)
				{
					print "The ".$type.": [".$interface."] duplex has changed from: [".$anvil->data->{last_scan}{$interface}{duplex}."] -> [".$duplex."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{ip_address} ne $ip_address)
				{
					print "The ".$type.": [".$interface."] ip address has changed from: [".$anvil->data->{last_scan}{$interface}{ip_address}."] -> [".$ip_address."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{subnet_mask} ne $subnet_mask)
				{
					print "The ".$type.": [".$interface."] subnet_mask has changed from: [".$anvil->data->{last_scan}{$interface}{subnet_mask}."] -> [".$subnet_mask."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{link_state} ne $link_state)
				{
					print "The ".$type.": [".$interface."] link status has changed from: [".$anvil->data->{last_scan}{$interface}{link_state}."] -> [".$link_state."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{mac_address} ne $mac_address)
				{
					print "The ".$type.": [".$interface."] MAC address has changed from: [".$anvil->data->{last_scan}{$interface}{mac_address}."] -> [".$mac_address."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{media} ne $media)
				{
					print "The ".$type.": [".$interface."] media has changed from: [".$anvil->data->{last_scan}{$interface}{media}."] -> [".$media."]. (Excuse me, how?!)\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{mii_polling_interval} ne $mii_polling_interval)
				{
					print "The ".$type.": [".$interface."] media independent interface (mii) polling interval has changed from: [".$anvil->data->{last_scan}{$interface}{mii_polling_interval}."ms] -> [".$mii_polling_interval."ms].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{mtu} ne $mtu)
				{
					print "The ".$type.": [".$interface."] maximum transmission unit (mtu) has changed from: [".$anvil->data->{last_scan}{$interface}{mtu}." bytes] -> [".$mtu." bytes].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{operational} ne $operational)
				{
					print "The ".$type.": [".$interface."] operational status has changed from: [".$anvil->data->{last_scan}{$interface}{operational}."] -> [".$operational."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{primary_reselect} ne $primary_reselect)
				{
					print "The ".$type.": [".$interface."] primary reselect policy has changed from: [".$anvil->data->{last_scan}{$interface}{primary_reselect}."] -> [".$primary_reselect."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{primary_interface} ne $primary_interface)
				{
					print "The ".$type.": [".$interface."] primary interface has changed from: [".$anvil->data->{last_scan}{$interface}{primary_interface}."] -> [".$primary_interface."].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{speed} ne $speed)
				{
					print "The ".$type.": [".$interface."] speed has changed from: [".$anvil->data->{last_scan}{$interface}{speed}." Mbps] -> [".$speed." Mbps].\n";
					$trigger = 1;
				}
				if ($anvil->data->{last_scan}{$interface}{type} ne $type)
				{
					print "The device: [".$interface."] type has changed from: [".$anvil->data->{last_scan}{$interface}{type}."] -> [".$type."].\n";
					$trigger = 1;
				}
			}
			
			# We'll use this to determine when an interface has disappeared. 
			$anvil->data->{last_scan}{$interface}{seen} = $scan_time;
			
			# Store new information we found.
			$anvil->data->{last_scan}{$interface}{active_interface}     = $active_interface; 
			$anvil->data->{last_scan}{$interface}{bond_mode}            = $bond_mode; 
			$anvil->data->{last_scan}{$interface}{bond_parent}          = $bond_parent; 
			$anvil->data->{last_scan}{$interface}{bridge_id}            = $bridge_id; 
			$anvil->data->{last_scan}{$interface}{bridge_stp_enabled}   = $bridge_stp_enabled; 
			$anvil->data->{last_scan}{$interface}{down_delay}           = $down_delay; 
			$anvil->data->{last_scan}{$interface}{duplex}               = $duplex;
			$anvil->data->{last_scan}{$interface}{ip_address}           = $ip_address; 
			$anvil->data->{last_scan}{$interface}{link_state}           = $link_state; 
			$anvil->data->{last_scan}{$interface}{mac_address}          = $mac_address; 
			$anvil->data->{last_scan}{$interface}{media}                = $media; 
			$anvil->data->{last_scan}{$interface}{mii_polling_interval} = $mii_polling_interval; 
			$anvil->data->{last_scan}{$interface}{mtu}                  = $mtu; 
			$anvil->data->{last_scan}{$interface}{operational}          = $operational;
			$anvil->data->{last_scan}{$interface}{primary_reselect}     = $primary_reselect; 
			$anvil->data->{last_scan}{$interface}{primary_interface}    = $primary_interface; 
			$anvil->data->{last_scan}{$interface}{speed}                = $speed;
			$anvil->data->{last_scan}{$interface}{subnet_mask}          = $subnet_mask; 
			$anvil->data->{last_scan}{$interface}{type}                 = $type; 
			$anvil->data->{last_scan}{$interface}{up_delay}             = $up_delay; 
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				"last_scan::${interface}::seen"                 => $anvil->data->{last_scan}{$interface}{seen},
				"last_scan::${interface}::active_interface"     => $anvil->data->{last_scan}{$interface}{active_interface},
				"last_scan::${interface}::bond_mode"            => $anvil->data->{last_scan}{$interface}{bond_mode},
				"last_scan::${interface}::bond_parent"          => $anvil->data->{last_scan}{$interface}{bond_parent},
				"last_scan::${interface}::bridge_id"            => $anvil->data->{last_scan}{$interface}{bridge_id}, 
				"last_scan::${interface}::bridge_stp_enabled"   => $anvil->data->{last_scan}{$interface}{bridge_stp_enabled}, 
				"last_scan::${interface}::down_delay"           => $anvil->data->{last_scan}{$interface}{down_delay},
				"last_scan::${interface}::duplex"               => $anvil->data->{last_scan}{$interface}{duplex},
				"last_scan::${interface}::ip_address"           => $anvil->data->{last_scan}{$interface}{ip_address},
				"last_scan::${interface}::link_state"           => $anvil->data->{last_scan}{$interface}{link_state}, 
				"last_scan::${interface}::mac_address"          => $anvil->data->{last_scan}{$interface}{mac_address}, 
				"last_scan::${interface}::media"                => $anvil->data->{last_scan}{$interface}{media}, 
				"last_scan::${interface}::mii_polling_interval" => $anvil->data->{last_scan}{$interface}{mii_polling_interval},
				"last_scan::${interface}::mtu"                  => $anvil->data->{last_scan}{$interface}{mtu}, 
				"last_scan::${interface}::operational"          => $anvil->data->{last_scan}{$interface}{operational},
				"last_scan::${interface}::primary_reselect"     => $anvil->data->{last_scan}{$interface}{primary_reselect},
				"last_scan::${interface}::primary_interface"    => $anvil->data->{last_scan}{$interface}{primary_interface},
				"last_scan::${interface}::speed"                => $anvil->data->{last_scan}{$interface}{speed},
				"last_scan::${interface}::subnet_mask"          => $anvil->data->{last_scan}{$interface}{subnet_mask},
				"last_scan::${interface}::type"                 => $anvil->data->{last_scan}{$interface}{type},
				"last_scan::${interface}::up_delay"             => $anvil->data->{last_scan}{$interface}{up_delay},
			}});
		}
	}
	closedir(DIRECTORY);
	
	# Now look for interfaces that disappeared.
	foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{last_scan}})
	{
		next if $anvil->data->{last_scan}{$interface}{seen} == $scan_time;
		print "The device: [".$interface."] appears to have disappeared!\n";
		delete $anvil->data->{last_scan}{$interface};
		
		$trigger = 1;
	}
	
	# Trigger?
	if ($trigger)
	{
		my $shell_call = $anvil->data->{path}{directories}{scan_agents}."/scan-network/scan-network".$anvil->Log->switches;
		print "Triggering the call to run: [".$shell_call."]\n";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
		my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			output      => $output,
			return_code => $return_code, 
		}});
	}
	
	if (time > $next_md5sum_check)
	{
		$next_md5sum_check = time + 30;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { next_md5sum_check => $next_md5sum_check }});
		if ($anvil->Storage->check_md5sums)
		{
			# NOTE: We exit with '0' to prevent systemctl from showing a scary red message.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "message_0014"});
			$anvil->nice_exit({exit_code => 0});
		}
	}
	
	sleep 2;
}
