and added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / examples / delayed_instance.c
1 /**  @example delayed_instance.c
2  *  This example implements the netSnmpExampleSleeper object.
3  *
4  *  It demonstrates 2 things:
5  *
6  *  - The instance helper, which is a way of registering an exact OID
7  *    such that GENEXT requests are handled entirely by the helper.
8  *
9  *  - how to implement objects which normally would block the agent as
10  *    it waits for external events in such a way that the agent can
11  *    continue responding to other requests while this implementation
12  *    waits.
13  *
14  *  - Added bonus: normally the nsTransactionTable is empty, since
15  *    there aren't any outstanding requests generally.  When accessed,
16  *    this module will create some however.  Try setting
17  *    netSnmpExampleSleeper.0 to 10 and then accessing it (use
18  *    "snmpget -t 15 ..." to access it), and then walk the
19  *    nsTransactionTable from another shell to see that not only is
20  *    the walk not blocked, but that the nsTransactionTable is not
21  *    empty.
22  *
23  */
24
25 #include <net-snmp/net-snmp-config.h>
26 #include <net-snmp/net-snmp-includes.h>
27 #include <net-snmp/agent/net-snmp-agent-includes.h>
28
29 #include "delayed_instance.h"
30
31 static u_long   delay_time = 1;
32
33 void
34 init_delayed_instance(void)
35 {
36     static oid      my_delayed_oid[] =
37         { 1, 3, 6, 1, 4, 1, 8072, 2, 1, 2, 0 };
38     /*
39      * delayed handler test
40      */
41     netsnmp_handler_registration *my_test;
42
43     my_test =
44         netsnmp_create_handler_registration("delayed_instance_example",
45                                             delayed_instance_handler,
46                                             my_delayed_oid,
47                                             OID_LENGTH(my_delayed_oid),
48                                             HANDLER_CAN_RWRITE);
49
50     netsnmp_register_instance(my_test);
51 }
52
53 #define DELAYED_INSTANCE_SET_NAME "test_delayed"
54
55 int
56 delayed_instance_handler(netsnmp_mib_handler *handler,
57                          netsnmp_handler_registration *reginfo,
58                          netsnmp_agent_request_info *reqinfo,
59                          netsnmp_request_info *requests)
60 {
61
62     DEBUGMSGTL(("delayed_instance", "Got request, mode = %d:\n",
63                 reqinfo->mode));
64
65     switch (reqinfo->mode) {
66         /*
67          * here we merely mention that we'll answer this request
68          * later.  we don't actually care about the mode type in this
69          * example, but for certain cases you may, so I'll leave in the
70          * otherwise useless switch and case statements 
71          */
72
73     default:
74         /*
75          * mark this variable as something that can't be handled now.
76          * We'll answer it later. 
77          */
78         requests->delegated = 1;
79
80         /*
81          * register an alarm to update the results at a later
82          * time.  Normally, we might have to query something else
83          * (like an external request sent to a different network
84          * or system socket, etc), but for this example we'll do
85          * something really simply and just insert an alarm for a
86          * certain period of time 
87          */
88         snmp_alarm_register(delay_time, /* seconds */
89                             0,  /* dont repeat. */
90                             return_delayed_response,    /* the function
91                                                          * to call */
92                             /*
93                              * here we create a "cache" of useful
94                              * information that we'll want later
95                              * on.  This argument is passed back
96                              * to us in the callback function for
97                              * an alarm 
98                              */
99                             (void *)
100                             netsnmp_create_delegated_cache(handler,
101                                                            reginfo,
102                                                            reqinfo,
103                                                            requests,
104                                                            NULL));
105         break;
106
107     }
108
109     return SNMP_ERR_NOERROR;
110 }
111
112 void
113 return_delayed_response(unsigned int clientreg, void *clientarg)
114 {
115     /*
116      * extract the cache from the passed argument 
117      */
118     netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) clientarg;
119
120     netsnmp_request_info *requests;
121     netsnmp_agent_request_info *reqinfo;
122     u_long         *delay_time_cache = NULL;
123
124     /*
125      * here we double check that the cache we created earlier is still
126      * * valid.  If not, the request timed out for some reason and we
127      * * don't need to keep processing things.  Should never happen, but
128      * * this double checks. 
129      */
130     cache = netsnmp_handler_check_cache(cache);
131
132     if (!cache) {
133         snmp_log(LOG_ERR, "illegal call to return delayed response\n");
134         return;
135     }
136
137     /*
138      * re-establish the previous pointers we are used to having 
139      */
140     reqinfo = cache->reqinfo;
141     requests = cache->requests;
142
143     DEBUGMSGTL(("delayed_instance",
144                 "continuing delayed request, mode = %d\n",
145                 cache->reqinfo->mode));
146
147     /*
148      * mention that it's no longer delegated, and we've now answered
149      * the query (which we'll do down below). 
150      */
151     requests->delegated = 0;
152
153     switch (cache->reqinfo->mode) {
154         /*
155          * registering as an instance means we don't need to deal with
156          * getnext processing, so we don't handle it here at all.
157          * 
158          * However, since the instance handler already reset the mode
159          * back to GETNEXT from the faked GET mode, we need to do the
160          * same thing in both cases.  This should be fixed in future
161          * versions of net-snmp hopefully. 
162          */
163
164     case MODE_GET:
165     case MODE_GETNEXT:
166         /*
167          * return the currend delay time 
168          */
169         snmp_set_var_typed_value(cache->requests->requestvb,
170                                  ASN_INTEGER,
171                                  (u_char *) & delay_time,
172                                  sizeof(delay_time));
173         break;
174
175     case MODE_SET_RESERVE1:
176         /*
177          * check type 
178          */
179         if (requests->requestvb->type != ASN_INTEGER) {
180             /*
181              * not an integer.  Bad dog, no bone. 
182              */
183             netsnmp_set_request_error(reqinfo, requests,
184                                       SNMP_ERR_WRONGTYPE);
185             /*
186              * we don't need the cache any longer 
187              */
188             netsnmp_free_delegated_cache(cache);
189             return;
190         }
191         break;
192
193     case MODE_SET_RESERVE2:
194         /*
195          * store old value for UNDO support in the future. 
196          */
197         memdup((u_char **) & delay_time_cache,
198                (u_char *) & delay_time, sizeof(delay_time));
199
200         /*
201          * malloc failed 
202          */
203         if (delay_time_cache == NULL) {
204             netsnmp_set_request_error(reqinfo, requests,
205                                       SNMP_ERR_RESOURCEUNAVAILABLE);
206             netsnmp_free_delegated_cache(cache);
207             return;
208         }
209
210         /*
211          * Add our temporary information to the request itself.
212          * This is then retrivable later.  The free function
213          * passed auto-frees it when the request is later
214          * deleted.  
215          */
216         netsnmp_request_add_list_data(requests,
217                                       netsnmp_create_data_list
218                                       (DELAYED_INSTANCE_SET_NAME,
219                                        delay_time_cache, free));
220         break;
221
222     case MODE_SET_ACTION:
223         /*
224          * update current value 
225          */
226         delay_time = *(requests->requestvb->val.integer);
227         DEBUGMSGTL(("testhandler", "updated delay_time -> %d\n",
228                     delay_time));
229         break;
230
231     case MODE_SET_UNDO:
232         /*
233          * ack, something somewhere failed.  We reset back to the
234          * previously old value by extracting the previosuly
235          * stored information back out of the request 
236          */
237         delay_time =
238             *((u_long *) netsnmp_request_get_list_data(requests,
239                                                        DELAYED_INSTANCE_SET_NAME));
240         break;
241
242     case MODE_SET_COMMIT:
243     case MODE_SET_FREE:
244         /*
245          * the only thing to do here is free the old memdup'ed
246          * value, but it's auto-freed by the datalist recovery, so
247          * we don't have anything to actually do here 
248          */
249         break;
250     }
251
252     /*
253      * free the information cache 
254      */
255     netsnmp_free_delegated_cache(cache);
256 }