|
1 /* |
|
2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. |
|
3 * |
|
4 * U.S. Government Rights - Commercial software. Government users are subject |
|
5 * to the Sun Microsystems, Inc. standard license agreement and applicable |
|
6 * provisions of the FAR and its supplements. |
|
7 * |
|
8 * |
|
9 * This distribution may include materials developed by third parties. Sun, |
|
10 * Sun Microsystems, the Sun logo and Solaris are trademarks or registered |
|
11 * trademarks of Sun Microsystems, Inc. in the U.S. and other countries. |
|
12 * |
|
13 */ |
|
14 |
|
15 /* @example demo_module_9.c |
|
16 * |
|
17 * |
|
18 * This example demonstrates how to implement objects that normally would |
|
19 * block the agent as it waits for external events in such a way that the |
|
20 * agent can continue responding to other requests while this implementation waits. |
|
21 * |
|
22 * This example uses the following features of SMA: |
|
23 * |
|
24 * - Use of the instance helper, which registers an exact OID such that GENEXT requests |
|
25 * are handled entirely by the helper. |
|
26 * |
|
27 * - Setting the delegated member of the requests structure to 1 to indicate to the |
|
28 * agent that this request should be delayed. The agent queues this request |
|
29 * to be handled later and then is available to handle other requests. The |
|
30 * agent is not blocked by this request. |
|
31 * |
|
32 * - Registering an SNMP alarm to update the results at a later time. |
|
33 * |
|
34 */ |
|
35 |
|
36 #include <net-snmp/net-snmp-config.h> |
|
37 #include <net-snmp/net-snmp-includes.h> |
|
38 #include <net-snmp/agent/net-snmp-agent-includes.h> |
|
39 #include "demo_module_9.h" |
|
40 |
|
41 // default delay time for SNMP alarm |
|
42 static u_long delay_time = 1; |
|
43 |
|
44 void |
|
45 init_demo_module_9(void) |
|
46 { |
|
47 static oid my_delayed_oid[] = |
|
48 { 1, 3, 6, 1, 4, 1,42, 2, 2, 4, 4, 9, 1, 1, 0 }; |
|
49 |
|
50 /* |
|
51 * Creates a registration handler, my_test, and passes |
|
52 * the pointer it returns to the netsnmp_register_instance |
|
53 * helper function. |
|
54 */ |
|
55 netsnmp_handler_registration *my_test; |
|
56 DEBUGMSGTL(("demo_module_9", "Initializing\n")); |
|
57 my_test = |
|
58 netsnmp_create_handler_registration("delayed_instance_example", |
|
59 delayed_instance_handler, |
|
60 my_delayed_oid, |
|
61 OID_LENGTH(my_delayed_oid), |
|
62 HANDLER_CAN_RWRITE); |
|
63 |
|
64 netsnmp_register_instance(my_test); |
|
65 } |
|
66 |
|
67 #define DELAYED_INSTANCE_SET_NAME "test_delayed" |
|
68 |
|
69 int |
|
70 delayed_instance_handler(netsnmp_mib_handler *handler, |
|
71 netsnmp_handler_registration *reginfo, |
|
72 netsnmp_agent_request_info *reqinfo, |
|
73 netsnmp_request_info *requests) |
|
74 { |
|
75 /* |
|
76 This handler is called to handle SNMP GET and SNMP SET |
|
77 requests for the my_delayed_oid object. If it is called to |
|
78 handle SNMP GET requests, the handler does not need to |
|
79 handle a GETNEXT if it is registered as an instance handler. |
|
80 Instance handlers only deliver one request at a time, so we |
|
81 do not need to loop over a list of requests. */ |
|
82 |
|
83 DEBUGMSGTL(("demo_module_9", "Handler got request, mode = %d:\n", |
|
84 reqinfo->mode)); |
|
85 |
|
86 switch (reqinfo->mode) { |
|
87 /* |
|
88 * here we merely mention that we'll answer this request |
|
89 * later. we don't actually care about the mode type in this |
|
90 * example, but for certain cases you may, so I'll leave in the |
|
91 * otherwise useless switch and case statements |
|
92 */ |
|
93 |
|
94 default: |
|
95 /* |
|
96 * Mark this variable as something that cannot be handled now |
|
97 * by setting the delegated member of the requests structure |
|
98 * to 1. The agent queues the request to be handled at a later |
|
99 * time and continues responding to other client requests. |
|
100 * |
|
101 */ |
|
102 requests->delegated = 1; |
|
103 DEBUGMSGTL(("demo_module_9", "Delegated is %d\n", |
|
104 requests->delegated)); |
|
105 |
|
106 /* |
|
107 * Register an alarm to update the results at a later |
|
108 * time. Normally, we might have to query something else |
|
109 * (like an external request sent to a different network |
|
110 * or system socket, etc), but for this example we'll do |
|
111 * something really simply and just insert an alarm for a |
|
112 * certain period of time. |
|
113 */ |
|
114 DEBUGMSGTL(("demo_module_9", "Delay is %d\n", |
|
115 delay_time)); |
|
116 snmp_alarm_register(delay_time, /* seconds */ |
|
117 0, /* dont repeat. */ |
|
118 return_delayed_response, /* the function |
|
119 * to call */ |
|
120 /* |
|
121 * Create a "cache" of useful |
|
122 * information that can be retrieved |
|
123 * at a later time. This argument is |
|
124 * passed back to the module in the callback |
|
125 * function for an alarm. |
|
126 */ |
|
127 (void *) |
|
128 netsnmp_create_delegated_cache(handler, |
|
129 reginfo, |
|
130 reqinfo, |
|
131 requests, |
|
132 NULL)); |
|
133 break; |
|
134 |
|
135 } |
|
136 |
|
137 return SNMP_ERR_NOERROR; |
|
138 } |
|
139 |
|
140 void |
|
141 return_delayed_response(unsigned int clientreg, void *clientarg) |
|
142 { |
|
143 /* |
|
144 * Extract the cache from the passed argument. |
|
145 */ |
|
146 netsnmp_delegated_cache *cache = (netsnmp_delegated_cache *) clientarg; |
|
147 |
|
148 netsnmp_request_info *requests; |
|
149 netsnmp_agent_request_info *reqinfo; |
|
150 u_long *delay_time_cache = NULL; |
|
151 |
|
152 /* |
|
153 * Make sure the cache created earlier is still |
|
154 * valid. If not, the request timed out for some reason and we |
|
155 * do not need to keep processing things. Should never happen, but |
|
156 * this double checks. |
|
157 */ |
|
158 cache = netsnmp_handler_check_cache(cache); |
|
159 |
|
160 if (!cache) { |
|
161 snmp_log(LOG_ERR, "illegal call to return delayed response\n"); |
|
162 return; |
|
163 } |
|
164 |
|
165 /* |
|
166 * Re-establish the previous pointers, |
|
167 */ |
|
168 reqinfo = cache->reqinfo; |
|
169 requests = cache->requests; |
|
170 |
|
171 DEBUGMSGTL(("demo_module_9", |
|
172 "continuing delayed request, mode = %d\n", |
|
173 cache->reqinfo->mode)); |
|
174 |
|
175 |
|
176 /* |
|
177 * Set delegated to zero to indicate that the request is no longer |
|
178 * delegated and answer the query. |
|
179 */ |
|
180 requests->delegated = 0; |
|
181 |
|
182 DEBUGMSGTL(("demo_module_9", "Set delegated to %d\n", |
|
183 requests->delegated)); |
|
184 |
|
185 |
|
186 switch (cache->reqinfo->mode) { |
|
187 /* |
|
188 * Registering as an instance means we do not need to deal with |
|
189 * GETNEXT processing, so we do not handle it here at all. |
|
190 * |
|
191 * However, since the instance handler already reset the mode |
|
192 * back to GETNEXT from the GET mode, we need to do the |
|
193 * same thing in both cases. |
|
194 * |
|
195 */ |
|
196 |
|
197 case MODE_GET: |
|
198 case MODE_GETNEXT: |
|
199 /* |
|
200 * Return the current delay time |
|
201 */ |
|
202 DEBUGMSGTL(("demo_module_9", "Got to MODE_GETNEXT\n")); |
|
203 |
|
204 snmp_set_var_typed_value(cache->requests->requestvb, |
|
205 ASN_INTEGER, |
|
206 (u_char *) & delay_time, |
|
207 sizeof(delay_time)); |
|
208 DEBUGMSGTL(("demo_module_9", |
|
209 "Got delay time = %d\n", |
|
210 delay_time)); |
|
211 break; |
|
212 |
|
213 case MODE_SET_RESERVE1: |
|
214 DEBUGMSGTL(("demo_module_9", "Got to MODE_SET_RESERVE1\n")); |
|
215 |
|
216 /* |
|
217 * check type |
|
218 */ |
|
219 if (requests->requestvb->type != ASN_INTEGER) { |
|
220 /* |
|
221 * If not an integer, return SNMP error. |
|
222 */ |
|
223 netsnmp_set_request_error(reqinfo, requests, |
|
224 SNMP_ERR_WRONGTYPE); |
|
225 /* |
|
226 * Free cache. It is no longer needed. |
|
227 */ |
|
228 netsnmp_free_delegated_cache(cache); |
|
229 return; |
|
230 } |
|
231 break; |
|
232 |
|
233 case MODE_SET_RESERVE2: |
|
234 DEBUGMSGTL(("demo_module_9", "Got to MODE_SET_RESERVE2\n")); |
|
235 /* |
|
236 * Store old value for UNDO support in the future. |
|
237 */ |
|
238 memdup((u_char **) & delay_time_cache, |
|
239 (u_char *) & delay_time, sizeof(delay_time)); |
|
240 |
|
241 /* |
|
242 * malloc failed |
|
243 */ |
|
244 if (delay_time_cache == NULL) { |
|
245 netsnmp_set_request_error(reqinfo, requests, |
|
246 SNMP_ERR_RESOURCEUNAVAILABLE); |
|
247 netsnmp_free_delegated_cache(cache); |
|
248 return; |
|
249 } |
|
250 |
|
251 /* |
|
252 * Add our temporary information to the request itself. |
|
253 * This is then retrivable later. The free function |
|
254 * passed auto-frees it when the request is later |
|
255 * deleted. |
|
256 */ |
|
257 netsnmp_request_add_list_data(requests, |
|
258 netsnmp_create_data_list |
|
259 (DELAYED_INSTANCE_SET_NAME, |
|
260 delay_time_cache, free)); |
|
261 break; |
|
262 |
|
263 case MODE_SET_ACTION: |
|
264 DEBUGMSGTL(("demo_module_9", "Got to MODE_SET_ACTION\n")); |
|
265 /* |
|
266 * Update current value. |
|
267 */ |
|
268 delay_time = *(requests->requestvb->val.integer); |
|
269 DEBUGMSGTL(("demo_module_9", "updated delay_time -> %d\n", |
|
270 delay_time)); |
|
271 break; |
|
272 |
|
273 case MODE_SET_UNDO: |
|
274 DEBUGMSGTL(("demo_module_9", "Got to MODE_SET_UNDO\n")); |
|
275 |
|
276 /* |
|
277 * A failure occurred. Reset to the |
|
278 * previously value by extracting the previosuly |
|
279 * stored information from the request. |
|
280 */ |
|
281 delay_time = |
|
282 *((u_long *) netsnmp_request_get_list_data(requests, |
|
283 DELAYED_INSTANCE_SET_NAME)); |
|
284 break; |
|
285 |
|
286 case MODE_SET_COMMIT: |
|
287 case MODE_SET_FREE: |
|
288 DEBUGMSGTL(("demo_module_9", "Got to MODE_SET_UNDO\n")); |
|
289 /* |
|
290 * The only thing to do here is free the old memdup'ed |
|
291 * value, but it's auto-freed by the datalist recovery, so |
|
292 * we don't have anything to actually do here |
|
293 */ |
|
294 break; |
|
295 } |
|
296 |
|
297 /* |
|
298 * free the information cache |
|
299 */ |
|
300 netsnmp_free_delegated_cache(cache); |
|
301 } |