#!/usr/bin/perl -w
#
# Tests for k5start daemon functionality.
#
# Written by Russ Allbery <rra@stanford.edu>
# Copyright 2008, 2009 Board of Trustees, Leland Stanford Jr. University
#
# See LICENSE for licensing terms.

use Cwd;

use Test::More;

# The full path to the newly-built k5start client.
our $K5START = "$ENV{BUILD}/../k5start";

# The path to our data directory, which contains the keytab to use to test.
our $DATA = "$ENV{BUILD}/data";

# Load our test utility programs.
require "$ENV{SOURCE}/libtest.pl";

# Decide whether we have the configuration to run the tests.
if (-f "$DATA/test.keytab" and -f "$DATA/test.principal") {
    plan tests => 31;
} else {
    plan skip_all => "no keytab configuration";
    exit 0;
}

# Get the test principal.
my $principal = contents ("$DATA/test.principal");

# Don't overwrite the user's ticket cache.
my $cwd = getcwd;
$ENV{KRB5CCNAME} = "$cwd/krb5cc_test";

# Start a k5start daemon and be sure it gets tickets and stays running.
unlink 'krb5cc_test';
my $pid = fork;
if (!defined $pid) {
    BAIL_OUT ("can't fork: $!");
} elsif ($pid == 0) {
    exec ($K5START, '-K', 1, '-f', "$DATA/test.keytab", '-p', 'pid',
          $principal) or BAIL_OUT ("can't run $K5START: $!");
}
my $tries = 0;
while (not -f 'krb5cc_test' and $tries < 10) {
    select (undef, undef, undef, 0.1);
    $tries++;
}
my ($default, $service) = klist ();
like ($default, qr/^\Q$principal\E(\@\S+)?\z/,
      'Authentication succeeded for the right principal');
like ($service, qr%^krbtgt/%, ' and the right service');
if (-f 'pid') {
    my $daemon = contents ('pid');
    is ($pid, $daemon, ' and the right PID is written');
    $pid = $daemon if $daemon;
} else {
    ok (0, ' and the right PID is written');
}
ok (kill (0, $pid), ' and k5start is still running');
unlink 'krb5cc_test';
ok (! -f 'krb5cc_test', 'Ticket cache was deleted');
kill (14, $pid) or warn "Can't kill $pid: $!\n";
$tries = 0;
while (not -f 'krb5cc_test' and $tries < 10) {
    select (undef, undef, undef, 0.1);
    $tries++;
}
ok (kill (0, $pid), ' and k5start is still running after ALRM');
($default, $service) = klist ();
like ($default, qr/^\Q$principal\E(\@\S+)?\z/,
      ' and recreates cache with the right principal');
like ($service, qr%^krbtgt/%, ' and the right service');
kill (15, $pid) or warn "Can't kill $pid: $!\n";
is (waitpid ($pid, 0), $pid, ' and k5start dies after SIGTERM');
unlink 'pid';

# Try again with the -b flag.
unlink 'krb5cc_test';
my ($out, $err, $status)
    = command ($K5START, '-bK', 1, '-f', "$DATA/test.keytab", '-p',
               "$cwd/pid", $principal);
is ($status, 0, 'Backgrounding k5start works');
is ($err, '', ' with no error output');
is ($out, '', ' and -q was added implicitly');
$tries = 0;
while (not -f 'krb5cc_test' and $tries < 10) {
    select (undef, undef, undef, 0.1);
    $tries++;
}
($default, $service) = klist ();
like ($default, qr/^\Q$principal\E(\@\S+)?\z/,
      'Authentication succeeded for the right principal');
like ($service, qr%^krbtgt/%, ' and the right service');
$pid = contents ('pid');
ok (kill (0, $pid), ' and the PID file is correct');
kill (15, $pid) or warn "Can't kill $pid: $!\n";
unlink 'pid';

# Now, run a command in the background.
unlink 'krb5cc_test', 'krb5cc_child', 'child-out';
($out, $err, $status)
    = command ($K5START, '-bK', 1, '-k', "$cwd/krb5cc_child", '-f',
               "$DATA/test.keytab", '-p', "$cwd/pid", '-c', "$cwd/child-pid",
               $principal, '--', "$ENV{SOURCE}/data/command",
               "$cwd/child-out");
is ($status, 0, 'Backgrounding k5start works');
is ($err, '', ' with no error output');
is ($out, '', ' and output was redirected properly');
$tries = 0;
while (not -f 'child-pid' and $tries < 10) {
    select (undef, undef, undef, 0.1);
    $tries++;
}
($default, $service) = klist ();
is ($default, undef, 'The normal ticket cache is untouched');
$ENV{KRB5CCNAME} = 'krb5cc_child';
($default, $service) = klist ();
like ($default, qr/^\Q$principal\E(\@\S+)?\z/,
      ' but the other cache has the right principal');
like ($service, qr%^krbtgt/%, ' and the right service');
$pid = contents ('pid');
ok (kill (0, $pid), 'k5start is running');
$child = contents ('child-pid');
ok (kill (0, $child), 'The child process is running');
$tries = 0;
while (not -S 'child-out' and $tries < 10) {
    select (undef, undef, undef, 0.1);
    $tries++;
}
kill (1, $child) or warn "Cannot send HUP to $child: $!\n";
select (undef, undef, undef, 0.1);
kill (15, $child) or warn "Cannot send TERM to $child: $!\n";
select (undef, undef, undef, 0.1);
ok (!kill (0, $pid), 'k5start is no longer running');
ok (!kill (0, $child), 'The child process is no longer running');
open (OUT, '<', 'child-out') or BAIL_OUT ("cannot open child-out: $!");
my $daemon = <OUT>;
chomp $daemon;
is ($child, $daemon, 'Child PID is correct');
my $dir = <OUT>;
is ($dir, "/\n", 'Child working directory is /');
my $cache = <OUT>;
is ($cache, "FILE:$cwd/krb5cc_child\n", 'Child cache is correct');
is (scalar (<OUT>), "got SIGHUP\n", 'SIGHUP was recorded');
is (scalar (<OUT>), "got SIGTERM\n", 'SIGTERM was recorded');
ok (eof OUT, 'No more child output written');
unlink 'krb5cc_child', 'pid', 'child-pid', 'child-out';
