529 lines
13 KiB
C
529 lines
13 KiB
C
#include <net-snmp/net-snmp-config.h>
|
|
#include <net-snmp/net-snmp-features.h>
|
|
#include <net-snmp/net-snmp-includes.h>
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#if HAVE_STDLIB_H
|
|
# include <stdlib.h>
|
|
#endif
|
|
#if HAVE_UNISTD_H
|
|
# include <unistd.h>
|
|
#endif
|
|
#if HAVE_STRING_H
|
|
# include <string.h>
|
|
#else
|
|
# include <strings.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
|
|
#if HAVE_LIMITS_H
|
|
# include <limits.h>
|
|
#endif
|
|
#if HAVE_SYS_PARAM_H
|
|
# include <sys/param.h>
|
|
#endif
|
|
#ifdef HAVE_SYS_STAT_H
|
|
# include <sys/stat.h>
|
|
#endif
|
|
#ifdef HAVE_FCNTL_H
|
|
# include <fcntl.h>
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
|
|
#if HAVE_DMALLOC_H
|
|
# include <dmalloc.h>
|
|
#endif
|
|
|
|
#include <net-snmp/types.h>
|
|
#include <net-snmp/library/snmp_debug.h>
|
|
#include <net-snmp/library/container.h>
|
|
#include <net-snmp/library/file_utils.h>
|
|
#include <net-snmp/library/text_utils.h>
|
|
|
|
netsnmp_feature_child_of(text_utils, libnetsnmp)
|
|
|
|
netsnmp_feature_provide(text_utils)
|
|
#ifdef NETSNMP_FEATURE_REQUIRE_TEXT_UTILS
|
|
netsnmp_feature_require(file_utils)
|
|
#endif /* NETSNMP_FEATURE_REQUIRE_TEXT_UTILS */
|
|
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TEXT_UTILS
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Prototypes
|
|
*
|
|
*/
|
|
/*
|
|
* parse methods
|
|
*/
|
|
static void
|
|
_pm_save_index_string_string(FILE *f, netsnmp_container *cin,
|
|
int flags);
|
|
static void
|
|
_pm_save_everything(FILE *f, netsnmp_container *cin, int flags);
|
|
|
|
static void
|
|
_pm_user_function(FILE *f, netsnmp_container *cin,
|
|
netsnmp_line_process_info *lpi, int flags);
|
|
|
|
|
|
/*
|
|
* line processors
|
|
*/
|
|
static int
|
|
_process_line_tvi(netsnmp_line_info *line_info, void *mem,
|
|
struct netsnmp_line_process_info_s* lpi);
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Text file processing functions
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* process text file, reading into extras
|
|
*/
|
|
netsnmp_container *
|
|
netsnmp_file_text_parse(netsnmp_file *f, netsnmp_container *cin,
|
|
int parse_mode, u_int flags, void *context)
|
|
{
|
|
netsnmp_container *c = cin;
|
|
FILE *fin;
|
|
int rc;
|
|
|
|
if (NULL == f)
|
|
return NULL;
|
|
|
|
if ((NULL == c) && (!(flags & PM_FLAG_NO_CONTAINER))) {
|
|
c = netsnmp_container_find("text_parse:binary_array");
|
|
if (NULL == c)
|
|
return NULL;
|
|
}
|
|
|
|
rc = netsnmp_file_open(f);
|
|
if (rc < 0) { /** error already logged */
|
|
if ((NULL !=c) && (c != cin))
|
|
CONTAINER_FREE(c);
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* get a stream from the file descriptor. This DOES NOT rewind the
|
|
* file (if fd was previously opened).
|
|
*/
|
|
fin = fdopen(f->fd, "r");
|
|
if (NULL == fin) {
|
|
if (NS_FI_AUTOCLOSE(f->ns_flags))
|
|
close(f->fd);
|
|
if ((NULL !=c) && (c != cin))
|
|
CONTAINER_FREE(c);
|
|
return NULL;
|
|
}
|
|
|
|
switch (parse_mode) {
|
|
|
|
case PM_SAVE_EVERYTHING:
|
|
_pm_save_everything(fin, c, flags);
|
|
break;
|
|
|
|
case PM_INDEX_STRING_STRING:
|
|
_pm_save_index_string_string(fin, c, flags);
|
|
break;
|
|
|
|
case PM_USER_FUNCTION:
|
|
if (NULL != context)
|
|
_pm_user_function(fin, c, (netsnmp_line_process_info*)context,
|
|
flags);
|
|
break;
|
|
|
|
default:
|
|
snmp_log(LOG_ERR, "unknown parse mode %d\n", parse_mode);
|
|
break;
|
|
}
|
|
|
|
|
|
/*
|
|
* close the stream, which will have the side effect of also closing
|
|
* the file descriptor, so we need to reset it.
|
|
*/
|
|
fclose(fin);
|
|
f->fd = -1;
|
|
|
|
return c;
|
|
}
|
|
|
|
netsnmp_feature_child_of(text_token_container_from_file, netsnmp_unused)
|
|
#ifndef NETSNMP_FEATURE_REMOVE_TEXT_TOKEN_CONTAINER_FROM_FILE
|
|
netsnmp_container *
|
|
netsnmp_text_token_container_from_file(const char *file, u_int flags,
|
|
netsnmp_container *cin, void *context)
|
|
{
|
|
netsnmp_line_process_info lpi;
|
|
netsnmp_container *c = cin, *c_rc;
|
|
netsnmp_file *fp;
|
|
|
|
if (NULL == file)
|
|
return NULL;
|
|
|
|
/*
|
|
* allocate file resources
|
|
*/
|
|
fp = netsnmp_file_fill(NULL, file, O_RDONLY, 0, 0);
|
|
if (NULL == fp) /** msg already logged */
|
|
return NULL;
|
|
|
|
memset(&lpi, 0x0, sizeof(lpi));
|
|
lpi.mem_size = sizeof(netsnmp_token_value_index);
|
|
lpi.process = _process_line_tvi;
|
|
lpi.user_context = context;
|
|
|
|
if (NULL == c) {
|
|
c = netsnmp_container_find("string:binary_array");
|
|
if (NULL == c) {
|
|
snmp_log(LOG_ERR,"malloc failed\n");
|
|
netsnmp_file_release(fp);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
c_rc = netsnmp_file_text_parse(fp, c, PM_USER_FUNCTION, 0, &lpi);
|
|
|
|
/*
|
|
* if we got a bad return and the user didn't pass us a container,
|
|
* we need to release the container we allocated.
|
|
*/
|
|
if ((NULL == c_rc) && (NULL == cin)) {
|
|
CONTAINER_FREE(c);
|
|
c = NULL;
|
|
}
|
|
else
|
|
c = c_rc;
|
|
|
|
/*
|
|
* release file resources
|
|
*/
|
|
netsnmp_file_release(fp);
|
|
|
|
return c;
|
|
}
|
|
#endif /* NETSNMP_FEATURE_REMOVE_TEXT_TOKEN_CONTAINER_FROM_FILE */
|
|
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Text file process modes helper functions
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @internal
|
|
* parse mode: save everything
|
|
*/
|
|
static void
|
|
_pm_save_everything(FILE *f, netsnmp_container *cin, int flags)
|
|
{
|
|
char line[STRINGMAX], *ptr;
|
|
size_t len;
|
|
|
|
netsnmp_assert(NULL != f);
|
|
netsnmp_assert(NULL != cin);
|
|
|
|
while (fgets(line, sizeof(line), f) != NULL) {
|
|
|
|
ptr = line;
|
|
len = strlen(line) - 1;
|
|
if (line[len] == '\n')
|
|
line[len] = 0;
|
|
|
|
/*
|
|
* save blank line or comment?
|
|
*/
|
|
if (flags & PM_FLAG_SKIP_WHITESPACE) {
|
|
if (NULL == (ptr = skip_white(ptr)))
|
|
continue;
|
|
}
|
|
|
|
ptr = strdup(line);
|
|
if (NULL == ptr) {
|
|
snmp_log(LOG_ERR,"malloc failed\n");
|
|
break;
|
|
}
|
|
|
|
CONTAINER_INSERT(cin,ptr);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
* parse mode:
|
|
*/
|
|
static void
|
|
_pm_save_index_string_string(FILE *f, netsnmp_container *cin,
|
|
int flags)
|
|
{
|
|
char line[STRINGMAX], *ptr;
|
|
netsnmp_token_value_index *tvi;
|
|
size_t count = 0, len;
|
|
|
|
netsnmp_assert(NULL != f);
|
|
netsnmp_assert(NULL != cin);
|
|
|
|
while (fgets(line, sizeof(line), f) != NULL) {
|
|
|
|
++count;
|
|
ptr = line;
|
|
len = strlen(line) - 1;
|
|
if (line[len] == '\n')
|
|
line[len] = 0;
|
|
|
|
/*
|
|
* save blank line or comment?
|
|
*/
|
|
if (flags & PM_FLAG_SKIP_WHITESPACE) {
|
|
if (NULL == (ptr = skip_white(ptr)))
|
|
continue;
|
|
}
|
|
|
|
tvi = SNMP_MALLOC_TYPEDEF(netsnmp_token_value_index);
|
|
if (NULL == tvi) {
|
|
snmp_log(LOG_ERR,"malloc failed\n");
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* copy whole line, then set second pointer to
|
|
* after token. One malloc, 2 strings!
|
|
*/
|
|
tvi->index = count;
|
|
tvi->token = strdup(line);
|
|
if (NULL == tvi->token) {
|
|
snmp_log(LOG_ERR,"malloc failed\n");
|
|
free(tvi);
|
|
break;
|
|
}
|
|
tvi->value.cp = skip_not_white(tvi->token);
|
|
if (NULL != tvi->value.cp) {
|
|
*(tvi->value.cp) = 0;
|
|
++(tvi->value.cp);
|
|
}
|
|
CONTAINER_INSERT(cin, tvi);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @internal
|
|
* parse mode:
|
|
*/
|
|
static void
|
|
_pm_user_function(FILE *f, netsnmp_container *cin,
|
|
netsnmp_line_process_info *lpi, int flags)
|
|
{
|
|
char buf[STRINGMAX];
|
|
netsnmp_line_info li;
|
|
void *mem = NULL;
|
|
int rc;
|
|
|
|
netsnmp_assert(NULL != f);
|
|
netsnmp_assert(NULL != cin);
|
|
|
|
/*
|
|
* static buf, or does the user want the memory?
|
|
*/
|
|
if (flags & PMLP_FLAG_ALLOC_LINE) {
|
|
if (0 != lpi->line_max)
|
|
li.line_max = lpi->line_max;
|
|
else
|
|
li.line_max = STRINGMAX;
|
|
li.line = (char *)calloc(li.line_max, 1);
|
|
if (NULL == li.line) {
|
|
snmp_log(LOG_ERR,"malloc failed\n");
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
li.line = buf;
|
|
li.line_max = sizeof(buf);
|
|
}
|
|
|
|
li.index = 0;
|
|
while (fgets(li.line, li.line_max, f) != NULL) {
|
|
|
|
++li.index;
|
|
li.start = li.line;
|
|
li.line_len = strlen(li.line) - 1;
|
|
if ((!(lpi->flags & PMLP_FLAG_LEAVE_NEWLINE)) &&
|
|
(li.line[li.line_len] == '\n'))
|
|
li.line[li.line_len] = 0;
|
|
|
|
/*
|
|
* save blank line or comment?
|
|
*/
|
|
if (!(lpi->flags & PMLP_FLAG_PROCESS_WHITESPACE)) {
|
|
if (NULL == (li.start = skip_white(li.start)))
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* do we need to allocate memory for the use?
|
|
* if the last call didn't use the memory we allocated,
|
|
* re-use it. Otherwise, allocate new chunk.
|
|
*/
|
|
if ((0 != lpi->mem_size) && (NULL == mem)) {
|
|
mem = calloc(lpi->mem_size, 1);
|
|
if (NULL == mem) {
|
|
snmp_log(LOG_ERR,"malloc failed\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* do they want a copy ot the line?
|
|
*/
|
|
if (lpi->flags & PMLP_FLAG_STRDUP_LINE) {
|
|
li.start = strdup(li.start);
|
|
if (NULL == li.start) {
|
|
snmp_log(LOG_ERR,"malloc failed\n");
|
|
break;
|
|
}
|
|
}
|
|
else if (lpi->flags & PMLP_FLAG_ALLOC_LINE) {
|
|
li.start = li.line;
|
|
}
|
|
|
|
/*
|
|
* call the user function. If the function wants to save
|
|
* the memory chunk, insert it in the container, the clear
|
|
* pointer so we reallocate next time.
|
|
*/
|
|
li.start_len = strlen(li.start);
|
|
rc = (*lpi->process)(&li, mem, lpi);
|
|
if (PMLP_RC_MEMORY_USED == rc) {
|
|
|
|
if (!(lpi->flags & PMLP_FLAG_NO_CONTAINER))
|
|
CONTAINER_INSERT(cin, mem);
|
|
|
|
mem = NULL;
|
|
|
|
if (lpi->flags & PMLP_FLAG_ALLOC_LINE) {
|
|
li.line = (char *)calloc(li.line_max, 1);
|
|
if (NULL == li.line) {
|
|
snmp_log(LOG_ERR,"malloc failed\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (PMLP_RC_MEMORY_UNUSED == rc ) {
|
|
/*
|
|
* they didn't use the memory. if li.start was a strdup, we have
|
|
* to release it. leave mem, we can re-use it (its a fixed size).
|
|
*/
|
|
if (lpi->flags & PMLP_FLAG_STRDUP_LINE)
|
|
free(li.start); /* no point in SNMP_FREE */
|
|
}
|
|
else {
|
|
if (PMLP_RC_STOP_PROCESSING != rc )
|
|
snmp_log(LOG_ERR, "unknown rc %d from text processor\n", rc);
|
|
break;
|
|
}
|
|
}
|
|
SNMP_FREE(mem);
|
|
}
|
|
|
|
/*------------------------------------------------------------------
|
|
*
|
|
* Test line process helper functions
|
|
*
|
|
*/
|
|
/**
|
|
* @internal
|
|
* process token value index line
|
|
*/
|
|
static int
|
|
_process_line_tvi(netsnmp_line_info *line_info, void *mem,
|
|
struct netsnmp_line_process_info_s* lpi)
|
|
{
|
|
netsnmp_token_value_index *tvi = (netsnmp_token_value_index *)mem;
|
|
char *ptr;
|
|
|
|
/*
|
|
* get token
|
|
*/
|
|
ptr = skip_not_white(line_info->start);
|
|
if (NULL == ptr) {
|
|
DEBUGMSGTL(("text:util:tvi", "no value after token '%s'\n",
|
|
line_info->start));
|
|
return PMLP_RC_MEMORY_UNUSED;
|
|
}
|
|
|
|
/*
|
|
* null terminate, search for value;
|
|
*/
|
|
*(ptr++) = 0;
|
|
ptr = skip_white(ptr);
|
|
if (NULL == ptr) {
|
|
DEBUGMSGTL(("text:util:tvi", "no value after token '%s'\n",
|
|
line_info->start));
|
|
return PMLP_RC_MEMORY_UNUSED;
|
|
}
|
|
|
|
/*
|
|
* get value
|
|
*/
|
|
switch((int)(intptr_t)lpi->user_context) {
|
|
|
|
case PMLP_TYPE_UNSIGNED:
|
|
tvi->value.ul = strtoul(ptr, NULL, 0);
|
|
if ((errno == ERANGE) && (ULONG_MAX == tvi->value.ul))
|
|
snmp_log(LOG_WARNING,"value overflow\n");
|
|
break;
|
|
|
|
|
|
case PMLP_TYPE_INTEGER:
|
|
tvi->value.sl = strtol(ptr, NULL, 0);
|
|
if ((errno == ERANGE) &&
|
|
((LONG_MAX == tvi->value.sl) ||
|
|
(LONG_MIN == tvi->value.sl)))
|
|
snmp_log(LOG_WARNING,"value over/under-flow\n");
|
|
break;
|
|
|
|
case PMLP_TYPE_STRING:
|
|
tvi->value.cp = strdup(ptr);
|
|
break;
|
|
|
|
case PMLP_TYPE_BOOLEAN:
|
|
if (isdigit((unsigned char)(*ptr)))
|
|
tvi->value.ul = strtoul(ptr, NULL, 0);
|
|
else if (strcasecmp(ptr,"true") == 0)
|
|
tvi->value.ul = 1;
|
|
else if (strcasecmp(ptr,"false") == 0)
|
|
tvi->value.ul = 0;
|
|
else {
|
|
snmp_log(LOG_WARNING,"bad value for boolean\n");
|
|
return PMLP_RC_MEMORY_UNUSED;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
snmp_log(LOG_ERR,"unsupported value type %d\n",
|
|
(int)(intptr_t)lpi->user_context);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* save token and value
|
|
*/
|
|
tvi->token = strdup(line_info->start);
|
|
tvi->index = line_info->index;
|
|
|
|
return PMLP_RC_MEMORY_USED;
|
|
}
|
|
|
|
#else /* ! NETSNMP_FEATURE_REMOVE_TEXT_UTILS */
|
|
netsnmp_feature_unused(text_utils);
|
|
#endif /* ! NETSNMP_FEATURE_REMOVE_TEXT_UTILS */
|