In order to get trap support back into relayd libagentx needs to get
notify support. Here's my interpretation on the matter. Relayd itself
still needs a proper NOTIFICATION-TYPE definition before that can be
implemented, but that shouldn't stop this diff.

I already have an OK rob@, but additional feedback welcome.

Since this has an additional manpage and library bump: any good/bad
timeframe for committing this?

martijn@

Index: Makefile
===================================================================
RCS file: /cvs/src/lib/libagentx/Makefile,v
retrieving revision 1.4
diff -u -p -r1.4 Makefile
--- Makefile    26 Oct 2020 15:57:03 -0000      1.4
+++ Makefile    4 Jan 2021 17:34:01 -0000
@@ -4,7 +4,7 @@ LIB=    agentx
 
 SRCS=  ax.c agentx.c agentx_log.c
 HDRS=  agentx.h
-MAN=   agentx.3
+MAN=   agentx.3 agentx_notify.3
 
 CFLAGS+=       -Wall
 CFLAGS+=       -Wstrict-prototypes -Wmissing-prototypes
Index: Symbols.list
===================================================================
RCS file: /cvs/src/lib/libagentx/Symbols.list,v
retrieving revision 1.3
diff -u -p -r1.3 Symbols.list
--- Symbols.list        27 Oct 2020 18:24:01 -0000      1.3
+++ Symbols.list        4 Jan 2021 17:34:01 -0000
@@ -29,6 +29,23 @@ agentx_index_oid_dynamic
 agentx_index_noid_dynamic
 agentx_index_ipaddress_dynamic
 agentx_index_free
+agentx_notify
+agentx_notify_counter32
+agentx_notify_counter64
+agentx_notify_gauge32
+agentx_notify_index
+agentx_notify_integer
+agentx_notify_ipaddress
+agentx_notify_nstring
+agentx_notify_null
+agentx_notify_object
+agentx_notify_oid
+agentx_notify_opaque
+agentx_notify_printf
+agentx_notify_send
+agentx_notify_string
+agentx_notify_timeticks
+agentx_notify_unsigned32
 agentx_object
 agentx_object_free
 agentx_varbind_integer
Index: agentx.3
===================================================================
RCS file: /cvs/src/lib/libagentx/agentx.3,v
retrieving revision 1.5
diff -u -p -r1.5 agentx.3
--- agentx.3    3 Dec 2020 22:47:21 -0000       1.5
+++ agentx.3    4 Jan 2021 17:34:01 -0000
@@ -621,6 +621,7 @@ in timeticks.
 .El
 .Sh SEE ALSO
 .Xr snmp 1 ,
+.Xr agentx_notify 3 ,
 .Xr snmpd 8
 .Sh STANDARDS
 .Rs
Index: agentx.c
===================================================================
RCS file: /cvs/src/lib/libagentx/agentx.c,v
retrieving revision 1.8
diff -u -p -r1.8 agentx.c
--- agentx.c    27 Oct 2020 18:24:01 -0000      1.8
+++ agentx.c    4 Jan 2021 17:34:01 -0000
@@ -87,6 +87,18 @@ struct agentx_object {
        TAILQ_ENTRY(agentx_object) axo_axr_objects;
 };
 
