| /* |
| * util_funcs.c |
| */ |
| /* |
| * Portions of this file are copyrighted by: |
| * Copyright Copyright 2003 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms specified in the COPYING file |
| * distributed with the Net-SNMP package. |
| */ |
| |
| #include <net-snmp/net-snmp-config.h> |
| |
| #if HAVE_IO_H |
| #include <io.h> |
| #endif |
| #include <stdio.h> |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_MALLOC_H |
| #include <malloc.h> |
| #endif |
| #include <sys/types.h> |
| #ifdef __alpha |
| #ifndef _BSD |
| #define _BSD |
| #define _myBSD |
| #endif |
| #endif |
| #if HAVE_SYS_WAIT_H |
| # include <sys/wait.h> |
| #endif |
| #ifdef __alpha |
| #ifdef _myBSD |
| #undef _BSD |
| #undef _myBSD |
| #endif |
| #endif |
| #ifndef WEXITSTATUS |
| # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) |
| #endif |
| #ifndef WIFEXITED |
| # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) |
| #endif |
| #if TIME_WITH_SYS_TIME |
| # ifdef WIN32 |
| # include <sys/timeb.h> |
| # else |
| # include <sys/time.h> |
| # endif |
| # include <time.h> |
| #else |
| # if HAVE_SYS_TIME_H |
| # include <sys/time.h> |
| # else |
| # include <time.h> |
| # endif |
| #endif |
| #if HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #if HAVE_FCNTL_H |
| #include <fcntl.h> |
| #endif |
| #include <errno.h> |
| #include <signal.h> |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| #include <ctype.h> |
| #if HAVE_WINSOCK_H |
| #include <winsock.h> |
| #endif |
| #if HAVE_NETINET_IN_H |
| #include <netinet/in.h> |
| #endif |
| #if HAVE_BASETSD_H |
| #include <basetsd.h> |
| #define ssize_t SSIZE_T |
| #endif |
| #if HAVE_RAISE |
| #define alarm raise |
| #endif |
| #ifdef HAVE_SYS_STAT_H |
| #include <sys/stat.h> |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/agent/net-snmp-agent-includes.h> |
| |
| #include "struct.h" |
| #include "util_funcs.h" |
| |
| #if HAVE_LIMITS_H |
| #include "limits.h" |
| #endif |
| #ifdef USING_UCD_SNMP_ERRORMIB_MODULE |
| #include "ucd-snmp/errormib.h" |
| #else |
| #define setPerrorstatus(x) snmp_log_perror(x) |
| #endif |
| |
| |
| #ifdef EXCACHETIME |
| static long cachetime; |
| #endif |
| |
| extern int numprocs, numextens; |
| |
| void |
| Exit(int var) |
| { |
| snmp_log(LOG_ERR, "Server Exiting with code %d\n", var); |
| exit(var); |
| } |
| |
| const char * |
| make_tempfile(void) |
| { |
| static char name[32]; |
| int fd = -1; |
| |
| strcpy(name, get_temp_file_pattern()); |
| #ifdef HAVE_MKSTEMP |
| fd = mkstemp(name); |
| #else |
| if (mktemp(name)) { |
| # ifndef WIN32 |
| fd = open(name, O_CREAT | O_EXCL | O_WRONLY); |
| # else |
| /* |
| Win32 needs _S_IREAD | _S_IWRITE to set permissions on file after closing |
| */ |
| fd = _open(name, _O_CREAT, _S_IREAD | _S_IWRITE | _O_EXCL | _O_WRONLY); |
| # endif |
| } |
| #endif |
| if (fd >= 0) { |
| close(fd); |
| DEBUGMSGTL(("make_tempfile", "temp file created: %s\n", name)); |
| return name; |
| } |
| snmp_log(LOG_ERR,"make_tempfile: error creating file %s\n", name); |
| return NULL; |
| } |
| |
| int |
| shell_command(struct extensible *ex) |
| { |
| #if HAVE_SYSTEM |
| const char *ofname; |
| char shellline[STRMAX]; |
| FILE *shellout; |
| |
| ofname = make_tempfile(); |
| if (ofname == NULL) { |
| ex->output[0] = 0; |
| ex->result = 127; |
| return ex->result; |
| } |
| |
| snprintf(shellline, sizeof(shellline), "%s > %s", ex->command, ofname); |
| shellline[ sizeof(shellline)-1 ] = 0; |
| ex->result = system(shellline); |
| ex->result = WEXITSTATUS(ex->result); |
| shellout = fopen(ofname, "r"); |
| if (shellout != NULL) { |
| if (fgets(ex->output, sizeof(ex->output), shellout) == NULL) { |
| ex->output[0] = 0; |
| } |
| fclose(shellout); |
| } |
| unlink(ofname); |
| #else |
| ex->output[0] = 0; |
| ex->result = 0; |
| #endif |
| return (ex->result); |
| } |
| |
| #define MAXOUTPUT 300 |
| |
| int |
| exec_command(struct extensible *ex) |
| { |
| #if HAVE_EXECV |
| int fd; |
| FILE *file; |
| |
| if ((fd = get_exec_output(ex)) != -1) { |
| file = fdopen(fd, "r"); |
| if (fgets(ex->output, sizeof(ex->output), file) == NULL) { |
| ex->output[0] = 0; |
| } |
| fclose(file); |
| wait_on_exec(ex); |
| } else |
| #endif |
| { |
| ex->output[0] = 0; |
| ex->result = 0; |
| } |
| return (ex->result); |
| } |
| |
| struct extensible * |
| get_exten_instance(struct extensible *exten, size_t inst) |
| { |
| int i; |
| |
| if (exten == NULL) |
| return (NULL); |
| for (i = 1; i != (int) inst && exten != NULL; i++) |
| exten = exten->next; |
| return (exten); |
| } |
| |
| void |
| wait_on_exec(struct extensible *ex) |
| { |
| #ifndef EXCACHETIME |
| if (ex->pid && waitpid(ex->pid, &ex->result, 0) < 0) { |
| setPerrorstatus("waitpid"); |
| } |
| ex->pid = 0; |
| #endif |
| } |
| |
| #define MAXARGS 30 |
| |
| int |
| get_exec_output(struct extensible *ex) |
| { |
| #if HAVE_EXECV |
| int fd[2], i, cnt; |
| char ctmp[STRMAX], *cptr1, *cptr2, argvs[STRMAX], **argv, |
| **aptr; |
| #ifdef EXCACHETIME |
| char cachefile[STRMAX]; |
| char cache[MAXCACHESIZE]; |
| ssize_t cachebytes; |
| long curtime; |
| static char lastcmd[STRMAX]; |
| int cfd; |
| static int lastresult; |
| int readcount; |
| #endif |
| |
| #ifdef EXCACHETIME |
| sprintf(cachefile, "%s/%s", get_persistent_directory(), CACHEFILE); |
| curtime = time(NULL); |
| if (curtime > (cachetime + EXCACHETIME) || |
| strcmp(ex->command, lastcmd) != 0) { |
| strcpy(lastcmd, ex->command); |
| cachetime = curtime; |
| #endif |
| if (pipe(fd)) { |
| setPerrorstatus("pipe"); |
| #ifdef EXCACHETIME |
| cachetime = 0; |
| #endif |
| return -1; |
| } |
| if ((ex->pid = fork()) == 0) { |
| close(1); |
| if (dup(fd[1]) != 1) { |
| setPerrorstatus("dup"); |
| return -1; |
| } |
| |
| /* |
| * write standard output and standard error to pipe. |
| */ |
| /* |
| * close all other file descriptors. |
| */ |
| for (cnt = getdtablesize() - 1; cnt >= 2; --cnt) |
| (void) close(cnt); |
| (void) dup(1); /* stderr */ |
| |
| /* |
| * set standard input to /dev/null |
| */ |
| close(0); |
| (void) open("/dev/null", O_RDWR); |
| |
| for (cnt = 1, cptr1 = ex->command, cptr2 = argvs; |
| cptr1 && *cptr1 != 0; cptr2++, cptr1++) { |
| *cptr2 = *cptr1; |
| if (*cptr1 == ' ') { |
| *(cptr2++) = 0; |
| if ((cptr1 = skip_white(cptr1)) == NULL) |
| break; |
| if (cptr1) { |
| *cptr2 = *cptr1; |
| if (*cptr1 != 0) |
| cnt++; |
| } |
| } |
| } |
| *cptr2 = 0; |
| *(cptr2 + 1) = 0; |
| argv = (char **) malloc((cnt + 2) * sizeof(char *)); |
| if (argv == NULL) |
| return 0; /* memory alloc error */ |
| aptr = argv; |
| *(aptr++) = argvs; |
| for (cptr2 = argvs, i = 1; i != cnt; cptr2++) |
| if (*cptr2 == 0) { |
| *(aptr++) = cptr2 + 1; |
| i++; |
| } |
| while (*cptr2 != 0) |
| cptr2++; |
| *(aptr++) = NULL; |
| copy_nword(ex->command, ctmp, sizeof(ctmp)); |
| execv(ctmp, argv); |
| perror(ctmp); |
| exit(1); |
| } else { |
| close(fd[1]); |
| if (ex->pid < 0) { |
| close(fd[0]); |
| setPerrorstatus("fork"); |
| #ifdef EXCACHETIME |
| cachetime = 0; |
| #endif |
| return -1; |
| } |
| #ifdef EXCACHETIME |
| unlink(cachefile); |
| /* |
| * XXX Use SNMP_FILEMODE_CLOSED instead of 644? |
| */ |
| if ((cfd = |
| open(cachefile, O_WRONLY | O_TRUNC | O_CREAT, |
| 0644)) < 0) { |
| setPerrorstatus(cachefile); |
| cachetime = 0; |
| return -1; |
| } |
| fcntl(fd[0], F_SETFL, O_NONBLOCK); /* don't block on reads */ |
| #ifdef HAVE_USLEEP |
| for (readcount = 0; readcount <= MAXREADCOUNT * 100 && |
| (cachebytes = read(fd[0], (void *) cache, MAXCACHESIZE)); |
| readcount++) { |
| #else |
| for (readcount = 0; readcount <= MAXREADCOUNT && |
| (cachebytes = read(fd[0], (void *) cache, MAXCACHESIZE)); |
| readcount++) { |
| #endif |
| if (cachebytes > 0) |
| write(cfd, (void *) cache, cachebytes); |
| else if (cachebytes == -1 && errno != EAGAIN) { |
| setPerrorstatus("read"); |
| break; |
| } else |
| #ifdef HAVE_USLEEP |
| usleep(10000); /* sleeps for 0.01 sec */ |
| #else |
| sleep(1); |
| #endif |
| } |
| close(cfd); |
| close(fd[0]); |
| /* |
| * wait for the child to finish |
| */ |
| if (ex->pid > 0 && waitpid(ex->pid, &ex->result, 0) < 0) { |
| setPerrorstatus("waitpid()"); |
| cachetime = 0; |
| return -1; |
| } |
| ex->pid = 0; |
| ex->result = WEXITSTATUS(ex->result); |
| lastresult = ex->result; |
| #else /* !EXCACHETIME */ |
| return (fd[0]); |
| #endif |
| } |
| #ifdef EXCACHETIME |
| } else { |
| ex->result = lastresult; |
| } |
| if ((cfd = open(cachefile, O_RDONLY)) < 0) { |
| setPerrorstatus(cachefile); |
| return -1; |
| } |
| return (cfd); |
| #endif |
| |
| #else /* !HAVE_EXECV */ |
| return -1; |
| #endif |
| } |
| |
| int |
| get_exec_pipes(char *cmd, int *fdIn, int *fdOut, int *pid) |
| { |
| #if HAVE_EXECV |
| int fd[2][2], i, cnt; |
| char ctmp[STRMAX], *cptr1, *cptr2, argvs[STRMAX], **argv, |
| **aptr; |
| /* |
| * Setup our pipes |
| */ |
| if (pipe(fd[0]) || pipe(fd[1])) { |
| setPerrorstatus("pipe"); |
| return 0; |
| } |
| if ((*pid = fork()) == 0) { /* First handle for the child */ |
| close(0); |
| if (dup(fd[0][0]) != 0) { |
| setPerrorstatus("dup 0"); |
| return 0; |
| } |
| close(1); |
| if (dup(fd[1][1]) != 1) { |
| setPerrorstatus("dup 1"); |
| return 0; |
| } |
| |
| /* |
| * write standard output and standard error to pipe. |
| */ |
| /* |
| * close all non-standard open file descriptors |
| */ |
| for (cnt = getdtablesize() - 1; cnt >= 2; --cnt) |
| (void) close(cnt); |
| (void) dup(1); /* stderr */ |
| |
| for (cnt = 1, cptr1 = cmd, cptr2 = argvs; *cptr1 != 0; |
| cptr2++, cptr1++) { |
| *cptr2 = *cptr1; |
| if (*cptr1 == ' ') { |
| *(cptr2++) = 0; |
| if ((cptr1 = skip_white(cptr1)) == NULL) |
| break; |
| *cptr2 = *cptr1; |
| if (*cptr1 != 0) |
| cnt++; |
| } |
| } |
| *cptr2 = 0; |
| *(cptr2 + 1) = 0; |
| argv = (char **) malloc((cnt + 2) * sizeof(char *)); |
| if (argv == NULL) |
| return 0; /* memory alloc error */ |
| aptr = argv; |
| *(aptr++) = argvs; |
| for (cptr2 = argvs, i = 1; i != cnt; cptr2++) |
| if (*cptr2 == 0) { |
| *(aptr++) = cptr2 + 1; |
| i++; |
| } |
| while (*cptr2 != 0) |
| cptr2++; |
| *(aptr++) = NULL; |
| copy_nword(cmd, ctmp, sizeof(ctmp)); |
| execv(ctmp, argv); |
| perror(ctmp); |
| exit(1); |
| } else { |
| close(fd[0][0]); |
| close(fd[1][1]); |
| if (*pid < 0) { |
| close(fd[0][1]); |
| close(fd[1][0]); |
| setPerrorstatus("fork"); |
| return 0; |
| } |
| *fdIn = fd[1][0]; |
| *fdOut = fd[0][1]; |
| return (1); /* We are returning 0 for error... */ |
| } |
| #endif /* !HAVE_EXECV */ |
| return 0; |
| } |
| |
| int |
| clear_cache(int action, |
| u_char * var_val, |
| u_char var_val_type, |
| size_t var_val_len, |
| u_char * statP, oid * name, size_t name_len) |
| { |
| |
| long tmp = 0; |
| |
| if (var_val_type != ASN_INTEGER) { |
| snmp_log(LOG_NOTICE, "Wrong type != int\n"); |
| return SNMP_ERR_WRONGTYPE; |
| } |
| tmp = *((long *) var_val); |
| if (tmp == 1 && action == COMMIT) { |
| #ifdef EXCACHETIME |
| cachetime = 0; /* reset the cache next read */ |
| #endif |
| } |
| return SNMP_ERR_NOERROR; |
| } |
| |
| char **argvrestartp, *argvrestartname, *argvrestart; |
| |
| RETSIGTYPE |
| restart_doit(int a) |
| { |
| snmp_shutdown("snmpd"); |
| |
| /* |
| * do the exec |
| */ |
| #if HAVE_EXECV |
| execv(argvrestartname, argvrestartp); |
| setPerrorstatus(argvrestartname); |
| #endif |
| } |
| |
| int |
| restart_hook(int action, |
| u_char * var_val, |
| u_char var_val_type, |
| size_t var_val_len, |
| u_char * statP, oid * name, size_t name_len) |
| { |
| |
| long tmp = 0; |
| |
| if (var_val_type != ASN_INTEGER) { |
| snmp_log(LOG_NOTICE, "Wrong type != int\n"); |
| return SNMP_ERR_WRONGTYPE; |
| } |
| tmp = *((long *) var_val); |
| if (tmp == 1 && action == COMMIT) { |
| #ifdef SIGALRM |
| signal(SIGALRM, restart_doit); |
| #endif |
| alarm(RESTARTSLEEP); |
| } |
| return SNMP_ERR_NOERROR; |
| } |
| |
| void |
| print_mib_oid(oid name[], size_t len) |
| { |
| char *buffer; |
| buffer = (char *) malloc(11 * len); /* maximum digit lengths for int32 + a '.' */ |
| if (!buffer) { |
| snmp_log(LOG_ERR, "Malloc failed - out of memory?"); |
| return; |
| } |
| sprint_mib_oid(buffer, name, len); |
| snmp_log(LOG_NOTICE, "Mib: %s\n", buffer); |
| free(buffer); |
| } |
| |
| void |
| sprint_mib_oid(char *buf, oid name[], size_t len) |
| { |
| int i; |
| for (i = 0; i < (int) len; i++) { |
| sprintf(buf, ".%d", (int) name[i]); |
| while (*buf != 0) |
| buf++; |
| } |
| } |
| |
| /*******************************************************************-o-****** |
| * header_simple_table |
| * |
| * Parameters: |
| * *vp Variable data. |
| * *name Fully instantiated OID name. |
| * *length Length of name. |
| * exact TRUE if an exact match is desired. |
| * *var_len Hook for size of returned data type. |
| * (**write_method) Hook for write method (UNUSED). |
| * max |
| * |
| * Returns: |
| * 0 If name matches vp->name (accounting for 'exact') and is |
| * not greater in length than 'max'. |
| * 1 Otherwise. |
| * |
| * |
| * Compare 'name' to vp->name for the best match or an exact match (if |
| * requested). Also check that 'name' is not longer than 'max' if |
| * max is greater-than/equal 0. |
| * Store a successful match in 'name', and increment the OID instance if |
| * the match was not exact. |
| * |
| * 'name' and 'length' are undefined upon failure. |
| * |
| */ |
| int |
| header_simple_table(struct variable *vp, oid * name, size_t * length, |
| int exact, size_t * var_len, |
| WriteMethod ** write_method, int max) |
| { |
| int i, rtest; /* Set to: -1 If name < vp->name, |
| * 1 If name > vp->name, |
| * 0 Otherwise. |
| */ |
| oid newname[MAX_OID_LEN]; |
| |
| for (i = 0, rtest = 0; |
| i < (int) vp->namelen && i < (int) (*length) && !rtest; i++) { |
| if (name[i] != vp->name[i]) { |
| if (name[i] < vp->name[i]) |
| rtest = -1; |
| else |
| rtest = 1; |
| } |
| } |
| if (rtest > 0 || |
| (exact == 1 |
| && (rtest || (int) *length != (int) (vp->namelen + 1)))) { |
| if (var_len) |
| *var_len = 0; |
| return MATCH_FAILED; |
| } |
| |
| memset(newname, 0, sizeof(newname)); |
| |
| if (((int) *length) <= (int) vp->namelen || rtest == -1) { |
| memmove(newname, vp->name, (int) vp->namelen * sizeof(oid)); |
| newname[vp->namelen] = 1; |
| *length = vp->namelen + 1; |
| } else if (((int) *length) > (int) vp->namelen + 1) { /* exact case checked earlier */ |
| *length = vp->namelen + 1; |
| memmove(newname, name, (*length) * sizeof(oid)); |
| if (name[*length - 1] < ULONG_MAX) { |
| newname[*length - 1] = name[*length - 1] + 1; |
| } else { |
| /* |
| * Careful not to overflow... |
| */ |
| newname[*length - 1] = name[*length - 1]; |
| } |
| } else { |
| *length = vp->namelen + 1; |
| memmove(newname, name, (*length) * sizeof(oid)); |
| if (!exact) { |
| if (name[*length - 1] < ULONG_MAX) { |
| newname[*length - 1] = name[*length - 1] + 1; |
| } else { |
| /* |
| * Careful not to overflow... |
| */ |
| newname[*length - 1] = name[*length - 1]; |
| } |
| } else { |
| newname[*length - 1] = name[*length - 1]; |
| } |
| } |
| if ((max >= 0 && ((int)newname[*length - 1] > max)) || |
| ( 0 == newname[*length - 1] )) { |
| if (var_len) |
| *var_len = 0; |
| return MATCH_FAILED; |
| } |
| |
| memmove(name, newname, (*length) * sizeof(oid)); |
| if (write_method) |
| *write_method = 0; |
| if (var_len) |
| *var_len = sizeof(long); /* default */ |
| return (MATCH_SUCCEEDED); |
| } |
| |
| /* |
| * header_generic(... |
| * Arguments: |
| * vp IN - pointer to variable entry that points here |
| * name IN/OUT - IN/name requested, OUT/name found |
| * length IN/OUT - length of IN/OUT oid's |
| * exact IN - TRUE if an exact match was requested |
| * var_len OUT - length of variable or 0 if function returned |
| * write_method |
| * |
| */ |
| |
| /*******************************************************************-o-****** |
| * generic_header |
| * |
| * Parameters: |
| * *vp (I) Pointer to variable entry that points here. |
| * *name (I/O) Input name requested, output name found. |
| * *length (I/O) Length of input and output oid's. |
| * exact (I) TRUE if an exact match was requested. |
| * *var_len (O) Length of variable or 0 if function returned. |
| * (**write_method) Hook to name a write method (UNUSED). |
| * |
| * Returns: |
| * MATCH_SUCCEEDED If vp->name matches name (accounting for exact bit). |
| * MATCH_FAILED Otherwise, |
| * |
| * |
| * Check whether variable (vp) matches name. |
| */ |
| int |
| header_generic(struct variable *vp, |
| oid * name, |
| size_t * length, |
| int exact, size_t * var_len, WriteMethod ** write_method) |
| { |
| oid newname[MAX_OID_LEN]; |
| int result; |
| |
| DEBUGMSGTL(("util_funcs", "header_generic: ")); |
| DEBUGMSGOID(("util_funcs", name, *length)); |
| DEBUGMSG(("util_funcs", " exact=%d\n", exact)); |
| |
| memcpy((char *) newname, (char *) vp->name, |
| (int) vp->namelen * sizeof(oid)); |
| newname[vp->namelen] = 0; |
| result = snmp_oid_compare(name, *length, newname, vp->namelen + 1); |
| DEBUGMSGTL(("util_funcs", " result: %d\n", result)); |
| if ((exact && (result != 0)) || (!exact && (result >= 0))) |
| return (MATCH_FAILED); |
| memcpy((char *) name, (char *) newname, |
| ((int) vp->namelen + 1) * sizeof(oid)); |
| *length = vp->namelen + 1; |
| |
| *write_method = 0; |
| *var_len = sizeof(long); /* default to 'long' results */ |
| return (MATCH_SUCCEEDED); |
| } |
| |
| /* |
| * checkmib(): provided for backwards compatibility, do not use: |
| */ |
| int |
| checkmib(struct variable *vp, oid * name, size_t * length, |
| int exact, size_t * var_len, WriteMethod ** write_method, int max) |
| { |
| /* |
| * checkmib used to be header_simple_table, with reveresed boolean |
| * return output. header_simple_table() was created to match |
| * header_generic(). |
| */ |
| return (!header_simple_table(vp, name, length, exact, var_len, |
| write_method, max)); |
| } |
| |
| char * |
| find_field(char *ptr, int field) |
| { |
| int i; |
| char *init = ptr; |
| |
| if (field == LASTFIELD) { |
| /* |
| * skip to end |
| */ |
| while (*ptr++); |
| ptr = ptr - 2; |
| /* |
| * rewind a field length |
| */ |
| while (*ptr != 0 && isspace(*ptr) && init <= ptr) |
| ptr--; |
| while (*ptr != 0 && !isspace(*ptr) && init <= ptr) |
| ptr--; |
| if (isspace(*ptr)) |
| ptr++; /* past space */ |
| if (ptr < init) |
| ptr = init; |
| if (!isspace(*ptr) && *ptr != 0) |
| return (ptr); |
| } else { |
| if ((ptr = skip_white(ptr)) == NULL) |
| return (NULL); |
| for (i = 1; *ptr != 0 && i != field; i++) { |
| if ((ptr = skip_not_white(ptr)) == NULL) |
| return (NULL); |
| if ((ptr = skip_white(ptr)) == NULL) |
| return (NULL); |
| } |
| if (*ptr != 0 && i == field) |
| return (ptr); |
| return (NULL); |
| } |
| return (NULL); |
| } |
| |
| int |
| parse_miboid(const char *buf, oid * oidout) |
| { |
| int i; |
| |
| if (!buf) |
| return 0; |
| if (*buf == '.') |
| buf++; |
| for (i = 0; isdigit(*buf); i++) { |
| oidout[i] = atoi(buf); |
| while (isdigit(*buf++)); |
| if (*buf == '.') |
| buf++; |
| } |
| /* |
| * oidout[i] = -1; hmmm |
| */ |
| return i; |
| } |
| |
| void |
| string_append_int(char *s, int val) |
| { |
| char textVal[16]; |
| |
| if (val < 10) { |
| *s++ = '0' + val; |
| *s = '\0'; |
| return; |
| } |
| sprintf(textVal, "%d", val); |
| strcpy(s, textVal); |
| return; |
| } |
| |
| struct internal_mib_table { |
| int max_size; /* Size of the current data table */ |
| int next_index; /* Index of the next free entry */ |
| int current_index; /* Index of the 'current' entry */ |
| int cache_timeout; |
| marker_t cache_marker; |
| RELOAD *reload; /* Routine to read in the data */ |
| COMPARE *compare; /* Routine to compare two entries */ |
| int data_size; /* Size of an individual entry */ |
| void *data; /* The table itself */ |
| }; |
| |
| mib_table_t |
| Initialise_Table(int size, int timeout, RELOAD *reload, COMPARE *compare) |
| { |
| struct internal_mib_table *t; |
| |
| t = (struct internal_mib_table *) |
| malloc(sizeof(struct internal_mib_table)); |
| if (t == NULL) |
| return NULL; |
| |
| t->max_size = 0; |
| t->next_index = 1; /* Don't use index 0 */ |
| t->current_index = 1; |
| t->cache_timeout = timeout; |
| t->cache_marker = NULL; |
| t->reload = reload; |
| t->compare = compare; |
| t->data_size = size; |
| t->data = NULL; |
| |
| return (mib_table_t) t; |
| } |
| |
| #define TABLE_ADD( x, y ) ((void*)((char*)(x) + y)) |
| #define TABLE_INDEX(t, i) (TABLE_ADD(t->data, i * t->data_size)) |
| #define TABLE_START(t) (TABLE_INDEX(t, 1)) |
| #define TABLE_NEXT(t) (TABLE_INDEX(t, t->next_index)) |
| #define TABLE_CURRENT(t) (TABLE_INDEX(t, t->current_index)) |
| |
| int |
| check_and_reload_table(struct internal_mib_table *table) |
| { |
| /* |
| * If the saved data is fairly recent, |
| * we don't need to reload it |
| */ |
| if (table->cache_marker && |
| !(atime_ready(table->cache_marker, table->cache_timeout * 1000))) |
| return 1; |
| |
| |
| /* |
| * Call the routine provided to read in the data |
| * |
| * N.B: Update the cache marker *before* calling |
| * this routine, to avoid problems with recursion |
| */ |
| if (!table->cache_marker) |
| table->cache_marker = atime_newMarker(); |
| else |
| atime_setMarker(table->cache_marker); |
| |
| table->next_index = 1; |
| if (table->reload((mib_table_t) table) < 0) { |
| free(table->cache_marker); |
| table->cache_marker = NULL; |
| return 0; |
| } |
| table->current_index = 1; |
| if (table->compare != NULL) /* Sort the table */ |
| qsort(TABLE_START(table), table->next_index, |
| table->data_size, table->compare); |
| return 1; |
| } |
| |
| int |
| Search_Table(mib_table_t t, void *entry, int exact) |
| { |
| struct internal_mib_table *table = (struct internal_mib_table *) t; |
| void *entry2; |
| int res; |
| |
| if (!check_and_reload_table(table)) |
| return -1; |
| |
| if (table->compare == NULL) { |
| /* |
| * XXX - not sure this is right ? |
| */ |
| memcpy(entry, table->data, table->data_size); |
| return 0; |
| } |
| |
| if (table->next_index == table->current_index) |
| table->current_index = 1; |
| |
| entry2 = TABLE_CURRENT(table); |
| res = table->compare(entry, entry2); |
| if ((res < 0) && (table->current_index != 1)) { |
| table->current_index = 1; |
| entry2 = TABLE_CURRENT(table); |
| res = table->compare(entry, entry2); |
| } |
| |
| while (res > 0) { |
| table->current_index++; |
| if (table->next_index == table->current_index) |
| return -1; |
| entry2 = TABLE_CURRENT(table); |
| res = table->compare(entry, entry2); |
| } |
| |
| if (exact && res != 0) |
| return -1; |
| |
| if (!exact && res == 0) { |
| table->current_index++; |
| if (table->next_index == table->current_index) |
| return -1; |
| entry2 = TABLE_CURRENT(table); |
| } |
| memcpy(entry, entry2, table->data_size); |
| return 0; |
| } |
| |
| int |
| Add_Entry(mib_table_t t, void *entry) |
| { |
| struct internal_mib_table *table = (struct internal_mib_table *) t; |
| int new_max; |
| void *new_data; /* Used for |
| * a) extending the data table |
| * b) the next entry to use |
| */ |
| |
| if (table->max_size <= table->next_index) { |
| /* |
| * Table is full, so extend it to double the size |
| */ |
| new_max = 2 * table->max_size; |
| if (new_max == 0) |
| new_max = 10; /* Start with 10 entries */ |
| |
| new_data = (void *) malloc(new_max * table->data_size); |
| if (new_data == NULL) |
| return -1; |
| |
| if (table->data) { |
| memcpy(new_data, table->data, |
| table->max_size * table->data_size); |
| free(table->data); |
| } |
| table->data = new_data; |
| table->max_size = new_max; |
| } |
| |
| /* |
| * Insert the new entry into the data array |
| */ |
| new_data = TABLE_NEXT(table); |
| memcpy(new_data, entry, table->data_size); |
| table->next_index++; |
| return 0; |
| } |
| |
| void * |
| Retrieve_Table_Data(mib_table_t t, int *max_idx) |
| { |
| struct internal_mib_table *table = (struct internal_mib_table *) t; |
| |
| if (!check_and_reload_table(table)) |
| return NULL; |
| *max_idx = table->next_index - 1; |
| return table->data; |
| } |