anvil/tools/anvil-manage-power
Digimer 6f0bc0d86f * Fixed a major bug where anvil-daemon would reset the job_progress to 0 when clearing the 'reboot_needed' flag, causing anvil-daemon to pick the job up and again reboot, repeatedly.
* Updated Jobs->update_progress to take 'picked_up_by' as an optional parameter, defaulting to '$$' (the caller's PID).
* Created System->get_uptime() to return the current uptime in seconds.
* Added a delay to anvil-manage-power to not proceed with a reboot if the uptime is less than 600 seconds. This way, if any future bug causes an infinite reboot, there will be more time to determine what's wrong and debug the system between reboots.

Signed-off-by: Digimer <digimer@alteeve.ca>
2018-10-06 03:16:08 -04:00

258 lines
8.8 KiB
Perl
Executable File

#!/usr/bin/perl
#
# This manages power on the host. It can set that a reboot is or is no longer required. It can also reboot or
# power off the machine.
#
# Examples;
# - Mark that a reboot is required - anvil-manage-power --reboot-needed 1
# - Clear that a reboot is needed - anvil-manage-power --reboot-needed 0
# - Report whether a reboot is needed or not - anvil-manage-power
# - Reboot the system - anvil-manage-power --reboot [-y]
# - Power the system off - anvil-manage-power --poweroff [-y]
#
# Exit codes;
# 0 = Normal exit.
# 1 = No database connections available.
#
# TODO: Don't reboot or power off until all external users are done with the database on this system (if
# applicable)
#
use strict;
use warnings;
use Anvil::Tools;
# Disable buffering
$| = 1;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1});
$anvil->Storage->read_config({file => $anvil->data->{path}{configs}{'anvil.conf'}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, key => "log_0115", variables => { program => $THIS_FILE }});
# Read switches
$anvil->data->{switches}{'poweroff'} = "";
$anvil->data->{switches}{'power-off'} = "";
$anvil->data->{switches}{'reboot'} = "";
$anvil->data->{switches}{'y'} = "";
$anvil->data->{switches}{'yes'} = "";
$anvil->data->{switches}{'reboot-needed'} = "";
$anvil->data->{switches}{'job-uuid'} = "";
$anvil->data->{switches}{'no-delay'} = "";
$anvil->Get->switches;
if ($anvil->data->{switches}{'power-off'})
{
$anvil->data->{switches}{'poweroff'} = 1;
}
if ($anvil->data->{switches}{'yes'})
{
$anvil->data->{switches}{'y'} = 1;
}
# Connect to DBs.
$anvil->Database->connect;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"});
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, exit.
print $anvil->Words->string({key => "error_0003"})."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => 0, key => "error_0003"});
$anvil->nice_exit({exit_code => 1});
}
# Are we being asked to reboot or power off?
if ($anvil->data->{switches}{'reboot'})
{
# Did the user confirm?
if ($anvil->data->{switches}{'y'})
{
do_poweroff($anvil, "reboot");
}
else
{
# Not yet, ask to confirm.
print $anvil->Words->string({key => "message_0059"})." ";
my $answer = <STDIN>;
chomp($answer);
if ($answer =~ /^y/i)
{
do_poweroff($anvil, "reboot");
}
else
{
# Abort and exit.
print $anvil->Words->string({key => "message_0061"})."\n";
$anvil->nice_exit({exit_code => 0});
}
}
}
if ($anvil->data->{switches}{'poweroff'})
{
# Did the user confirm?
if ($anvil->data->{switches}{'y'})
{
do_poweroff($anvil, "poweroff");
}
else
{
# Not yet, ask to confirm.
print $anvil->Words->string({key => "message_0060"})." ";
my $answer = <STDIN>;
chomp($answer);
if ($answer =~ /^y/i)
{
do_poweroff($anvil, "poweroff");
}
else
{
# Abort and exit.
print $anvil->Words->string({key => "message_0061"})."\n";
$anvil->nice_exit({exit_code => 0});
}
}
}
my $reboot_needed = $anvil->System->reboot_needed({debug => 2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { reboot_needed => $reboot_needed }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::reboot-needed" => $anvil->data->{switches}{'reboot-needed'} }});
if ($anvil->data->{switches}{'reboot-needed'} eq "1")
{
# Enable
if (not $reboot_needed)
{
$reboot_needed = $anvil->System->reboot_needed({debug => 2, set => 1});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { reboot_needed => $reboot_needed }});
print $anvil->Words->string({key => "message_0048"})."\n";
}
else
{
# Was already set, do nothing
print $anvil->Words->string({key => "message_0049"})."\n";
}
}
elsif ($anvil->data->{switches}{'reboot-needed'} eq "0")
{
# Disabled
if ($reboot_needed)
{
$reboot_needed = $anvil->System->reboot_needed({debug => 2, set => 0});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { reboot_needed => $reboot_needed }});
print $anvil->Words->string({key => "message_0050"})."\n";
}
else
{
# Was already disabled, do nothing
print $anvil->Words->string({key => "message_0051"})."\n";
}
}
elsif ($anvil->data->{switches}{'reboot-needed'})
{
# Bad call
print $anvil->Words->string({key => "message_0052", variables => { program => $THIS_FILE }})."\n";
}
# Get the current state
if ($reboot_needed)
{
# Report that we need to reboot
print $anvil->Words->string({key => "message_0053"})."\n";
}
else
{
# Report that we're not.
print $anvil->Words->string({key => "message_0054"})."\n";
}
# We're done
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Private functions. #
#############################################################################################################
# This does a reboot or power off
sub do_poweroff
{
my ($anvil, $task) = @_;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { task => $task }});
# We'll wait until the system has at least 10 minutes of uptime, unless '--no-wait' was given.
my $uptime = $anvil->data->{switches}{'no-wait'} ? 0 : $anvil->System->get_uptime;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"switches::no-wait" => $anvil->data->{switches}{'no-delay'},
uptime => $uptime,
}});
my $say_task = $task eq "poweroff" ? "message_0062" : "message_0063";
my $percent = $task eq "poweroff" ? 100 : 50;
print $anvil->Words->string({key => $say_task})."\n";
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => $say_task});
# To minimize the trouble of a problem where the reboot needed flag isn't cleared, and so the system
# wants to repeatedly reboot, we need to add a delay to not let anvil-daemon ask us to
# reboot/power-off until the system uptime is more than ten minutes.
if (($uptime) && ($uptime < 600))
{
# We'll wait until the system has been running for ten minutes.
my $difference = 600 - $uptime;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, key => "log_0224", variables => {
task => $task eq "poweroff" ? "#!string!log_0225!#" : "#!string!log_0226!#",
difference => $difference,
uptime => $uptime,
say_time => $anvil->Get->date_and_time({offset => $difference, time_only => 1}),
}});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => $say_task});
sleep $difference;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, secure => 0, key => "log_0227", variables => { task => $task eq "poweroff" ? "#!string!log_0225!#" : "#!string!log_0226!#" }});
}
# If I don't have a job_uuid, try to find one.
my $job_uuid = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'} }});
if ($anvil->data->{switches}{'job-uuid'})
{
$job_uuid = $anvil->data->{switches}{'job-uuid'};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }});
}
else
{
$job_uuid = $anvil->Job->get_job_uuid({debug => 2, program => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }});
}
if ($job_uuid)
{
$anvil->Job->clear({job_uuid => $job_uuid});
$anvil->Job->update_progress({
debug => 2,
progress => $percent,
message => $say_task,
job_uuid => $job_uuid,
});
}
# Make sure the 'reboot needed' flag is set. When 'anvil-daemon' starts, it will use this to confirm
# that it is starting post-reboot and clear it.
$reboot_needed = $anvil->System->reboot_needed({set => 1});
# Now do the deed.
my $shell_call = $anvil->data->{path}{exe}{systemctl}." ".$task;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my $output = $anvil->System->call({shell_call => $shell_call, source => $THIS_FILE, line => __LINE__});
# Unlikely we're still alive, but 'poweroff' and 'reboot' do return once enqueued, so...
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output }});
$anvil->nice_exit({exit_code => 0});
}