#!/usr/bin/perl

use strict;
use warnings;
use autodie;

# Run tests against a KDC (Kerberos Domain Controller).

## no critic (InputOutput::RequireCheckedSyscalls);

use Carp;
use Data::Dumper;
use File::Temp qw/ tempfile /;
use IPC::Run qw/ run timeout /;
use Test::More;
use Pod::Usage;
use Getopt::Long::Descriptive;

use Stanford::Orange::Util qw/ slurp run_command_improved spew/;
use Stanford::KDC::RegressionTesting;
use Stanford::FlexTest::Nagios;

# This needs to match ...?
my $PRINCIPAL_PREFIX  = 'testing__';
my $AD_TEST_PRINCIPAL = 'krbtst_hXjxc5uiyvaqk';

my $REALM = 'stanford.edu';
my $PORT  = '88';

my $WIN_REALM;
my $WIN_DC;

# For Nagios-style tests we need these variables.
my $NAGIOS_SERVER;
my $NAGIOS_FLEXTESTER;
my $HOSTNAME;

# We don't use the require and shortcircuit modifiers as shortcircuit is
# not available in the wheezy version of Getopt::Long::Descriptive.
#<<<  perltidy: I like it like this!
my ($opt, $usage) = describe_options(
    'kdc-test %o <some-arg>',
    [ 'manual',            'print manual page and exit' ],
    [ 'master|m=s',        'the KDC MASTER to use'],
    [ 'port|p=s',          'the KDC port to use (defaults to 88)',
                             { default  => '88' }],
    [ 'keytab|k=s',        'the full path to the keytab file with kadmin credentials'],
    [ 'local|l',           'run the kadmin command locally'],
    [ 'principal|u=s',     'the principal used when authenticating with the keytab file',
                             { default  => 'service/regression-testing' }],
    [ 'slave|s=s',         'the KDC SLAVE to use for authentication', ],
    [ 'realm|r=s',         'the realm to use', { default => 'stanford.edu' }],
    [ 'win-realm=s',        'the Windows AD realm name (e.g., WINUAT.STANFORD.EDU)', ],
    [ 'win-dc=s',           'the Windows AD domain controller (e.g., winuatdc1.winuat.stanford.edu)', ],
    [ 'test=s',            'which test to run', ],
    [ 'nagios-server=s',   'send Nagios passive result to this Nagios server', ],
    [ 'hostname=s',        'the hostname to use when sending Nagios passive results', ],
    [ 'verbose|v',         'print extra stuff'            ],
    [ 'help|h',            'print usage message and exit', ],
    );
#>>>
if ($opt->manual) {
    pod2usage(-verbose => 2);
    exit 0;
}

if ($opt->help()) {
    print($usage->text);
    exit;
}

# Cannot have '--local' and '--keytab' simultaneously
if ($opt->local() && $opt->keytab()) {
    my $msg
      = 'if you use (--local|-l) you must not specify the keytab file or principal';
    exit_with_error($msg);
}

my $VERBOSE = $opt->verbose();
my $LOCAL   = $opt->local();

my $KADMIN_KEYTAB_PATH = $opt->keytab();
if (!$KADMIN_KEYTAB_PATH) {
    $KADMIN_KEYTAB_PATH = '/etc/heimdal-kdc/regression-testing.keytab';
}

my $KADMIN_PRINCIPAL = $opt->principal();

my $NUMBER_OF_TESTS_RUN = 0;

#######################################################################################
sub exit_with_error {
    my ($msg) = @_;

    print "error: $msg.\n";
    exit 1;
}

sub myok {
    my ($x, $y) = @_;

    ok($x, $y);
    ++$NUMBER_OF_TESTS_RUN;

    return;
}

sub progress {
    my ($msg) = @_;
    if ($VERBOSE) {
        print $msg . "\n";
    }
    return;
}

sub add_to_tests_run {
    my ($number) = @_;
    $NUMBER_OF_TESTS_RUN = $NUMBER_OF_TESTS_RUN + $number;
    return;
}

sub run_tests {
    my ($master, $slaves_aref, $caller, $header) = @_;

    print_header($header);

    # if $slaves_aref points to an empty array, then this is test that
    # does not require a slave.
    my @slaves = @{$slaves_aref};

    if (scalar(@slaves) > 0) {
        foreach my $slave (@slaves) {
            print_subheader("running tests for slave $slave");
            $caller->($master, $slave);
        }
    } else {
        # This is a test that does not require a slave.
        $caller->($master);
    }

    return;
}

