The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl
use strict;
use NetSNMP::agent;
use NetSNMP::ASN;
use POE qw< Component::NetSNMP::agent >;
use Test::More;


# try to determine the AgentX socket
my $socket = "/var/agentx/master";

PATH:
for my $path (qw< /etc/snmp/snmpd.conf /etc/snmp/snmpd.local.conf >) {
    open my $fh, "<", $path or next;
    my @lines = grep { /^\s*agentXSocket/i } <$fh>;

    for my $line (@lines) {
        $socket = $1, last PATH if $line =~ /agentXSocket\s+(.+)/
    }
}

# determine the transport specification and address of the socket
my ($spec, $addr);

if ($socket =~ m:^/:) {
    ($spec, $addr) = ("unix", $socket);
}
else {
    ($spec, $addr) = split /:/, $socket;
}

# check if we can access the socket
if ($spec eq "unix") {
    my @stat = stat($addr);
    plan skip_all => "because this script is running under an account "
        . "which can't access AgentX socket $addr"
        if not @stat or $< != $stat[4];
}


plan tests => 7;

use constant {
    AGENT_OID   => ".1.3.6.1.4.1.32272",
};

my $agent = eval { POE::Component::NetSNMP::agent->spawn(
    Alias   => "snmp_agent",
    AgentX  => 1,
)};
is($@, "", "spawn a session");

my $r = eval { $agent->register(AGENT_OID.".1", \&agent_handler_as_coderef) };
is($@, "", "registering an OID with a coderef");

POE::Session->create(
    inline_states => {
        _start => sub {
            pass "registering an OID with a POE event";
            $_[KERNEL]->post("snmp_agent", register => AGENT_OID.".2", "handler");

            $_[KERNEL]->delay_add("snmpget", 1, AGENT_OID.".1");
            $_[KERNEL]->delay_add("snmpget", 2, AGENT_OID.".2");

            $_[KERNEL]->delay("stop", 3);
        },
        stop    => sub { $_[KERNEL]->stop },
        reaper  => sub {},
        handler => \&agent_handler_as_event,
        snmpget => sub {
            my $pid = fork;

            if ($pid == 0) {
                exec qw< snmpget -v2c -c public localhost >, $_[ARG0];
                exit
            }

            $_[KERNEL]->sig_child($pid, "reaper");
        },
    },
);

POE::Kernel->run;
exit;


sub agent_handler_as_coderef {
    my ($kernel, $heap, $args) = @_[ KERNEL, HEAP, ARG1 ];
    my ($handler, $reg_info, $request_info, $requests) = @$args;

    pass "agent handler (coderef)";

    # the rest of the code works like a classic NetSNMP::agent callback
    my $mode = $request_info->getMode;

    for (my $request = $requests; $request; $request = $request->next) {
        if ($mode == MODE_GET) {
            pass "'get' request";
            $request->setValue(ASN_OCTET_STR, "ichi");
        }
        else {
            fail "unexpected request"
        }
    }
}


sub agent_handler_as_event {
    my ($kernel, $heap, $args) = @_[ KERNEL, HEAP, ARG1 ];
    my ($handler, $reg_info, $request_info, $requests) = @$args;

    pass "agent handler (event)";

    # the rest of the code works like a classic NetSNMP::agent callback
    my $mode = $request_info->getMode;

    for (my $request = $requests; $request; $request = $request->next) {
        if ($mode == MODE_GET) {
            pass "'get' request";
            $request->setValue(ASN_OCTET_STR, "ni");
        }
        else {
            fail "unexpected request"
        }
    }
}