Implement signals

Minimum viable product. TERM closes gracefully, KILL just removes PID, and USR1 rotates immediately and restarts timer.

Other changes:
- Whitespace
- Implemented using lazy, excessive use of global variables. This needs to be improved.
- Comment blocks
This commit is contained in:
John Mertz 2022-07-23 03:43:34 -04:00
parent 3f07586ae9
commit 3f54f5f4bc
1 changed files with 244 additions and 202 deletions

View File

@ -2,10 +2,14 @@
use strict;
use warnings;
use POSIX;
use lib "$ENV{HOME}/perl5/lib/perl5";
our $debug = 1; # For testing, will output configuration and errors if true
our %settings;
our @e;
our $pidfile = "/tmp/$ENV{USER}-wallpaper.pid";
use AnyEvent::Sway;
our $s = AnyEvent::Sway->new();
@ -30,28 +34,31 @@ are currently enabled will be set.\n
exit(0);
}
# SIGUSR reset the timer, force immediate reload, continue looping
$SIG{USR1} = sub {
# TODO: Update the timeout method so that the daemon will simply run the job as a function.
# when SIGUSR1 is received, force the alarm to expire immediately in order to rotate the image without delay.
print "This should rotate the image, but it doesn't yet";
alarm 0;
run(%settings);
};
# SIGTERM reset the timer, clean pid, and allow for the main loop to finish run
$SIG{TERM} = sub {
alarm 0;
clean();
exit(0);
$settings{daemon} = 0;
};
$SIG{INT} = sub {
# SIGKILL clean pid then exit immediately
$SIG{KILL} = sub {
clean();
exit(0);
};
sub clean
{
my $pidfile = "/var/run/$ENV{HOME}-wallpaper.pid";
if (-e $pidfile) {
open (my $fh, '<', $pidfile);
my $p = <$fh>;
close($fh);
chomp $p;
kill($p);
unlink($pidfile);
@ -60,14 +67,12 @@ sub clean
sub choose_image
{
my $path = shift;
my @w = glob("$path/*");
my @w = glob("$settings{path}/*");
return undef unless (scalar(@w));
my @i;
foreach (@w) {
if (-d "$path/$_") {
if (-d "$settings{path}/$_") {
next;
}
if ($_ =~ m/\.(png|jpg)$/) {
@ -159,12 +164,62 @@ sub set_background
return "Failed to run Sway IPC command '$cmd'";
}
my @targets;
my $daemon = 0;
my $delay = 300;
my $crop = 1;
my $path;
sub run
{
my @err;
# Local copy of targets so that it will re-check active every time
my @t = $settings{targets} if (defined($settings{targets}));
unless (scalar(@t)) {
@t = get_active();
push(@err, "No target outputs") unless (scalar(@t));
}
foreach my $a (@t) {
my $image = choose_image($settings{path});
if (defined($image)) {
if ( -r "$image" ) {
print "selected $image\n";
my $cropped;
if ($settings{crop}) {
if ($settings{debug}) {
print "Cropping image for '$a' using '$image'\n";
}
my ($ow, $oh) = get_size($a);
$cropped = crop($image, $ow, $oh);
unless ($cropped) {
push(@err, "Failed to generate cropped image") unless ($cropped);
next;
}
} else {
$cropped = $image;
}
my $e = set_background($a,$cropped);
push(@err, $e) if (defined($e));
if ($settings{crop}) {
print "Deleting $cropped\n";
#unlink($cropped) || push(@err, "Failed to delete $cropped after setting: $!");
}
} else {
push(@err, "$a: Unable to read image $image");
}
} else {
push(@err, "$a: Unable to select image from $settings{path}");
}
}
if ($settings{debug} && $settings{daemon}) {
print STDERR join("\n",@err);
} else {
@e = @err;
}
}
################################################################################
# Collect arguments
################################################################################
my @targets;
my $daemon;
my $delay;
my $crop;
my $path;
while (my $arg = shift(@ARGV)) {
if ($arg eq '-h' || $arg eq '--help') {
usage();
@ -191,13 +246,13 @@ while (my $arg = shift(@ARGV)) {
}
}
die "Invalid rotation delay: $delay" unless ($delay =~ m/^\d+$/);
################################################################################
# Validate arguments
################################################################################
if ($path) {
die "Invalid wallpaper path '$path'. Not a directory." unless (-d $path);
} else {
$path = "$ENV{HOME}/wallpapers";
}
die "Invalid rotation delay: $delay" if (defined($delay) && $delay !~ m/^\d+$/);
die "Invalid wallpaper path '$path'. Not a directory." if (defined($path) && !-d $path);
if (scalar(@targets)) {
my @kept;
@ -216,72 +271,59 @@ if (scalar(@targets)) {
@targets = @kept;
}
if ($daemon) {
$settings{targets} = @targets || undef;
$settings{daemon} = $daemon || 0;
$settings{path} = $path || "$ENV{HOME}/wallpapers";
$settings{crop} = $crop || 1;
$settings{delay} = $delay || 300;
$settings{debug} = $debug || 0;
################################################################################
# For if daemonized
################################################################################
if ($settings{daemon}) {
my $p = fork();
if ($p) {
my $pidfile = "/var/run/$ENV{HOME}-wallpaper.pid";
open(my $fh, ">", $pidfile) || die "Failed to open pidfile: $pidfile";
print $fh "Daemonized as PID: $p\n" || die "Failed to write pid ($p) to pidfile $pidfile";
if (open(my $fh, ">", $pidfile)) {
print $fh "$p" || die "Failed to write pid ($p) to pidfile $pidfile";
close($fh);
} else {
print "Failed to open pidfile: $pidfile: $!\n";
}
exit(0);
}
#setpgrp;
#setsid;
umask 0;
# TODO: configure systemD logging
}
if ($debug) {
print "Initial configuration:\n";
print " Targets: ( " . ( join(" ",@targets) || "All active" ) . " )\n";
print " Daemon: $daemon\n";
print " Path: $path\n";
}
################################################################################
# Main
################################################################################
my @e;
do {
my @err;
# Local copy of targets so that it will re-check active every time
my @t = @targets;
unless (scalar(@t)) {
@t = get_active();
push(@err, "No target outputs") unless (scalar(@t));
}
foreach my $a (@t) {
my $image = choose_image($path);
if (defined($image)) {
if ( -r "$image" ) {
print "selected $image\n";
my $cropped;
if ($crop) {
if ($debug) {
print "Cropping image for '$a' using '$image'\n";
}
my ($ow, $oh) = get_size($a);
$cropped = crop($image, $ow, $oh);
unless ($cropped) {
push(@err, "Failed to generate cropped image") unless ($cropped);
next;
}
} else {
$cropped = $image;
}
my $e = set_background($a,$cropped);
push(@err, $e) if (defined($e));
if ($crop) {
print "Deleting $cropped\n";
#unlink($cropped) || push(@err, "Failed to delete $cropped after setting: $!");
}
} else {
push(@err, "$a: Unable to read image $image");
}
} else {
push(@err, "$a: Unable to select image from $path");
run(%settings);
if ($settings{daemon}) {
my $normal = "reload wallpaper";
eval {
$SIG{ALRM} = sub { print "$normal\n" };
alarm $settings{delay};
POSIX::pause();
alarm 0;
};
if ($@ && $@ !~ quotemeta($normal)) {
if ($settings{debug}) {
print $normal;
}
}
if ($debug && $daemon) {
print STDERR join("\n",@err);
sleep($delay);
} else {
@e = @err;
}
} while ($daemon);
} while ($settings{daemon});
################################################################################
# If we made it to here, it was not daemonized. Output errors and exit
################################################################################
if (scalar(@e)) {
die "Failure while not running as daemon:\n" .