sub print_header {
    my ($msg) = @_;

    if (!$NAGIOS_SERVER) {
        print "\nTEST: $msg\n";
        print "-----\n";
    }

    return;
}

sub print_subheader {
    my ($msg) = @_;

    if (!$NAGIOS_SERVER) {
        print "-> $msg \n";
    }

    return;
}

# Set up the kadmin object.
sub kadmin_setup {
    my ($master, $replicas_aref) = @_;

    my %args = (
        'debug'                   => $VERBOSE,
        'kadmin_server'           => $master,
        'kdc_master'              => $master,
        'kdc_server_port'         => $PORT,
        'kadmin_principal'        => $KADMIN_PRINCIPAL,
        'kadmin_principal_keytab' => $KADMIN_KEYTAB_PATH,
        'kdc_servers'             => $replicas_aref,
        'realm'                   => $REALM,
        'use_local'               => $LOCAL,
        'principal_prefix'        => $PRINCIPAL_PREFIX,
    );

    if ($WIN_REALM) {
        $args{'win_realm'} = $WIN_REALM;
    }

    if ($WIN_DC) {
        $args{'win_dc'} = $WIN_DC;
    }

    if ($NAGIOS_SERVER) {
        $args{'in_nagios_mode'} = 1;
    } else {
        $args{'in_nagios_mode'} = 0;
    }

    # $kadmin pointing to the MASTER for authentication.
    my $kadmin = Stanford::KDC::RegressionTesting->new(%args);

    return $kadmin;
}

sub pad {
    my ($x) = @_;

    if ($x < 10) {
        return '0' . $x;
    } else {
        return q{} . $x;
    }
}

sub process_test_results {
    my ($nagios_test_name, $flextests, $aterror) = @_;

    if ($NAGIOS_SERVER) {
        $NAGIOS_FLEXTESTER->process_flextest_results($nagios_test_name,
            $flextests, $aterror);
    } else {
        if ($aterror) {
            my $msg = "could not complete test run: $aterror";
            exit_with_error($msg);
        } else {
            add_to_tests_run($flextests->count());
        }
    }

    return;
}

sub test_check {
    my ($master, $slaves_aref) = @_;

    #<<<  I like it this way.
    return run_tests(
        $master,
        [$master],
        \&test_check_aux,
        'testing Heimdal DB consistency'
        );
    #>>>
}

sub test_check_aux {
    my ($master) = @_;

    my $kadmin = kadmin_setup($master, [$master]);

    # Do DB check
    myok($kadmin->check(),
        "Heimdal DB on $master has no strange configurations");
    return;
}

sub test_simple {
    my ($master) = @_;

    #<<<  perltidy: I like it like this!
    return run_tests(
        $master,
        [],
        \&test_simple_aux,
        'testing existence of some system principals'
        );
    #>>>
}

sub test_simple_aux {
    my ($master) = @_;
    my $nagios_test_name = 'kdc_simple';

    my $kadmin = kadmin_setup($master, [$master]);

    my $flextests;
    eval { $flextests = $kadmin->test_simple(); };
    my $aterror = $@;

    process_test_results($nagios_test_name, $flextests, $aterror);

    return;
}

# Create, verify, and delete a principal. We don't need any slaves
# here.
sub test_basic {
    my ($master) = @_;

    #<<<  perltidy: I like it like this!
    return run_tests(
        $master,
        [],
        \&test_basic_aux,
        'testing create, verify exists, delete'
        );
    #>>>
}

sub test_basic_aux {
    my ($master) = @_;
    my $nagios_test_name = 'kdc_basic';

    my $kadmin = kadmin_setup($master, [$master]);

    my $flextests;
    eval { $flextests = $kadmin->test_basic(); };
    my $aterror = $@;

    process_test_results($nagios_test_name, $flextests, $aterror);

    return;
}

sub test_replication {
    my ($master, $slaves_aref) = @_;

    #<<<  perltidy: I like it like this!
    return run_tests(
        $master,
        $slaves_aref,
        \&test_replication_aux,
        'testing replication'
        );
    #>>>
}

