summaryrefslogtreecommitdiffstats
path: root/emergencyd.pl
diff options
context:
space:
mode:
Diffstat (limited to 'emergencyd.pl')
-rwxr-xr-xemergencyd.pl182
1 files changed, 113 insertions, 69 deletions
diff --git a/emergencyd.pl b/emergencyd.pl
index 0668110..1cb7536 100755
--- a/emergencyd.pl
+++ b/emergencyd.pl
@@ -134,84 +134,128 @@ sub curtime {
$year+1900,$mon+1,$mday,$hour,$min,$sec);
}
+sub check_mac {
+ my $cmdline = shift;
+ my $timestamp = shift;
+ my $mac_check = shift;
+ my $validate = "$cmdline|$timestamp";
+ my $mac_checked = 0;
+ foreach my $key ( @authorized_keys ) {
+ if ( $mac_check eq hmac_sha256_hex($validate, $key) ) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
my $mintime = "0";
+my $running = 1;
+
+sub check_and_update_timestamp {
+ my $timestamp = shift;
+ if ( ($timestamp ge curtime(-30)) && ($timestamp le curtime(30))
+ && ($timestamp gt $mintime) ) {
+ $mintime = $timestamp;
+ return 1;
+ }
+ return 0;
+}
+
+sub cmd_ping {
+ return "PONG\n";
+}
+
+sub cmd_date {
+ return [ "DATE", curtime, $mintime ];
+}
+
+sub cmd_noop {
+ return "NOOP\n";
+}
+
+sub cmd_dpid {
+ return [ "DPID" , $$ ];
+}
+
+sub cmd_die {
+ $running = 0;
+ return "BYE!\n";
+}
+
+sub cmd_rkey {
+ read_keys;
+ return "DONE\n";
+}
+
+sub cmd_logm {
+ my $s = shift or die "Missing argument to LOGM\n";
+ my $level = LOG_NOTICE;
+ my %levels = ("EMERG"=>LOG_EMERG, "ALERT"=>LOG_ALERT,
+ "CRIT"=>LOG_CRIT, "ERR"=>LOG_ERR, "WARNING"=>LOG_WARNING,
+ "NOTICE"=>LOG_NOTICE, "INFO"=>LOG_INFO, "DEBUG"=>LOG_DEBUG);
+ if ( $s =~ /^([A-Z0-9]*)\s+(.*)/ && exists($levels{$1}) ) {
+ $level = $levels{$1};
+ $s = $2;
+ }
+ syslog $level, $s;
+ return "DONE\n";
+}
+
+sub cmd_syrq {
+ my $s = shift or die "Missing argument to SYRQ\n";
+ open my $sysrq_trigger, ">", "/proc/sysrq-trigger"
+ or die "Couldn't open /proc/sysrq-trigger for writing: $!";
+ print $sysrq_trigger $s
+ or die "Couldn't write to /proc/sysrq-trigger: $!";
+ close $sysrq_trigger;
+ return "DONE\n";
+}
+
+my %dispatch = (
+ "PING" => \&cmd_ping,
+ "DATE" => \&cmd_date,
+ "NOOP" => \&cmd_noop,
+ "DPID" => \&cmd_dpid,
+ "DIE!" => \&cmd_die,
+ "RKEY" => \&cmd_rkey,
+ "LOGM" => \&cmd_logm,
+ "SYRQ" => \&cmd_syrq,
+ );
+
PACKET:
-while (1) {
+while ( $running ) {
my $buf;
my $sender = recv($socket, $buf, 16384, 0);
die "Failed to receive packet: $!" unless defined($sender);
my @lines = split /\015*\012|\|/s, $buf;
- my $command = $lines[0] // "";
+ my $cmdline = $lines[0] // "";
my $timestamp = $lines[1] // "";
- my $maccheck = $lines[2] // "";
- next PACKET if $command eq "";
- if ( $command eq "PING" ) {
- send $socket, "PONG\n", 0, $sender;
- } elsif ( $command eq "DATE" ) {
- send $socket, ("DATE\n".curtime."\n".$mintime."\n"), 0, $sender;
- } else {
- my $validate = "$command|$timestamp";
- my $mac_checked = 0;
- foreach my $key ( @authorized_keys ) {
- if ( $maccheck eq hmac_sha256_hex($validate, $key) ) {
- $mac_checked = 1;
- }
- }
- unless ( $mac_checked ) {
- send $socket, "!MAC\n", 0, $sender;
- next PACKET;
+ my $mac_check = $lines[2] // "";
+ next PACKET if $cmdline eq "";
+ my $resp = undef;
+ eval {
+ my ($command, $arg) = $cmdline =~ /^([A-Z0-9\!]{4})(?:\s+(.*))?$/
+ or die "!BAD\n";
+ unless ( $command eq "PING" || $command eq "DATE" ) {
+ check_mac $cmdline, $timestamp, $mac_check
+ or die "!MAC\n";
+ check_and_update_timestamp $timestamp
+ or die "!DAT\n";
}
- my $datechecked = ($timestamp ge curtime(-30))
- && ($timestamp le curtime(30))
- && ($timestamp gt $mintime);
- unless ( $datechecked ) {
- send $socket, "!DAT\n", 0, $sender;
- next PACKET;
- }
- $mintime = $timestamp;
- if ( $command eq "NOOP" ) {
- send $socket, "NOOP\n", 0, $sender;
- } elsif ( $command eq "DPID" ) {
- send $socket, "DPID\n$$\n", 0, $sender;
- } elsif ( $command eq "DIE!" ) {
- send $socket, "BYE!\n", 0, $sender;
- exit 0;
- } elsif ( $command eq "RKEY" ) {
- my $resp = "DONE\n";
- eval { read_keys };
- if ( $@ ) {
- $resp = "!ERR\n$@";
- }
- send $socket, $resp, 0, $sender;
- } elsif ( $command =~ /^LOGM\s+(.*)$/ ) {
- my $s = $1;
- my $level = LOG_NOTICE;
- my %levels = ("EMERG"=>LOG_EMERG, "ALERT"=>LOG_ALERT,
- "CRIT"=>LOG_CRIT, "ERR"=>LOG_ERR, "WARNING"=>LOG_WARNING,
- "NOTICE"=>LOG_NOTICE, "INFO"=>LOG_INFO, "DEBUG"=>LOG_DEBUG);
- if ( $s =~ /^([A-Z0-9]*)\s+(.*)/ && exists($levels{$1}) ) {
- $level = $levels{$1};
- $s = $2;
- }
- syslog $level, $s;
- send $socket, "DONE\n", 0, $sender;
- } elsif ( $command =~ /^SYRQ\s+(.*)$/ ) {
- my $s = $1;
- my $resp = "DONE\n";
- eval {
- open my $sysrq_trigger, ">", "/proc/sysrq-trigger"
- or die "Couldn't open /proc/sysrq-trigger for writing: $!";
- print $sysrq_trigger $s
- or die "Couldn't write to /proc/sysrq-trigger: $!";
- close $sysrq_trigger;
- };
- if ( $@ ) {
- $resp = "!ERR\n$@";
- }
- send $socket, $resp, 0, $sender;
- } else {
- send $socket, "!UNK\n", 0, $sender;
+ my $sub = $dispatch{$command};
+ die "!UNK\n" unless defined($sub);
+ $resp = &{$sub}($arg);
+ };
+ if ( $@ ) {
+ $resp = $@;
+ if ( ref($resp) eq "" && $resp !~ /^\!/ ) {
+ $resp = "!ERR\n" . $resp;
}
}
+ if ( defined($resp) ) {
+ $resp = join("\n", @{$resp}) . "\n" if ref($resp) eq "ARRAY";
+ send $socket, $resp, 0, $sender;
+ }
}
+exit 0;