and added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / util_funcs.c
1 /*
2  * util_funcs.c
3  */
4
5 #include <net-snmp/net-snmp-config.h>
6
7 #if HAVE_IO_H
8 #include <io.h>
9 #endif
10 #include <stdio.h>
11 #if HAVE_STDLIB_H
12 #include <stdlib.h>
13 #endif
14 #if HAVE_MALLOC_H
15 #include <malloc.h>
16 #endif
17 #include <sys/types.h>
18 #ifdef __alpha
19 #ifndef _BSD
20 #define _BSD
21 #define _myBSD
22 #endif
23 #endif
24 #if HAVE_SYS_WAIT_H
25 # include <sys/wait.h>
26 #endif
27 #ifdef __alpha
28 #ifdef _myBSD
29 #undef _BSD
30 #undef _myBSD
31 #endif
32 #endif
33 #ifndef WEXITSTATUS
34 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
35 #endif
36 #ifndef WIFEXITED
37 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
38 #endif
39 #if TIME_WITH_SYS_TIME
40 # ifdef WIN32
41 #  include <sys/timeb.h>
42 # else
43 #  include <sys/time.h>
44 # endif
45 # include <time.h>
46 #else
47 # if HAVE_SYS_TIME_H
48 #  include <sys/time.h>
49 # else
50 #  include <time.h>
51 # endif
52 #endif
53 #if HAVE_UNISTD_H
54 #include <unistd.h>
55 #endif
56 #if HAVE_FCNTL_H
57 #include <fcntl.h>
58 #endif
59 #include <errno.h>
60 #include <signal.h>
61 #if HAVE_STRING_H
62 #include <string.h>
63 #else
64 #include <strings.h>
65 #endif
66 #include <ctype.h>
67 #if HAVE_WINSOCK_H
68 #include <winsock.h>
69 #endif
70 #if HAVE_NETINET_IN_H
71 #include <netinet/in.h>
72 #endif
73 #if HAVE_BASETSD_H
74 #include <basetsd.h>
75 #define ssize_t SSIZE_T
76 #endif
77 #if HAVE_RAISE
78 #define alarm raise
79 #endif
80
81 #include <net-snmp/net-snmp-includes.h>
82 #include <net-snmp/agent/net-snmp-agent-includes.h>
83
84 #include "struct.h"
85 #include "util_funcs.h"
86
87 #if HAVE_LIMITS_H
88 #include "limits.h"
89 #endif
90 #ifdef USING_UCD_SNMP_ERRORMIB_MODULE
91 #include "ucd-snmp/errormib.h"
92 #else
93 #define setPerrorstatus(x) snmp_log_perror(x)
94 #endif
95
96
97 #ifdef EXCACHETIME
98 static long     cachetime;
99 #endif
100
101 extern int      numprocs, numextens;
102
103 void
104 Exit(int var)
105 {
106     snmp_log(LOG_ERR, "Server Exiting with code %d\n", var);
107     exit(var);
108 }
109
110 #ifdef BRCM_SNMP_NOT_USE
111 static const char *
112 make_tempfile(void)
113 {
114     static char     name[32];
115     int             fd = -1;
116
117     strcpy(name, "/tmp/snmpdXXXXXX");
118 #ifdef HAVE_MKSTEMP
119     fd = mkstemp(name);
120 #else
121     if (mktemp(name))
122         fd = open(name, O_CREAT | O_EXCL | O_WRONLY);
123 #endif
124     if (fd >= 0) {
125         close(fd);
126         return name;
127     }
128     return NULL;
129 }
130
131 int
132 shell_command(struct extensible *ex)
133 {
134 #if HAVE_SYSTEM
135     const char     *ofname;
136     char            shellline[STRMAX];
137     FILE           *shellout;
138
139     ofname = make_tempfile();
140     if (ofname == NULL) {
141         ex->output[0] = 0;
142         ex->result = 127;
143         return ex->result;
144     }
145
146     snprintf(shellline, sizeof(shellline), "%s > %s", ex->command, ofname);
147     shellline[ sizeof(shellline)-1 ] = 0;
148     ex->result = system(shellline);
149     ex->result = WEXITSTATUS(ex->result);
150     shellout = fopen(ofname, "r");
151     if (shellout != NULL) {
152         if (fgets(ex->output, sizeof(ex->output), shellout) == NULL) {
153             ex->output[0] = 0;
154         }
155         fclose(shellout);
156     }
157     unlink(ofname);
158 #else
159     ex->output[0] = 0;
160     ex->result = 0;
161 #endif
162     return (ex->result);
163 }
164
165 #define MAXOUTPUT 300
166
167 int
168 exec_command(struct extensible *ex)
169 {
170 #if HAVE_EXECV
171     int             fd;
172     FILE           *file;
173
174     if ((fd = get_exec_output(ex)) != -1) {
175         file = fdopen(fd, "r");
176         if (fgets(ex->output, sizeof(ex->output), file) == NULL) {
177             ex->output[0] = 0;
178         }
179         fclose(file);
180         wait_on_exec(ex);
181     } else
182 #endif
183     {
184         ex->output[0] = 0;
185         ex->result = 0;
186     }
187     return (ex->result);
188 }
189
190 void
191 wait_on_exec(struct extensible *ex)
192 {
193 #ifndef EXCACHETIME
194     if (ex->pid && waitpid(ex->pid, &ex->result, 0) < 0) {
195         setPerrorstatus("waitpid");
196     }
197     ex->pid = 0;
198 #endif
199 }
200
201 #define MAXARGS 30
202
203 int
204 get_exec_output(struct extensible *ex)
205 {
206 #if HAVE_EXECV
207     int             fd[2], i, cnt;
208     char            ctmp[STRMAX], *cptr1, *cptr2, argvs[STRMAX], **argv,
209         **aptr;
210 #ifdef EXCACHETIME
211     char            cachefile[STRMAX];
212     char            cache[MAXCACHESIZE];
213     ssize_t         cachebytes;
214     long            curtime;
215     static char     lastcmd[STRMAX];
216     int             cfd;
217     static int      lastresult;
218     int             readcount;
219 #endif
220
221 #ifdef EXCACHETIME
222     sprintf(cachefile, "%s/%s", get_persistent_directory(), CACHEFILE);
223     curtime = time(NULL);
224     if (curtime > (cachetime + EXCACHETIME) ||
225         strcmp(ex->command, lastcmd) != 0) {
226         strcpy(lastcmd, ex->command);
227         cachetime = curtime;
228 #endif
229         if (pipe(fd)) {
230             setPerrorstatus("pipe");
231 #ifdef EXCACHETIME
232             cachetime = 0;
233 #endif
234             return -1;
235         }
236         if ((ex->pid = fork()) == 0) {
237             close(1);
238             if (dup(fd[1]) != 1) {
239                 setPerrorstatus("dup");
240                 return -1;
241             }
242
243             /*
244              * write standard output and standard error to pipe. 
245              */
246             /*
247              * close all other file descriptors. 
248              */
249             for (cnt = getdtablesize() - 1; cnt >= 2; --cnt)
250                 (void) close(cnt);
251             (void) dup(1);      /* stderr */
252
253             /*
254              * set standard input to /dev/null 
255              */
256             close(0);
257             (void) open("/dev/null", O_RDWR);
258
259             for (cnt = 1, cptr1 = ex->command, cptr2 = argvs;
260                  cptr1 && *cptr1 != 0; cptr2++, cptr1++) {
261                 *cptr2 = *cptr1;
262                 if (*cptr1 == ' ') {
263                     *(cptr2++) = 0;
264                     if ((cptr1 = skip_white(cptr1)) == NULL)
265                         break;
266                     if (cptr1) {
267                         *cptr2 = *cptr1;
268                         if (*cptr1 != 0)
269                             cnt++;
270                     }
271                 }
272             }
273             *cptr2 = 0;
274             *(cptr2 + 1) = 0;
275             argv = (char **) malloc((cnt + 2) * sizeof(char *));
276             if (argv == NULL)
277                 return 0;       /* memory alloc error */
278             aptr = argv;
279             *(aptr++) = argvs;
280             for (cptr2 = argvs, i = 1; i != cnt; cptr2++)
281                 if (*cptr2 == 0) {
282                     *(aptr++) = cptr2 + 1;
283                     i++;
284                 }
285             while (*cptr2 != 0)
286                 cptr2++;
287             *(aptr++) = NULL;
288             copy_nword(ex->command, ctmp, sizeof(ctmp));
289             execv(ctmp, argv);
290             perror(ctmp);
291             exit(1);
292         } else {
293             close(fd[1]);
294             if (ex->pid < 0) {
295                 close(fd[0]);
296                 setPerrorstatus("fork");
297 #ifdef EXCACHETIME
298                 cachetime = 0;
299 #endif
300                 return -1;
301             }
302 #ifdef EXCACHETIME
303             unlink(cachefile);
304             /*
305              * XXX  Use SNMP_FILEMODE_CLOSED instead of 644? 
306              */
307             if ((cfd =
308                  open(cachefile, O_WRONLY | O_TRUNC | O_CREAT,
309                       0644)) < 0) {
310                 setPerrorstatus(cachefile);
311                 cachetime = 0;
312                 return -1;
313             }
314             fcntl(fd[0], F_SETFL, O_NONBLOCK);  /* don't block on reads */
315 #ifdef HAVE_USLEEP
316             for (readcount = 0; readcount <= MAXREADCOUNT * 100 &&
317                  (cachebytes = read(fd[0], (void *) cache, MAXCACHESIZE));
318                  readcount++) {
319 #else
320             for (readcount = 0; readcount <= MAXREADCOUNT &&
321                  (cachebytes = read(fd[0], (void *) cache, MAXCACHESIZE));
322                  readcount++) {
323 #endif
324                 if (cachebytes > 0)
325                     write(cfd, (void *) cache, cachebytes);
326                 else if (cachebytes == -1 && errno != EAGAIN) {
327                     setPerrorstatus("read");
328                     break;
329                 } else
330 #ifdef HAVE_USLEEP
331                     usleep(10000);      /* sleeps for 0.01 sec */
332 #else
333                     sleep(1);
334 #endif
335             }
336             close(cfd);
337             close(fd[0]);
338             /*
339              * wait for the child to finish 
340              */
341             if (ex->pid > 0 && waitpid(ex->pid, &ex->result, 0) < 0) {
342                 setPerrorstatus("waitpid()");
343                 cachetime = 0;
344                 return -1;
345             }
346             ex->pid = 0;
347             ex->result = WEXITSTATUS(ex->result);
348             lastresult = ex->result;
349 #else                           /* !EXCACHETIME */
350             return (fd[0]);
351 #endif
352         }
353 #ifdef EXCACHETIME
354     } else {
355         ex->result = lastresult;
356     }
357     if ((cfd = open(cachefile, O_RDONLY)) < 0) {
358         setPerrorstatus(cachefile);
359         return -1;
360     }
361     return (cfd);
362 #endif
363
364 #else                           /* !HAVE_EXECV */
365     return -1;
366 #endif
367 }
368
369 int
370 get_exec_pipes(char *cmd, int *fdIn, int *fdOut, int *pid)
371 {
372 #if HAVE_EXECV
373     int             fd[2][2], i, cnt;
374     char            ctmp[STRMAX], *cptr1, *cptr2, argvs[STRMAX], **argv,
375         **aptr;
376     /*
377      * Setup our pipes 
378      */
379     if (pipe(fd[0]) || pipe(fd[1])) {
380         setPerrorstatus("pipe");
381         return 0;
382     }
383     if ((*pid = fork()) == 0) { /* First handle for the child */
384         close(0);
385         if (dup(fd[0][0]) != 0) {
386             setPerrorstatus("dup 0");
387             return 0;
388         }
389         close(1);
390         if (dup(fd[1][1]) != 1) {
391             setPerrorstatus("dup 1");
392             return 0;
393         }
394
395         /*
396          * write standard output and standard error to pipe. 
397          */
398         /*
399          * close all non-standard open file descriptors 
400          */
401         for (cnt = getdtablesize() - 1; cnt >= 2; --cnt)
402             (void) close(cnt);
403         (void) dup(1);          /* stderr */
404
405         for (cnt = 1, cptr1 = cmd, cptr2 = argvs; *cptr1 != 0;
406              cptr2++, cptr1++) {
407             *cptr2 = *cptr1;
408             if (*cptr1 == ' ') {
409                 *(cptr2++) = 0;
410                 if ((cptr1 = skip_white(cptr1)) == NULL)
411                     break;
412                 *cptr2 = *cptr1;
413                 if (*cptr1 != 0)
414                     cnt++;
415             }
416         }
417         *cptr2 = 0;
418         *(cptr2 + 1) = 0;
419         argv = (char **) malloc((cnt + 2) * sizeof(char *));
420         if (argv == NULL)
421             return 0;           /* memory alloc error */
422         aptr = argv;
423         *(aptr++) = argvs;
424         for (cptr2 = argvs, i = 1; i != cnt; cptr2++)
425             if (*cptr2 == 0) {
426                 *(aptr++) = cptr2 + 1;
427                 i++;
428             }
429         while (*cptr2 != 0)
430             cptr2++;
431         *(aptr++) = NULL;
432         copy_nword(cmd, ctmp, sizeof(ctmp));
433         execv(ctmp, argv);
434         perror(ctmp);
435         exit(1);
436     } else {
437         close(fd[0][0]);
438         close(fd[1][1]);
439         if (*pid < 0) {
440             close(fd[0][1]);
441             close(fd[1][0]);
442             setPerrorstatus("fork");
443             return 0;
444         }
445         *fdIn = fd[1][0];
446         *fdOut = fd[0][1];
447         return (1);             /* We are returning 0 for error... */
448     }
449 #endif                          /* !HAVE_EXECV */
450     return 0;
451 }
452
453 int
454 clear_cache(int action,
455             u_char * var_val,
456             u_char var_val_type,
457             size_t var_val_len,
458             u_char * statP, oid * name, size_t name_len)
459 {
460
461     long            tmp = 0;
462
463     if (var_val_type != ASN_INTEGER) {
464         snmp_log(LOG_NOTICE, "Wrong type != int\n");
465         return SNMP_ERR_WRONGTYPE;
466     }
467     tmp = *((long *) var_val);
468     if (tmp == 1 && action == COMMIT) {
469 #ifdef EXCACHETIME
470         cachetime = 0;          /* reset the cache next read */
471 #endif
472     }
473     return SNMP_ERR_NOERROR;
474 }
475 #endif /* BRCM_SNMP_NOT_USE */
476 char          **argvrestartp, *argvrestartname, *argvrestart;
477
478 #ifdef BRCM_SNMP_NOT_USE
479 RETSIGTYPE
480 restart_doit(int a)
481 {
482     snmp_shutdown("snmpd");
483
484     /*
485      * do the exec 
486      */
487 #if HAVE_EXECV
488     execv(argvrestartname, argvrestartp);
489     setPerrorstatus(argvrestartname);
490 #endif
491 }
492
493 int
494 restart_hook(int action,
495              u_char * var_val,
496              u_char var_val_type,
497              size_t var_val_len,
498              u_char * statP, oid * name, size_t name_len)
499 {
500
501     long            tmp = 0;
502
503     if (var_val_type != ASN_INTEGER) {
504         snmp_log(LOG_NOTICE, "Wrong type != int\n");
505         return SNMP_ERR_WRONGTYPE;
506     }
507     tmp = *((long *) var_val);
508     if (tmp == 1 && action == COMMIT) {
509 #ifdef SIGALRM
510         signal(SIGALRM, restart_doit);
511 #endif
512         alarm(RESTARTSLEEP);
513     }
514     return SNMP_ERR_NOERROR;
515 }
516
517 void
518 print_mib_oid(oid name[], size_t len)
519 {
520     char           *buffer;
521     buffer = (char *) malloc(11 * len); /* maximum digit lengths for int32 + a '.' */
522     if (!buffer) {
523         snmp_log(LOG_ERR, "Malloc failed - out of memory?");
524         return;
525     }
526     sprint_mib_oid(buffer, name, len);
527     snmp_log(LOG_NOTICE, "Mib: %s\n", buffer);
528     free(buffer);
529 }
530
531 void
532 sprint_mib_oid(char *buf, oid name[], size_t len)
533 {
534     int             i;
535     for (i = 0; i < (int) len; i++) {
536         sprintf(buf, ".%d", (int) name[i]);
537         while (*buf != 0)
538             buf++;
539     }
540 }
541 #endif /* BRCM_SNMP_NOT_USED */
542 /*******************************************************************-o-******
543  * header_simple_table
544  *
545  * Parameters:
546  *        *vp            Variable data.
547  *        *name          Fully instantiated OID name.
548  *        *length        Length of name.
549  *         exact         TRUE if an exact match is desired.
550  *        *var_len       Hook for size of returned data type.
551  *      (**write_method) Hook for write method (UNUSED).
552  *         max
553  *      
554  * Returns:
555  *      0       If name matches vp->name (accounting for 'exact') and is
556  *                      not greater in length than 'max'.
557  *      1       Otherwise.
558  *
559  *
560  * Compare 'name' to vp->name for the best match or an exact match (if
561  *      requested).  Also check that 'name' is not longer than 'max' if
562  *      max is greater-than/equal 0.
563  * Store a successful match in 'name', and increment the OID instance if
564  *      the match was not exact.  
565  *
566  * 'name' and 'length' are undefined upon failure.
567  *
568  */
569 int
570 header_simple_table(struct variable *vp, oid * name, size_t * length,
571                     int exact, size_t * var_len,
572                     WriteMethod ** write_method, int max)
573 {
574     int             i, rtest;   /* Set to:      -1      If name < vp->name,
575                                  *              1       If name > vp->name,
576                                  *              0       Otherwise.
577                                  */
578     oid             newname[MAX_OID_LEN];
579
580     for (i = 0, rtest = 0;
581          i < (int) vp->namelen && i < (int) (*length) && !rtest; i++) {
582         if (name[i] != vp->name[i]) {
583             if (name[i] < vp->name[i])
584                 rtest = -1;
585             else
586                 rtest = 1;
587         }
588     }
589     if (rtest > 0 ||
590         (exact == 1
591          && (rtest || (int) *length != (int) (vp->namelen + 1)))) {
592         if (var_len)
593             *var_len = 0;
594         return MATCH_FAILED;
595     }
596
597     memset(newname, 0, sizeof(newname));
598
599     if (((int) *length) <= (int) vp->namelen || rtest == -1) {
600         memmove(newname, vp->name, (int) vp->namelen * sizeof(oid));
601         newname[vp->namelen] = 1;
602         *length = vp->namelen + 1;
603     } else if (((int) *length) > (int) vp->namelen + 1) {       /* exact case checked earlier */
604         *length = vp->namelen + 1;
605         memmove(newname, name, (*length) * sizeof(oid));
606         if (name[*length - 1] < ULONG_MAX) {
607             newname[*length - 1] = name[*length - 1] + 1;
608         } else {
609             /*
610              * Careful not to overflow...  
611              */
612             newname[*length - 1] = name[*length - 1];
613         }
614     } else {
615         *length = vp->namelen + 1;
616         memmove(newname, name, (*length) * sizeof(oid));
617         if (!exact) {
618             if (name[*length - 1] < ULONG_MAX) {
619                 newname[*length - 1] = name[*length - 1] + 1;
620             } else {
621                 /*
622                  * Careful not to overflow...  
623                  */
624                 newname[*length - 1] = name[*length - 1];
625             }
626         } else {
627             newname[*length - 1] = name[*length - 1];
628         }
629     }
630     if ((max >= 0 && (newname[*length - 1] > max)) ||
631                ( 0 == newname[*length - 1] )) {
632         if (var_len)
633             *var_len = 0;
634         return MATCH_FAILED;
635     }
636
637     memmove(name, newname, (*length) * sizeof(oid));
638     if (write_method)
639         *write_method = 0;
640     if (var_len)
641         *var_len = sizeof(long);        /* default */
642     return (MATCH_SUCCEEDED);
643 }
644
645 /*
646  * header_generic(...
647  * Arguments:
648  * vp     IN      - pointer to variable entry that points here
649  * name    IN/OUT  - IN/name requested, OUT/name found
650  * length  IN/OUT  - length of IN/OUT oid's 
651  * exact   IN      - TRUE if an exact match was requested
652  * var_len OUT     - length of variable or 0 if function returned
653  * write_method
654  * 
655  */
656
657 /*******************************************************************-o-******
658  * generic_header
659  *
660  * Parameters:
661  *        *vp      (I)     Pointer to variable entry that points here.
662  *        *name    (I/O)   Input name requested, output name found.
663  *        *length  (I/O)   Length of input and output oid's.
664  *         exact   (I)     TRUE if an exact match was requested.
665  *        *var_len (O)     Length of variable or 0 if function returned.
666  *      (**write_method)   Hook to name a write method (UNUSED).
667  *      
668  * Returns:
669  *      MATCH_SUCCEEDED If vp->name matches name (accounting for exact bit).
670  *      MATCH_FAILED    Otherwise,
671  *
672  *
673  * Check whether variable (vp) matches name.
674  */
675 int
676 header_generic(struct variable *vp,
677                oid * name,
678                size_t * length,
679                int exact, size_t * var_len, WriteMethod ** write_method)
680 {
681     oid             newname[MAX_OID_LEN];
682     int             result;
683
684     DEBUGMSGTL(("util_funcs", "header_generic: "));
685     DEBUGMSGOID(("util_funcs", name, *length));
686     DEBUGMSG(("util_funcs", " exact=%d\n", exact));
687
688     memcpy((char *) newname, (char *) vp->name,
689            (int) vp->namelen * sizeof(oid));
690     newname[vp->namelen] = 0;
691     result = snmp_oid_compare(name, *length, newname, vp->namelen + 1);
692     DEBUGMSGTL(("util_funcs", "  result: %d\n", result));
693     if ((exact && (result != 0)) || (!exact && (result >= 0)))
694         return (MATCH_FAILED);
695     memcpy((char *) name, (char *) newname,
696            ((int) vp->namelen + 1) * sizeof(oid));
697     *length = vp->namelen + 1;
698
699     *write_method = 0;
700     *var_len = sizeof(long);    /* default to 'long' results */
701     return (MATCH_SUCCEEDED);
702 }
703
704 #ifdef BRCM_SNMP_NOT_USE
705 /*
706  * checkmib(): provided for backwards compatibility, do not use: 
707  */
708 int
709 checkmib(struct variable *vp, oid * name, size_t * length,
710          int exact, size_t * var_len, WriteMethod ** write_method, int max)
711 {
712     /*
713      * checkmib used to be header_simple_table, with reveresed boolean
714      * return output.  header_simple_table() was created to match
715      * header_generic(). 
716      */
717     return (!header_simple_table(vp, name, length, exact, var_len,
718                                  write_method, max));
719 }
720
721 char           *
722 find_field(char *ptr, int field)
723 {
724     int             i;
725     char           *init = ptr;
726
727     if (field == LASTFIELD) {
728         /*
729          * skip to end 
730          */
731         while (*ptr++);
732         ptr = ptr - 2;
733         /*
734          * rewind a field length 
735          */
736         while (*ptr != 0 && isspace(*ptr) && init <= ptr)
737             ptr--;
738         while (*ptr != 0 && !isspace(*ptr) && init <= ptr)
739             ptr--;
740         if (isspace(*ptr))
741             ptr++;              /* past space */
742         if (ptr < init)
743             ptr = init;
744         if (!isspace(*ptr) && *ptr != 0)
745             return (ptr);
746     } else {
747         if ((ptr = skip_white(ptr)) == NULL)
748             return (NULL);
749         for (i = 1; *ptr != 0 && i != field; i++) {
750             if ((ptr = skip_not_white(ptr)) == NULL)
751                 return (NULL);
752             if ((ptr = skip_white(ptr)) == NULL)
753                 return (NULL);
754         }
755         if (*ptr != 0 && i == field)
756             return (ptr);
757         return (NULL);
758     }
759     return (NULL);
760 }
761
762 int
763 parse_miboid(const char *buf, oid * oidout)
764 {
765     int             i;
766
767     if (!buf)
768         return 0;
769     if (*buf == '.')
770         buf++;
771     for (i = 0; isdigit(*buf); i++) {
772         oidout[i] = atoi(buf);
773         while (isdigit(*buf++));
774         if (*buf == '.')
775             buf++;
776     }
777     /*
778      * oidout[i] = -1; hmmm 
779      */
780     return i;
781 }
782
783 void
784 string_append_int(char *s, int val)
785 {
786     char            textVal[16];
787
788     if (val < 10) {
789         *s++ = '0' + val;
790         *s = '\0';
791         return;
792     }
793     sprintf(textVal, "%d", val);
794     strcpy(s, textVal);
795     return;
796 }
797
798 struct internal_mib_table {
799     int             max_size;   /* Size of the current data table */
800     int             next_index; /* Index of the next free entry */
801     int             current_index;      /* Index of the 'current' entry */
802     int             cache_timeout;
803     marker_t        cache_marker;
804     RELOAD         *reload;     /* Routine to read in the data */
805     COMPARE        *compare;    /* Routine to compare two entries */
806     int             data_size;  /* Size of an individual entry */
807     void           *data;       /* The table itself */
808 };
809
810 mib_table_t
811 Initialise_Table(int size, int timeout, RELOAD reload, COMPARE compare)
812 {
813     struct internal_mib_table *t;
814
815     t = (struct internal_mib_table *)
816         malloc(sizeof(struct internal_mib_table));
817     if (t == NULL)
818         return NULL;
819
820     t->max_size = 0;
821     t->next_index = 1;          /* Don't use index 0 */
822     t->current_index = 1;
823     t->cache_timeout = timeout;
824     t->cache_marker = NULL;
825     t->reload = reload;
826     t->compare = compare;
827     t->data_size = size;
828     t->data = NULL;
829
830     return (mib_table_t) t;
831 }
832
833 #define TABLE_ADD( x, y )       ((void*)((char*)(x) + y))
834 #define TABLE_INDEX(t, i)       (TABLE_ADD(t->data, i * t->data_size))
835 #define TABLE_START(t)          (TABLE_INDEX(t, 1))
836 #define TABLE_NEXT(t)           (TABLE_INDEX(t, t->next_index))
837 #define TABLE_CURRENT(t)        (TABLE_INDEX(t, t->current_index))
838
839 int
840 check_and_reload_table(struct internal_mib_table *table)
841 {
842     /*
843      * If the saved data is fairly recent,
844      *    we don't need to reload it
845      */
846     if (table->cache_marker &&
847         !(atime_ready(table->cache_marker, table->cache_timeout * 1000)))
848         return 1;
849
850
851     /*
852      * Call the routine provided to read in the data
853      *
854      * N.B:  Update the cache marker *before* calling
855      *   this routine, to avoid problems with recursion
856      */
857     if (!table->cache_marker)
858         table->cache_marker = atime_newMarker();
859     else
860         atime_setMarker(table->cache_marker);
861
862     table->next_index = 1;
863     if (table->reload((mib_table_t) table) < 0) {
864         free(table->cache_marker);
865         table->cache_marker = NULL;
866         return 0;
867     }
868     table->current_index = 1;
869     if (table->compare != NULL) /* Sort the table */
870         qsort(TABLE_START(table), table->next_index,
871               table->data_size, table->compare);
872     return 1;
873 }
874
875 int
876 Search_Table(mib_table_t t, void *entry, int exact)
877 {
878     struct internal_mib_table *table = (struct internal_mib_table *) t;
879     void           *entry2;
880     int             res;
881
882     if (!check_and_reload_table(table))
883         return -1;
884
885     if (table->compare == NULL) {
886         /*
887          * XXX - not sure this is right ? 
888          */
889         memcpy(entry, table->data, table->data_size);
890         return 0;
891     }
892
893     if (table->next_index == table->current_index)
894         table->current_index = 1;
895
896     entry2 = TABLE_CURRENT(table);
897     res = table->compare(entry, entry2);
898     if ((res < 0) && (table->current_index != 1)) {
899         table->current_index = 1;
900         entry2 = TABLE_CURRENT(table);
901         res = table->compare(entry, entry2);
902     }
903
904     while (res > 0) {
905         table->current_index++;
906         if (table->next_index == table->current_index)
907             return -1;
908         entry2 = TABLE_CURRENT(table);
909         res = table->compare(entry, entry2);
910     }
911
912     if (exact && res != 0)
913         return -1;
914
915     if (!exact && res == 0) {
916         table->current_index++;
917         if (table->next_index == table->current_index)
918             return -1;
919         entry2 = TABLE_CURRENT(table);
920     }
921     memcpy(entry, entry2, table->data_size);
922     return 0;
923 }
924
925 int
926 Add_Entry(mib_table_t t, void *entry)
927 {
928     struct internal_mib_table *table = (struct internal_mib_table *) t;
929     int             new_max;
930     void           *new_data;   /* Used for
931                                  *      a) extending the data table
932                                  *      b) the next entry to use
933                                  */
934
935     if (table->max_size <= table->next_index) {
936         /*
937          * Table is full, so extend it to double the size
938          */
939         new_max = 2 * table->max_size;
940         if (new_max == 0)
941             new_max = 10;       /* Start with 10 entries */
942
943         new_data = (void *) malloc(new_max * table->data_size);
944         if (new_data == NULL)
945             return -1;
946
947         if (table->data) {
948             memcpy(new_data, table->data,
949                    table->max_size * table->data_size);
950             free(table->data);
951         }
952         table->data = new_data;
953         table->max_size = new_max;
954     }
955
956     /*
957      * Insert the new entry into the data array
958      */
959     new_data = TABLE_NEXT(table);
960     memcpy(new_data, entry, table->data_size);
961     table->next_index++;
962     return 0;
963 }
964
965 void           *
966 Retrieve_Table_Data(mib_table_t t, int *max_idx)
967 {
968     struct internal_mib_table *table = (struct internal_mib_table *) t;
969
970     if (!check_and_reload_table(table))
971         return NULL;
972     *max_idx = table->next_index - 1;
973     return table->data;
974 }
975 #endif /* BRCM_SNMP_NOT_USE */