| /** @example delayed_instance.c |
| * This example implements the netSnmpExampleSleeper object. |
| * |
| * It demonstrates 2 things: |
| * |
| * - The instance helper, which is a way of registering an exact OID |
| * such that GENEXT requests are handled entirely by the helper. |
| * |
| * - how to implement objects which normally would block the agent as |
| * it waits for external events in such a way that the agent can |
| * continue responding to other requests while this implementation |
| * waits. |
| * |
| * - Added bonus: normally the nsTransactionTable is empty, since |
| * there aren't any outstanding requests generally. When accessed, |
| * this module will create some however. Try setting |
| * netSnmpExampleSleeper.0 to 10 and then accessing it (use |
| * "snmpget -t 15 ..." to access it), and then walk the |
| * nsTransactionTable from another shell to see that not only is |
| * the walk not blocked, but that the nsTransactionTable is not |
| * empty. |
| * |
| */ |
| |
| #include <net-snmp/net-snmp-config.h> |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| #include "delayed_instance.h" |
| |
| static u_long delay_time = 1; |
| |
| void |
| init_delayed_instance(void) |
| { |
| static oid my_delayed_oid[] = |
| { 1, 3, 6, 1, 4, 1, 8072, 2, 1, 2, 0 }; |
| /* |
| * delayed handler test |
| */ |
| netsnmp_handler_registration *my_test; |
| |
| my_test = |
| netsnmp_create_handler_registration("delayed_instance_example", |
| delayed_instance_handler, |
| my_delayed_oid, |
| OID_LENGTH(my_delayed_oid), |
| HANDLER_CAN_RWRITE); |
| |
| netsnmp_register_instance(my_test); |
| } |
| |
| #define DELAYED_INSTANCE_SET_NAME "test_delayed" |
| |
| int |
| delayed_instance_handler(netsnmp_mib_handler *handler, |
| netsnmp_handler_registration *reginfo, |
| netsnmp_agent_request_info *reqinfo, |
| netsnmp_request_info *requests) |
| { |
| |
| DEBUGMSGTL(("delayed_instance", "Got request, mode = %d:\n", |
| reqinfo->mode)); |
| |
| switch (reqinfo->mode) { |
| /* |
| * here we merely mention that we'll answer this request |
| * later. we don't actually care about the mode type in this |
| * example, but for certain cases you may, so I'll leave in the |
| * otherwise useless switch and case statements |
| */ |
| |
| default: |
| /* |
| * mark this variable as something that can't be handled now. |
| * We'll answer it later. |
| */ |
| requests->delegated = 1; |
| |
| /* |
| * register an alarm to update the results at a later |
| * time. Normally, we might have to query something else |
| * (like an external request sent to a different network |
| * or system socket, etc), but for this example we'll do |
| * something really simply and just insert an alarm for a |
| * certain period of time |
| */ |
| snmp_alarm_register(delay_time, /* seconds */ |
| 0, /* dont repeat. */ |
| return_delayed_response, /* the function |
| * to call */ |
| /* |
| * here we create a "cache" of useful |
| * information that we'll want later |
| * on. This argument is passed back |
| * to us in the callback function for |
| * an alarm |
| */ |
| (void *) |
| netsnmp_create_delegated_cache(handler, |
| reginfo, |
| reqinfo, |
| requests, |
| NULL)); |
| break; |
| |
| } |
| |
| return SNMP_ERR_NOERROR; |
| } |
| |
| void |
| return_delayed_response(unsigned int clientreg, void *clientarg) |
| { |
| /* |
| * extract the cache from the passed argument |
| */ |
| netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) clientarg; |
| |
| netsnmp_request_info *requests; |
| netsnmp_agent_request_info *reqinfo; |
| u_long *delay_time_cache = NULL; |
| |
| /* |
| * here we double check that the cache we created earlier is still |
| * * valid. If not, the request timed out for some reason and we |
| * * don't need to keep processing things. Should never happen, but |
| * * this double checks. |
| */ |
| cache = netsnmp_handler_check_cache(cache); |
| |
| if (!cache) { |
| snmp_log(LOG_ERR, "illegal call to return delayed response\n"); |
| return; |
| } |
| |
| /* |
| * re-establish the previous pointers we are used to having |
| */ |
| reqinfo = cache->reqinfo; |
| requests = cache->requests; |
| |
| DEBUGMSGTL(("delayed_instance", |
| "continuing delayed request, mode = %d\n", |
| cache->reqinfo->mode)); |
| |
| /* |
| * mention that it's no longer delegated, and we've now answered |
| * the query (which we'll do down below). |
| */ |
| requests->delegated = 0; |
| |
| switch (cache->reqinfo->mode) { |
| /* |
| * registering as an instance means we don't need to deal with |
| * getnext processing, so we don't handle it here at all. |
| * |
| * However, since the instance handler already reset the mode |
| * back to GETNEXT from the faked GET mode, we need to do the |
| * same thing in both cases. This should be fixed in future |
| * versions of net-snmp hopefully. |
| */ |
| |
| case MODE_GET: |
| case MODE_GETNEXT: |
| /* |
| * return the currend delay time |
| */ |
| snmp_set_var_typed_value(cache->requests->requestvb, |
| ASN_INTEGER, |
| (u_char *) & delay_time, |
| sizeof(delay_time)); |
| break; |
| |
| case MODE_SET_RESERVE1: |
| /* |
| * check type |
| */ |
| if (requests->requestvb->type != ASN_INTEGER) { |
| /* |
| * not an integer. Bad dog, no bone. |
| */ |
| netsnmp_set_request_error(reqinfo, requests, |
| SNMP_ERR_WRONGTYPE); |
| /* |
| * we don't need the cache any longer |
| */ |
| netsnmp_free_delegated_cache(cache); |
| return; |
| } |
| break; |
| |
| case MODE_SET_RESERVE2: |
| /* |
| * store old value for UNDO support in the future. |
| */ |
| delay_time_cache = netsnmp_memdup(&delay_time, sizeof(delay_time)); |
| |
| /* |
| * malloc failed |
| */ |
| if (delay_time_cache == NULL) { |
| netsnmp_set_request_error(reqinfo, requests, |
| SNMP_ERR_RESOURCEUNAVAILABLE); |
| netsnmp_free_delegated_cache(cache); |
| return; |
| } |
| |
| /* |
| * Add our temporary information to the request itself. |
| * This is then retrivable later. The free function |
| * passed auto-frees it when the request is later |
| * deleted. |
| */ |
| netsnmp_request_add_list_data(requests, |
| netsnmp_create_data_list |
| (DELAYED_INSTANCE_SET_NAME, |
| delay_time_cache, free)); |
| break; |
| |
| case MODE_SET_ACTION: |
| /* |
| * update current value |
| */ |
| delay_time = *(requests->requestvb->val.integer); |
| DEBUGMSGTL(("testhandler", "updated delay_time -> %d\n", |
| delay_time)); |
| break; |
| |
| case MODE_SET_UNDO: |
| /* |
| * ack, something somewhere failed. We reset back to the |
| * previously old value by extracting the previosuly |
| * stored information back out of the request |
| */ |
| delay_time = |
| *((u_long *) netsnmp_request_get_list_data(requests, |
| DELAYED_INSTANCE_SET_NAME)); |
| break; |
| |
| case MODE_SET_COMMIT: |
| case MODE_SET_FREE: |
| /* |
| * the only thing to do here is free the old memdup'ed |
| * value, but it's auto-freed by the datalist recovery, so |
| * we don't have anything to actually do here |
| */ |
| break; |
| } |
| |
| /* |
| * free the information cache |
| */ |
| netsnmp_free_delegated_cache(cache); |
| } |