#include #include #include #include #include #if HAVE_STDLIB_H # include #endif #if HAVE_UNISTD_H # include #endif #if HAVE_STRING_H # include #else # include #endif #include #if HAVE_LIMITS_H # include #endif #if HAVE_SYS_PARAM_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #include #if HAVE_DMALLOC_H # include #endif #include #include #include #include #include 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 */