# This test MUST be run on the slave. Since the kadmind service does NOT
# run on a slave, we set the slave kadmin object to local. Of course, this
# means we need to run the commands on the master in non-local mode
# meaning that the principal and keytab options must be set.
sub test_replication_aux {
    my ($master, $slave) = @_;

    if ($KADMIN_KEYTAB_PATH) {
        my $msg
          = 'cannot specify a keytab file when running the replication test';
        exit_with_error($msg);
    }

    if ($LOCAL) {
        my $msg = 'cannot use local mode for the replication test';
        exit_with_error($msg);
    }

    # $kadmin pointing to the MASTER.
    my $kadmin_master = kadmin_setup($master, [$master]);

    # $kadmin pointing to the SLAVE.
    my $kadmin_slave = kadmin_setup($slave, [$slave]);
    $kadmin_slave->use_local(1);

    # Remember we are running this test on the SLAVE but this test needs
    # to run kadmin commands on the MASTER. As such, we need to export the
    # testing principal on the slave so this test can use it to access the
    # kadmin service on the master.
    my ($fh, $filename) = tempfile();
    my $keytab_file = $filename;

    $kadmin_slave->ext_keytab($kadmin_master->kadmin_principal(),
        $keytab_file);

    $kadmin_master->kadmin_principal_keytab($keytab_file);
    # $kadmin_slave->kadmin_principal_keytab($keytab_file);

    # Create the principal
    my $principal = random_long_principal($PRINCIPAL_PREFIX);
    $kadmin_master->create_principal($principal);

    # Make sure it exists in the MASTER
    myok($kadmin_master->principal_exists($principal),
        'new principal exists in MASTER');

    # Make sure it exists in the SLAVE
    myok($kadmin_slave->principal_exists($principal),
        'new principal exists in SLAVE');

    # Delete it in MASTER
    myok($kadmin_master->delete_principal($principal),
        'deleted principal in MASTER');

    myok(
        !$kadmin_slave->principal_exists($principal),
        'verified that principal no longer exists on SLAVE'
    );

    unlink $filename;
    return;
}

sub test_authentication {
    my ($master, $slaves_aref) = @_;

    #<<<  perltidy: I like it like this!
    return run_tests(
        $master,
        $slaves_aref,
        \&test_authentication_aux,
        'testing authentication using password'
        );
    #>>>
}

sub test_authentication_aux {
    my ($master, $kdc) = @_;

    # We set up to TWO kadmin objects: one that has its master point to
    # the real master and the other that has its master point to something
    # bogus. We need the second to be sure that the kinit does not fall
    # through to a functioning master.
    my $kadmin1 = kadmin_setup($master,             [$kdc]);
    my $kadmin2 = kadmin_setup('bogus.bogus.bogus', [$kdc]);

    my $principal = random_long_principal($PRINCIPAL_PREFIX);
    myok(!$kadmin1->principal_exists($principal),
        'principal does not yet exist');

    my $password = $kadmin1->generate_password();
    $kadmin1->create_principal($principal, $password);

    myok($kadmin2->kinit($principal, $password), 'kinit works');
    myok($kadmin2->have_tickets(),    'we have tickets');
    myok($kadmin2->destroy_tickets(), 'destroyed tickets');
    myok(!$kadmin2->have_tickets(),   'we no longer have tickets');

    myok(
        $kadmin1->delete_principal($principal),
        'deleted test_authentication principal'
    );

    return;
}

sub test_dictionary_password {
    my ($master) = @_;

    my $nagios_test_name = 'kdc_dictionary';

    print_header('testing dictionary check');

    if ($LOCAL) {
        print "in local mode so skipping these checks\n";
        return;
    }

    my $kadmin = kadmin_setup($master, [$master]);

    my $flextests;
    eval { $flextests = $kadmin->test_dictionary_password(); };
    my $aterror = $@;

    process_test_results($nagios_test_name, $flextests, $aterror);

    return;
}

sub test_password_history {
    my ($master) = @_;

    #<<<  perltidy: I like it like this!
    return run_tests(
        $master,
        [],
        \&test_password_history_aux,
        'testing password history'
        );
    #>>>
}

sub test_password_history_aux {
    my ($master) = @_;

    my $nagios_test_name = 'kdc_history';

    if ($LOCAL) {
        print "in local mode so skipping these checks\n";
        return;
    }

    my $kadmin = kadmin_setup($master, [$master]);

    my $flextests;
    eval { $flextests = $kadmin->test_password_history(); };
    my $aterror = $@;

    process_test_results($nagios_test_name, $flextests, $aterror);

    return;
}

sub test_ad_krb5_sync {
    my ($master) = @_;

    #<<<  perltidy: I like it like this!
    return run_tests(
        $master,
        [],
        \&test_ad_krb5_sync_aux,
        'testing AD cross-realm trust'
        );
    #>>>
}