+struct agentx_notify {
+       struct agentx_context *axn_axc;
+       struct ax_varbind *axn_vb;
+       struct ax_oid axn_trapoid;
+       uint32_t axn_uptime;
+       int axn_sent;
+       size_t axn_nvb;
+       size_t axn_vbsize;
+       int axn_fail;
+       TAILQ_ENTRY(agentx_notify) axn_axc_notifications;
+};
+
 struct agentx_varbind {
        struct agentx_get *axv_axg;
        struct agentx_object *axv_axo;
@@ -160,6 +172,9 @@ static void agentx_object_free_finalize(
 static void agentx_object_reset(struct agentx_object *);
 static int agentx_object_cmp(struct agentx_object *,
     struct agentx_object *);
+static int agentx_notify_add(struct agentx_notify *, const uint32_t [], 
size_t);
+static int agentx_notify_finalize(struct ax_pdu *, void *);
+static void agentx_notify_free(struct agentx_notify *);
 static void agentx_get_start(struct agentx_context *,
     struct ax_pdu *);
 static void agentx_get_finalize(struct agentx_get *);
@@ -596,6 +611,7 @@ agentx_context(struct agentx_session *ax
        axc->axc_dstate = AX_DSTATE_OPEN;
        TAILQ_INIT(&(axc->axc_agentcaps));
        TAILQ_INIT(&(axc->axc_regions));
+       TAILQ_INIT(&(axc->axc_notifications));
 
        TAILQ_INSERT_HEAD(&(axs->axs_contexts), axc, axc_axs_contexts);
 
@@ -690,6 +706,7 @@ agentx_context_free(struct agentx_contex
 {
        struct agentx_agentcaps *axa, *tsaa;
        struct agentx_region *axr, *tsar;
+       struct agentx_notify *axn, *taxn;
 
        if (axc == NULL)
                return;
@@ -700,6 +717,14 @@ agentx_context_free(struct agentx_contex
 #endif
        axc->axc_dstate = AX_DSTATE_CLOSE;
 
+       TAILQ_FOREACH_SAFE(axn, &(axc->axc_notifications),
+           axn_axc_notifications, taxn) {
+               axn->axn_axc = NULL;
+               TAILQ_REMOVE(&(axc->axc_notifications), axn,
+                   axn_axc_notifications);
+               if (axn->axn_sent)
+                       agentx_notify_free(axn);
+       }
        TAILQ_FOREACH_SAFE(axa, &(axc->axc_agentcaps), axa_axc_agentcaps,
            tsaa) {
                if (axa->axa_dstate != AX_DSTATE_CLOSE)
@@ -720,6 +745,9 @@ agentx_context_free_finalize(struct agen
        if (axc->axc_dstate != AX_DSTATE_CLOSE)
                agentx_log_axc_fatalx(axc, "%s: unexpected context free",
                    __func__);
+       if (!TAILQ_EMPTY(&(axc->axc_notifications)))
+               agentx_log_axc_fatalx(axc, "%s: notifications not empty",
+                   __func__);
 #endif
        if (!TAILQ_EMPTY(&(axc->axc_regions)) ||
            !TAILQ_EMPTY(&(axc->axc_agentcaps)))
@@ -734,11 +762,20 @@ agentx_context_reset(struct agentx_conte
 {
        struct agentx_agentcaps *axa, *tsaa;
        struct agentx_region *axr, *tsar;
+       struct agentx_notify *axn, *taxn;
 
        axc->axc_cstate = AX_CSTATE_CLOSE;
        axc->axc_sysuptimespec.tv_sec = 0;
        axc->axc_sysuptimespec.tv_nsec = 0;
 
+       TAILQ_FOREACH_SAFE(axn, &(axc->axc_notifications),
+           axn_axc_notifications, taxn) {
+               axn->axn_axc = NULL;
+               TAILQ_REMOVE(&(axc->axc_notifications), axn,
+                   axn_axc_notifications);
+               if (axn->axn_sent)
+                       agentx_notify_free(axn);
+       }
        TAILQ_FOREACH_SAFE(axa, &(axc->axc_agentcaps), axa_axc_agentcaps, tsaa)
                agentx_agentcaps_reset(axa);
        TAILQ_FOREACH_SAFE(axr, &(axc->axc_regions), axr_axc_regions, tsar)
@@ -2473,6 +2510,379 @@ agentx_object_implied(struct agentx_obje
            __func__);
 #endif
        return 0;
+}
+
+struct agentx_notify *
+agentx_notify(struct agentx_context *axc, const uint32_t oid[], size_t oidlen)
+{
+       struct agentx_notify *axn;
+       size_t i;
+
+       if (axc->axc_dstate == AX_DSTATE_CLOSE)
+               agentx_log_axc_fatalx(axc, "%s: use after free", __func__);
+
+       if ((axn = calloc(1, sizeof(*axn))) == NULL)
+               return NULL;
+       axn->axn_axc = axc;
+       TAILQ_INSERT_TAIL(&(axc->axc_notifications), axn,
+           axn_axc_notifications);
+
+       for (i = 0; i < oidlen; i++)
+               axn->axn_trapoid.aoi_id[i] = oid[i];
+       axn->axn_trapoid.aoi_idlen = oidlen;
+       if ((axn->axn_uptime = agentx_context_uptime(axc)) == 0) {
+               agentx_notify_free(axn);
+               errno = ENOTCONN;
+               return NULL;
+       }
+
+       agentx_notify_timeticks(axn, AGENTX_OID(AGENTX_SYSUPTIME, 0),
+           axn->axn_uptime);
+       agentx_notify_oid(axn, AGENTX_OID(AGENTX_SNMPTRAPOID, 0), oid, oidlen);
+
+       if (axn->axn_fail) {
+               agentx_notify_free(axn);
+               return NULL;
+       }
+       return axn;
+}
+
+void
+agentx_notify_integer(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, int32_t value)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_INTEGER;
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_int32 = value;
+}
+
+void
+agentx_notify_string(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, const char *value)
+{
+       agentx_notify_nstring(axn, oid, oidlen, value, strlen(value));
+}
+
+void
+agentx_notify_nstring(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, const char *value, size_t slen)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       if ((axn->axn_vb[axn->axn_nvb].avb_data.avb_ostring.aos_string =
+           malloc(slen)) == NULL) {
+               agentx_log_axc_warnx(axn->axn_axc, "Failed to bind string to "
+                   "notify");
+               axn->axn_fail = 1;
+               return;
+       }
+
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_OCTETSTRING;
+       memcpy(axn->axn_vb[axn->axn_nvb].avb_data.avb_ostring.aos_string, value,
+           slen);
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_ostring.aos_slen = slen;
+}
+
+void
+agentx_notify_printf(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, const char *fmt, ...)
+{
+       va_list ap;
+       int r;
+
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       va_start(ap, fmt);
+       r = vasprintf((char **)
+           &(axn->axn_vb[axn->axn_nvb].avb_data.avb_ostring.aos_string),
+           fmt, ap);
+       va_end(ap);
+
+       if (r == -1) {
+               agentx_log_axc_warn(axn->axn_axc, "Failed to bind string to "
+                   "notify");
+               axn->axn_vb[axn->axn_nvb].avb_data.avb_ostring.aos_string =
+                   NULL;
+               axn->axn_fail = 1;
+               return;
+       }
+
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_OCTETSTRING;
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_ostring.aos_slen = r;
+}
+
+void
+agentx_notify_null(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       axn->axn_vb[axn->axn_nvb++].avb_type = AX_DATA_TYPE_NULL;
+}
+
+void
+agentx_notify_oid(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, const uint32_t value[], size_t vlen)
+{
+       size_t i;
+
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_OID;
+       for (i = 0; i < vlen; i++)
+               axn->axn_vb[axn->axn_nvb].avb_data.avb_oid.aoi_id[i] = value[i];
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_oid.aoi_idlen = vlen;
+}
+
+void
+agentx_notify_object(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, struct agentx_object *axo)
+{
+       agentx_notify_oid(axn, oid, oidlen, axo->axo_oid.aoi_id,
+           axo->axo_oid.aoi_idlen);
+}
+
+void
+agentx_notify_index(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, struct agentx_index *axi)
+{
+       agentx_notify_oid(axn, oid, oidlen, axi->axi_vb.avb_oid.aoi_id,
+           axi->axi_vb.avb_oid.aoi_idlen);
+}
+
+void
+agentx_notify_ipaddress(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, const struct in_addr *value)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       if ((axn->axn_vb[axn->axn_nvb].avb_data.avb_ostring.aos_string =
+           malloc(4)) == NULL) {
+               agentx_log_axc_warn(axn->axn_axc, "Couldn't bind ipaddress  to"
+                   "notify");
+               axn->axn_fail = 1;
+               return;
+       }
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_IPADDRESS;
+       memcpy(axn->axn_vb[axn->axn_nvb].avb_data.avb_ostring.aos_string,
+           value, 4);
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_ostring.aos_slen = 4;
+}
+
+void
+agentx_notify_counter32(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, uint32_t value)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_COUNTER32;
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_uint32 = value;
+}
+
+void
+agentx_notify_gauge32(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, uint32_t value)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_GAUGE32;
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_uint32 = value;
+}
+
+void
+agentx_notify_unsigned32(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, uint32_t value)
+{
+       agentx_notify_gauge32(axn, oid, oidlen, value);
+}
+
+void
+agentx_notify_timeticks(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, uint32_t value)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_TIMETICKS;
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_uint32 = value;
+}
+
+void
+agentx_notify_opaque(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, const char *value, size_t slen)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       if ((axn->axn_vb[axn->axn_nvb].avb_data.avb_ostring.aos_string =
+           malloc(slen)) == NULL) {
+               agentx_log_axc_warn(axn->axn_axc, "Failed to bind opaque to "
+                   "notify");
+               axn->axn_fail = 1;
+               return;
+       }
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_OPAQUE;
+       memcpy(axn->axn_vb[axn->axn_nvb].avb_data.avb_ostring.aos_string, value,
+           slen);
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_ostring.aos_slen = slen;
+}
+
+void
+agentx_notify_counter64(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen, uint64_t value)
+{
+       if (agentx_notify_add(axn, oid, oidlen) == -1)
+               return;
+
+       axn->axn_vb[axn->axn_nvb].avb_type = AX_DATA_TYPE_COUNTER64;
+       axn->axn_vb[axn->axn_nvb++].avb_data.avb_uint64 = value;
+}
+
+void
+agentx_notify_send(struct agentx_notify *axn)
+{
+       struct agentx_context *axc = axn->axn_axc;
+       struct agentx_session *axs;
+       struct agentx *ax;
+       uint32_t packetid;
+       char *logmsg = NULL;
+       size_t i;
+       int fail = 0;
+
+       if (axn->axn_fail || axc == NULL) {
+               agentx_log_axc_warn(axc, "Failed to send notify: %s: %s",
+                   ax_oid2string(&(axn->axn_trapoid)),
+                   axn->axn_fail ? "Prior error" : "Connection reset");
+               agentx_notify_free(axn);
+               return;
+       }
+       axs = axc->axc_axs;
+       ax = axs->axs_ax;
+
+       if ((packetid = ax_notify(ax->ax_ax, axs->axs_id,
+           AGENTX_CONTEXT_CTX(axc), axn->axn_vb, axn->axn_nvb)) == 0) {
+               agentx_log_axc_warn(axc, "Couldn't generate %s: %s",
+                   ax_oid2string(&(axn->axn_trapoid)),
+                   ax_pdutype2string(AX_PDU_TYPE_NOTIFY));
+               agentx_notify_free(axn);
+               return;
+       }
+
+       for (i = 2; i < axn->axn_nvb; i++) {
+               if (i == 2)
+                       fail |= agentx_strcat(&logmsg, " {");
+               else
+                       fail |= agentx_strcat(&logmsg, ",{");
+               fail |= agentx_strcat(&logmsg,
+                   ax_varbind2string(&(axn->axn_vb[i])));
+               fail |= agentx_strcat(&logmsg, "}");
+               if (fail)
+                       break;
+       }
+       if (fail)
+               agentx_log_axc_warn(axc, "Failed to generate notify log");
+       else {
+               agentx_log_axc_debug(axc, "Notify %s(%u):%s",
+                   ax_oid2string(&(axn->axn_trapoid)), axn->axn_uptime,
+                   logmsg);
+       }
+       free(logmsg);
+       agentx_log_axc_info(axc, "Notify %s(%u): sent",
+           ax_oid2string(&(axn->axn_trapoid)), axn->axn_uptime);
+       axn->axn_sent = 1;
+       if (agentx_request(ax, packetid, agentx_notify_finalize, axn) == -1)
+               agentx_notify_free(axn);
+}
+
+static int
+agentx_notify_add(struct agentx_notify *axn, const uint32_t oid[],
+    size_t oidlen)
+{
+       struct ax_varbind *vb;
+       size_t i;
+
+       if (oidlen < 1) {
+#ifdef AX_DEBUG
+               agentx_log_axc_fatalx(axn->axn_axc, "%s: oidlen == 0",
+                   __func__);
+#else
+               agentx_log_axc_warnx(axn->axn_axc, "%s: oidlen == 0", __func__);
+               axn->axn_fail = 1;
+               return -1;
+#endif
+       }
+       if (oidlen > AGENTX_OID_MAX_LEN) {
+#ifdef AX_DEBUG
+               agentx_log_axc_fatalx(axn->axn_axc, "%s: oidlen > %d", __func__,
+                   AGENTX_OID_MAX_LEN);
+#else
+               agentx_log_axc_warnx(axn->axn_axc, "%s: oidlen > %d", __func__,
+                   AGENTX_OID_MAX_LEN);
+               axn->axn_fail = 1;
+               return -1;
+#endif
+       }
+
+       if (axn->axn_fail)
+               return -1;
+
+       if (axn->axn_nvb == axn->axn_vbsize) {
+               if ((vb = recallocarray(axn->axn_vb, axn->axn_vbsize,
+                   axn->axn_vbsize + 5, sizeof (*vb))) == NULL) {
+                       agentx_log_axc_warnx(axn->axn_axc, "Failed to add "
+                           "varbind to notify");
+                       axn->axn_fail = 1;
+                       return -1;
+               }
+               axn->axn_vb = vb;
+               axn->axn_vbsize += 5;
+       }
+
+       for (i = 0; i < oidlen; i++)
+               axn->axn_vb[axn->axn_nvb].avb_oid.aoi_id[i] = oid[i];
+       axn->axn_vb[axn->axn_nvb].avb_oid.aoi_idlen = oidlen;
+
+       return 0;
+}
+
+static int
+agentx_notify_finalize(struct ax_pdu *pdu, void *cookie)
+{
+       struct agentx_notify *axn = cookie;
+       struct agentx_context *axc = axn->axn_axc;
+
+       if (pdu->ap_payload.ap_response.ap_error != AX_PDU_ERROR_NOERROR)
+               agentx_log_axc_warnx(axc, "Failed to send notify %s(%u): %s",
+                   ax_oid2string(&(axn->axn_trapoid)), axn->axn_uptime,
+                   ax_error2string(pdu->ap_payload.ap_response.ap_error));
+       else
+               agentx_log_axc_info(axc, "notify sent %s(%u)",
+                   ax_oid2string(&(axn->axn_trapoid)), axn->axn_uptime);
+
+       agentx_notify_free(axn);
+       return 0;
+}
+
+static void
+agentx_notify_free(struct agentx_notify *axn)
+{
+       size_t i;
+
+       for (i = 0; i < axn->axn_nvb; i++)
+               ax_varbind_free(&(axn->axn_vb[i]));
+       free(axn->axn_vb);
+       if (axn->axn_axc != NULL)
+               TAILQ_REMOVE(&(axn->axn_axc->axc_notifications), axn,
+                   axn_axc_notifications);
+       free(axn);
 }
 
 static void
Index: agentx.h
===================================================================
RCS file: /cvs/src/lib/libagentx/agentx.h,v
retrieving revision 1.5
diff -u -p -r1.5 agentx.h
--- agentx.h    27 Oct 2020 18:24:01 -0000      1.5
+++ agentx.h    4 Jan 2021 17:34:01 -0000
@@ -27,6 +27,7 @@ struct agentx_agentcaps;
 struct agentx_region;
 struct agentx_index;
 struct agentx_object;
+struct agentx_notify;
 struct agentx_varbind;
 
 enum agentx_request_type {
@@ -40,6 +41,8 @@ enum agentx_request_type {
 #define AGENTX_OID_INDEX_MAX_LEN 10
 #define AGENTX_MIB2 1, 3, 6, 1, 2, 1
 #define AGENTX_ENTERPRISES 1, 3, 6, 1, 4, 1
+#define AGENTX_SYSUPTIME AGENTX_MIB2, 1, 3
+#define AGENTX_SNMPTRAPOID 1, 3, 6, 1, 6, 3, 1, 1, 4, 1
 #define AGENTX_OID(...) (uint32_t []) { __VA_ARGS__ }, \
     (sizeof((uint32_t []) { __VA_ARGS__ }) / sizeof(uint32_t))
 
@@ -147,3 +150,36 @@ void agentx_varbind_set_index_object(str
     struct agentx_index *, struct agentx_object *);
 void agentx_varbind_set_index_ipaddress(struct agentx_varbind *,
     struct agentx_index *, const struct in_addr *);
+
+struct agentx_notify *agentx_notify(struct agentx_context *, const uint32_t[],
+    size_t);
+void agentx_notify_integer(struct agentx_notify *, const uint32_t[], size_t,
+    int32_t);
+void agentx_notify_string(struct agentx_notify *, const uint32_t[], size_t,
+    const char *);
+void agentx_notify_nstring(struct agentx_notify *, const uint32_t[], size_t,
+    const char *, size_t);
+void agentx_notify_printf(struct agentx_notify *, const uint32_t[], size_t,
+    const char *, ...) __attribute__((__format__ (printf, 4, 5)));
+void agentx_notify_null(struct agentx_notify *, const uint32_t[], size_t);
+void agentx_notify_oid(struct agentx_notify *, const uint32_t[], size_t,
+    const uint32_t[], size_t);
+void agentx_notify_object(struct agentx_notify *, const uint32_t[], size_t,
+    struct agentx_object *);
+void agentx_notify_index(struct agentx_notify *, const uint32_t[], size_t,
+    struct agentx_index *);
+void agentx_notify_ipaddress(struct agentx_notify *, const uint32_t[], size_t,
+    const struct in_addr *);
+void agentx_notify_counter32(struct agentx_notify *, const uint32_t[], size_t,
+    uint32_t);
+void agentx_notify_gauge32(struct agentx_notify *, const uint32_t[], size_t,
+    uint32_t);
+void agentx_notify_unsigned32(struct agentx_notify *, const uint32_t[], size_t,
+    uint32_t);
+void agentx_notify_timeticks(struct agentx_notify *, const uint32_t[], size_t,
+    uint32_t);
+void agentx_notify_opaque(struct agentx_notify *, const uint32_t[], size_t,
+    const char *, size_t);
+void agentx_notify_counter64(struct agentx_notify *, const uint32_t[], size_t,
+    uint64_t);
+void agentx_notify_send(struct agentx_notify *);
Index: agentx_internal.h
===================================================================
RCS file: /cvs/src/lib/libagentx/agentx_internal.h,v
retrieving revision 1.2
diff -u -p -r1.2 agentx_internal.h
--- agentx_internal.h   26 Oct 2020 16:02:16 -0000      1.2
+++ agentx_internal.h   4 Jan 2021 17:34:01 -0000
@@ -67,6 +67,7 @@ struct agentx_context {
        enum agentx_dstate axc_dstate;
        TAILQ_HEAD(, agentx_agentcaps) axc_agentcaps;
        TAILQ_HEAD(, agentx_region) axc_regions;
+       TAILQ_HEAD(, agentx_notify) axc_notifications;
        RB_HEAD(axc_objects, agentx_object) axc_objects;
        TAILQ_ENTRY(agentx_context) axc_axs_contexts;
 };
Index: agentx_notify.3
===================================================================
RCS file: agentx_notify.3
diff -N agentx_notify.3
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ agentx_notify.3     4 Jan 2021 17:34:01 -0000
@@ -0,0 +1,177 @@
+.\" $OpenBSD: agentx.3,v 1.5 2020/12/03 22:47:21 jmc Exp $
+.\"
+.\" Copyright (c) 2020 Martijn van Duren <mart...@openbsd.org>
+.\"
+.\" 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.
+.\"
+.Dd $Mdocdate: December 3 2020 $
+.Dt AGENTX_NOTIFY 3
+.Os
+.Sh NAME
+.Nm agentx_notify
+.Nm agentx_notify_integer
+.Nm agentx_notify_string
+.Nm agentx_notify_nstring
+.Nm agentx_notify_printf
+.Nm agentx_notify_null
+.Nm agentx_notify_oid
+.Nm agentx_notify_object
+.Nm agentx_notify_index
+.Nm agentx_notify_ipaddress
+.Nm agentx_notify_counter32
+.Nm agentx_notify_gauge32
+.Nm agentx_notify_unsigned32
+.Nm agentx_notify_timeticks
+.Nm agentx_notify_opaque
+.Nm agentx_notify_counter64
+.Nm agentx_notify_send
+.Sh SYNOPSIS
+.In agentx.h
+.Ft struct agentx_notify *
+.Fo agentx_notify
+.Fa "struct agentx_context *axc" "const uint32_t trapoid[]" "size_t oidlen"
+.Fc
+.Ft void
+.Fo agentx_notify_integer
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "int32_t value"
+.Fc
+.Ft void
+.Fo agentx_notify_string
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "const char *string"
+.Fc
+.Ft void
+.Fo agentx_notify_nstring
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "const char *string" "size_t slen"
+.Fc
+.Ft void
+.Fo agentx_notify_printf
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "const char *fmt" ...
+.Fc
+.Ft void
+.Fo agentx_notify_null
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fc
+.Ft void
+.Fo agentx_notify_oid
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "const uint32_t oidvalue[]" "size_t oidvlen"
+.Fc
+.Ft void
+.Fo agentx_notify_object
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "struct agentx_object *axo"
+.Fc
+.Ft void
+.Fo agentx_notify_index
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "struct agentx_index *axi"
+.Fc
+.Ft void
+.Fo agentx_notify_ipaddress
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "const struct in_addr *addr"
+.Fc
+.Ft void
+.Fo agentx_notify_counter32
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "uint32_t value"
+.Fc
+.Ft void
+.Fo agentx_notify_gauge32
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "uint32_t value"
+.Fc
+.Ft void
+.Fo agentx_notify_unsigned32
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "uint32_t value"
+.Fc
+.Ft void
+.Fo agentx_notify_timeticks
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "uint32_t value"
+.Fc
+.Ft void
+.Fo agentx_notify_opaque
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "const char *string" "size_t slen"
+.Fc
+.Ft void
+.Fo agentx_notify_counter64
+.Fa "struct agentx_notify *axn" "const uint32_t oid[]" "size_t oidlen"
+.Fa "uint64_t value"
+.Fc
+.Ft void
+.Fn agentx_notify_send "struct agentx_notify *axn"
+.Sh DESCRIPTION
+The
+.Nm agentx_notify
+interface allows to send notify messages or
+.Dq SNMP trap
+messages to the agentx master.
+To send a message a notify context needs to be set up on an context initiated
+with
+.Xr agentx_context 3 .
+The
+.Fa trapoid
+and
+.Fa oidlen
+combination identifies the trap to be sent.
+The sysuptime parameter is set internally.
+.Pp
+Additional objects can be added to the notify by calling agentx_notify_<type>.
+These functions must be called in the order specified by the MIB and work
+similar to the agentx_varbind_<type> functions.
+.Pp
+Once the notify is complete it can be send out by
+.Fn agentx_notify_send .
+This function also frees all allocated memory related to this notify.
+.Sh RETURN VALUES
+Upon succesful completion,
+.Fn agentx_notify
+returns a
+.Vt "struct agentx_notify"
+pointer.
+Otherwise,
+.Dv NULL
+is returned and the global variable
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er ENOTCONN
+The
+.Vt "struct agentx"
+pointer to which
+.Fa axc
+belongs is not connected.
+.El
+.Pp
+.Fn agentx_notify
+may also fail and set
+.Va errno
+for any of the errors specified for the routine
+.Xr malloc 3 .
+.Sh SEE ALSO
+.Xr agentx 3
+.Sh HISTORY
+The
+.Nm agentx_notify
+API first appeared in
+.Ox 6.9 .
+.Sh AUTHORS
+.An Martijn van Duren Aq Mt mart...@openbsd.org
Index: ax.c
===================================================================
RCS file: /cvs/src/lib/libagentx/ax.c,v
retrieving revision 1.7
diff -u -p -r1.7 ax.c
--- ax.c        2 Jan 2021 01:06:31 -0000       1.7
+++ ax.c        4 Jan 2021 17:34:01 -0000
@@ -551,6 +551,19 @@ ax_unregister(struct ax *ax, uint32_t se
        return ax_pdu_queue(ax);
 }
 
+uint32_t
+ax_notify(struct ax *ax, uint32_t sessionid, struct ax_ostring *context,
+    struct ax_varbind *vblist, size_t nvb)
+{
+       if (ax_pdu_header(ax, AX_PDU_TYPE_NOTIFY, 0, sessionid, 0, 0,
+           context) == -1)
+               return 0;
+
+       if (ax_pdu_add_varbindlist(ax, vblist, nvb) == -1)
+               return 0;
+       return ax_pdu_queue(ax);
+}
+
 int
 ax_response(struct ax *ax, uint32_t sessionid, uint32_t transactionid,
     uint32_t packetid, struct ax_ostring *context, uint32_t sysuptime,
Index: ax.h
===================================================================
RCS file: /cvs/src/lib/libagentx/ax.h,v
retrieving revision 1.4
diff -u -p -r1.4 ax.h
--- ax.h        2 Jan 2021 01:06:31 -0000       1.4
+++ ax.h        4 Jan 2021 17:34:01 -0000
@@ -219,6 +219,8 @@ uint32_t ax_register(struct ax *, uint8_
     uint32_t);
 uint32_t ax_unregister(struct ax *, uint32_t, struct ax_ostring *,
     uint8_t, uint8_t, struct ax_oid *, uint32_t);
+uint32_t ax_notify(struct ax *, uint32_t, struct ax_ostring *,
+    struct ax_varbind *, size_t);
 int ax_response(struct ax *, uint32_t, uint32_t, uint32_t,
     struct ax_ostring *, uint32_t, uint16_t, uint16_t,
     struct ax_varbind *, size_t);
Index: shlib_version
===================================================================
RCS file: /cvs/src/lib/libagentx/shlib_version,v
retrieving revision 1.2
diff -u -p -r1.2 shlib_version
--- shlib_version       26 Oct 2020 15:45:56 -0000      1.2
+++ shlib_version       4 Jan 2021 17:34:01 -0000
@@ -1,2 +1,2 @@
 major=1
-minor=0
+minor=1


Reply via email to