From: blambert Date: Fri, 25 Apr 2014 06:57:11 +0000 (+0000) Subject: Support running user-defined actions on receipt of snmp traps. X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=70201ef177bc5bc25de6c00ea68edb50056c6db2;p=openbsd Support running user-defined actions on receipt of snmp traps. testing sthen@ much man page guidance jmc@ ok reyk@ --- diff --git a/usr.sbin/snmpd/Makefile b/usr.sbin/snmpd/Makefile index 753ab01ae1e..eec5451f50e 100644 --- a/usr.sbin/snmpd/Makefile +++ b/usr.sbin/snmpd/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.12 2014/04/14 12:55:10 blambert Exp $ +# $OpenBSD: Makefile,v 1.13 2014/04/25 06:57:11 blambert Exp $ PROG= snmpd MAN= snmpd.8 snmpd.conf.5 SRCS= parse.y ber.c log.c control.c snmpe.c \ mps.c trap.c mib.c smi.c kroute.c snmpd.c timer.c \ - pf.c proc.c usm.c agentx.c + pf.c proc.c usm.c agentx.c traphandler.c LDADD= -levent -lutil -lkvm -lcrypto DPADD= ${LIBEVENT} ${LIBUTIL} diff --git a/usr.sbin/snmpd/ber.c b/usr.sbin/snmpd/ber.c index 9ae498292ef..53e801a6514 100644 --- a/usr.sbin/snmpd/ber.c +++ b/usr.sbin/snmpd/ber.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ber.c,v 1.26 2014/04/14 12:55:10 blambert Exp $ */ +/* $OpenBSD: ber.c,v 1.27 2014/04/25 06:57:11 blambert Exp $ */ /* * Copyright (c) 2007, 2012 Reyk Floeter @@ -614,7 +614,7 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...) va_list ap; int *d, level = -1; unsigned long *t; - long long *i; + long long *i, l; void **ptr; size_t *len, ret = 0, n = strlen(fmt); char **s; @@ -640,6 +640,13 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...) goto fail; ret++; break; + case 'd': + d = va_arg(ap, int *); + if (ber_get_integer(ber, &l) == -1) + goto fail; + *d = l; + ret++; + break; case 'e': e = va_arg(ap, struct ber_element **); *e = ber; @@ -651,7 +658,6 @@ ber_scanf_elements(struct ber_element *ber, char *fmt, ...) goto fail; ret++; break; - case 'd': case 'i': i = va_arg(ap, long long *); if (ber_get_integer(ber, i) == -1) diff --git a/usr.sbin/snmpd/parse.y b/usr.sbin/snmpd/parse.y index fe5d7fe39fd..c2048c254ff 100644 --- a/usr.sbin/snmpd/parse.y +++ b/usr.sbin/snmpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.31 2014/04/24 08:51:08 blambert Exp $ */ +/* $OpenBSD: parse.y,v 1.32 2014/04/25 06:57:11 blambert Exp $ */ /* * Copyright (c) 2007, 2008, 2012 Reyk Floeter @@ -125,13 +125,13 @@ typedef struct { %token SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER %token READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER %token SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR DISABLED -%token SOCKET RESTRICTED AGENTX +%token SOCKET RESTRICTED AGENTX HANDLE DEFAULT %token STRING %token NUMBER %type hostcmn %type optwrite yesno seclevel socktype -%type objtype -%type oid hostoid +%type objtype cmd +%type oid hostoid trapoid %type auth %type enc @@ -243,6 +243,19 @@ main : LISTEN ON STRING { } host { hlist = NULL; } + | TRAP HANDLE hostcmn trapoid cmd { + struct trapcmd *cmd = $5.data; + + cmd->cmd_oid = $4; + + if (trapcmd_add(cmd) != 0) { + free($4); + free(cmd); + yyerror("duplicate oid"); + YYERROR; + } + conf->sc_traphandler = 1; + } | RTFILTER yesno { if ($2 == 1) conf->sc_rtfilter = ROUTE_FILTER(RTM_NEWADDR) | @@ -396,6 +409,19 @@ oid : STRING { } ; +trapoid : oid { $$ = $1; } + | DEFAULT { + struct ber_oid *sysoid; + if ((sysoid = + calloc(1, sizeof(*sysoid))) == NULL) { + yyerror("calloc"); + YYERROR; + } + ber_string2oid("1.3", sysoid); + $$ = sysoid; + } + ; + hostoid : /* empty */ { $$ = NULL; } | OBJECTID oid { $$ = $2; } ; @@ -488,6 +514,52 @@ socktype : RESTRICTED { $$ = SOCK_TYPE_RESTRICTED; } | /* nothing */ { $$ = 0; } ; +cmd : STRING { + struct trapcmd *cmd; + size_t span, limit; + char *pos, **args, **args2; + int nargs = 32; /* XXX */ + + if ((cmd = calloc(1, sizeof(*cmd))) == NULL || + (args = calloc(nargs, sizeof(char *))) == NULL) { + free(cmd); + free($1); + YYERROR; + } + + pos = $1; + limit = strlen($1); + + while ((span = strcspn(pos, " \t")) != 0 && + pos < $1 + limit) { + pos[span] = '\0'; + args[cmd->cmd_argc] = strdup(pos); + if (args[cmd->cmd_argc] == NULL) { + trapcmd_free(cmd); + free(args); + free($1); + YYERROR; + } + cmd->cmd_argc++; + if (cmd->cmd_argc >= nargs - 1) { + nargs *= 2; + args2 = calloc(nargs, sizeof(char *)); + if (args2 == NULL) { + trapcmd_free(cmd); + free(args); + free($1); + YYERROR; + } + args = args2; + } + pos += span + 1; + } + free($1); + cmd->cmd_argv = args; + $$.data = cmd; + } + ; + %% struct keywords { @@ -527,11 +599,13 @@ lookup(char *s) { "authkey", AUTHKEY }, { "community", COMMUNITY }, { "contact", CONTACT }, + { "default", DEFAULT }, { "description", DESCR }, { "disabled", DISABLED}, { "enc", ENC }, { "enckey", ENCKEY }, { "filter-routes", RTFILTER }, + { "handle", HANDLE }, { "include", INCLUDE }, { "integer", INTEGER }, { "listen", LISTEN }, diff --git a/usr.sbin/snmpd/snmpd.c b/usr.sbin/snmpd/snmpd.c index e36820f322e..dc80527a540 100644 --- a/usr.sbin/snmpd/snmpd.c +++ b/usr.sbin/snmpd/snmpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: snmpd.c,v 1.20 2014/04/21 19:47:27 reyk Exp $ */ +/* $OpenBSD: snmpd.c,v 1.21 2014/04/25 06:57:11 blambert Exp $ */ /* * Copyright (c) 2007, 2008, 2012 Reyk Floeter @@ -51,7 +51,9 @@ int check_child(pid_t, const char *); struct snmpd *snmpd_env; static struct privsep_proc procs[] = { - { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown } + { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown }, + { "traphandler", PROC_ALERT, snmpd_dispatch_traphandler, traphandler, + traphandler_shutdown } }; void diff --git a/usr.sbin/snmpd/snmpd.conf.5 b/usr.sbin/snmpd/snmpd.conf.5 index e03e000cf3a..2fbb7693a37 100644 --- a/usr.sbin/snmpd/snmpd.conf.5 +++ b/usr.sbin/snmpd/snmpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: snmpd.conf.5,v 1.30 2014/04/14 15:34:48 jmc Exp $ +.\" $OpenBSD: snmpd.conf.5,v 1.31 2014/04/25 06:57:11 blambert Exp $ .\" .\" Copyright (c) 2007, 2008, 2012 Reyk Floeter .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 14 2014 $ +.Dd $Mdocdate: April 25 2014 $ .Dt SNMPD.CONF 5 .Os .Sh NAME @@ -195,6 +195,23 @@ configured trap community. The default community is specified by the global .Ic trap community option. +.It Ic trap handle Ar oid Qq Ar command +Execute +.Ic command +upon receipt of an SNMP trap that begins with a prefix of +.Ic oid . +Alternately, the string +.Qq Ic default +may be used, in which case the prefix used is +.Ic 1.3 . +The invoked +.Ar command +will receive the following information about the trap on standard input, +one per line, in this order: +the resolved hostname of the host sending the trap, +the IP address of the host sending the trap, +and any variable bindings contained in the trap +(the OID followed by the value, separated by a single space). .El .Sh USER CONFIGURATION Users for the SNMP User-based Security Model (USM, RFC 3414) must be diff --git a/usr.sbin/snmpd/snmpd.h b/usr.sbin/snmpd/snmpd.h index 1355faf71e7..7383fea488a 100644 --- a/usr.sbin/snmpd/snmpd.h +++ b/usr.sbin/snmpd/snmpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: snmpd.h,v 1.51 2014/04/21 19:47:27 reyk Exp $ */ +/* $OpenBSD: snmpd.h,v 1.52 2014/04/25 06:57:11 blambert Exp $ */ /* * Copyright (c) 2007, 2008, 2012 Reyk Floeter @@ -75,7 +75,8 @@ enum imsg_type { IMSG_CTL_END, IMSG_CTL_NOTIFY, IMSG_CTL_VERBOSE, - IMSG_CTL_RELOAD + IMSG_CTL_RELOAD, + IMSG_ALERT }; struct imsgev { @@ -111,6 +112,7 @@ TAILQ_HEAD(control_socks, control_sock); enum privsep_procid { PROC_PARENT, /* Parent process and application interface */ PROC_SNMPE, /* SNMP engine */ + PROC_ALERT, /* SNMP trap receiver */ PROC_MAX }; @@ -515,10 +517,24 @@ struct snmpd { int sc_min_seclevel; int sc_readonly; + int sc_traphandler; struct privsep sc_ps; }; +struct trapcmd { + struct ber_oid *cmd_oid; + /* sideways return for intermediate lookups */ + struct trapcmd *cmd_maybe; + + int cmd_argc; + char **cmd_argv; + + RB_ENTRY(trapcmd) cmd_entry; +}; +RB_HEAD(trapcmd_tree, trapcmd); +extern struct trapcmd_tree trapcmd_tree; + /* control.c */ int control_init(struct privsep *, struct control_sock *); int control_listen(struct control_sock *); @@ -682,4 +698,13 @@ struct imsgbuf * struct imsgev * proc_iev(struct privsep *, enum privsep_procid, int); +/* traphandler.c */ +pid_t traphandler(struct privsep *, struct privsep_proc *); +void traphandler_shutdown(void); +int snmpd_dispatch_traphandler(int, struct privsep_proc *, struct imsg *); +void trapcmd_free(struct trapcmd *); +int trapcmd_add(struct trapcmd *); +struct trapcmd * + trapcmd_lookup(struct ber_oid *); + #endif /* _SNMPD_H */ diff --git a/usr.sbin/snmpd/traphandler.c b/usr.sbin/snmpd/traphandler.c new file mode 100644 index 00000000000..08be75451e8 --- /dev/null +++ b/usr.sbin/snmpd/traphandler.c @@ -0,0 +1,470 @@ +/* $OpenBSD: traphandler.c,v 1.1 2014/04/25 06:57:11 blambert Exp $ */ +/* + * Copyright (c) 2014 Bret Stephen Lambert + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ber.h" +#include "snmpd.h" +#include "mib.h" + +int trapsock; +struct event trapev; +char trap_path[PATH_MAX]; + +void traphandler_init(struct privsep *, struct privsep_proc *, void *arg); +int traphandler_dispatch_parent(int, struct privsep_proc *, struct imsg *); +int traphandler_bind(struct address *); +void traphandler_recvmsg(int, short, void *); +int traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *); +int traphandler_fork_handler(struct privsep_proc *, struct imsg *); +int traphandler_parse(char *, size_t, struct ber_element **, + struct ber_element **, u_int *, struct ber_oid *); +void traphandler_v1translate(struct ber_oid *, u_int, u_int); + +int trapcmd_cmp(struct trapcmd *, struct trapcmd *); +void trapcmd_exec(struct trapcmd *, struct sockaddr *, + struct ber_element *, char *, u_int); + +char *traphandler_hostname(struct sockaddr *, int); + +RB_PROTOTYPE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp) +RB_GENERATE(trapcmd_tree, trapcmd, cmd_entry, trapcmd_cmp) + +struct trapcmd_tree trapcmd_tree = RB_INITIALIZER(&trapcmd_tree); + +static struct privsep_proc procs[] = { + { "parent", PROC_PARENT, traphandler_dispatch_parent } +}; + +pid_t +traphandler(struct privsep *ps, struct privsep_proc *p) +{ + struct snmpd *env = ps->ps_env; + + if (env->sc_traphandler && + (trapsock = traphandler_bind(&env->sc_address)) == -1) + fatal("could not create trap listener socket"); + + return (proc_run(ps, p, procs, nitems(procs), traphandler_init, + NULL)); +} + +void +traphandler_init(struct privsep *ps, struct privsep_proc *p, void *arg) +{ + struct snmpd *env = ps->ps_env; + + if (!env->sc_traphandler) + return; + + /* listen for SNMP trap messages */ + event_set(&trapev, trapsock, EV_READ|EV_PERSIST, traphandler_recvmsg, + ps); + event_add(&trapev, NULL); +} + +int +traphandler_bind(struct address *addr) +{ + int s; + + if ((s = snmpd_socket_af(&addr->ss, htons(SNMPD_TRAPPORT))) == -1) + return (-1); + + if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) + goto bad; + + if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1) + goto bad; + + return (s); + bad: + close (s); + return (-1); +} + +void +traphandler_shutdown(void) +{ + event_del(&trapev); + close(trapsock); +} + +int +traphandler_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) +{ + switch (imsg->hdr.type) { + default: + break; + } + + return (-1); +} + +int +snmpd_dispatch_traphandler(int fd, struct privsep_proc *p, struct imsg *imsg) +{ + switch (imsg->hdr.type) { + case IMSG_ALERT: + return (traphandler_priv_recvmsg(p, imsg)); + default: + break; + } + + return (-1); +} + +void +traphandler_recvmsg(int fd, short events, void *arg) +{ + struct privsep *ps = arg; + char buf[8196]; + struct iovec iov[2]; + struct sockaddr_storage ss; + socklen_t slen; + ssize_t n; + struct ber_element *req, *iter; + struct ber_oid trapoid; + u_int uptime; + + slen = sizeof(ss); + if ((n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ss, + &slen)) == -1) + return; + + if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1) + goto done; + + iov[0].iov_base = &ss; + iov[0].iov_len = ss.ss_len; + iov[1].iov_base = buf; + iov[1].iov_len = n; + + /* Forward it to the parent process */ + if (proc_composev_imsg(ps, PROC_PARENT, -1, IMSG_ALERT, + -1, iov, 2) == -1) + goto done; + + done: + if (req != NULL) + ber_free_elements(req); + return; +} + +/* + * Validate received message + */ +int +traphandler_parse(char *buf, size_t n, struct ber_element **req, + struct ber_element **vbinds, u_int *uptime, struct ber_oid *trapoid) +{ + struct ber ber; + struct ber_element *elm; + u_int vers, gtype, etype; + + bzero(&ber, sizeof(ber)); + ber.fd = -1; + ber_set_application(&ber, smi_application); + ber_set_readbuf(&ber, buf, n); + + if ((*req = ber_read_elements(&ber, NULL)) == NULL) + goto done; + + if (ber_scanf_elements(*req, "{dSe", &vers, &elm) == -1) + goto done; + + switch (vers) { + case SNMP_V1: + if (ber_scanf_elements(elm, "{oSddd", + trapoid, >ype, &etype, uptime) == -1) + goto done; + traphandler_v1translate(trapoid, gtype, etype); + break; + + case SNMP_V2: + if (ber_scanf_elements(elm, "{SSSS{e}}", &elm) == -1 || + ber_scanf_elements(elm, "{SdS}{So}e", + uptime, trapoid, vbinds) == -1) + goto done; + break; + + default: + log_warnx("unsupported SNMP trap version '%d'", vers); + goto done; + } + + ber_free(&ber); + return (0); + + done: + ber_free(&ber); + if (*req) + ber_free_elements(*req); + *req = NULL; + return (-1); +} + +void +traphandler_v1translate(struct ber_oid *oid, u_int gtype, u_int etype) +{ + /* append 'specific trap' number to 'enterprise specific' traps */ + if (gtype >= 6) { + oid->bo_id[oid->bo_n] = 0; + oid->bo_id[oid->bo_n + 1] = etype; + oid->bo_n += 2; + } +} + +int +traphandler_priv_recvmsg(struct privsep_proc *p, struct imsg *imsg) +{ + ssize_t n; + pid_t pid; + + if ((n = IMSG_DATA_SIZE(imsg)) <= 0) + return (-1); /* XXX */ + + switch ((pid = fork())) { + case 0: + traphandler_fork_handler(p, imsg); + /* NOTREACHED */ + case -1: + log_warn("%s: couldn't fork traphandler", __func__); + return (0); + default: + log_debug("forked process %i to handle trap", pid); + return (0); + } + /* NOTREACHED */ +} + +int +traphandler_fork_handler(struct privsep_proc *p, struct imsg *imsg) +{ + char oidbuf[SNMP_MAX_OID_STRLEN]; + struct sockaddr *sa; + char *buf; + ssize_t n; + struct ber_element *req, *iter; + struct trapcmd *cmd; + struct ber_oid trapoid; + u_int uptime; + struct passwd *pw; + extern int debug; + + pw = p->p_ps->ps_pw; + + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + fatal("traphandler_fork_handler: cannot drop privileges"); + + closefrom(STDERR_FILENO + 1); + log_init(debug); + + n = IMSG_DATA_SIZE(imsg); + + sa = imsg->data; + n -= sa->sa_len; + buf = (char *)imsg->data + sa->sa_len; + + if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1) + fatalx("couldn't parse SNMP trap message"); + + smi_oid2string(&trapoid, oidbuf, sizeof(oidbuf), 0); + if ((cmd = trapcmd_lookup(&trapoid)) != NULL) + trapcmd_exec(cmd, sa, iter, oidbuf, uptime); + + if (req != NULL) + ber_free_elements(req); + + exit(0); +} + +void +trapcmd_exec(struct trapcmd *cmd, struct sockaddr *sa, + struct ber_element *iter, char *trapoid, u_int uptime) +{ + char oidbuf[SNMP_MAX_OID_STRLEN]; + struct ber_oid oid; + struct ber_element *elm; + int n, s[2], status = 0; + char *value, *host; + pid_t child = -1; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) { + log_warn("could not create pipe for OID '%s'", + smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0)); + return; + } + + switch (child = fork()) { + case 0: + dup2(s[1], STDIN_FILENO); + + close(s[0]); + close(s[1]); + + closefrom(STDERR_FILENO + 1); + + /* path to command is in argv[0], args follow */ + execve(cmd->cmd_argv[0], cmd->cmd_argv, NULL); + + /* this shouldn't happen */ + log_warn("could not exec trap command for OID '%s'", + smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0)); + _exit(1); + /* NOTREACHED */ + + case -1: + log_warn("could not fork trap command for OID '%s'", + smi_oid2string(cmd->cmd_oid, oidbuf, sizeof(oidbuf), 0)); + close(s[0]); + close(s[1]); + return; + } + + close(s[1]); + + host = traphandler_hostname(sa, 0); + if (dprintf(s[0], "%s\n", host) == -1) + goto out; + + host = traphandler_hostname(sa, 1); + if (dprintf(s[0], "%s\n", host) == -1) + goto out; + + if (dprintf(s[0], + "iso.org.dod.internet.mgmt.mib-2.system.sysUpTime.0 %u\n", + uptime) == -1) + goto out; + + if (dprintf(s[0], + "iso.org.dod.internet.snmpV2.snmpModules.snmpMIB.snmpMIBObjects." + "snmpTrap.snmpTrapOID.0 %s\n", trapoid) == -1) + goto out; + + for (; iter != NULL; iter = iter->be_next) { + if (ber_scanf_elements(iter, "{oe}", &oid, &elm) == -1) + goto out; + if ((value = smi_print_element(elm)) == NULL) + goto out; + smi_oid2string(&oid, oidbuf, sizeof(oidbuf), 0); + n = dprintf(s[0], "%s %s\n", oidbuf, value); + free(value); + if (n == -1) + goto out; + } + out: + close(s[0]); + waitpid(child, &status, 0); + + if (WIFSIGNALED(status)) { + log_warnx("child %i exited due to receipt of signal %i", + child, WTERMSIG(status)); + } else if (WEXITSTATUS(status) != 0) { + log_warnx("child %i exited with status %i", + child, WEXITSTATUS(status)); + } else { + log_debug("child %i finished", child); + } + close(s[1]); + + return; +} + +char * +traphandler_hostname(struct sockaddr *sa, int numeric) +{ + static char buf[NI_MAXHOST]; + int flag = 0; + + if (numeric) + flag = NI_NUMERICHOST; + + bzero(buf, sizeof(buf)); + if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, flag) != 0) + return ("Unknown"); + + return (buf); +} + +struct trapcmd * +trapcmd_lookup(struct ber_oid *oid) +{ + struct trapcmd key, *res; + + bzero(&key, sizeof(key)); + key.cmd_oid = oid; + + if ((res = RB_FIND(trapcmd_tree, &trapcmd_tree, &key)) == NULL) + res = key.cmd_maybe; + return (res); +} + +int +trapcmd_cmp(struct trapcmd *cmd1, struct trapcmd *cmd2) +{ + int ret; + + ret = ber_oid_cmp(cmd2->cmd_oid, cmd1->cmd_oid); + switch (ret) { + case 2: + /* cmd1 is a child of cmd2 */ + cmd1->cmd_maybe = cmd2; + return (1); + default: + return (ret); + } + /* NOTREACHED */ +} + +int +trapcmd_add(struct trapcmd *cmd) +{ + return (RB_INSERT(trapcmd_tree, &trapcmd_tree, cmd) != NULL); +} + +void +trapcmd_free(struct trapcmd *cmd) +{ + RB_REMOVE(trapcmd_tree, &trapcmd_tree, cmd); + free(cmd->cmd_argv); + free(cmd->cmd_oid); + free(cmd); +}