#!/usr/bin/perl -w # Bootbox -- shutdown, reboot, and session choice for X-Windows console login # Author: Jim Carter , 2009-08-05 # The normal way that this program ends is that it is killed. Make sure the # spawned processes exit also. our %pids; # Keys are processes to be killed. sub termhandler { my @pids = keys %pids; warn "Killing @pids\n"; #DEBUG kill 'TERM', @pids if @pids; exit 0; } $SIG{'TERM'} = \&termhandler; $SIG{'INT'} = \&termhandler; # Common code for doing the xmessage thing. Args: # $timeout Number of seconds xmessage exits before restarting it # \@cmd Ref. to the command to be executed # \&action Ref. to subroutine called on each line coming out of xmessage # (should be exactly one on each execution). The subroutine's # only argument is the line that was read (with newline). # Returns Nothing. sub doxmsg { my($timeout, $cmd, $subrt) = @_; while (1) { my $now = time(); my $pid = open(XMSG, '-|', @$cmd) or die "Can't spawn '@{$cmd}': $!\n"; $pids{$pid}++; # Record PID of xmessage process while () { next if length($_) <= 1; &{$subrt}($_); } close XMSG; delete $pids{$pid}; last if time() - $now < 2; # If program is wacked out sleep $timeout; } } # Action subroutine, arg is the session name (with ending newline). sub savesession { my($sess) = @_; my $dmrc = '/var/lib/xdm/dmrc'; open(CHOICE, '>', $dmrc) or die "Can't write $dmrc: $!\n"; print CHOICE $sess; close CHOICE; } # Section for picking the session. The new session's name is written in # /var/lib/xdm/dmrc where /etc/X11/xdm/Xsession can find it. sub picksession { my @sessions = qw(Custom Failsafe); my $f; foreach $f (glob "/usr/share/xsessions/*.desktop") { open(DTOP, $f) or next; while () { if (/^Name=(\S*)/) { push(@sessions, $1); last; } } close DTOP; } my @cmd = (qw(xmessage -geometry +0+0 -buttons), join(',', @sessions), '-print', 'Session Selection'); &doxmsg(1, \@cmd, \&savesession); } our %actions = ( Shutdown => "shutdown -h now", Reboot => "shutdown -r now", Simulate => "shutdown -k now", ); # Action subroutine for calling shutdown. Arg is the action name (with # ending newline). sub doshut { my($a) = @_; chomp $a; if (exists($actions{$a})) { my $cmd = $actions{$a}; my $hpid; if (!($hpid = fork())) { exec $cmd; die "Failed to exec $cmd\n"; } } } # Section for machine control (shutdown). sub shutdown { my @cmd = (qw(xmessage -geometry -0+0 -buttons), join(',', sort keys %actions), '-print', "System Control"); &doxmsg(1, \@cmd, \&doshut); } # Main thread, spawn background processes for each dialog box. our $pid; if (!($pid = fork())) { &picksession(); } $pids{$pid}++; if (!($pid = fork())) { &shutdown(); } $pids{$pid}++; while (wait() > 0) { } exit 0;