sub test_ad_krb5_sync_aux {
    my ($master) = @_;
    my $nagios_test_name = 'kdc_ad_krb5_sync';

    my $kadmin = kadmin_setup($master, [$master]);

    my $flextests;
    eval { $flextests = $kadmin->test_ad_krb5_sync($AD_TEST_PRINCIPAL); };
    my $aterror = $@;

    process_test_results($nagios_test_name, $flextests, $aterror);

    return;
}

sub parse_slave_list {
    my ($slaves) = @_;
    return split(/,/xsm, $slaves);
}

sub test_password_expiration {
    my ($master) = @_;
    #<<<  perltidy: I like it like this!
    return run_tests(
        $master,
        [],
        \&test_password_expiration_aux,
        'testing password expiration logs'
        );
    #>>>
}

sub test_password_expiration_aux {
    my ($master) = @_;
    my $nagios_test_name = 'kdc_password_expiration';

    my $kadmin = kadmin_setup($master, [$master]);

    my $flextests;
    eval { $flextests = $kadmin->test_password_sync_log(); };
    my $aterror = $@;

    process_test_results($nagios_test_name, $flextests, $aterror);

    return;
}

##########################################################################

my %TEST_FREFS = (
    'simple'              => \&test_simple,
    'basic'               => \&test_basic,
    'dictionary'          => \&test_dictionary_password,
    'history'             => \&test_password_history,
    'check'               => \&test_check,
    'replication'         => \&test_replication,
    'authentication'      => \&test_authentication,
    'ad-krb5-sync'        => \&test_ad_krb5_sync,
    'password-expiration' => \&test_password_expiration,
);

my %NON_LOCAL_TESTS = (
    'dictionary'   => 1,
    'history'      => 1,
    'ad-krb5-sync' => 1,
);

# Parse the options passed on the command line and call the tests.
sub process_tests {
    my ($options) = @_;

    my ($master, $slave, $test);

    $test = $options->test();
    if (!$test) {
        my $msg = 'no test to run';
        exit_with_error($msg);
    } elsif (!exists($TEST_FREFS{$test})) {
        my $msg = qq{unrecognized test '$test'};
        exit_with_error($msg);
    }

    # If we are running in local mode make sure the test does not
    # prohibit this.
    if ($LOCAL and $NON_LOCAL_TESTS{$test}) {
        exit_with_error(qq{you cannot run the '$test' test in local mode});
    }

    # If running in local mode set $master to localhost.
    if ($LOCAL) {
        $master = 'localhost';
    } else {
        $master = $options->master();
    }

    $slave = $options->slave();

    my @slaves = ();
    if ($slave) {
        @slaves = parse_slave_list($slave);
    }

    # These are the tests that use a master and no replicas.
    my $master_only_rx = qr{
            simple
           |basic
           |dictionary
           |history
           |ad-krb5-sync
           |check
           |password-expiration
        }isxm;

    if ($test =~ m{$master_only_rx}) {    ## no critic (RegularExpressions)

        # If the test is simple, basic, dictionary, history, or
        # ad-krb5-sync, we only need to pass the master.
        my $test_fref = $TEST_FREFS{$test};
        $test_fref->($master);
        ## use critic
    } elsif ($test =~ m{(replication|authentication)}ixsm) {
        if (scalar(@slaves) == 0) {
            my $msg = qq{the '$test' test requires the --slave parameter};
            exit_with_error($msg);
        } else {
            my $test_fref = $TEST_FREFS{$test};
            $test_fref->($master, \@slaves);
        }
    } elsif ($test eq 'ALL') {
        my @tests = qw/simple basic dictionary history/;
        foreach my $test (@tests) {
            my $test_fref = $TEST_FREFS{$test};
            $test_fref->($master);
        }
    } else {
        my $msg = 'if you got here something is wrong with this script';
        exit_with_error($msg);
    }

    return;
}

##################################################################

if (!$opt->test()) {
    my $msg = 'you must specify a test with the --test parameter';
    exit_with_error($msg);
}

if (!$opt->local() && !$opt->master()) {
    my $msg
      = 'you must specify a MASTER KDC server with the --master parameter';
    exit_with_error($msg);
}

$REALM         = $opt->realm();
$PORT          = $opt->port();
$WIN_REALM     = $opt->win_realm();
$WIN_DC        = $opt->win_dc();
$NAGIOS_SERVER = $opt->nagios_server();
$HOSTNAME      = $opt->hostname();

