#!/usr/bin/perl
# $Id: ssh-agent-setup,v 1.1 2004/01/08 04:20:08 ulucs Exp $

# ssh-agent-setup:
#	Find a working ssh-agent or else start one.  In either case,
#	emit an environment-setting command string to enable use of
#	the agent by subsequent processes.
#
#	After starting a new ssh-agent or finding an old one with no
#	identities, invoke ssh-add to add the current user's identity.
#	This prompts for a passphrase if one is needed.
#
# Proper usage is:
#
#   eval `setup-ssh-agent`
#

foreach $var ('SSH_AUTH_SOCK', 'SSH_AUTHENTICATION_SOCKET') {

    $SSH_AUTH_SOCK = $ENV{$var};

    if (defined($SSH_AUTH_SOCK) && &reuse_agent()) {
	exit;
    }
}

$USER = $ENV{'USER'};

$tmp_ssh_USER = "/tmp/ssh-$USER";

if (opendir SOCKDIR, $tmp_ssh_USER) {

    foreach $socket (readdir SOCKDIR) {
        $SSH_AUTH_SOCK = "$tmp_ssh_USER/$socket";
        
        next unless ((-S $SSH_AUTH_SOCK) || (-p $SSH_AUTH_SOCK));

        if (&reuse_agent()) {
            exit;
        }
    }
}

&start_agent();
exit;


# Try to contact the agent on the socket in $SSH_AUTH_SOCK.
# If successful, and it has identities, emit socket setup
#   commands in prevailing shell syntax and return true.
# If successful, but it has no identities, invoke ssh-add.
#   If ssh-add succeeds, emit socket setup and return true.
#   Otherwise (ssh-add failure), abort this process.
# Otherwise (agent-contact failure), delete the given socket
#   and return false.

sub reuse_agent {

    $ENV{'SSH_AUTH_SOCK'} =
	$ENV{'SSH_AUTHENTICATION_SOCKET'} = $SSH_AUTH_SOCK;

    $response = `ssh-add -l 2>&1`;
    if ($?) {
        if (($? >> 8) == 1) {			# agent didn't answer
	    unlink $SSH_AUTH_SOCK;
	    return 0;
	}
	die "$response\n",
	    "ssh-add failed (exit status ", $? >> 8, ")\n";
    }

    if (($response eq "The agent has no identities.\n")
     &&	(system("ssh-add 1>&2"))) {
	    die "ssh-add failed (exit status ", $? >> 8, ")\n";
	}

    $csh_like = ($ENV{'SHELL'} =~ /csh$/);

    foreach $var ('SSH_AUTH_SOCK', 'SSH_AUTHENTICATION_SOCKET') {
	if ($csh_like) {
	    print "setenv $var $SSH_AUTH_SOCK;\n";
	} else {
	    print "$var=$SSH_AUTH_SOCK; export $var;\n";
	}
    }

    1;						# return true
}


# Invoke ssh-agent and save the shell commands that it emits.
# Extract an SSH_AUTH_SOCK (or SSH_AUTHENTICATION_SOCKET) binding
#   from those commands.
# Establish that binding and invoke ssh-add to add the user's
# identity to the new agent, possibly asking for a passphrase.
# Emit the command strings produced by ssh-agent.

sub start_agent {
    local $setup = `ssh-agent`;

    if ($?) {
        die "$0: ssh-agent failed (status $?)\n", $setup;
    }

  extract:
    {
	foreach $var ('SSH_AUTH_SOCK', 'SSH_AUTHENTICATION_SOCKET') {
	    if ($setup =~ /^(setenv )?$var[ =]([^;]+);/) {
		$ENV{$var} = $2;
		last extract;
	    }
	}
	die "$0: ssh-agent produced no SSH_AUTH_SOCK binding:\n", $setup;
    }

    if (system("ssh-add 1>&2")) {
        die "ssh-agent wrote:\n", $setup;
    } else {
        print $setup;
    }
}
