Mercurial > illumos > onarm
comparison usr/src/cmd/agents/snmp/agent/agent.c @ 0:c9caec207d52 b86
Initial porting based on b86
author | Koji Uno <koji.uno@sun.com> |
---|---|
date | Tue, 02 Jun 2009 18:56:50 +0900 |
parents | |
children | 1a15d5aaf794 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c9caec207d52 |
---|---|
1 /* | |
2 * CDDL HEADER START | |
3 * | |
4 * The contents of this file are subject to the terms of the | |
5 * Common Development and Distribution License, Version 1.0 only | |
6 * (the "License"). You may not use this file except in compliance | |
7 * with the License. | |
8 * | |
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE | |
10 * or http://www.opensolaris.org/os/licensing. | |
11 * See the License for the specific language governing permissions | |
12 * and limitations under the License. | |
13 * | |
14 * When distributing Covered Code, include this CDDL HEADER in each | |
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. | |
16 * If applicable, add the following below this CDDL HEADER, with the | |
17 * fields enclosed by brackets "[]" replaced with your own identifying | |
18 * information: Portions Copyright [yyyy] [name of copyright owner] | |
19 * | |
20 * CDDL HEADER END | |
21 */ | |
22 /* | |
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. | |
24 * Use is subject to license terms. | |
25 */ | |
26 | |
27 #pragma ident "@(#)agent.c 1.25 05/06/12 SMI" | |
28 | |
29 #include <stdio.h> | |
30 #include <stdlib.h> | |
31 #include <string.h> | |
32 #include <strings.h> | |
33 #include <sys/types.h> | |
34 #include <sys/socket.h> | |
35 #include <netinet/in.h> | |
36 #include <arpa/inet.h> | |
37 | |
38 | |
39 #include "impl.h" | |
40 #include "error.h" | |
41 #include "trace.h" | |
42 #include "snmp.h" | |
43 #include "pdu.h" | |
44 #include "request.h" | |
45 #include "trap.h" | |
46 | |
47 #include "node.h" | |
48 #include "access.h" | |
49 | |
50 static int ssa_mem_free = 1; /* on */ | |
51 | |
52 #if defined(_LP64) | |
53 #define COLUMN_OFFSET(x) (x) * 2 | |
54 #else | |
55 #define COLUMN_OFFSET(x) (x) | |
56 #endif | |
57 | |
58 #define OCTET_STRING 2 | |
59 | |
60 /***** LOCAL VARIABLES *****/ | |
61 | |
62 /* | |
63 * The idea of these cache variables is to avoid to call | |
64 * the same *(entry->get) function for variables contained | |
65 * in a single SNMP PDU. | |
66 * | |
67 * There is still a problem in this mechanism. | |
68 * When you query a whole table row by row and when | |
69 * you reached the end of the table, the *(entry->get) | |
70 * will be called several times: | |
71 * - for the first column, *(entry->get) will be called | |
72 * once to find that the end of the table is reached | |
73 * (==> the knowledge that we reached the last row is cached) | |
74 * and then *(entry->get) will be called on the first row of | |
75 * the same table (==> the first row is cached). | |
76 * - Same behaviour for all the remaining columns | |
77 * | |
78 * Possible solutions: | |
79 * - two caches but this implies the copy of the cached | |
80 * structures + all theis pointers | |
81 * - only one cache + caching the knowledge that we reached the last | |
82 * row ??? | |
83 * | |
84 */ | |
85 | |
86 static Entry *cache_input_entry = NULL; | |
87 static Subid cache_input_index[MAX_OID_LEN]; | |
88 static void *cache_output_pointer = NULL; | |
89 static int cache_output_snmp_error = -1; | |
90 static Subid cache_output_index[MAX_OID_LEN]; | |
91 | |
92 | |
93 /***** LOCAL FUNCTIONS *****/ | |
94 | |
95 static int agent_get_next(SNMP_pdu *pdu, char *error_label); | |
96 static int agent_get_next_loop(SNMP_variable *variable, Node *node, Oid *suffix); | |
97 static int agent_get(SNMP_pdu *pdu, char *error_label); | |
98 static int agent_set(int pass, SNMP_pdu *pdu, char *error_label); | |
99 | |
100 | |
101 /****************************************************************/ | |
102 | |
103 /* returns: */ | |
104 /* 0 in case of success (the pdu should be sent */ | |
105 /* back to its originator even if an SNMP error */ | |
106 /* was detected) */ | |
107 /* -1 in case of failure (no pdu should be sent */ | |
108 /* back) */ | |
109 | |
110 int agent_process(Address *address, SNMP_pdu *pdu) | |
111 { | |
112 int snmpEnableAuthTraps = FALSE; | |
113 Manager *mngr; | |
114 | |
115 | |
116 if(pdu == NULL) | |
117 { | |
118 error("BUG: agent_process(): pdu is NULL"); | |
119 return -1; | |
120 } | |
121 | |
122 | |
123 /* check host */ | |
124 if(is_valid_manager(address,&mngr) == NULL) | |
125 { | |
126 error("agent_process(): unauthorized manager (%s)", | |
127 ip_address_string(&(address->sin_addr))); | |
128 | |
129 snmpEnableAuthTraps = request_snmpEnableAuthTraps(error_label); | |
130 switch(snmpEnableAuthTraps) | |
131 { | |
132 case TRUE: | |
133 if(trap_send_to_all_destinators(NULL, | |
134 SNMP_TRAP_AUTHFAIL, 0, | |
135 NULL, error_label)) | |
136 { | |
137 error("trap_send_to_all_destinators() failed: %s\n", | |
138 error_label); | |
139 } | |
140 break; | |
141 | |
142 case FALSE: | |
143 default: | |
144 break; | |
145 } | |
146 | |
147 return -1; | |
148 } | |
149 | |
150 /* if mngr == NULL -> allow requests from any hosts */ | |
151 | |
152 /* check pdu type */ | |
153 if(pdu->type != GETNEXT_REQ_MSG | |
154 && (pdu->type != GET_REQ_MSG) | |
155 && (pdu->type != SET_REQ_MSG) ) | |
156 { | |
157 error("agent_process(): bad PDU type (0x%x)", pdu->type); | |
158 return -1; | |
159 } | |
160 | |
161 | |
162 /* check host */ | |
163 if(!is_valid_community(pdu->community, pdu->type,mngr)) | |
164 { | |
165 /* | |
166 * Earlier, the community name is displayed here | |
167 * in this error message. But since these error | |
168 * messages are readable by all users, it is not advisible | |
169 * to display community names in the error messages. | |
170 */ | |
171 error("agent_process() : bad community from %s", | |
172 ip_address_string(&(address->sin_addr))); | |
173 | |
174 snmpEnableAuthTraps = request_snmpEnableAuthTraps(error_label); | |
175 switch(snmpEnableAuthTraps) | |
176 { | |
177 case TRUE: | |
178 if(trap_send_to_all_destinators(NULL, | |
179 SNMP_TRAP_AUTHFAIL, 0, | |
180 NULL, error_label)) | |
181 { | |
182 error("trap_send_to_all_destinators() failed: %s\n", | |
183 error_label); | |
184 } | |
185 break; | |
186 | |
187 case FALSE: | |
188 default: | |
189 break; | |
190 } | |
191 | |
192 return -1; | |
193 } | |
194 | |
195 | |
196 if(cache_input_entry != NULL && cache_output_pointer != NULL) | |
197 if(ssa_mem_free != 0 && cache_input_entry->dealloc != NULL){ | |
198 (*(cache_input_entry->dealloc))(cache_output_pointer); | |
199 cache_output_pointer = NULL; | |
200 } | |
201 | |
202 cache_input_entry = NULL; | |
203 | |
204 switch(pdu->type) | |
205 { | |
206 case GETNEXT_REQ_MSG: | |
207 if(agent_get_next(pdu, error_label)) | |
208 { | |
209 error("agent_get_next() failed: %s", error_label); | |
210 return -1; | |
211 } | |
212 return 0; | |
213 | |
214 case GET_REQ_MSG: | |
215 if(agent_get(pdu, error_label)) | |
216 { | |
217 error("agent_get() failed: %s", error_label); | |
218 return -1; | |
219 } | |
220 return 0; | |
221 | |
222 case SET_REQ_MSG: | |
223 switch(agent_set(FIRST_PASS, pdu, error_label)) | |
224 { | |
225 case 0: | |
226 switch(agent_set(SECOND_PASS, pdu, error_label)) | |
227 { | |
228 case 0: | |
229 case 1: | |
230 return 0; | |
231 | |
232 case -1: | |
233 error("agent_set(SECOND_PASS) failed: %s", | |
234 error_label); | |
235 return -1; | |
236 } | |
237 | |
238 /* never reached */ | |
239 break; | |
240 | |
241 case 1: | |
242 return 0; | |
243 | |
244 case -1: | |
245 error("agent_set(FIRST_PASS) failed: %s", | |
246 error_label); | |
247 return -1; | |
248 } | |
249 } | |
250 | |
251 /* never reached */ | |
252 return -1; | |
253 } | |
254 | |
255 | |
256 /****************************************************************/ | |
257 | |
258 /* returns: */ | |
259 /* 0 in case of success (the pdu should be sent */ | |
260 /* back to its originator even if an SNMP error */ | |
261 /* was detected) */ | |
262 /* -1 in case of failure (no pdu should be sent */ | |
263 /* back) */ | |
264 | |
265 static int agent_get_next(SNMP_pdu *pdu, char *error_label) | |
266 { | |
267 SNMP_variable *variable; | |
268 Node *node; | |
269 Oid suffix; | |
270 int index = 1; | |
271 int snmp_error; | |
272 | |
273 | |
274 error_label[0] = '\0'; | |
275 | |
276 pdu->type = GET_RSP_MSG; | |
277 | |
278 for(variable = pdu->first_variable; variable; variable = variable->next_variable) | |
279 { | |
280 node = node_find(NEXT_ENTRY, &(variable->name), &suffix); | |
281 if(node == NULL) | |
282 { | |
283 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
284 pdu->error_index = index; | |
285 return 0; | |
286 } | |
287 /* we should not forget to free suffix.subids */ | |
288 | |
289 if(trace_level > 0) | |
290 { | |
291 trace("!! getnext(): processing the variable %s\n\n", | |
292 node->label); | |
293 } | |
294 | |
295 if(variable->type != NULLOBJ) | |
296 { | |
297 error("ASN.1 type (0x%x) is not NULL for node %s", | |
298 variable->type, node->label); | |
299 variable->type = NULLOBJ; | |
300 } | |
301 | |
302 if(variable->val.string) | |
303 { | |
304 error("val is not NULL for node %s", | |
305 node->label); | |
306 free(variable->val.string); | |
307 variable->val.string = NULL; | |
308 } | |
309 | |
310 if(variable->val_len) | |
311 { | |
312 error("val_len is not 0 for node %s", | |
313 node->label); | |
314 variable->val_len = 0; | |
315 } | |
316 | |
317 snmp_error = agent_get_next_loop(variable, node, &suffix); | |
318 | |
319 if(snmp_error != SNMP_ERR_NOERROR) | |
320 { | |
321 pdu->error_status = snmp_error; | |
322 pdu->error_index = index; | |
323 return 0; | |
324 } | |
325 | |
326 index++; | |
327 } | |
328 | |
329 return 0; | |
330 } | |
331 | |
332 | |
333 /* This function will free suffix->subids */ | |
334 /* It returns a positive snmp_error code. */ | |
335 static int | |
336 agent_get_next_loop(SNMP_variable *variable, Node *node, Oid *suffix) | |
337 { | |
338 Object *object; | |
339 Column *column; | |
340 Entry *entry; | |
341 Integer integer = 0; | |
342 Integer *integer_ptr; | |
343 String string = { NULL, 0 }; | |
344 String *string_ptr; | |
345 Oid oid = { NULL, 0 }; | |
346 Oid *oid_ptr; | |
347 char *pointer; | |
348 int snmp_error; | |
349 Subid index[MAX_OID_LEN]; | |
350 Index *pIndex; | |
351 int index_len; | |
352 | |
353 /* create index struct */ | |
354 IndexType index_obj; | |
355 int index_buffer[256]; | |
356 | |
357 int was_cached ; | |
358 int i; | |
359 int get_entry; | |
360 | |
361 bzero(index, sizeof(Subid) * MAX_OID_LEN); | |
362 bzero(&index_obj, sizeof(index_obj)); | |
363 bzero(index_buffer, sizeof(index_buffer)); | |
364 index_obj.value = index_buffer; | |
365 | |
366 if(node == NULL) | |
367 { | |
368 if(trace_level > 0) | |
369 { | |
370 trace("!! End of MIB\n\n"); | |
371 } | |
372 | |
373 SSAOidZero(suffix); | |
374 return SNMP_ERR_NOSUCHNAME; | |
375 } | |
376 | |
377 if(trace_level > 0) | |
378 trace("!! Trying %s with suffix %s\n\n", | |
379 node->label, SSAOidString(suffix)); | |
380 | |
381 switch(node->type) | |
382 { | |
383 case OBJECT: | |
384 object = node->data.object; | |
385 | |
386 switch(suffix->len) | |
387 { | |
388 case 0: | |
389 if( !(object->access & READ_FLAG) ) | |
390 return SNMP_ERR_NOSUCHNAME; | |
391 | |
392 switch(object->asn1_type) | |
393 { | |
394 case INTEGER: | |
395 case COUNTER: | |
396 case GAUGE: | |
397 case TIMETICKS: | |
398 snmp_error = (*(object->get))(&integer); | |
399 break; | |
400 | |
401 case OBJID: | |
402 snmp_error = (*(object->get))(&oid); | |
403 if(snmp_error != SNMP_ERR_NOERROR && | |
404 ssa_mem_free != 0){ | |
405 if(object->dealloc != NULL) | |
406 (*(object->dealloc))(&oid); | |
407 } | |
408 break; | |
409 | |
410 case STRING: | |
411 case IPADDRESS: | |
412 case OPAQUE: | |
413 snmp_error = (*(object->get))(&string); | |
414 if(snmp_error != SNMP_ERR_NOERROR && | |
415 ssa_mem_free != 0){ | |
416 if(object->dealloc != NULL) | |
417 (*(object->dealloc))(&string); | |
418 } | |
419 break; | |
420 } | |
421 | |
422 if(snmp_error != SNMP_ERR_NOERROR) | |
423 { | |
424 if(snmp_error < 0) | |
425 { | |
426 error("the get() method of %s returned %d", | |
427 node->label, | |
428 snmp_error); | |
429 snmp_error = SNMP_ERR_GENERR; | |
430 } | |
431 return snmp_error; | |
432 } | |
433 | |
434 /* variable->name */ | |
435 SSAOidZero(&(variable->name)); | |
436 variable->name.subids = (Subid *) malloc((object->name.len + 1) * | |
437 (int32_t)sizeof(Subid)); | |
438 (void)memcpy(variable->name.subids, object->name.subids, | |
439 object->name.len * (int32_t)sizeof(Subid)); | |
440 variable->name.subids[object->name.len] = 0; | |
441 variable->name.len = object->name.len + 1; | |
442 | |
443 /* variable->type */ | |
444 variable->type = object->asn1_type; | |
445 | |
446 /* variable->val, variable->val_len */ | |
447 switch(object->asn1_type) | |
448 { | |
449 case INTEGER: | |
450 case COUNTER: | |
451 case GAUGE: | |
452 case TIMETICKS: | |
453 variable->val.integer = (Integer *) malloc(sizeof(Integer)); | |
454 *(variable->val.integer) = integer; | |
455 variable->val_len = sizeof(Integer); | |
456 break; | |
457 | |
458 case OBJID: | |
459 variable->val.objid = (Subid *) malloc(oid.len * | |
460 (int32_t)sizeof(Subid)); | |
461 (void)memcpy(variable->val.objid, oid.subids, oid.len * | |
462 (int32_t)sizeof(Subid)); | |
463 variable->val_len = oid.len * | |
464 (int32_t)sizeof(Subid); | |
465 if(ssa_mem_free !=0 && object->dealloc != NULL) | |
466 (*(object->dealloc))(&oid); | |
467 break; | |
468 | |
469 case STRING: | |
470 case IPADDRESS: | |
471 case OPAQUE: | |
472 variable->val.string = (u_char *) malloc(string.len); | |
473 (void)memcpy(variable->val.string, string.chars, string.len); | |
474 variable->val_len = string.len; | |
475 if(ssa_mem_free != 0 && object->dealloc != NULL) | |
476 (*(object->dealloc))(&string); | |
477 break; | |
478 | |
479 } | |
480 | |
481 return SNMP_ERR_NOERROR; | |
482 | |
483 | |
484 case 1: | |
485 if(suffix->subids[0] != 0) | |
486 { | |
487 SSAOidZero(suffix); | |
488 return SNMP_ERR_NOSUCHNAME; | |
489 } | |
490 SSAOidZero(suffix); | |
491 | |
492 return agent_get_next_loop(variable, node->next, suffix); | |
493 | |
494 default: | |
495 SSAOidZero(suffix); | |
496 return SNMP_ERR_NOSUCHNAME; | |
497 } | |
498 | |
499 | |
500 case COLUMN: | |
501 column = node->data.column; | |
502 entry = column->entry; | |
503 pIndex = entry->first_index; | |
504 | |
505 if( !(column->access & READ_FLAG) ) | |
506 { | |
507 SSAOidZero(suffix); | |
508 return SNMP_ERR_NOSUCHNAME; | |
509 } | |
510 | |
511 if (entry->n_indexs < 0 || entry->n_indexs > MAX_OID_LEN) { | |
512 SSAOidZero(suffix); | |
513 return SNMP_ERR_NOSUCHNAME; | |
514 } | |
515 | |
516 index_len = 0; | |
517 for (pIndex=entry->first_index; pIndex; pIndex=pIndex->next_index) | |
518 { | |
519 if (pIndex->index_type == OCTET_STRING) | |
520 index_len = index_len + (pIndex->index_len)+1; /* add extra suffix for str len */ | |
521 else | |
522 index_len = index_len +1; /* one subid per index */ | |
523 } | |
524 index_obj.len = index_len; | |
525 | |
526 for (i=0; i < suffix->len; i++) | |
527 { | |
528 index[i] = suffix->subids[i]; | |
529 index_obj.value[i] = suffix->subids[i]; | |
530 } | |
531 | |
532 for (i = suffix->len; i < index_len; i++) /* Zero out remainder of suffixes */ | |
533 { | |
534 index[i] = 0; | |
535 index_obj.value[i] = 0; | |
536 } | |
537 | |
538 for (i=0; i < index_len; i++) | |
539 { | |
540 if ((cache_input_entry == entry) && (cache_input_index[i] == index[i])) | |
541 was_cached = 1; | |
542 else | |
543 { | |
544 was_cached = 0; | |
545 break; | |
546 } | |
547 | |
548 } | |
549 | |
550 if (was_cached) | |
551 { | |
552 pointer = cache_output_pointer; | |
553 snmp_error = cache_output_snmp_error; | |
554 for (i=0;i < index_len; i++) | |
555 index[i]= cache_output_index[i]; | |
556 } | |
557 else | |
558 { | |
559 if (cache_input_entry != NULL && cache_output_pointer != NULL) | |
560 if (ssa_mem_free!=0 && cache_input_entry->dealloc != NULL) | |
561 { | |
562 (*(cache_input_entry->dealloc))(cache_output_pointer); | |
563 cache_output_pointer = NULL; | |
564 } | |
565 cache_input_entry= entry; | |
566 for (i=0; i < index_len; i++) | |
567 cache_input_index[i] = index[i]; | |
568 | |
569 if (suffix->len == 0) | |
570 get_entry = FIRST_ENTRY; | |
571 else | |
572 get_entry = NEXT_ENTRY; | |
573 | |
574 snmp_error = (*(entry->get)) (get_entry, &pointer,&index_obj); | |
575 | |
576 for (i=0; i< index_len; i++) | |
577 index[i] = index_obj.value[i]; | |
578 cache_output_pointer = pointer; | |
579 cache_output_snmp_error = snmp_error; | |
580 for (i=0; i < index_len; i++) | |
581 cache_output_index[i] = index[i]; | |
582 } | |
583 | |
584 if (suffix->len !=0) | |
585 SSAOidZero(suffix); | |
586 | |
587 if(pointer == NULL) | |
588 { | |
589 if(snmp_error == END_OF_TABLE) | |
590 { | |
591 if(trace_level > 0) | |
592 { | |
593 trace("!! End of table %s\n\n", | |
594 node->parent->parent->label); | |
595 } | |
596 return agent_get_next_loop(variable, node->next, suffix); | |
597 } | |
598 | |
599 if(snmp_error < 0) | |
600 { | |
601 error("the get() method of %s returned %d", | |
602 node->parent->label, | |
603 snmp_error); | |
604 snmp_error = SNMP_ERR_GENERR; | |
605 } | |
606 return snmp_error; | |
607 } | |
608 | |
609 /* variable->name */ | |
610 SSAOidZero(&(variable->name)); | |
611 variable->name.subids = (Subid *) malloc((column->name.len + index_len) * sizeof(Subid)); | |
612 memcpy(variable->name.subids, column->name.subids, column->name.len * sizeof(Subid)); | |
613 for (i=0; i < index_len; i++) | |
614 variable->name.subids[column->name.len + i] = index[i]; | |
615 variable->name.len = column->name.len + index_len; | |
616 | |
617 /* variable->type */ | |
618 variable->type = column->asn1_type; | |
619 | |
620 /* variable->val, variable->val_len */ | |
621 switch(column->asn1_type) | |
622 { | |
623 case INTEGER: | |
624 case COUNTER: | |
625 case GAUGE: | |
626 case TIMETICKS: | |
627 integer_ptr = (Integer *) (pointer + | |
628 COLUMN_OFFSET(column->offset)); | |
629 variable->val.integer = (Integer *) malloc(sizeof(Integer)); | |
630 *(variable->val.integer) = *integer_ptr; | |
631 variable->val_len = sizeof(Integer); | |
632 break; | |
633 | |
634 case OBJID: | |
635 oid_ptr = (Oid *) (pointer + | |
636 COLUMN_OFFSET(column->offset)); | |
637 /* fix the null subid */ | |
638 if(oid_ptr->subids == NULL){ | |
639 variable->val.objid = NULL; | |
640 }else{ | |
641 variable->val.objid = (Subid *) malloc(oid_ptr->len * | |
642 (int32_t)sizeof(Subid)); | |
643 (void)memcpy(variable->val.objid, oid_ptr->subids, oid_ptr->len * | |
644 (int32_t)sizeof(Subid)); | |
645 } | |
646 variable->val_len = oid_ptr->len * | |
647 (int32_t)sizeof(Subid); | |
648 break; | |
649 | |
650 case STRING: | |
651 case IPADDRESS: | |
652 case OPAQUE: | |
653 string_ptr = (String *) (pointer + | |
654 COLUMN_OFFSET(column->offset)); | |
655 if(string_ptr->chars == NULL){ | |
656 variable->val.string =(u_char*)NULL; | |
657 }else{ | |
658 variable->val.string = (u_char *) malloc(string_ptr->len); | |
659 (void)memcpy(variable->val.string, string_ptr->chars, string_ptr->len); | |
660 } | |
661 variable->val_len = string_ptr->len; | |
662 break; | |
663 } | |
664 | |
665 | |
666 return SNMP_ERR_NOERROR; | |
667 | |
668 | |
669 case NODE: | |
670 return agent_get_next_loop(variable, node->next, suffix); | |
671 } | |
672 | |
673 /* never reached */ | |
674 return -1; | |
675 } | |
676 | |
677 | |
678 /****************************************************************/ | |
679 | |
680 /* returns: */ | |
681 /* 0 in case of success (the pdu should be sent */ | |
682 /* back to its originator even if an SNMP error */ | |
683 /* was detected) */ | |
684 /* -1 in case of failure (no pdu should be sent */ | |
685 /* back) */ | |
686 | |
687 static int agent_get(SNMP_pdu *pdu, char *error_label) | |
688 { | |
689 SNMP_variable *variable; | |
690 Node *node; | |
691 Object *object; | |
692 Column *column; | |
693 Entry *entry=NULL; | |
694 Oid suffix; | |
695 int index_err = 1; | |
696 Integer integer; | |
697 Oid oid; | |
698 String string; | |
699 Integer *integer_ptr; | |
700 Oid *oid_ptr; | |
701 String *string_ptr; | |
702 int snmp_error; | |
703 char *pointer=NULL; | |
704 Subid index[MAX_OID_LEN]; | |
705 | |
706 /* create index struct */ | |
707 IndexType index_obj; | |
708 int index_buffer[256]; | |
709 | |
710 int was_cached; | |
711 int i; | |
712 Index *pIndex; | |
713 int index_len; | |
714 | |
715 index_obj.len = 0; | |
716 index_obj.type = 0; | |
717 index_obj.value = index_buffer; | |
718 | |
719 | |
720 error_label[0] = '\0'; | |
721 | |
722 pdu->type = GET_RSP_MSG; | |
723 | |
724 for(variable = pdu->first_variable; variable; variable = variable->next_variable) | |
725 { | |
726 node = node_find(EXACT_ENTRY, &(variable->name), &suffix); | |
727 if(node == NULL) | |
728 { | |
729 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
730 pdu->error_index = index_err; | |
731 return 0; | |
732 } | |
733 /* we should not forget to free suffix.subids */ | |
734 | |
735 if(trace_level > 0) | |
736 { | |
737 trace("!! get(): processing the variable %s\n\n", | |
738 node->label); | |
739 } | |
740 | |
741 if(variable->type != NULLOBJ) | |
742 { | |
743 error("agent_get(): ASN.1 type (0x%x) is not NULL for node %s", | |
744 variable->type, | |
745 node->label); | |
746 variable->type = NULLOBJ; | |
747 } | |
748 | |
749 if(variable->val.string) | |
750 { | |
751 error("agent_get(): val is not NULL for node %s", node->label); | |
752 free(variable->val.string); | |
753 variable->val.string = NULL; | |
754 } | |
755 | |
756 if(variable->val_len) | |
757 { | |
758 error("agent_get(): val_len is not 0 for node %s", node->label); | |
759 variable->val_len = 0; | |
760 } | |
761 | |
762 switch(node->type) | |
763 { | |
764 case OBJECT: | |
765 object = node->data.object; | |
766 | |
767 if( (suffix.len != 1) | |
768 || (suffix.subids[0] != 0) ) | |
769 { | |
770 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
771 pdu->error_index = index_err; | |
772 SSAOidZero(&suffix); | |
773 return 0; | |
774 } | |
775 | |
776 if( !(object->access & READ_FLAG) ) | |
777 { | |
778 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
779 pdu->error_index = index_err; | |
780 SSAOidZero(&suffix); | |
781 return 0; | |
782 } | |
783 | |
784 switch(object->asn1_type) | |
785 { | |
786 case INTEGER: | |
787 case COUNTER: | |
788 case GAUGE: | |
789 case TIMETICKS: | |
790 snmp_error = (*(object->get))(&integer); | |
791 break; | |
792 | |
793 case OBJID: | |
794 snmp_error = (*(object->get))(&oid); | |
795 if(snmp_error != SNMP_ERR_NOERROR && | |
796 ssa_mem_free != 0){ | |
797 if(object->dealloc != NULL) | |
798 (*(object->dealloc))(&oid); | |
799 } | |
800 break; | |
801 | |
802 case STRING: | |
803 case IPADDRESS: | |
804 case OPAQUE: | |
805 snmp_error = (*(object->get))(&string); | |
806 if(snmp_error != SNMP_ERR_NOERROR && | |
807 ssa_mem_free != 0){ | |
808 if(object->dealloc != NULL) | |
809 (*(object->dealloc))(&string); | |
810 } | |
811 break; | |
812 } | |
813 | |
814 if(snmp_error != SNMP_ERR_NOERROR) | |
815 { | |
816 if(snmp_error < 0) | |
817 { | |
818 error("the get() method of %s returned %d", | |
819 node->label, | |
820 snmp_error); | |
821 snmp_error = SNMP_ERR_GENERR; | |
822 } | |
823 pdu->error_status = snmp_error; | |
824 pdu->error_index = index_err; | |
825 SSAOidZero(&suffix); | |
826 return 0; | |
827 } | |
828 | |
829 /* variable->name */ | |
830 | |
831 /* variable->type */ | |
832 variable->type = object->asn1_type; | |
833 | |
834 /* variable->val, variable->val_len */ | |
835 switch(object->asn1_type) | |
836 { | |
837 case INTEGER: | |
838 case COUNTER: | |
839 case GAUGE: | |
840 case TIMETICKS: | |
841 variable->val.integer = (Integer *) malloc(sizeof(Integer)); | |
842 *(variable->val.integer) = integer; | |
843 variable->val_len = sizeof(Integer); | |
844 break; | |
845 | |
846 case OBJID: | |
847 variable->val.objid = (Subid *) malloc(oid.len * | |
848 (int32_t)sizeof(Subid)); | |
849 (void)memcpy(variable->val.objid, oid.subids, oid.len * | |
850 (int32_t)sizeof(Subid)); | |
851 variable->val_len = oid.len * | |
852 (int32_t)sizeof(Subid); | |
853 if (ssa_mem_free != 0){ | |
854 if(object->dealloc != NULL) | |
855 (*(object->dealloc))(&oid); | |
856 } | |
857 break; | |
858 | |
859 case STRING: | |
860 case IPADDRESS: | |
861 case OPAQUE: | |
862 variable->val.string = (u_char *) malloc(string.len); | |
863 (void)memcpy(variable->val.string, string.chars, string.len); | |
864 variable->val_len = string.len; | |
865 /*if(snmp_error != SNMP_ERR_NOERROR &&*/ | |
866 if (ssa_mem_free != 0){ | |
867 if(object->dealloc != NULL) | |
868 (*(object->dealloc))(&string); | |
869 } | |
870 break; | |
871 } | |
872 | |
873 | |
874 break; | |
875 | |
876 | |
877 case COLUMN: | |
878 column = node->data.column; | |
879 entry = column->entry; | |
880 pIndex=entry->first_index; | |
881 | |
882 if( !(column->access & READ_FLAG) ) | |
883 { | |
884 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
885 pdu->error_index = index_err; | |
886 SSAOidZero(&suffix); | |
887 return 0; | |
888 } | |
889 | |
890 if (suffix.subids == NULL) { | |
891 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
892 pdu->error_index = index_err; | |
893 SSAOidZero(&suffix); | |
894 return 0; | |
895 } | |
896 | |
897 index_len = 0; | |
898 for (pIndex=entry->first_index; pIndex; pIndex=pIndex->next_index) | |
899 { | |
900 if (pIndex->index_type == OCTET_STRING) | |
901 index_len = index_len + (pIndex->index_len)+1; /* add extra suffix for str len */ | |
902 else | |
903 index_len = index_len +1; /* one subid per index */ | |
904 } | |
905 | |
906 | |
907 for (i=0; i < index_len; i++) | |
908 { | |
909 index[i] = suffix.subids[i]; | |
910 index_obj.value[i] = suffix.subids[i]; | |
911 } | |
912 index_obj.len = index_len; | |
913 for (i=0; i < index_len; i++) | |
914 { | |
915 if( (cache_input_entry == entry) && (cache_input_index[i] == index[i]) ) | |
916 was_cached = 1; | |
917 else | |
918 { | |
919 was_cached = 0; | |
920 break; | |
921 } | |
922 } | |
923 if (was_cached) | |
924 { | |
925 pointer = cache_output_pointer; | |
926 snmp_error = cache_output_snmp_error; | |
927 } | |
928 else | |
929 { | |
930 if(cache_input_entry != NULL && cache_output_pointer != NULL) | |
931 if(ssa_mem_free != 0 && cache_input_entry->dealloc != NULL) | |
932 { | |
933 (*(cache_input_entry->dealloc))(cache_output_pointer); | |
934 cache_output_pointer = NULL; | |
935 } | |
936 cache_input_entry = entry; | |
937 for (i=0; i < index_len; i++) | |
938 cache_input_index[i] = index[i]; | |
939 snmp_error = (*(entry->get))(EXACT_ENTRY,&pointer,&index_obj); | |
940 cache_output_pointer = pointer; | |
941 cache_output_snmp_error = snmp_error; | |
942 } | |
943 | |
944 | |
945 if(pointer == NULL) | |
946 { | |
947 if(snmp_error < 0) | |
948 { | |
949 error("the get() method of %s returned %d", | |
950 node->parent->label, | |
951 snmp_error); | |
952 snmp_error = SNMP_ERR_GENERR; | |
953 } | |
954 | |
955 pdu->error_status = snmp_error; | |
956 pdu->error_index = index_err; | |
957 SSAOidZero(&suffix); | |
958 return 0; | |
959 } | |
960 | |
961 /* variable->type */ | |
962 variable->type = column->asn1_type; | |
963 | |
964 /* variable->val, variable->val_len */ | |
965 switch(column->asn1_type) | |
966 { | |
967 case INTEGER: | |
968 case COUNTER: | |
969 case GAUGE: | |
970 case TIMETICKS: | |
971 integer_ptr = (Integer *) (pointer + | |
972 COLUMN_OFFSET(column->offset)); | |
973 variable->val.integer = (Integer *) malloc(sizeof(Integer)); | |
974 *(variable->val.integer) = *integer_ptr; | |
975 variable->val_len = sizeof(Integer); | |
976 break; | |
977 | |
978 case OBJID: | |
979 oid_ptr = (Oid *) (pointer + | |
980 COLUMN_OFFSET(column->offset)); | |
981 variable->val.objid = (Subid *) malloc(oid_ptr->len * | |
982 (int32_t)sizeof(Subid)); | |
983 (void)memcpy(variable->val.objid, oid_ptr->subids, oid_ptr->len * | |
984 (int32_t)sizeof(Subid)); | |
985 variable->val_len = oid_ptr->len * | |
986 (int32_t)sizeof(Subid); | |
987 break; | |
988 | |
989 case STRING: | |
990 case IPADDRESS: | |
991 case OPAQUE: | |
992 string_ptr = (String *) (pointer + | |
993 COLUMN_OFFSET(column->offset)); | |
994 variable->val.string = (u_char *) malloc(string_ptr->len); | |
995 (void)memcpy(variable->val.string, string_ptr->chars, string_ptr->len); | |
996 variable->val_len = string_ptr->len; | |
997 break; | |
998 } | |
999 | |
1000 break; | |
1001 | |
1002 | |
1003 case NODE: | |
1004 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
1005 pdu->error_index = index_err; | |
1006 SSAOidZero(&suffix); | |
1007 return 0; | |
1008 } | |
1009 | |
1010 SSAOidZero(&suffix); | |
1011 | |
1012 index_err++; | |
1013 } | |
1014 /* Moved this down from the column loop because the cache must be | |
1015 freed only when the required columns in the row are all read. | |
1016 - Added pointer check because pointer will point to nothing | |
1017 if this is a non-column get. | |
1018 - Added setting cache_output_pointer to NULL because if it is not | |
1019 NULL, the next iteration of agent_process will try to delete it */ | |
1020 /* Bug fix 4127458 . Added check to see if entry has been initialized */ | |
1021 if(ssa_mem_free != 0 && entry != NULL && entry->dealloc != NULL && pointer != NULL){ | |
1022 /* remember to turn off the caching */ | |
1023 (*(entry->dealloc))(pointer); | |
1024 pointer = cache_output_pointer = NULL; | |
1025 } | |
1026 | |
1027 return 0; | |
1028 } | |
1029 | |
1030 | |
1031 /****************************************************************/ | |
1032 | |
1033 /* returns */ | |
1034 /* 0 in case of success. If we are in the */ | |
1035 /* FIRST_PASS, we should go to the second pass. */ | |
1036 /* -1 in case of failure. If we are in the */ | |
1037 /* FIRST_PASS, we should not go to the */ | |
1038 /* second pass and no pdu should be sent back. */ | |
1039 /* 1 If we are in the FIRST_PASS, we should not go */ | |
1040 /* to the second pass but a pdu */ | |
1041 /* with an SNMP error should be sent back to its */ | |
1042 /* originator */ | |
1043 | |
1044 static int agent_set(int pass, SNMP_pdu *pdu, char *error_label) | |
1045 { | |
1046 SNMP_variable *variable; | |
1047 Node *node; | |
1048 Object *object; | |
1049 Column *column; | |
1050 Entry *entry; | |
1051 Oid suffix; | |
1052 int index = 1; | |
1053 Integer integer = 0; | |
1054 Oid oid = { NULL, 0 }; | |
1055 String string = { NULL, 0 }; | |
1056 int snmp_error; | |
1057 int i; | |
1058 | |
1059 /* create index struct */ | |
1060 IndexType index_obj; | |
1061 int index_buffer[256]; | |
1062 | |
1063 index_obj.len = 0; | |
1064 index_obj.value = index_buffer; | |
1065 | |
1066 error_label[0] = '\0'; | |
1067 | |
1068 pdu->type = GET_RSP_MSG; | |
1069 | |
1070 for(variable = pdu->first_variable; variable; variable = variable->next_variable) | |
1071 { | |
1072 node = node_find(EXACT_ENTRY, &(variable->name), &suffix); | |
1073 if(node == NULL) | |
1074 { | |
1075 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
1076 pdu->error_index = index; | |
1077 return 1; | |
1078 } | |
1079 /* we should not forget to free suffix.subids */ | |
1080 | |
1081 if(trace_level > 0) | |
1082 { | |
1083 trace("!! set(%s): processing the variable %s\n\n", | |
1084 (pass == FIRST_PASS)? "FIRST_PASS": "SECOND_PASS", | |
1085 node->label); | |
1086 } | |
1087 | |
1088 /* | |
1089 if(variable->val.string == NULL) | |
1090 { | |
1091 (void)sprintf(error_label, "val.string is NULL for node %s", | |
1092 node->label); | |
1093 SSAOidZero(&suffix); | |
1094 return -1; | |
1095 } | |
1096 | |
1097 if(variable->val_len == 0) | |
1098 { | |
1099 (void)sprintf(error_label, "val_len is 0 for node %s", | |
1100 node->label); | |
1101 SSAOidZero(&suffix); | |
1102 return -1; | |
1103 } | |
1104 */ | |
1105 | |
1106 switch(node->type) | |
1107 { | |
1108 case OBJECT: | |
1109 object = node->data.object; | |
1110 | |
1111 /* check the ASN.1 type */ | |
1112 if(variable->type != object->asn1_type) | |
1113 { | |
1114 (void)sprintf(error_label, "wrong ASN.1 type (0x%x) for node %s", | |
1115 variable->type, node->label); | |
1116 SSAOidZero(&suffix); | |
1117 return -1; | |
1118 } | |
1119 | |
1120 /* check the suffix */ | |
1121 if( (suffix.len != 1) | |
1122 || (suffix.subids[0] != 0) ) | |
1123 { | |
1124 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
1125 pdu->error_index = index; | |
1126 SSAOidZero(&suffix); | |
1127 return 1; | |
1128 } | |
1129 | |
1130 /* check the access */ | |
1131 if( !(object->access & WRITE_FLAG) ) | |
1132 { | |
1133 pdu->error_status = SNMP_ERR_READONLY; | |
1134 pdu->error_index = index; | |
1135 SSAOidZero(&suffix); | |
1136 return 1; | |
1137 } | |
1138 | |
1139 /* check the value length */ | |
1140 switch(object->asn1_type) | |
1141 { | |
1142 case INTEGER: | |
1143 case COUNTER: | |
1144 case GAUGE: | |
1145 case TIMETICKS: | |
1146 case IPADDRESS: | |
1147 if(variable->val_len != 4) | |
1148 { | |
1149 (void)sprintf(error_label, "val_len is not 4 (%d) for node %s", | |
1150 variable->val_len, node->label); | |
1151 SSAOidZero(&suffix); | |
1152 return -1; | |
1153 } | |
1154 | |
1155 if(variable->val.integer == NULL) | |
1156 { | |
1157 (void)sprintf(error_label, "val.integer is NULL for node %s", | |
1158 node->label); | |
1159 SSAOidZero(&suffix); | |
1160 return -1; | |
1161 } | |
1162 | |
1163 break; | |
1164 } | |
1165 | |
1166 /* in case of enumerated integer, check the value */ | |
1167 if( (object->asn1_type == INTEGER) | |
1168 && (object->first_enum != NULL) ) | |
1169 { | |
1170 Enum *enums; | |
1171 | |
1172 | |
1173 integer = *(variable->val.integer); | |
1174 | |
1175 for(enums = object->first_enum; enums; enums = enums->next_enum) | |
1176 { | |
1177 if(enums->value == integer) | |
1178 { | |
1179 break; | |
1180 } | |
1181 } | |
1182 | |
1183 if(enums == NULL) | |
1184 { | |
1185 pdu->error_status = SNMP_ERR_BADVALUE; | |
1186 pdu->error_index = index; | |
1187 SSAOidZero(&suffix); | |
1188 return 1; | |
1189 } | |
1190 } | |
1191 | |
1192 switch(object->asn1_type) | |
1193 { | |
1194 case INTEGER: | |
1195 case COUNTER: | |
1196 case GAUGE: | |
1197 case TIMETICKS: | |
1198 integer = *(variable->val.integer); | |
1199 | |
1200 snmp_error = (*(object->set))(pass, &integer); | |
1201 break; | |
1202 | |
1203 case OBJID: | |
1204 if(SSAOidInit(&oid, variable->val.objid, | |
1205 variable->val_len / (int32_t)sizeof(Subid), | |
1206 error_label)) { | |
1207 SSAOidZero(&suffix); | |
1208 return -1; | |
1209 } | |
1210 | |
1211 snmp_error = (*(object->set))(pass, &oid); | |
1212 SSAOidZero(&oid); | |
1213 break; | |
1214 | |
1215 case STRING: | |
1216 case IPADDRESS: | |
1217 case OPAQUE: | |
1218 if(SSAStringInit(&string, variable->val.string, | |
1219 variable->val_len, error_label)) { | |
1220 SSAOidZero(&suffix); | |
1221 return -1; | |
1222 } | |
1223 | |
1224 snmp_error = (*(object->set))(pass, &string); | |
1225 SSAStringZero(&string); | |
1226 break; | |
1227 } | |
1228 | |
1229 if(snmp_error != SNMP_ERR_NOERROR) | |
1230 { | |
1231 if(snmp_error < 0) | |
1232 { | |
1233 error("the set(%s) method of %s returned %d", | |
1234 (pass == FIRST_PASS)? "FIRST_PASS": "SECOND_PASS", | |
1235 node->label, snmp_error); | |
1236 snmp_error = SNMP_ERR_GENERR; | |
1237 } | |
1238 pdu->error_status = snmp_error; | |
1239 pdu->error_index = index; | |
1240 SSAOidZero(&suffix); | |
1241 return 1; | |
1242 } | |
1243 | |
1244 break; | |
1245 | |
1246 | |
1247 case COLUMN: | |
1248 column = node->data.column; | |
1249 entry = column->entry; | |
1250 | |
1251 /* check the ASN.1 type */ | |
1252 if(variable->type != column->asn1_type) | |
1253 { | |
1254 (void)sprintf(error_label, "wrong ASN.1 type (0x%x) for node %s", | |
1255 variable->type, node->label); | |
1256 SSAOidZero(&suffix); | |
1257 return -1; | |
1258 } | |
1259 | |
1260 /* check the suffix */ | |
1261 if (suffix.subids == NULL) | |
1262 { | |
1263 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
1264 pdu->error_index = index; | |
1265 SSAOidZero(&suffix); | |
1266 return 1; | |
1267 } | |
1268 | |
1269 /* check the access */ | |
1270 if( !(column->access & WRITE_FLAG) ) | |
1271 { | |
1272 pdu->error_status = SNMP_ERR_READONLY; | |
1273 pdu->error_index = index; | |
1274 SSAOidZero(&suffix); | |
1275 return 1; | |
1276 } | |
1277 | |
1278 /* check the value length */ | |
1279 switch(column->asn1_type) | |
1280 { | |
1281 case INTEGER: | |
1282 case COUNTER: | |
1283 case GAUGE: | |
1284 case TIMETICKS: | |
1285 case IPADDRESS: | |
1286 if(variable->val_len != 4) | |
1287 { | |
1288 (void)sprintf(error_label, "val_len is not 4 (%d) for node %s", | |
1289 variable->val_len, node->label); | |
1290 SSAOidZero(&suffix); | |
1291 return -1; | |
1292 } | |
1293 | |
1294 if(variable->val.integer == NULL) | |
1295 { | |
1296 (void)sprintf(error_label, "val.integer is NULL for node %s", | |
1297 node->label); | |
1298 SSAOidZero(&suffix); | |
1299 return -1; | |
1300 } | |
1301 | |
1302 break; | |
1303 } | |
1304 | |
1305 /* in case of enumerated integer, check the value */ | |
1306 if( (column->asn1_type == INTEGER) | |
1307 && (column->first_enum != NULL) ) | |
1308 { | |
1309 Enum *enums; | |
1310 | |
1311 | |
1312 integer = *(variable->val.integer); | |
1313 | |
1314 for(enums = column->first_enum; enums; enums = enums->next_enum) | |
1315 { | |
1316 if(enums->value == integer) | |
1317 { | |
1318 break; | |
1319 } | |
1320 } | |
1321 | |
1322 if(enums == NULL) | |
1323 { | |
1324 pdu->error_status = SNMP_ERR_BADVALUE; | |
1325 pdu->error_index = index; | |
1326 SSAOidZero(&suffix); | |
1327 return 1; | |
1328 } | |
1329 } | |
1330 | |
1331 switch(column->asn1_type) | |
1332 { | |
1333 case INTEGER: | |
1334 case COUNTER: | |
1335 case GAUGE: | |
1336 case TIMETICKS: | |
1337 integer = *(variable->val.integer); | |
1338 | |
1339 break; | |
1340 | |
1341 case OBJID: | |
1342 if(SSAOidInit(&oid, variable->val.objid, | |
1343 variable->val_len / (int32_t)sizeof(Subid), error_label)) { | |
1344 SSAOidZero(&suffix); | |
1345 return -1; | |
1346 } | |
1347 | |
1348 break; | |
1349 | |
1350 case STRING: | |
1351 case IPADDRESS: | |
1352 case OPAQUE: | |
1353 if(SSAStringInit(&string, variable->val.string, variable->val_len, error_label)) | |
1354 { | |
1355 SSAOidZero(&suffix); | |
1356 return -1; | |
1357 } | |
1358 | |
1359 break; | |
1360 } | |
1361 | |
1362 index_obj.len = suffix.len; | |
1363 for (i = 0; i < suffix.len; i++) | |
1364 { | |
1365 index_obj.value[i] = suffix.subids[i]; | |
1366 } | |
1367 | |
1368 | |
1369 switch(column->asn1_type) | |
1370 { | |
1371 case INTEGER: | |
1372 case COUNTER: | |
1373 case GAUGE: | |
1374 case TIMETICKS: | |
1375 snmp_error = (*(column->set))(pass, index_obj, &integer); | |
1376 break; | |
1377 | |
1378 case OBJID: | |
1379 snmp_error = (*(column->set))(pass, index_obj, &oid); | |
1380 break; | |
1381 | |
1382 case STRING: | |
1383 case IPADDRESS: | |
1384 case OPAQUE: | |
1385 snmp_error = (*(column->set))(pass, index_obj, &string); | |
1386 break; | |
1387 } | |
1388 | |
1389 switch(column->asn1_type) | |
1390 { | |
1391 case OBJID: | |
1392 SSAOidZero(&oid); | |
1393 break; | |
1394 | |
1395 case STRING: | |
1396 case IPADDRESS: | |
1397 case OPAQUE: | |
1398 SSAStringZero(&string); | |
1399 break; | |
1400 } | |
1401 | |
1402 if(snmp_error != SNMP_ERR_NOERROR) | |
1403 { | |
1404 if(snmp_error < 0) | |
1405 { | |
1406 error("the set(%s) method of %s returned %d", | |
1407 (pass == FIRST_PASS)? "FIRST_PASS": "SECOND_PASS", | |
1408 node->parent->label, | |
1409 snmp_error); | |
1410 snmp_error = SNMP_ERR_GENERR; | |
1411 } | |
1412 | |
1413 pdu->error_status = snmp_error; | |
1414 pdu->error_index = index; | |
1415 SSAOidZero(&suffix); | |
1416 return 1; | |
1417 } | |
1418 | |
1419 break; | |
1420 | |
1421 | |
1422 case NODE: | |
1423 pdu->error_status = SNMP_ERR_NOSUCHNAME; | |
1424 pdu->error_index = index; | |
1425 SSAOidZero(&suffix); | |
1426 return 1; | |
1427 } | |
1428 | |
1429 SSAOidZero(&suffix); | |
1430 | |
1431 index++; | |
1432 } | |
1433 | |
1434 return 0; | |
1435 } | |
1436 | |
1437 | |
1438 /****************************************************************/ | |
1439 | |
1440 /* flag == 0 means turn off auto mem free */ | |
1441 void SSAAutoMemFree(int flag) | |
1442 { | |
1443 ssa_mem_free = flag; | |
1444 } | |
1445 | |
1446 |