# We must be in either local mode or have the principal specified.
if (!$LOCAL && !$KADMIN_PRINCIPAL) {
    my $msg = 'you must specify either local mode '
      . '(--local) or provide a principal (--principal)';
    exit_with_error($msg);
}

# In case we are running Nagios-style tests we need a
# Stanford::FlexTest::Nagios object
if ($NAGIOS_SERVER) {
    $NAGIOS_FLEXTESTER = Stanford::FlexTest::Nagios->new(
        nagios_servers => [$NAGIOS_SERVER],
        verbose        => $VERBOSE,
        hostname       => $HOSTNAME,
    );
}

process_tests($opt);

if (!$NAGIOS_SERVER) {
    done_testing($NUMBER_OF_TESTS_RUN);
}

__END__

=head1 NAME

kdc-test - Run client-side tests against Stanford Kerberos KDC servers

=head1 SYNOPSIS

kdc-test --test=I<test> --master=I<kerberos-master> [--slave=I<server-list>]
         (--local|--keytab=I<path_to_keytab>) [--verbose] [--port=I<kdc-port>]

=head1 DESCRIPTION

The B<kdc-test> script runs kadmin tests against the KDC servers. As we do
not run the kadmind service on KDC slaves, most of these tests are run
against the master only. Each test that can only be run against the master
will be marked "[master only]". Only use the B<--slave> option for tests
marked "[any KDC]" and for the B<replication> test.

* simple

* basic

* dictionary

* history

* check

* password-expiration

* ad-krb5-sync

* authentication

* replication

=head1 AUTHENTICATION AND AUTHORIZATION

There are two ways to authenticate when running this script: local
authentication or with credentials supplied by a keytab file.

=head2 local authentication

You can run this script directly on the KDC you want to test if you
are logged in as root. To do this use the C<--local> command-line option.
Note that if you do this you should also omit the C<--master> option as
when running locally the implied master is the localhost.

The dictionary, history, and ad-krb5-sync commands will not be run in
local mode as kadmin run in local mode skips plugins needed by these tests.

=head2 keytab authentication

You specify Kerberos credentials via a Kerberos keytab file using the
command-line switch B<--keytab>. This keytab file must contain the
credentials for a principal that has authorization to create and delete
certain principals. If the principal used for this purpose is
B<service/regression-testing@stanford.edu> then these entries must be
added to the C</etc/heimdal-kdc/kadmind.acl> file:

