blob: 9737fe4339293ced3f2bbd364d23250419c13e86 [file] [log] [blame]
/*
* read_config.c
*/
#include <config.h>
#include <stdio.h>
#include <ctype.h>
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif
#if HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sys/types.h>
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#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
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#if HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#if HAVE_WINSOCK_H
#include <winsock.h>
#endif
#if HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#if HAVE_NETDB_H
#include <netdb.h>
#endif
#include <errno.h>
#if HAVE_DMALLOC_H
#include <dmalloc.h>
#endif
#include "asn1.h"
#include "mib.h"
#include "parse.h"
#include "system.h"
#include "snmp_api.h"
#include "snmp_debug.h"
#include "snmp_logging.h"
#include "snmp_impl.h"
#include "default_store.h"
#include "callback.h"
#include "read_config.h"
#include "tools.h"
static int config_errors;
struct config_files *config_files = NULL;
struct config_line *
register_premib_handler(const char *type,
const char *token,
void (*parser) (const char *, char *),
void (*releaser) (void),
const char *help)
{
struct config_line *ltmp;
ltmp = register_config_handler(type, token, parser, releaser, help);
if (ltmp != NULL)
ltmp->config_time = PREMIB_CONFIG;
return (ltmp);
}
struct config_line *
register_app_premib_handler(const char *token,
void (*parser) (const char *, char *),
void (*releaser) (void),
const char *help)
{
return(register_premib_handler( NULL, token, parser, releaser, help ));
}
/*******************************************************************-o-******
* register_config_handler
*
* Parameters:
* *type
* *token
* *parser
* *releaser
*
* Returns:
* Pointer to a new config line entry -OR- NULL on error.
*/
struct config_line *
register_config_handler(const char *type_param,
const char *token,
void (*parser) (const char *, char *),
void (*releaser) (void),
const char *help)
{
struct config_files **ctmp = &config_files;
struct config_line **ltmp;
const char *type = type_param;
if ( type == NULL )
type = ds_get_string(DS_LIBRARY_ID, DS_LIB_APPTYPE);
/*
* Find type in current list -OR- create a new file type.
*/
while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
ctmp = &((*ctmp)->next);
}
if (*ctmp == NULL) {
*ctmp = (struct config_files *)
calloc(1,sizeof(struct config_files));
if ( !*ctmp ) {
return NULL;
}
(*ctmp)->fileHeader = strdup(type);
}
/*
* Find parser type in current list -OR- create a new
* line parser entry.
*/
ltmp = &((*ctmp)->start);
while (*ltmp != NULL && strcmp((*ltmp)->config_token, token)) {
ltmp = &((*ltmp)->next);
}
if (*ltmp == NULL) {
*ltmp = (struct config_line *)
calloc(1,sizeof(struct config_line));
if ( !*ltmp ) {
return NULL;
}
(*ltmp)->config_time = NORMAL_CONFIG;
(*ltmp)->config_token = strdup(token);
if (help != NULL)
(*ltmp)->help = strdup(help);
}
/*
* Add/Replace the parse/free functions for the given line type
* in the given file type.
*/
(*ltmp)->parse_line = parser;
(*ltmp)->free_func = releaser;
return (*ltmp);
} /* end register_config_handler() */
struct config_line *
register_app_config_handler(const char *token,
void (*parser) (const char *, char *),
void (*releaser) (void),
const char *help)
{
return(register_config_handler( NULL, token, parser, releaser, help ));
}
void
unregister_config_handler(const char *type_param,
const char *token)
{
struct config_files **ctmp = &config_files;
struct config_line **ltmp, *ltmp2;
const char *type = type_param;
if ( type == NULL )
type = ds_get_string(DS_LIBRARY_ID, DS_LIB_APPTYPE);
/* find type in current list */
while (*ctmp != NULL && strcmp((*ctmp)->fileHeader,type)) {
ctmp = &((*ctmp)->next);
}
if (*ctmp == NULL) {
/* Not found, return. */
return;
}
ltmp = &((*ctmp)->start);
if (*ltmp == NULL) {
/* Not found, return. */
return;
}
if (strcmp((*ltmp)->config_token,token) == 0) {
/* found it at the top of the list */
ltmp2 = (*ltmp)->next;
free((*ltmp)->config_token);
SNMP_FREE((*ltmp)->help);
free(*ltmp);
(*ctmp)->start = ltmp2;
return;
}
while ((*ltmp)->next != NULL && strcmp((*ltmp)->next->config_token,token)) {
ltmp = &((*ltmp)->next);
}
if ((*ltmp)->next != NULL) {
free((*ltmp)->next->config_token);
SNMP_FREE((*ltmp)->next->help);
ltmp2 = (*ltmp)->next->next;
free((*ltmp)->next);
(*ltmp)->next = ltmp2;
}
}
void
unregister_app_config_handler(const char *token)
{
unregister_config_handler( NULL, token );
}
void
unregister_all_config_handlers()
{
struct config_files *ctmp, *save;
struct config_line *ltmp;
free_config();
/* Keep using config_files until there are no more! */
for (ctmp = config_files; ctmp;) {
for (ltmp = ctmp->start; ltmp; ltmp = ctmp->start) {
unregister_config_handler (ctmp->fileHeader, ltmp->config_token);
}
free (ctmp->fileHeader);
save = ctmp->next;
free (ctmp);
ctmp = save;
config_files = save;
}
}
#ifdef TESTING
void print_config_handlers (void)
{
struct config_files *ctmp = config_files;
struct config_line *ltmp;
for(;ctmp != NULL; ctmp = ctmp->next) {
DEBUGMSGTL(("read_config", "read_conf: %s\n", ctmp->fileHeader));
for(ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
DEBUGMSGTL(("read_config", " %s\n", ltmp->config_token));
}
}
#endif
int linecount;
const char *curfilename;
struct config_line *
read_config_get_handlers(const char *type) {
struct config_files *ctmp = config_files;
for(;ctmp != NULL && strcmp(ctmp->fileHeader,type); ctmp = ctmp->next);
if (ctmp)
return ctmp->start;
return NULL;
}
void read_config_with_type(const char *filename,
const char *type)
{
struct config_line *ctmp = read_config_get_handlers(type);
if (ctmp)
read_config(filename, ctmp, EITHER_CONFIG);
else
DEBUGMSGTL(("read_config",
"read_config: I have no registrations for type:%s,file:%s\n",
type, filename));
}
struct config_line *
read_config_find_handler(struct config_line *line_handlers, const char *token) {
struct config_line *lptr;
for(lptr = line_handlers; lptr != NULL; lptr = lptr->next) {
if (!strcasecmp(token, lptr->config_token)) {
return lptr;
}
}
return NULL;
}
/* searches a config_line linked list for a match */
int
run_config_handler(struct config_line *lptr,
const char *token,
char *cptr, int when) {
char tmpbuf[STRINGMAX];
lptr = read_config_find_handler(lptr, token);
if (lptr != NULL) {
if (when == EITHER_CONFIG || lptr->config_time == when) {
DEBUGMSGTL(("read_config","Found a parser. Calling it: %s / %s\n",
token, cptr));
(*(lptr->parse_line))(token,cptr);
}
} else if (when != PREMIB_CONFIG &&
!ds_get_boolean(DS_LIBRARY_ID, DS_LIB_NO_TOKEN_WARNINGS)) {
sprintf(tmpbuf,"Unknown token: %s.", token);
config_pwarn(tmpbuf);
return SNMPERR_GENERR;
}
return SNMPERR_SUCCESS;
}
/* takens an arbitrary string and tries to intepret it based on the
known configruation handlers for all registered types. May produce
inconsistent results when multiple tokens of the same name are
registered under different file types. */
/* we allow = delimeters here */
#define SNMP_CONFIG_DELIMETERS " \t="
int
snmp_config_when(char *line, int when) {
char *cptr, buf[STRINGMAX], tmpbuf[STRINGMAX];
struct config_line *lptr = NULL;
struct config_files *ctmp = config_files;
if (line == NULL) {
config_perror("snmp_config() called with a null string.");
return SNMPERR_GENERR;
}
strncpy(buf, line, STRINGMAX);
buf[STRINGMAX-1] = '\0';
cptr = strtok(buf, SNMP_CONFIG_DELIMETERS);
if (cptr && cptr[0] == '[') {
if (cptr[strlen(cptr)-1] != ']') {
config_perror("no matching ']'");
return SNMPERR_GENERR;
}
lptr = read_config_get_handlers(cptr+1);
if (lptr == NULL) {
sprintf(tmpbuf,"No handlers regestered for type %s.", cptr+1);
config_perror(tmpbuf);
return SNMPERR_GENERR;
}
cptr = strtok(NULL, SNMP_CONFIG_DELIMETERS);
lptr = read_config_find_handler(lptr, cptr);
} else {
/* we have to find a token */
for(;ctmp != NULL && lptr == NULL; ctmp = ctmp->next)
lptr = read_config_find_handler(ctmp->start, cptr);
}
if (lptr == NULL &&
ds_get_boolean(DS_LIBRARY_ID, DS_LIB_NO_TOKEN_WARNINGS)) {
sprintf(tmpbuf,"Unknown token: %s.", cptr);
config_pwarn(tmpbuf);
return SNMPERR_GENERR;
}
/* use the original string instead since strtok messed up the original */
line = skip_white(line + (cptr - buf) + strlen(cptr) + 1);
return(run_config_handler(lptr, cptr, line, EITHER_CONFIG));
}
int
snmp_config(char *line) {
return snmp_config_when(line, EITHER_CONFIG);
}
void
snmp_config_remember_in_list(char *line, struct read_config_memory **mem) {
if (mem == NULL)
return;
while (*mem != NULL)
mem = &((*mem)->next);
*mem = SNMP_MALLOC_STRUCT(read_config_memory);
if (line)
(*mem)->line = strdup(line);
}
void
snmp_config_process_memory_list(struct read_config_memory *mem, int when) {
while(mem) {
DEBUGMSGTL(("read_config","processing memory: %s\n",mem->line));
snmp_config_when(mem->line, when);
mem = mem->next;
}
}
void
snmp_config_remember_free_list(struct read_config_memory *mem) {
struct read_config_memory *tmpmem;
while(mem) {
SNMP_FREE(mem->line);
tmpmem = mem->next;
free(mem);
mem = tmpmem;
}
}
/* default storage location implementation */
static struct read_config_memory *memorylist = NULL;
void
snmp_config_remember(char *line) {
snmp_config_remember_in_list(line, &memorylist);
}
void
snmp_config_process_memories(void) {
snmp_config_process_memory_list(memorylist, EITHER_CONFIG);
}
void
snmp_config_process_memories_when(int when) {
snmp_config_process_memory_list(memorylist, when);
}
/*******************************************************************-o-******
* read_config
*
* Parameters:
* *filename
* *line_handler
* when
*
* Read <filename> and process each line in accordance with the list of
* <line_handler> functions.
*
*
* For each line in <filename>, search the list of <line_handler>'s
* for an entry that matches the first token on the line. This comparison is
* case insensitive.
*
* For each match, check that <when> is the designated time for the
* <line_handler> function to be executed before processing the line.
*/
void read_config(const char *filename,
struct config_line *line_handler,
int when)
{
FILE *ifile;
char line[STRINGMAX], token[STRINGMAX], tmpbuf[STRINGMAX];
char *cptr;
int i;
struct config_line *lptr;
linecount = 0;
curfilename = filename;
if ((ifile = fopen(filename, "r")) == NULL) {
#ifdef ENOENT
if (errno == ENOENT) {
DEBUGMSGTL(("read_config", "%s: %s\n", filename, strerror(errno)));
} else
#endif /* ENOENT */
#ifdef EACCES
if (errno == EACCES) {
DEBUGMSGTL(("read_config", "%s: %s\n", filename, strerror(errno)));
} else
#endif /* EACCES */
#if defined(ENOENT) || defined(EACCES)
{
snmp_log_perror(filename);
}
#else /* defined(ENOENT) || defined(EACCES) */
snmp_log_perror(filename);
#endif /* ENOENT */
return;
} else {
DEBUGMSGTL(("read_config", "Reading configuration %s\n", filename));
}
while (fgets(line, sizeof(line), ifile) != NULL)
{
lptr = line_handler;
linecount++;
cptr = line;
i = strlen(line)-1;
if (line[i] == '\n')
line[i] = 0;
/* check blank line or # comment */
if ((cptr = skip_white(cptr)))
{
cptr = copy_nword(cptr, token, sizeof(token));
if (token[0] == '[') {
token[strlen(token)-1] = '\0';
lptr = read_config_get_handlers(&token[1]);
if (lptr == NULL) {
sprintf(tmpbuf,"No handlers regestered for type %s.",
&token[1]);
config_perror(tmpbuf);
continue;
}
DEBUGMSGTL(("read_config","Switching to new context: %s%s\n",
((cptr)?"(this line only) ":""),&token[1]));
if (cptr == NULL) {
/* change context permanently */
line_handler = lptr;
continue;
} else {
/* the rest of this line only applies. */
cptr = copy_nword(cptr,token,sizeof(token));
}
} else {
lptr = line_handler;
}
if (cptr == NULL) {
sprintf(tmpbuf,"Blank line following %s token.", token);
config_perror(tmpbuf);
} else {
DEBUGMSGTL(("read_config", "%s:%d examining: %s\n",
filename, linecount, line));
run_config_handler(lptr, token, cptr, when);
}
}
}
fclose(ifile);
return;
} /* end read_config() */
void
free_config (void)
{
struct config_files *ctmp = config_files;
struct config_line *ltmp;
for(;ctmp != NULL; ctmp = ctmp->next)
for(ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
if (ltmp->free_func)
(*(ltmp->free_func))();
}
void
read_configs (void)
{
char *optional_config = ds_get_string(DS_LIBRARY_ID, DS_LIB_OPTIONALCONFIG);
char *type = ds_get_string(DS_LIBRARY_ID, DS_LIB_APPTYPE);
DEBUGMSGTL(("read_config","reading normal configuration tokens\n"));
if (!ds_get_boolean(DS_LIBRARY_ID, DS_LIB_DONT_READ_CONFIGS))
read_config_files(NORMAL_CONFIG);
/* do this even when the normal above wasn't done */
if (optional_config && type) {
struct stat statbuf;
if (stat(optional_config, &statbuf)) {
DEBUGMSGTL(("read_config","Optional File \"%s\" does not exist.\n",
optional_config));
snmp_log_perror(optional_config);
} else {
DEBUGMSGTL(("read_config","Reading optional config file: \"%s\"\n",
optional_config));
read_config_with_type(optional_config, type);
}
}
snmp_config_process_memories_when(NORMAL_CONFIG);
snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_POST_READ_CONFIG,
NULL);
}
void
read_premib_configs (void)
{
DEBUGMSGTL(("read_config","reading premib configuration tokens\n"));
if (!ds_get_boolean(DS_LIBRARY_ID, DS_LIB_DONT_READ_CONFIGS))
read_config_files(PREMIB_CONFIG);
snmp_config_process_memories_when(PREMIB_CONFIG);
snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
SNMP_CALLBACK_POST_PREMIB_READ_CONFIG,
NULL);
}
/*******************************************************************-o-******
* read_config_files
*
* Parameters:
* when == PREMIB_CONFIG, NORMAL_CONFIG -or- EITHER_CONFIG
*
*
* Traverse the list of config file types, performing the following actions
* for each --
*
* First, build a search path for config files. If the contents of
* environment variable SNMPCONFPATH are NULL, then use the following
* path list (where the last entry exists only if HOME is non-null):
*
* SNMPSHAREPATH:SNMPLIBPATH:${HOME}/.snmp
*
* Then, In each of these directories, read config files by the name of:
*
* <dir>/<fileHeader>.conf -AND-
* <dir>/<fileHeader>.local.conf
*
* where <fileHeader> is taken from the config file type structure.
*
*
* PREMIB_CONFIG causes free_config() to be invoked prior to any other action.
*
*
* EXITs if any 'config_errors' are logged while parsing config file lines.
*/
void
read_config_files (int when)
{
int i, j;
char configfile[300];
char *envconfpath, *homepath;
char *cptr1, *cptr2;
char defaultPath[SPRINT_MAX_LEN];
struct config_files *ctmp = config_files;
struct config_line *ltmp;
struct stat statbuf;
config_errors = 0;
if (when == PREMIB_CONFIG)
free_config();
/* read all config file types */
for(;ctmp != NULL; ctmp = ctmp->next) {
ltmp = ctmp->start;
/* read the config files */
if ((envconfpath = getenv("SNMPCONFPATH")) == NULL) {
homepath=getenv("HOME");
sprintf(defaultPath,"%s%c%s%c%s%s%s%s%c%s",
SNMPCONFPATH, ENV_SEPARATOR_CHAR,
SNMPSHAREPATH, ENV_SEPARATOR_CHAR, SNMPLIBPATH,
((homepath == NULL) ? "" : ENV_SEPARATOR),
((homepath == NULL) ? "" : homepath),
((homepath == NULL) ? "" : "/.snmp"),
ENV_SEPARATOR_CHAR, PERSISTENT_DIRECTORY);
envconfpath = defaultPath;
}
envconfpath = strdup(envconfpath); /* prevent actually writing in env */
DEBUGMSGTL(("read_config","config path used:%s\n", envconfpath));
cptr1 = cptr2 = envconfpath;
i = 1;
while (i && *cptr2 != 0) {
while(*cptr1 != 0 && *cptr1 != ENV_SEPARATOR_CHAR)
cptr1++;
if (*cptr1 == 0)
i = 0;
else
*cptr1 = 0;
/*
* for proper persistent storage retrival, we need to read old backup
* copies of the previous storage files. If the application in
* question has died without the proper call to snmp_clean_persistent,
* then we read all the configuration files we can, starting with
* the oldest first.
*/
if (strncmp(cptr2, PERSISTENT_DIRECTORY,
strlen(PERSISTENT_DIRECTORY)) == 0 ||
(getenv("SNMP_PERSISTENT_FILE") != NULL &&
strncmp(cptr2, getenv("SNMP_PERSISTENT_FILE"),
strlen(getenv("SNMP_PERSISTENT_FILE"))) == 0)) {
/* limit this to the known storage directory only */
for(j=0; j <= MAX_PERSISTENT_BACKUPS; j++) {
sprintf(configfile,"%s/%s.%d.conf",cptr2, ctmp->fileHeader, j);
if (stat(configfile, &statbuf) != 0) {
/* file not there, continue */
break;
} else {
/* backup exists, read it */
DEBUGMSGTL(("read_config_files","old config file found: %s, parsing\n", configfile));
read_config (configfile, ltmp, when);
}
}
}
sprintf(configfile,"%s/%s.conf",cptr2, ctmp->fileHeader);
read_config (configfile, ltmp, when);
sprintf(configfile,"%s/%s.local.conf",cptr2, ctmp->fileHeader);
read_config (configfile, ltmp, when);
cptr2 = ++cptr1;
}
free(envconfpath);
}
if (config_errors) {
snmp_log(LOG_ERR, "ucd-snmp: %d error(s) in config file(s)\n", config_errors);
/* exit(1); */
}
}
void read_config_print_usage(const char *lead)
{
struct config_files *ctmp = config_files;
struct config_line *ltmp;
if (lead == NULL)
lead = "";
for(ctmp = config_files; ctmp != NULL; ctmp = ctmp->next) {
snmp_log(LOG_INFO, "%sIn %s.conf and %s.local.conf:\n", lead, ctmp->fileHeader,
ctmp->fileHeader);
for(ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next) {
DEBUGIF("read_config_usage") {
if (ltmp->config_time == PREMIB_CONFIG)
DEBUGMSG(("read_config_usage", "*"));
else
DEBUGMSG(("read_config_usage", " "));
}
if (ltmp->help) {
snmp_log(LOG_INFO, "%s%s%-15s %s\n", lead, lead,
ltmp->config_token, ltmp->help);
} else {
DEBUGIF("read_config_usage") {
snmp_log(LOG_INFO, "%s%s%-15s [NO HELP]\n", lead, lead,
ltmp->config_token);
}
}
}
}
}
/*******************************************************************-o-******
* read_config_store
*
* Parameters:
* *type
* *line
*
*
* Append line to a file named either ENV(SNMP_PERSISTENT_FILE) or
* "<PERSISTENT_DIRECTORY>/<type>.conf".
* Add a trailing newline to the stored file if necessary.
*
* Intended for use by applications to store permenant configuration
* information generated by sets or persistent counters.
*
*/
void
read_config_store(const char *type, const char *line)
{
#ifdef PERSISTENT_DIRECTORY
char file[512], *filep;
FILE *fout;
#ifdef PERSISTENT_MASK
mode_t oldmask;
#endif
/* store configuration directives in the following order of preference:
1. ENV variable SNMP_PERSISTENT_FILE
2. configured <PERSISTENT_DIRECTORY>/<type>.conf
*/
if ((filep = getenv("SNMP_PERSISTENT_FILE")) == NULL) {
sprintf(file,"%s/%s.conf",PERSISTENT_DIRECTORY,type);
filep = file;
}
#ifdef PERSISTENT_MASK
oldmask = umask(PERSISTENT_MASK);
#endif
if (mkdirhier(filep, AGENT_DIRECTORY_MODE, 1)) {
snmp_log(LOG_ERR, "Failed to create the persistent directory for %s\n",
file);
}
if ((fout = fopen(filep, "a")) != NULL) {
fprintf(fout,line);
if (line[strlen(line)] != '\n')
fprintf(fout,"\n");
DEBUGMSGTL(("read_config","storing: %s\n",line));
fclose(fout);
} else {
DEBUGMSGTL(("read_config","open failure"));
}
#ifdef PERSISTENT_MASK
umask(oldmask);
#endif
#endif
} /* end read_config_store() */
void
read_app_config_store(const char *line)
{
read_config_store(ds_get_string(DS_LIBRARY_ID, DS_LIB_APPTYPE), line);
}
/*******************************************************************-o-******
* snmp_save_persistent
*
* Parameters:
* *type
*
*
* Save the file "<PERSISTENT_DIRECTORY>/<type>.conf" into a backup copy
* called "<PERSISTENT_DIRECTORY>/<type>.%d.conf", which %d is an
* incrementing number on each call, but less than MAX_PERSISTENT_BACKUPS.
*
* Should be called just before all persistent information is supposed to be
* written to move aside the existing persistent cache.
* snmp_clean_persistent should then be called afterward all data has been
* saved to remove these backup files.
*
* Note: on an rename error, the files are removed rather than saved.
*
*/
void
snmp_save_persistent(const char *type)
{
char file[512], fileold[SPRINT_MAX_LEN];
struct stat statbuf;
int j;
DEBUGMSGTL(("snmp_save_persistent","saving %s files...\n", type));
sprintf(file,"%s/%s.conf", PERSISTENT_DIRECTORY, type);
if (stat(file, &statbuf) == 0) {
for(j=0; j <= MAX_PERSISTENT_BACKUPS; j++) {
sprintf(fileold,"%s/%s.%d.conf", PERSISTENT_DIRECTORY, type, j);
if (stat(fileold, &statbuf) != 0) {
DEBUGMSGTL(("snmp_save_persistent"," saving old config file: %s -> %s.\n", file, fileold));
if (rename(file, fileold)) {
unlink(file);/* moving it failed, try nuking it, as leaving
it around is very bad. */
}
break;
}
}
}
/* save a warning header to the top of the new file */
sprintf(fileold, "#\n# net-snmp (or ucd-snmp) persistent data file.\n#\n# DO NOT STORE CONFIGURATION ENTRIES HERE.\n# Please save normal configuration tokens for %s in SNMPCONFPATH/%s.conf.\n# Only \"createUser\" tokens should be placed here by %s administrators.\n#\n", type, type, type);
read_config_store(type, fileold);
}
/*******************************************************************-o-******
* snmp_clean_persistent
*
* Parameters:
* *type
*
*
* Unlink all backup files called "<PERSISTENT_DIRECTORY>/<type>.%d.conf".
*
* Should be called just after we successfull dumped the last of the
* persistent data, to remove the backup copies of previous storage dumps.
*
* XXX Worth overwriting with random bytes first? This would
* ensure that the data is destroyed, even a buffer containing the
* data persists in memory or swap. Only important if secrets
* will be stored here.
*/
void
snmp_clean_persistent(const char *type)
{
char file[512];
struct stat statbuf;
int j;
DEBUGMSGTL(("snmp_clean_persistent","cleaning %s files...\n", type));
sprintf(file,"%s/%s.conf",PERSISTENT_DIRECTORY,type);
if (stat(file, &statbuf) == 0) {
for(j=0; j <= MAX_PERSISTENT_BACKUPS; j++) {
sprintf(file,"%s/%s.%d.conf", PERSISTENT_DIRECTORY, type, j);
if (stat(file, &statbuf) == 0) {
DEBUGMSGTL(("snmp_clean_persistent"," removing old config file: %s\n", file));
unlink(file);
}
}
}
}
/* config_perror: prints a warning string associated with a file and
line number of a .conf file and increments the error count. */
void config_perror(const char *string)
{
snmp_log(LOG_ERR, "%s: line %d: Error: %s\n", curfilename, linecount, string);
config_errors++;
}
void config_pwarn(const char *string)
{
snmp_log(LOG_WARNING, "%s: line %d: Warning: %s\n", curfilename, linecount, string);
}
/* skip all white spaces and return 1 if found something either end of
line or a comment character */
char *skip_white(char *ptr)
{
if (ptr == NULL) return (NULL);
while (*ptr != 0 && isspace(*ptr)) ptr++;
if (*ptr == 0 || *ptr == '#') return (NULL);
return (ptr);
}
char *skip_not_white(char *ptr)
{
if (ptr == NULL) return (NULL);
while (*ptr != 0 && !isspace(*ptr)) ptr++;
if (*ptr == 0 || *ptr == '#') return (NULL);
return (ptr);
}
char *skip_token(char *ptr)
{
ptr = skip_white(ptr);
ptr = skip_not_white(ptr);
ptr = skip_white(ptr);
return (ptr);
}
/* copy_word
copies the next 'token' from 'from' into 'to', maximum len-1 characters.
currently a token is anything seperate by white space
or within quotes (double or single) (i.e. "the red rose"
is one token, \"the red rose\" is three tokens)
a '\' character will allow a quote character to be treated
as a regular character
It returns a pointer to first non-white space after the end of the token
being copied or to 0 if we reach the end.
Note: Partially copied words (greater than len) still returns a !NULL ptr
Note: partially copied words are, however, null terminated.
*/
char *copy_nword(char *from, char *to, int len)
{
char quote;
if ( (*from == '\"') || (*from =='\'') ){
quote = *(from++);
while ( (*from != quote) && (*from != 0) ) {
if ((*from == '\\') && (*(from+1) != 0)) {
if (len > 0) { /* don't copy beyond len bytes */
*to++ = *(from+1);
if (--len == 0)
*(to-1) = '\0'; /* null protect the last spot */
}
from = from +2;
} else {
if (len > 0) { /* don't copy beyond len bytes */
*to++ = *from++;
if (--len == 0)
*(to-1) = '\0'; /* null protect the last spot */
} else
from++;
}
}
if (*from == 0) {
DEBUGMSGTL(("read_config_copy_word",
"no end quote found in config string\n"));
} else from++;
}
else {
while (*from != 0 && !isspace(*from)) {
if ((*from == '\\') && (*(from+1) != 0)) {
if (len > 0) { /* don't copy beyond len bytes */
*to++ = *(from+1);
if (--len == 0)
*(to-1) = '\0'; /* null protect the last spot */
}
from = from +2;
} else {
if (len > 0) { /* don't copy beyond len bytes */
*to++ = *from++;
if (--len == 0)
*(to-1) = '\0'; /* null protect the last spot */
} else
from++;
}
}
}
if (len > 0)
*to = 0;
from = skip_white(from);
return(from);
} /* copy_word */
/* copy_word
copies the next 'token' from 'from' into 'to'.
currently a token is anything seperate by white space
or within quotes (double or single) (i.e. "the red rose"
is one token, \"the red rose\" is three tokens)
a '\' character will allow a quote character to be treated
as a regular character
It returns a pointer to first non-white space after the end of the token
being copied or to 0 if we reach the end.*/
static int have_warned = 0;
char *copy_word(char *from, char *to)
{
if (!have_warned) {
snmp_log(LOG_INFO, "copy_word() called. Use copy_nword() instead.\n");
have_warned = 1;
}
return copy_nword(from, to, SPRINT_MAX_LEN);
} /* copy_word */
/* read_config_save_octet_string(): saves an octet string as a length
followed by a string of hex */
char *read_config_save_octet_string(char *saveto, u_char *str, size_t len) {
int i;
u_char *cp;
/* is everything easily printable */
for(i=0, cp=str; i < (int)len && cp &&
(isalpha(*cp) || isdigit(*cp) || *cp == ' '); cp++, i++);
if (len != 0 && i == (int)len) {
*saveto++ = '"';
memcpy(saveto, str, len);
saveto += len;
*saveto++ = '"';
*saveto = '\0';
} else {
if (str != NULL) {
sprintf(saveto, "0x");
saveto += 2;
for(i = 0; i < (int)len; i++) {
sprintf(saveto,"%02x", str[i]);
saveto = saveto + 2;
}
} else {
sprintf(saveto,"\"\"");
saveto += 2;
}
}
return saveto;
}
/* read_config_read_octet_string(): reads an octet string that was
saved by the read_config_save_octet_string() function */
char *read_config_read_octet_string(char *readfrom, u_char **str, size_t *len) {
u_char *cptr=NULL;
char *cptr1;
u_int tmp;
int i;
if (readfrom == NULL || str == NULL)
return NULL;
if (strncasecmp(readfrom,"0x",2) == 0) {
/* A hex string submitted. How long? */
readfrom += 2;
cptr1 = skip_not_white(readfrom);
if (cptr1)
*len = (cptr1 - readfrom);
else
*len = strlen(readfrom);
if (*len % 2) {
DEBUGMSGTL(("read_config_read_octet_string","invalid hex string: wrong length"));
return NULL;
}
*len = *len / 2;
/* malloc data space if needed (+1 for good measure) */
if (*str == NULL) {
if ((cptr = (u_char *)malloc(*len + 1)) == NULL) {
return NULL;
}
*str = cptr;
} else {
cptr = *str;
}
/* copy validated data */
for(i = 0; i < (int)*len; i++) {
if (1 == sscanf(readfrom,"%2x",&tmp))
*cptr++ = (u_char) tmp;
else {
/* we may lose memory, but don't know caller's buffer XX free(cptr); */
return (NULL);
}
readfrom += 2;
}
*cptr++ = '\0';
readfrom = skip_white(readfrom);
} else {
/* Normal string */
/* malloc string space if needed (including NULL terminator) */
if (*str == NULL) {
char buf[SNMP_MAXBUF];
readfrom = copy_nword(readfrom, buf, sizeof(buf));
*len = strlen(buf);
if ((cptr = (u_char *) malloc(*len + 1)) == NULL)
return NULL;
*str = cptr;
if (cptr) {
memcpy(cptr, buf, *len+1);
}
} else {
readfrom = copy_nword(readfrom, (char *)*str, *len);
}
}
return readfrom;
}
/* read_config_save_objid(): saves an objid as a numerical string */
char *read_config_save_objid(char *saveto, oid *objid, size_t len) {
int i;
if (len == 0) {
strcat(saveto, "NULL");
saveto += strlen(saveto);
return saveto;
}
/* in case len=0, this makes it easier to read it back in */
for(i=0; i < (int)len; i++) {
sprintf(saveto,".%ld", objid[i]);
saveto += strlen(saveto);
}
return saveto;
}
/* read_config_read_objid(): reads an objid from a format saved by the above */
char *read_config_read_objid(char *readfrom, oid **objid, size_t *len) {
if (objid == NULL || readfrom == NULL)
return NULL;
if (*objid == NULL) {
*len = 0;
if ((*objid = (oid*)malloc(MAX_OID_LEN * sizeof(oid))) == NULL)
return NULL;
*len = MAX_OID_LEN;
}
if (strncmp(readfrom,"NULL",4) == 0) {
/* null length oid */
*len = 0;
} else {
/* qualify the string for read_objid */
char buf[SPRINT_MAX_LEN];
copy_nword(readfrom, buf, sizeof(buf));
if (!read_objid(buf, *objid, len)) {
DEBUGMSGTL(("read_config_read_objid","Invalid OID"));
*len = 0;
return NULL;
}
}
readfrom = skip_token(readfrom);
return readfrom;
}
/* read_config_read_data():
reads data of a given type from a token(s) on a configuration line.
Returns: character pointer to the next token in the configuration line.
NULL if none left.
NULL if an unknown type.
*/
char *read_config_read_data(int type, char *readfrom, void *dataptr, size_t *len) {
int *intp;
char **charpp;
oid **oidpp;
unsigned int *uintp;
if (dataptr && readfrom)
switch(type) {
case ASN_INTEGER:
intp = (int *) dataptr;
*intp = atoi(readfrom);
readfrom = skip_token(readfrom);
return readfrom;
case ASN_UNSIGNED:
uintp = (unsigned int *) dataptr;
*uintp = strtoul(readfrom, NULL, 0);
readfrom = skip_token(readfrom);
return readfrom;
case ASN_OCTET_STR:
case ASN_BIT_STR:
charpp = (char **) dataptr;
return read_config_read_octet_string(readfrom, (u_char **) charpp, len);
case ASN_OBJECT_ID:
oidpp = (oid **) dataptr;
return read_config_read_objid(readfrom, oidpp, len);
default:
DEBUGMSGTL(("read_config_read_data","Fail: Unknown type: %d", type));
return NULL;
}
return NULL;
}
/* read_config_read_data():
reads data of a given type from a token(s) on a configuration line.
Returns: character pointer to the next token in the configuration line.
NULL if none left.
NULL if an unknown type.
*/
char *read_config_store_data(int type, char *storeto, void *dataptr, size_t *len) {
int *intp;
u_char **charpp;
unsigned int *uintp;
oid **oidpp;
if (dataptr && storeto)
switch(type) {
case ASN_INTEGER:
intp = (int *) dataptr;
sprintf(storeto," %d", *intp);
return (storeto + strlen(storeto));
case ASN_UNSIGNED:
uintp = (unsigned int *) dataptr;
sprintf(storeto," %u", *uintp);
return (storeto + strlen(storeto));
case ASN_OCTET_STR:
case ASN_BIT_STR:
*storeto++ = ' ';
charpp = (u_char **) dataptr;
return read_config_save_octet_string(storeto, *charpp, *len);
case ASN_OBJECT_ID:
*storeto++ = ' ';
oidpp = (oid **) dataptr;
return read_config_save_objid(storeto, *oidpp, *len);
default:
DEBUGMSGTL(("read_config_store_data","Fail: Unknown type: %d", type));
return NULL;
}
return NULL;
}