#! /usr/bin/perl -w

#
# Cryptoflex e-gate plugged/unplugged
# flush agent(s) and lock screen on removal
#

use strict;

exit unless defined $ENV{ACTION} and $ENV{DEVICE};
exit unless $ENV{ACTION} =~ /^(add|remove)$/;

open STDIN, '</dev/null';
open STDOUT, '>/dev/null';
open STDERR, '>&STDOUT';

# load up process list to locate ssh-agent and xscreensaver processes
my @procs;
{
    my %pid;
    my %exe;
    opendir D, '/proc';
    while (my $p = readdir D)
    {
	next unless $p =~ /^\d+$/;
	my $exe = readlink "/proc/$p/exe" or next;
	open S, "/proc/$p/status" or next;
	my ($ppid) = map /^PPid:\s+(\d+)/, <S>;
	close S;
	$pid{$p} = { exe => $exe, pid => $p, ppid => $ppid } if $ppid;
	$exe{$exe}{$p}++;
    }
    closedir D;

    # see if we can find any instances of ssh-agent or xscreensaver
    # (indirectly) parented by xdm
    my @e = '/usr/bin/xscreensaver'; # lock/unlock
    push @e, '/usr/bin/ssh-agent' if $ENV{ACTION} eq 'remove'; # flush
    for my $e (@e)
    {
	next unless $exe{$e};
        for my $p (keys %{$exe{$e}})
	{
	    # determine if this is parented by xdm
	    my $pp = $pid{$p}{ppid};
	    while ($pp > 1 and $pid{$pp})
	    {
		if ($pid{$pp}{exe} eq '/usr/X11R6/bin/xdm')
		{
		    push @procs, $pid{$p};
		    last;
		}
		$pp = $pid{$pp}{ppid};
	    }
	}
    }
}

sub environ
{
    # fetch environment
    open E, "/proc/$_[0]/environ" or return;
    local $/ = "\x00";
    my %e = map /^([^=]+)=([^\x00]+)/, <E>;
    close E;
    %e;
}

for my $p (@procs)
{
    if ($p->{exe} =~ /xscreensaver/)
    {
	my %e = environ $p->{pid};
	next unless $e{DISPLAY} and $e{XAUTHORITY};
	$ENV{$_} = $e{$_} for qw/DISPLAY XAUTHORITY/;

	my $cmd = ($ENV{ACTION} eq 'add') ? '-deactivate' : '-lock';
	system '/usr/bin/xscreensaver-command', $cmd;
	next;
    }

    if ($ENV{ACTION} eq 'remove' and $p->{exe} =~ /ssh-agent/)
    {
	my %e = environ $p->{ppid};
	next unless $e{SSH_AUTH_SOCK};
	$ENV{SSH_AUTH_SOCK} = $e{SSH_AUTH_SOCK};
	system '/usr/bin/ssh-add', '-D';
	next;
    }
}

# arrange to have ourself called on removal by hotplug's usb.agent
# - ugly, but matching SYSFS{product} doesn't seem to work on removal,
# and usb-agent is called anyway
if ($ENV{ACTION} eq 'add' and $ENV{DEVICE} =~ m!^/proc/bus/usb/[\d/]+$!)
{
    (my $mangled_dev = $ENV{DEVICE}) =~ tr!/!%!;
    symlink $0, "/var/run/usb/$mangled_dev";
}