service/regression-testing   get    kadmin/*

service/regression-testing   all    testing__*

service/regression-testing   all    krbtst_hXjxc5uiyvaqk

Also, be sure that the kerberos servers you are attempting to reach are
running the kadmind service and allow connection to the kadmind port from
the machine you are running this script on.

=head1 PRINCIPALS CREATED

Several of the tests will create and then delete a random principal. These
random principals have the form "testing__YYYYMMDDHHMMSS__RRRRRR" where
"YYYYMMDDHHMMSS" is the current date and time and "RRRRRR" are six random
lower-case letters. Those tests that create and then delete a random
principal will be marked "(CRUD)" in the section "TESTS" below.

If a test that creates one of these principals is run in non-local mode,
an entry for that principal will be created in the Heimdal history
database. Use the manage-heimdal-history script to delete these extra
entries.

Tests run in local mode will not create Heimdal password history
database entries.

=head1 TESTS

Some of the tests only make sense when running on a KDC master. Those
are marked "[master only]". Tests that create and then delete a test
principal are marked "(CRUD)".

=head2 ALL

Run all the master tests except for B<check> and B<authentication>.

=head2 simple [any KDC]

The B<simple> test looks for all principals that start "kadmin" and
passes if it finds at least two.

=head2 basic (CRUD) [master only]

The B<basic> test creates and deletes a service principal.

=head2 dictionary (CRUD) [master only, not in local mode]

This test verifies that you cannot use easy-to-guess passwords by
exercising the krb5-strength plugin. Due to the fact that the dictionary
is not checked when run in local mode, this command will exit with an
error if C<--local> is specified.

=head2 history (CRUD) [master only, not in local mode]

This test verifies that the password history feature is functioning
properly. It creates a principal, sets its password, and then attempts to
change the password to the same password. This password re-use should be
caught by the heimdal-history system. Due to the fact that the history
database is not checked when run in local mode, this command will exit
with an error if C<--local> is specified.

=head2 check [any KDC]

This test runs the "kadmin check" command. This test can be very slow.

=head2 password-expiration [master only]

This test scans the password-expiration sync log password-expiration.log
looking for any recent errors. Recent is defined as happening in the last
2.5 hours. This is important as if the password-expiration syncing is
broken people's password expiration LDAP attribute will not get cleared
preventing them from logging in.

Notet that this test does not use kadmin or the Heimdal KDC service: it only
looks at the password expiration sync log file.

=head2 ad-krb5-sync [master only, not in local mode]

The C<ad-krb5-sync> tests the syncing of passwords from the Kerberos
master to the Active Directory via the C<krb5-sync> plugin. The test
changes the password of the principal C<krbtst_hXjxc5uiyvaqk> to a random
value and checks that the password was synced to the Active Directory by
attempting to get a Kerberos ticket for C<krbtst_hXjxc5uiyvaqk> in the
Active Directory _domain. Thus, for this test to work, the principal
C<krbtst_hXjxc5uiyvaqk> must exist in both the Kerberos stanford.edu realm
and in the Active Directory domain you are testing against.

This test can be run on the master only. It cannot be run in local mode as
running in local mode skips the creation of the AD password file that is
used to sync the password to the Active Directory.

You need to provide two extra parameters, one for the AD domain name and
one for the AD domain controller. Example:

    kdc-test --test ad-krb5-sync                  \
             --master kdc-master-qa.stanford.edu  \
             --principal service/regression-testing-qa  \
             --win-realm WINUAT.STANFORD.EDU      \
             --win-dc    winuatdc1.winuat.stanford.edu

=head2 replication (CRUD) [slave only, not in local mode]

This test is a hybrid: it B<must> be run on the SLAVE and
a principal that has the access described in the section I<AUTHENTICATION
AND AUTHORIZATION> above must be specified via the C<--principal> option.
Do B<not> use the C<--local> or C<--keytab> options with this test.

This test creates a test principal on the master, waits a bit, and then
verifies that the principal exists on slave. Example:

    kdc-test --master kerberos-qa1.stanford.edu \
             --slave kerberos-qa2.stanford.edu \
             --principal service/regression-testing-qa  \
             --test replication

=head2 authentication (CRUD) [master only, not in local mode]

This test creates a test principal and then does a kinit against it.
Use the B<--slave> option for this test. Example:

    kdc-test --master kerberos-qa1.stanford.edu \
             --slave kerberos-qa2.stanford.edu \
             --principal service/regression-testing-qa  \
             --test authentication


=head1 OPTIONS

=over 4

=item B<--master>

Specify which server is kerberos master. This is required for all tests.

=item B<--slave>

The KDC slave to perform authentication against. Only needed for the
"authentication" and "replication" tests.

=item B<--test>

A required option specifying which test to run. See the above section
B<TESTS> for a description of which tests are available.

=item B<--local>

Run the test in local mode (C<kadmin -l>).
See the above TESTS section to see which tests cannot be run
in local mode.

=item B<--verbose>

Display detailed processing information.

=item B<--principal>

This option specifies the principal to use when running the
tests.

=item B<--keytab>

The keytab file used when running tests. The default value is
F</etc/heimdal-kdc/regression-testing.keytab>.

=item B<--win-realm> and B<--win-dc>

These options are required when running the ad-krb5-sync test. See the
ad-krb5-sync test in the TESTS section above for an example.

=item B<--nagios-server> and B<--hostname>

Use these two options when running kdc-test in Nagios mode.  A passive
alert will be sent to the IP address indicated by C<--nagios-server>.  The
C<--hostname> option tells the Nagios server which hostname to associate
this passive monitor to.

=back


=head1 EXIT STATUS

The script will exit with 0 if the script completes and there were no
failures, 1 for any other reason.

=head1 NOTES

Later.


=head1 SEE ALSO

ldapsearch(1)

=head1 AUTHOR

Adam Lewenberg <adamhl@stanford.edu>

=head1 COPYRIGHT AND LICENSE

Copyright 2017 The Board of Trustees of the Leland Stanford Junior
University.  All rights reserved.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of Stanford University not be used in
advertising or publicity pertaining to distribution of the software
without specific, written prior permission.  Stanford University makes no
representations about the suitability of this software for any purpose.
It is provided "as is" without express or implied warranty.

THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=cut

#############################################################################
#############################################################################




1;
