#!/usr/bin/env perl use warnings; use strict; use English; use POSIX; sub debug(@) { print join ' ', POSIX::strftime('%F %T', localtime), @_, "\n"; } sub sid_is_attached($) { my ($intf) = @_; my $re = eval { my $hex = qr/[a-fA-F0-9]{2}/; my $mac = qr/$hex : $hex : $hex : $hex : $hex : $hex/x; return qr/Access Point: $mac/; }; grep { /$re/ and return 1 } `iwconfig $intf`; return 0; } sub sid_get_sid($) { my ($intf) = @_; my $re = eval { my $sid = qr/[^"]+/; return qr/ESSID:"($sid)"/; }; grep { /$re/ and return $1 } `iwconfig $intf`; die 'race condition'; } sub sid_is_available($$) { my ($sid, $intf) = @_; my $re = eval { my $sid = qr/[^"]+/; return qr/ESSID:"($sid)"/; }; grep { /$re/ and return 1 } `iwlist $intf scan`; return 0; } sub sid_connect($$) { my ($sid,$intf) = @_; my $cmd = "iwconfig $intf essid $sid"; system $cmd; } sub dhcp_is_running($) { my ($intf) = @_; grep { /dhcpcd $intf/ and return 1 } `ps ax`; return 0; } sub car(@) { return shift } sub dhcp_kill($) { my ($intf) = @_; debug "having to kill -9 dhcpcd"; map { system "echo kill -9 $_" } map { car split } grep { /dhcpcd $intf/ } `ps ax`; } sub dhcp_start($) { my ($intf) = @_; system "dhcpcd $intf"; } sub dhcp_stop($) { my ($intf) = @_; my $ret = system "dhcpcd --exit $intf"; $ret == 0 and return 1; dhcp_is_running $intf and dhcp_kill $intf; dhcp_is_running $intf and return 0; return 1; } sub dhcp_has_default_route($) { my ($intf) = @_; return grep { /^0\.0\.0\.0.*$intf/ and return 1 } ` route -n `; debug "no default route"; return 0; } sub test_wifi($$) { my ($sid, $intf) = @_; return sid_is_attached $intf; } sub fix_wifi($$) { my ($sid, $intf) = @_; sid_is_available($sid, $intf) or do { sleep 1; return}; sid_connect $sid, $intf; } sub test_ip($$) { my ($sid, $intf) = @_; return dhcp_is_running $intf and dhcp_has_default_route $intf; } sub fix_ip($$) { my ($sid, $intf) = @_; dhcp_stop $intf; dhcp_start $intf; } sub test_if($$) { my ($sid, $intf) = @_; grep { /UP/ and return 1 } `ifconfig $intf `; return 0; } sub fix_if($$) { my ($sid, $intf) = @_; system("ifconfig $intf up"); } $EUID == 0 or die "got root?"; my ($intf) = @ARGV; my ($sid) = `cat $ENV{HOME}/.wifi`; $intf ne "" or die "got interface?"; dhcp_stop $intf or die "you have unkillable dhcp running?"; close STDOUT; open STDOUT, '>' . $ENV{HOME} . '/var/wifi-minder.log'; close STDERR; open STDERR, ">&STDOUT"; debug "forking into background"; fork and exit; fork and exit; POSIX::setsid(); =head1 we have the following states: no wifi wifi no ip network ip network for each state, we have a test and an action to fix the state =cut my @states = ( { 'test' => 'test_if', 'fix' => 'fix_if' }, { 'test' => 'test_wifi', 'fix' => 'fix_wifi' }, { 'test' => 'test_ip', 'fix' => 'fix_ip' }, ); while (1) { for my $state (@states) { no strict qw(refs); $state->{'test'}($sid, $intf) and next; $state->{'fix'}($sid, $intf); $state->{'test'}($sid, $intf) or last; } debug "everything ok, sleeping..."; sleep 10; }