net-snmp 5.7
read_config.c
00001 /*
00002  * read_config.c
00003  */
00004 /* Portions of this file are subject to the following copyright(s).  See
00005  * the Net-SNMP's COPYING file for more details and other copyrights
00006  * that may apply:
00007  */
00008 /*
00009  * Portions of this file are copyrighted by:
00010  * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
00011  * Use is subject to license terms specified in the COPYING file
00012  * distributed with the Net-SNMP package.
00013  */
00014 
00067 #include <net-snmp/net-snmp-config.h>
00068 #include <net-snmp/net-snmp-features.h>
00069 
00070 #include <stdio.h>
00071 #include <ctype.h>
00072 #if HAVE_STDLIB_H
00073 #include <stdlib.h>
00074 #endif
00075 #if HAVE_STRING_H
00076 #include <string.h>
00077 #else
00078 #include <strings.h>
00079 #endif
00080 #if HAVE_UNISTD_H
00081 #include <unistd.h>
00082 #endif
00083 #include <sys/types.h>
00084 #if HAVE_SYS_PARAM_H
00085 #include <sys/param.h>
00086 #endif
00087 #if TIME_WITH_SYS_TIME
00088 # include <sys/time.h>
00089 # include <time.h>
00090 #else
00091 # if HAVE_SYS_TIME_H
00092 #  include <sys/time.h>
00093 # else
00094 #  include <time.h>
00095 # endif
00096 #endif
00097 #ifdef HAVE_SYS_STAT_H
00098 #include <sys/stat.h>
00099 #endif
00100 #if HAVE_NETINET_IN_H
00101 #include <netinet/in.h>
00102 #endif
00103 #if HAVE_ARPA_INET_H
00104 #include <arpa/inet.h>
00105 #endif
00106 #if HAVE_SYS_SELECT_H
00107 #include <sys/select.h>
00108 #endif
00109 #if HAVE_SYS_SOCKET_H
00110 #include <sys/socket.h>
00111 #endif
00112 #if HAVE_NETDB_H
00113 #include <netdb.h>
00114 #endif
00115 #include <errno.h>
00116 
00117 #if HAVE_DIRENT_H
00118 # include <dirent.h>
00119 # define NAMLEN(dirent) strlen((dirent)->d_name)
00120 #else
00121 # define dirent direct
00122 # define NAMLEN(dirent) (dirent)->d_namlen
00123 # if HAVE_SYS_NDIR_H
00124 #  include <sys/ndir.h>
00125 # endif
00126 # if HAVE_SYS_DIR_H
00127 #  include <sys/dir.h>
00128 # endif
00129 # if HAVE_NDIR_H
00130 #  include <ndir.h>
00131 # endif
00132 #endif
00133 
00134 #if HAVE_DMALLOC_H
00135 #include <dmalloc.h>
00136 #endif
00137 
00138 #include <net-snmp/types.h>
00139 #include <net-snmp/output_api.h>
00140 #include <net-snmp/config_api.h>
00141 #include <net-snmp/library/read_config.h>       /* for "internal" definitions */
00142 #include <net-snmp/utilities.h>
00143 
00144 #include <net-snmp/library/mib.h>
00145 #include <net-snmp/library/parse.h>
00146 #include <net-snmp/library/snmp_api.h>
00147 #include <net-snmp/library/callback.h>
00148 
00149 netsnmp_feature_child_of(read_config_all, libnetsnmp)
00150 
00151 netsnmp_feature_child_of(unregister_app_config_handler, read_config_all)
00152 netsnmp_feature_child_of(read_config_register_app_prenetsnmp_mib_handler, netsnmp_unused)
00153 netsnmp_feature_child_of(read_config_register_const_config_handler, netsnmp_unused)
00154 
00155 static int      config_errors;
00156 
00157 struct config_files *config_files = NULL;
00158 
00159 
00160 static struct config_line *
00161 internal_register_config_handler(const char *type_param,
00162                                  const char *token,
00163                                  void (*parser) (const char *, char *),
00164                                  void (*releaser) (void), const char *help,
00165                                  int when)
00166 {
00167     struct config_files **ctmp = &config_files;
00168     struct config_line  **ltmp;
00169     const char           *type = type_param;
00170 
00171     if (type == NULL || *type == '\0') {
00172         type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00173                                      NETSNMP_DS_LIB_APPTYPE);
00174     }
00175 
00176     /*
00177      * Handle multiple types (recursively)
00178      */
00179     if (strchr(type, ':')) {
00180         struct config_line *ltmp2 = NULL;
00181         char                buf[STRINGMAX];
00182         char               *cptr = buf;
00183         strncpy(buf, type, STRINGMAX - 1);
00184         buf[STRINGMAX - 1] = '\0';
00185         while (cptr) {
00186             char* c = cptr;
00187             cptr = strchr(cptr, ':');
00188             if(cptr) {
00189                 *cptr = '\0';
00190                 ++cptr;
00191             }
00192             ltmp2 = internal_register_config_handler(c, token, parser,
00193                                                      releaser, help, when);
00194         }
00195         return ltmp2;
00196     }
00197     
00198     /*
00199      * Find type in current list  -OR-  create a new file type.
00200      */
00201     while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
00202         ctmp = &((*ctmp)->next);
00203     }
00204 
00205     if (*ctmp == NULL) {
00206         *ctmp = (struct config_files *)
00207             calloc(1, sizeof(struct config_files));
00208         if (!*ctmp) {
00209             return NULL;
00210         }
00211 
00212         (*ctmp)->fileHeader = strdup(type);
00213         DEBUGMSGTL(("9:read_config:type", "new type %s\n", type));
00214     }
00215 
00216     DEBUGMSGTL(("9:read_config:register_handler", "registering %s %s\n",
00217                 type, token));
00218     /*
00219      * Find parser type in current list  -OR-  create a new
00220      * line parser entry.
00221      */
00222     ltmp = &((*ctmp)->start);
00223 
00224     while (*ltmp != NULL && strcmp((*ltmp)->config_token, token)) {
00225         ltmp = &((*ltmp)->next);
00226     }
00227 
00228     if (*ltmp == NULL) {
00229         *ltmp = (struct config_line *)
00230             calloc(1, sizeof(struct config_line));
00231         if (!*ltmp) {
00232             return NULL;
00233         }
00234 
00235         (*ltmp)->config_time = when;
00236         (*ltmp)->config_token = strdup(token);
00237         if (help != NULL)
00238             (*ltmp)->help = strdup(help);
00239     }
00240 
00241     /*
00242      * Add/Replace the parse/free functions for the given line type
00243      * in the given file type.
00244      */
00245     (*ltmp)->parse_line = parser;
00246     (*ltmp)->free_func = releaser;
00247 
00248     return (*ltmp);
00249 
00250 }                               /* end register_config_handler() */
00251 
00252 struct config_line *
00253 register_prenetsnmp_mib_handler(const char *type,
00254                                 const char *token,
00255                                 void (*parser) (const char *, char *),
00256                                 void (*releaser) (void), const char *help)
00257 {
00258     return internal_register_config_handler(type, token, parser, releaser,
00259                                             help, PREMIB_CONFIG);
00260 }
00261 
00262 #ifndef NETSNMP_FEATURE_REMOVE_READ_CONFIG_REGISTER_APP_PRENETSNMP_MIB_HANDLER
00263 struct config_line *
00264 register_app_prenetsnmp_mib_handler(const char *token,
00265                                     void (*parser) (const char *, char *),
00266                                     void (*releaser) (void),
00267                                     const char *help)
00268 {
00269     return (register_prenetsnmp_mib_handler
00270             (NULL, token, parser, releaser, help));
00271 }
00272 #endif /* NETSNMP_FEATURE_REMOVE_READ_CONFIG_REGISTER_APP_PRENETSNMP_MIB_HANDLER */
00273 
00306 struct config_line *
00307 register_config_handler(const char *type,
00308                         const char *token,
00309                         void (*parser) (const char *, char *),
00310                         void (*releaser) (void), const char *help)
00311 {
00312     return internal_register_config_handler(type, token, parser, releaser,
00313                                             help, NORMAL_CONFIG);
00314 }
00315 
00316 #ifndef NETSNMP_FEATURE_REMOVE_READ_CONFIG_REGISTER_CONST_CONFIG_HANDLER
00317 struct config_line *
00318 register_const_config_handler(const char *type,
00319                               const char *token,
00320                               void (*parser) (const char *, const char *),
00321                               void (*releaser) (void), const char *help)
00322 {
00323     return internal_register_config_handler(type, token,
00324                                             (void(*)(const char *, char *))
00325                                             parser, releaser,
00326                                             help, NORMAL_CONFIG);
00327 }
00328 #endif /* NETSNMP_FEATURE_REMOVE_READ_CONFIG_REGISTER_CONST_CONFIG_HANDLER */
00329 
00330 struct config_line *
00331 register_app_config_handler(const char *token,
00332                             void (*parser) (const char *, char *),
00333                             void (*releaser) (void), const char *help)
00334 {
00335     return (register_config_handler(NULL, token, parser, releaser, help));
00336 }
00337 
00338 
00339 
00351 void
00352 unregister_config_handler(const char *type_param, const char *token)
00353 {
00354     struct config_files **ctmp = &config_files;
00355     struct config_line  **ltmp;
00356     const char           *type = type_param;
00357 
00358     if (type == NULL || *type == '\0') {
00359         type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00360                                      NETSNMP_DS_LIB_APPTYPE);
00361     }
00362 
00363     /*
00364      * Handle multiple types (recursively)
00365      */
00366     if (strchr(type, ':')) {
00367         char                buf[STRINGMAX];
00368         char               *cptr = buf;
00369         strncpy(buf, type, STRINGMAX - 1);
00370         buf[STRINGMAX - 1] = '\0';
00371         while (cptr) {
00372             char* c = cptr;
00373             cptr = strchr(cptr, ':');
00374             if(cptr) {
00375                 *cptr = '\0';
00376                 ++cptr;
00377             }
00378             unregister_config_handler(c, token);
00379         }
00380         return;
00381     }
00382     
00383     /*
00384      * find type in current list 
00385      */
00386     while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
00387         ctmp = &((*ctmp)->next);
00388     }
00389 
00390     if (*ctmp == NULL) {
00391         /*
00392          * Not found, return. 
00393          */
00394         return;
00395     }
00396 
00397     ltmp = &((*ctmp)->start);
00398     if (*ltmp == NULL) {
00399         /*
00400          * Not found, return. 
00401          */
00402         return;
00403     }
00404     if (strcmp((*ltmp)->config_token, token) == 0) {
00405         /*
00406          * found it at the top of the list 
00407          */
00408         struct config_line *ltmp2 = (*ltmp)->next;
00409         SNMP_FREE((*ltmp)->config_token);
00410         SNMP_FREE((*ltmp)->help);
00411         SNMP_FREE(*ltmp);
00412         (*ctmp)->start = ltmp2;
00413         return;
00414     }
00415     while ((*ltmp)->next != NULL
00416            && strcmp((*ltmp)->next->config_token, token)) {
00417         ltmp = &((*ltmp)->next);
00418     }
00419     if ((*ltmp)->next != NULL) {
00420         struct config_line *ltmp2 = (*ltmp)->next->next;
00421         SNMP_FREE((*ltmp)->next->config_token);
00422         SNMP_FREE((*ltmp)->next->help);
00423         SNMP_FREE((*ltmp)->next);
00424         (*ltmp)->next = ltmp2;
00425     }
00426 }
00427 
00428 #ifndef NETSNMP_FEATURE_REMOVE_UNREGISTER_APP_CONFIG_HANDLER
00429 void
00430 unregister_app_config_handler(const char *token)
00431 {
00432     unregister_config_handler(NULL, token);
00433 }
00434 #endif /* NETSNMP_FEATURE_REMOVE_UNREGISTER_APP_CONFIG_HANDLER */
00435 
00436 void
00437 unregister_all_config_handlers(void)
00438 {
00439     struct config_files *ctmp, *save;
00440     struct config_line *ltmp;
00441 
00442     free_config();
00443 
00444     /*
00445      * Keep using config_files until there are no more! 
00446      */
00447     for (ctmp = config_files; ctmp;) {
00448         for (ltmp = ctmp->start; ltmp; ltmp = ctmp->start) {
00449             unregister_config_handler(ctmp->fileHeader,
00450                                       ltmp->config_token);
00451         }
00452         SNMP_FREE(ctmp->fileHeader);
00453         save = ctmp->next;
00454         SNMP_FREE(ctmp);
00455         ctmp = save;
00456         config_files = save;
00457     }
00458 }
00459 
00460 #ifdef TESTING
00461 void
00462 print_config_handlers(void)
00463 {
00464     struct config_files *ctmp = config_files;
00465     struct config_line *ltmp;
00466 
00467     for (; ctmp != NULL; ctmp = ctmp->next) {
00468         DEBUGMSGTL(("read_config", "read_conf: %s\n", ctmp->fileHeader));
00469         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
00470             DEBUGMSGTL(("read_config", "                   %s\n",
00471                         ltmp->config_token));
00472     }
00473 }
00474 #endif
00475 
00476 int             linecount,    prev_linecount;
00477 const char     *curfilename, *prev_filename;
00478 
00479 struct config_line *
00480 read_config_get_handlers(const char *type)
00481 {
00482     struct config_files *ctmp = config_files;
00483     for (; ctmp != NULL && strcmp(ctmp->fileHeader, type);
00484          ctmp = ctmp->next);
00485     if (ctmp)
00486         return ctmp->start;
00487     return NULL;
00488 }
00489 
00490 int
00491 read_config_with_type_when(const char *filename, const char *type, int when)
00492 {
00493     struct config_line *ctmp = read_config_get_handlers(type);
00494     if (ctmp)
00495         return read_config(filename, ctmp, when);
00496     else
00497         DEBUGMSGTL(("read_config",
00498                     "read_config: I have no registrations for type:%s,file:%s\n",
00499                     type, filename));
00500     return SNMPERR_GENERR;     /* No config files read */
00501 }
00502 
00503 int
00504 read_config_with_type(const char *filename, const char *type)
00505 {
00506     return read_config_with_type_when(filename, type, EITHER_CONFIG);
00507 }
00508 
00509 
00510 struct config_line *
00511 read_config_find_handler(struct config_line *line_handlers,
00512                          const char *token)
00513 {
00514     struct config_line *lptr;
00515 
00516     for (lptr = line_handlers; lptr != NULL; lptr = lptr->next) {
00517         if (!strcasecmp(token, lptr->config_token)) {
00518             return lptr;
00519         }
00520     }
00521     return NULL;
00522 }
00523 
00524 
00525 /*
00526  * searches a config_line linked list for a match 
00527  */
00528 int
00529 run_config_handler(struct config_line *lptr,
00530                    const char *token, char *cptr, int when)
00531 {
00532     char           *cp;
00533     lptr = read_config_find_handler(lptr, token);
00534     if (lptr != NULL) {
00535         if (when == EITHER_CONFIG || lptr->config_time == when) {
00536             DEBUGMSGTL(("read_config:parser",
00537                         "Found a parser.  Calling it: %s / %s\n", token,
00538                         cptr));
00539             /*
00540              * Stomp on any trailing whitespace
00541              */
00542             cp = &(cptr[strlen(cptr)-1]);
00543             while ((cp > cptr) && isspace((unsigned char)(*cp))) {
00544                 *(cp--) = '\0';
00545             }
00546             (*(lptr->parse_line)) (token, cptr);
00547         }
00548         else
00549             DEBUGMSGTL(("9:read_config:parser",
00550                         "%s handler not registered for this time\n", token));
00551     } else if (when != PREMIB_CONFIG && 
00552                !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
00553                                        NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
00554         netsnmp_config_warn("Unknown token: %s.", token);
00555         return SNMPERR_GENERR;
00556     }
00557     return SNMPERR_SUCCESS;
00558 }
00559 
00560 /*
00561  * takens an arbitrary string and tries to intepret it based on the
00562  * known configuration handlers for all registered types.  May produce
00563  * inconsistent results when multiple tokens of the same name are
00564  * registered under different file types. 
00565  */
00566 
00567 /*
00568  * we allow = delimeters here 
00569  */
00570 #define SNMP_CONFIG_DELIMETERS " \t="
00571 
00572 int
00573 snmp_config_when(char *line, int when)
00574 {
00575     char           *cptr, buf[STRINGMAX];
00576     struct config_line *lptr = NULL;
00577     struct config_files *ctmp = config_files;
00578     char           *st;
00579 
00580     if (line == NULL) {
00581         config_perror("snmp_config() called with a null string.");
00582         return SNMPERR_GENERR;
00583     }
00584 
00585     strncpy(buf, line, STRINGMAX);
00586     buf[STRINGMAX - 1] = '\0';
00587     cptr = strtok_r(buf, SNMP_CONFIG_DELIMETERS, &st);
00588     if (cptr && cptr[0] == '[') {
00589         if (cptr[strlen(cptr) - 1] != ']') {
00590             netsnmp_config_error("no matching ']' for type %s.", cptr + 1);
00591             return SNMPERR_GENERR;
00592         }
00593         cptr[strlen(cptr) - 1] = '\0';
00594         lptr = read_config_get_handlers(cptr + 1);
00595         if (lptr == NULL) {
00596             netsnmp_config_error("No handlers regestered for type %s.",
00597                                  cptr + 1);
00598             return SNMPERR_GENERR;
00599         }
00600         cptr = strtok_r(NULL, SNMP_CONFIG_DELIMETERS, &st);
00601         lptr = read_config_find_handler(lptr, cptr);
00602     } else {
00603         /*
00604          * we have to find a token 
00605          */
00606         for (; ctmp != NULL && lptr == NULL; ctmp = ctmp->next)
00607             lptr = read_config_find_handler(ctmp->start, cptr);
00608     }
00609     if (lptr == NULL && netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
00610                                           NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
00611         netsnmp_config_warn("Unknown token: %s.", cptr);
00612         return SNMPERR_GENERR;
00613     }
00614 
00615     /*
00616      * use the original string instead since strtok_r messed up the original 
00617      */
00618     line = skip_white(line + (cptr - buf) + strlen(cptr) + 1);
00619 
00620     return (run_config_handler(lptr, cptr, line, when));
00621 }
00622 
00623 int
00624 netsnmp_config(char *line)
00625 {
00626     int             ret = SNMP_ERR_NOERROR;
00627     DEBUGMSGTL(("snmp_config", "remembering line \"%s\"\n", line));
00628     netsnmp_config_remember(line);      /* always remember it so it's read
00629                                          * processed after a free_config()
00630                                          * call */
00631     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
00632                                NETSNMP_DS_LIB_HAVE_READ_CONFIG)) {
00633         DEBUGMSGTL(("snmp_config", "  ... processing it now\n"));
00634         ret = snmp_config_when(line, NORMAL_CONFIG);
00635     }
00636     return ret;
00637 }
00638 
00639 void
00640 netsnmp_config_remember_in_list(char *line,
00641                                 struct read_config_memory **mem)
00642 {
00643     if (mem == NULL)
00644         return;
00645 
00646     while (*mem != NULL)
00647         mem = &((*mem)->next);
00648 
00649     *mem = SNMP_MALLOC_STRUCT(read_config_memory);
00650     if (*mem != NULL) {
00651         if (line)
00652             (*mem)->line = strdup(line);
00653     }
00654 }
00655 
00656 void
00657 netsnmp_config_remember_free_list(struct read_config_memory **mem)
00658 {
00659     struct read_config_memory *tmpmem;
00660     while (*mem) {
00661         SNMP_FREE((*mem)->line);
00662         tmpmem = (*mem)->next;
00663         SNMP_FREE(*mem);
00664         *mem = tmpmem;
00665     }
00666 }
00667 
00668 void
00669 netsnmp_config_process_memory_list(struct read_config_memory **memp,
00670                                    int when, int clear)
00671 {
00672 
00673     struct read_config_memory *mem;
00674 
00675     if (!memp)
00676         return;
00677 
00678     mem = *memp;
00679 
00680     while (mem) {
00681         DEBUGMSGTL(("read_config:mem", "processing memory: %s\n", mem->line));
00682         snmp_config_when(mem->line, when);
00683         mem = mem->next;
00684     }
00685 
00686     if (clear)
00687         netsnmp_config_remember_free_list(memp);
00688 }
00689 
00690 /*
00691  * default storage location implementation 
00692  */
00693 static struct read_config_memory *memorylist = NULL;
00694 
00695 void
00696 netsnmp_config_remember(char *line)
00697 {
00698     netsnmp_config_remember_in_list(line, &memorylist);
00699 }
00700 
00701 void
00702 netsnmp_config_process_memories(void)
00703 {
00704     netsnmp_config_process_memory_list(&memorylist, EITHER_CONFIG, 1);
00705 }
00706 
00707 void
00708 netsnmp_config_process_memories_when(int when, int clear)
00709 {
00710     netsnmp_config_process_memory_list(&memorylist, when, clear);
00711 }
00712 
00713 /*******************************************************************-o-******
00714  * read_config
00715  *
00716  * Parameters:
00717  *      *filename
00718  *      *line_handler
00719  *       when
00720  *
00721  * Read <filename> and process each line in accordance with the list of
00722  * <line_handler> functions.
00723  *
00724  *
00725  * For each line in <filename>, search the list of <line_handler>'s 
00726  * for an entry that matches the first token on the line.  This comparison is
00727  * case insensitive.
00728  *
00729  * For each match, check that <when> is the designated time for the
00730  * <line_handler> function to be executed before processing the line.
00731  *
00732  * Returns SNMPERR_SUCCESS if the file is processed successfully.
00733  * Returns SNMPERR_GENERR  if it cannot.
00734  *    Note that individual config token errors do not trigger SNMPERR_GENERR
00735  *    It's only if the whole file cannot be processed for some reason.
00736  */
00737 int
00738 read_config(const char *filename,
00739             struct config_line *line_handler, int when)
00740 {
00741     static int      depth = 0;
00742     static int      files = 0;
00743     FILE           *ifile;
00744     char            line[STRINGMAX], token[STRINGMAX];
00745     char           *cptr;
00746     int             i, ret;
00747     struct config_line *lptr;
00748 
00749     linecount = 0;
00750     curfilename = filename;
00751 
00752     if ((ifile = fopen(filename, "r")) == NULL) {
00753 #ifdef ENOENT
00754         if (errno == ENOENT) {
00755             DEBUGMSGTL(("read_config", "%s: %s\n", filename,
00756                         strerror(errno)));
00757         } else
00758 #endif                          /* ENOENT */
00759 #ifdef EACCES
00760         if (errno == EACCES) {
00761             DEBUGMSGTL(("read_config", "%s: %s\n", filename,
00762                         strerror(errno)));
00763         } else
00764 #endif                          /* EACCES */
00765 #if defined(ENOENT) || defined(EACCES)
00766         {
00767             snmp_log_perror(filename);
00768         }
00769 #else                           /* defined(ENOENT) || defined(EACCES) */
00770             snmp_log_perror(filename);
00771 #endif                          /* ENOENT */
00772         return SNMPERR_GENERR;
00773     }
00774 
00775 #define CONFIG_MAX_FILES 4096
00776     if (files > CONFIG_MAX_FILES) {
00777         netsnmp_config_error("maximum conf file count (%d) exceeded\n",
00778                              CONFIG_MAX_FILES);
00779         return SNMPERR_GENERR;
00780     }
00781 #define CONFIG_MAX_RECURSE_DEPTH 16
00782     if (depth > CONFIG_MAX_RECURSE_DEPTH) {
00783         netsnmp_config_error("nested include depth > %d\n",
00784                              CONFIG_MAX_RECURSE_DEPTH);
00785         return SNMPERR_GENERR;
00786     }
00787     ++files;
00788     ++depth;
00789 
00790     DEBUGMSGTL(("read_config:file", "Reading configuration %s (%d)\n",
00791                 filename, when));
00792     
00793     while (fgets(line, sizeof(line), ifile) != NULL) {
00794         lptr = line_handler;
00795         linecount++;
00796         cptr = line;
00797         i = strlen(line) - 1;
00798         if (line[i] == '\n')
00799             line[i] = 0;
00800         DEBUGMSGTL(("9:read_config:line", "%s:%d examining: %s\n",
00801                     filename, linecount, line));
00802         /*
00803          * check blank line or # comment 
00804          */
00805         if ((cptr = skip_white(cptr))) {
00806             cptr = copy_nword(cptr, token, sizeof(token));
00807             if (token[0] == '[') {
00808                 if (token[strlen(token) - 1] != ']') {
00809                     netsnmp_config_error("no matching ']' for type %s.",
00810                                          &token[1]);
00811                     continue;
00812                 }
00813                 token[strlen(token) - 1] = '\0';
00814                 lptr = read_config_get_handlers(&token[1]);
00815                 if (lptr == NULL) {
00816                     netsnmp_config_error("No handlers regestered for type %s.",
00817                                          &token[1]);
00818                     continue;
00819                 }
00820                 DEBUGMSGTL(("read_config:context",
00821                             "Switching to new context: %s%s\n",
00822                             ((cptr) ? "(this line only) " : ""),
00823                             &token[1]));
00824                 if (cptr == NULL) {
00825                     /*
00826                      * change context permanently 
00827                      */
00828                     line_handler = lptr;
00829                     continue;
00830                 } else {
00831                     /*
00832                      * the rest of this line only applies. 
00833                      */
00834                     cptr = copy_nword(cptr, token, sizeof(token));
00835                 }
00836             } else if ((token[0] == 'i') && (strncasecmp(token,"include", 7 )==0)) {
00837                 if ( strcasecmp( token, "include" )==0) {
00838                     if (when != PREMIB_CONFIG && 
00839                         !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
00840                                                 NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
00841                         netsnmp_config_warn("Ambiguous token '%s' - use 'includeSearch' (or 'includeFile') instead.", token);
00842                     }
00843                     continue;
00844                 } else if ( strcasecmp( token, "includedir" )==0) {
00845                     DIR *d;
00846                     struct dirent *entry;
00847                     char  fname[SNMP_MAXPATH];
00848                     int   len;
00849 
00850                     if (cptr == NULL) {
00851                         if (when != PREMIB_CONFIG)
00852                             netsnmp_config_error("Blank line following %s token.", token);
00853                         continue;
00854                     }
00855                     if ((d=opendir(cptr)) == NULL ) {
00856                         if (when != PREMIB_CONFIG)
00857                             netsnmp_config_error("Can't open include dir '%s'.", cptr);
00858                         continue;
00859                     }
00860                     prev_filename  = curfilename;
00861                     prev_linecount = linecount;
00862                     while ((entry = readdir( d )) != NULL ) {
00863                         if ( entry->d_name && entry->d_name[0] != '.') {
00864                             len = NAMLEN(entry);
00865                             if ((len > 5) && (strcmp(&(entry->d_name[len-5]),".conf") == 0)) {
00866                                 snprintf(fname, SNMP_MAXPATH, "%s/%s",
00867                                          cptr, entry->d_name);
00868                                 (void)read_config(fname, line_handler, when);
00869                             }
00870                         }
00871                     }
00872                     closedir(d);
00873                     curfilename = prev_filename;
00874                     linecount   = prev_linecount;
00875                     continue;
00876                 } else if ( strcasecmp( token, "includefile" )==0) {
00877                     char  fname[SNMP_MAXPATH], *cp;
00878 
00879                     if (cptr == NULL) {
00880                         if (when != PREMIB_CONFIG)
00881                             netsnmp_config_error("Blank line following %s token.", token);
00882                         continue;
00883                     }
00884                     if ( cptr[0] == '/' ) {
00885                         strncpy(fname, cptr, SNMP_MAXPATH);
00886                         fname[SNMP_MAXPATH-1]='\0';
00887                     } else {
00888                         strncpy(fname, filename, SNMP_MAXPATH);
00889                         fname[SNMP_MAXPATH-1]='\0';
00890                         cp = strrchr(fname, '/');
00891                         *(++cp) = '\0';
00892                         strncat(fname, cptr, SNMP_MAXPATH-strlen(fname));
00893                         fname[SNMP_MAXPATH-1]='\0';
00894                     }
00895                     prev_filename  = curfilename;
00896                     prev_linecount = linecount;
00897                     ret = read_config(fname, line_handler, when);
00898                     curfilename = prev_filename;
00899                     linecount   = prev_linecount;
00900                     if ((ret != SNMPERR_SUCCESS) && (when != PREMIB_CONFIG))
00901                         netsnmp_config_error("Included file '%s' not found.", fname);
00902                     continue;
00903                 } else if ( strcasecmp( token, "includesearch" )==0) {
00904                     struct config_files ctmp;
00905                     int len;
00906 
00907                     if (cptr == NULL) {
00908                         if (when != PREMIB_CONFIG)
00909                             netsnmp_config_error("Blank line following %s token.", token);
00910                         continue;
00911                     }
00912                     len = strlen(cptr);
00913                     ctmp.fileHeader = cptr;
00914                     ctmp.start = line_handler;
00915                     ctmp.next = NULL;
00916                     if ((len > 5) && (strcmp(&cptr[len-5],".conf") == 0))
00917                        cptr[len-5] = 0; /* chop off .conf */
00918                     prev_filename  = curfilename;
00919                     prev_linecount = linecount;
00920                     ret = read_config_files_of_type(when,&ctmp);
00921                     curfilename = prev_filename;
00922                     linecount   = prev_linecount;
00923                     if ((len > 5) && (cptr[len-5] == 0))
00924                        cptr[len-5] = '.'; /* restore .conf */
00925                     if (( ret != SNMPERR_SUCCESS ) && (when != PREMIB_CONFIG))
00926                         netsnmp_config_error("Included config '%s' not found.", cptr);
00927                     continue;
00928                 } else {
00929                     lptr = line_handler;
00930                 }
00931             } else {
00932                 lptr = line_handler;
00933             }
00934             if (cptr == NULL) {
00935                 netsnmp_config_error("Blank line following %s token.", token);
00936             } else {
00937                 DEBUGMSGTL(("read_config:line", "%s:%d examining: %s\n",
00938                             filename, linecount, line));
00939                 run_config_handler(lptr, token, cptr, when);
00940             }
00941         }
00942     }
00943     fclose(ifile);
00944     --depth;
00945     return SNMPERR_SUCCESS;
00946 
00947 }                               /* end read_config() */
00948 
00949 
00950 
00951 void
00952 free_config(void)
00953 {
00954     struct config_files *ctmp = config_files;
00955     struct config_line *ltmp;
00956 
00957     for (; ctmp != NULL; ctmp = ctmp->next)
00958         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
00959             if (ltmp->free_func)
00960                 (*(ltmp->free_func)) ();
00961 }
00962 
00963 /*
00964  * Return SNMPERR_SUCCESS if any config files are processed
00965  * Return SNMPERR_GENERR if _no_ config files are processed
00966  *    Whether this is actually an error is left to the application
00967  */
00968 int
00969 read_configs_optional(const char *optional_config, int when)
00970 {
00971     char *newp, *cp, *st = NULL;
00972     int              ret = SNMPERR_GENERR;
00973     char *type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00974                                        NETSNMP_DS_LIB_APPTYPE);
00975 
00976     if ((NULL == optional_config) || (NULL == type))
00977         return ret;
00978 
00979     DEBUGMSGTL(("read_configs_optional",
00980                 "reading optional configuration tokens for %s\n", type));
00981     
00982     newp = strdup(optional_config);      /* strtok_r messes it up */
00983     cp = strtok_r(newp, ",", &st);
00984     while (cp) {
00985         struct stat     statbuf;
00986         if (stat(cp, &statbuf)) {
00987             DEBUGMSGTL(("read_config",
00988                         "Optional File \"%s\" does not exist.\n", cp));
00989             snmp_log_perror(cp);
00990         } else {
00991             DEBUGMSGTL(("read_config:opt",
00992                         "Reading optional config file: \"%s\"\n", cp));
00993             if ( read_config_with_type_when(cp, type, when) == SNMPERR_SUCCESS )
00994                 ret = SNMPERR_SUCCESS;
00995         }
00996         cp = strtok_r(NULL, ",", &st);
00997     }
00998     free(newp);
00999     return ret;
01000 }
01001 
01002 void
01003 read_configs(void)
01004 {
01005     char *optional_config = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01006                                                NETSNMP_DS_LIB_OPTIONALCONFIG);
01007 
01008     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
01009                         SNMP_CALLBACK_PRE_READ_CONFIG, NULL);
01010 
01011     DEBUGMSGTL(("read_config", "reading normal configuration tokens\n"));
01012 
01013     if ((NULL != optional_config) && (*optional_config == '-')) {
01014         (void)read_configs_optional(++optional_config, NORMAL_CONFIG);
01015         optional_config = NULL; /* clear, so we don't read them twice */
01016     }
01017 
01018     (void)read_config_files(NORMAL_CONFIG);
01019 
01020     /*
01021      * do this even when the normal above wasn't done 
01022      */
01023     if (NULL != optional_config)
01024         (void)read_configs_optional(optional_config, NORMAL_CONFIG);
01025 
01026     netsnmp_config_process_memories_when(NORMAL_CONFIG, 1);
01027 
01028     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, 
01029                            NETSNMP_DS_LIB_HAVE_READ_CONFIG, 1);
01030     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
01031                         SNMP_CALLBACK_POST_READ_CONFIG, NULL);
01032 }
01033 
01034 void
01035 read_premib_configs(void)
01036 {
01037     char *optional_config = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01038                                                NETSNMP_DS_LIB_OPTIONALCONFIG);
01039 
01040     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
01041                         SNMP_CALLBACK_PRE_PREMIB_READ_CONFIG, NULL);
01042 
01043     DEBUGMSGTL(("read_config", "reading premib configuration tokens\n"));
01044 
01045     if ((NULL != optional_config) && (*optional_config == '-')) {
01046         (void)read_configs_optional(++optional_config, PREMIB_CONFIG);
01047         optional_config = NULL; /* clear, so we don't read them twice */
01048     }
01049 
01050     (void)read_config_files(PREMIB_CONFIG);
01051 
01052     if (NULL != optional_config)
01053         (void)read_configs_optional(optional_config, PREMIB_CONFIG);
01054 
01055     netsnmp_config_process_memories_when(PREMIB_CONFIG, 0);
01056 
01057     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, 
01058                            NETSNMP_DS_LIB_HAVE_READ_PREMIB_CONFIG, 1);
01059     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
01060                         SNMP_CALLBACK_POST_PREMIB_READ_CONFIG, NULL);
01061 }
01062 
01063 /*******************************************************************-o-******
01064  * set_configuration_directory
01065  *
01066  * Parameters:
01067  *      char *dir - value of the directory
01068  * Sets the configuration directory. Multiple directories can be
01069  * specified, but need to be seperated by 'ENV_SEPARATOR_CHAR'.
01070  */
01071 void
01072 set_configuration_directory(const char *dir)
01073 {
01074     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, 
01075                           NETSNMP_DS_LIB_CONFIGURATION_DIR, dir);
01076 }
01077 
01078 /*******************************************************************-o-******
01079  * get_configuration_directory
01080  *
01081  * Parameters: -
01082  * Retrieve the configuration directory or directories.
01083  * (For backwards compatibility that is:
01084  *       SNMPCONFPATH, SNMPSHAREPATH, SNMPLIBPATH, HOME/.snmp
01085  * First check whether the value is set.
01086  * If not set give it the default value.
01087  * Return the value.
01088  * We always retrieve it new, since we have to do it anyway if it is just set.
01089  */
01090 const char     *
01091 get_configuration_directory(void)
01092 {
01093     char            defaultPath[SPRINT_MAX_LEN];
01094     char           *homepath;
01095 
01096     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01097                                       NETSNMP_DS_LIB_CONFIGURATION_DIR)) {
01098         homepath = netsnmp_getenv("HOME");
01099         snprintf(defaultPath, sizeof(defaultPath), "%s%c%s%c%s%s%s%s",
01100                 SNMPCONFPATH, ENV_SEPARATOR_CHAR,
01101                 SNMPSHAREPATH, ENV_SEPARATOR_CHAR, SNMPLIBPATH,
01102                 ((homepath == NULL) ? "" : ENV_SEPARATOR),
01103                 ((homepath == NULL) ? "" : homepath),
01104                 ((homepath == NULL) ? "" : "/.snmp"));
01105         defaultPath[ sizeof(defaultPath)-1 ] = 0;
01106         set_configuration_directory(defaultPath);
01107     }
01108     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01109                                   NETSNMP_DS_LIB_CONFIGURATION_DIR));
01110 }
01111 
01112 /*******************************************************************-o-******
01113  * set_persistent_directory
01114  *
01115  * Parameters:
01116  *      char *dir - value of the directory
01117  * Sets the configuration directory. 
01118  * No multiple directories may be specified.
01119  * (However, this is not checked)
01120  */
01121 void
01122 set_persistent_directory(const char *dir)
01123 {
01124     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, 
01125                           NETSNMP_DS_LIB_PERSISTENT_DIR, dir);
01126 }
01127 
01128 /*******************************************************************-o-******
01129  * get_persistent_directory
01130  *
01131  * Parameters: -
01132  * Function will retrieve the persisten directory value.
01133  * First check whether the value is set.
01134  * If not set give it the default value.
01135  * Return the value. 
01136  * We always retrieve it new, since we have to do it anyway if it is just set.
01137  */
01138 const char     *
01139 get_persistent_directory(void)
01140 {
01141     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01142                                       NETSNMP_DS_LIB_PERSISTENT_DIR)) {
01143         const char *persdir = netsnmp_getenv("SNMP_PERSISTENT_DIR");
01144         if (NULL == persdir)
01145             persdir = NETSNMP_PERSISTENT_DIRECTORY;
01146         set_persistent_directory(persdir);
01147     }
01148     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01149                                   NETSNMP_DS_LIB_PERSISTENT_DIR));
01150 }
01151 
01152 /*******************************************************************-o-******
01153  * set_temp_file_pattern
01154  *
01155  * Parameters:
01156  *      char *pattern - value of the file pattern
01157  * Sets the temp file pattern. 
01158  * Multiple patterns may not be specified.
01159  * (However, this is not checked)
01160  */
01161 void
01162 set_temp_file_pattern(const char *pattern)
01163 {
01164     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, 
01165                           NETSNMP_DS_LIB_TEMP_FILE_PATTERN, pattern);
01166 }
01167 
01168 /*******************************************************************-o-******
01169  * get_temp_file_pattern
01170  *
01171  * Parameters: -
01172  * Function will retrieve the temp file pattern value.
01173  * First check whether the value is set.
01174  * If not set give it the default value.
01175  * Return the value. 
01176  * We always retrieve it new, since we have to do it anyway if it is just set.
01177  */
01178 const char     *
01179 get_temp_file_pattern(void)
01180 {
01181     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01182                                       NETSNMP_DS_LIB_TEMP_FILE_PATTERN)) {
01183         set_temp_file_pattern(NETSNMP_TEMP_FILE_PATTERN);
01184     }
01185     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01186                                   NETSNMP_DS_LIB_TEMP_FILE_PATTERN));
01187 }
01188 
01196 static int
01197 read_config_files_in_path(const char *path, struct config_files *ctmp,
01198                           int when, const char *perspath, const char *persfile)
01199 {
01200     int             done, j;
01201     char            configfile[300];
01202     char           *cptr1, *cptr2, *envconfpath;
01203     struct stat     statbuf;
01204     int             ret = SNMPERR_GENERR;
01205 
01206     if ((NULL == path) || (NULL == ctmp))
01207         return SNMPERR_GENERR;
01208 
01209     envconfpath = strdup(path);
01210 
01211     DEBUGMSGTL(("read_config:path", " config path used for %s:%s (persistent path:%s)\n",
01212                 ctmp->fileHeader, envconfpath, perspath));
01213     cptr1 = cptr2 = envconfpath;
01214     done = 0;
01215     while ((!done) && (*cptr2 != 0)) {
01216         while (*cptr1 != 0 && *cptr1 != ENV_SEPARATOR_CHAR)
01217             cptr1++;
01218         if (*cptr1 == 0)
01219             done = 1;
01220         else
01221             *cptr1 = 0;
01222 
01223         DEBUGMSGTL(("read_config:dir", " config dir: %s\n", cptr2 ));
01224         if (stat(cptr2, &statbuf) != 0) {
01225             /*
01226              * Directory not there, continue 
01227              */
01228             DEBUGMSGTL(("read_config:dir", " Directory not present: %s\n", cptr2 ));
01229             cptr2 = ++cptr1;
01230             continue;
01231         }
01232 #ifdef S_ISDIR
01233         if (!S_ISDIR(statbuf.st_mode)) {
01234             /*
01235              * Not a directory, continue 
01236              */
01237             DEBUGMSGTL(("read_config:dir", " Not a directory: %s\n", cptr2 ));
01238             cptr2 = ++cptr1;
01239             continue;
01240         }
01241 #endif
01242 
01243         /*
01244          * for proper persistent storage retrieval, we need to read old backup
01245          * copies of the previous storage files.  If the application in
01246          * question has died without the proper call to snmp_clean_persistent,
01247          * then we read all the configuration files we can, starting with
01248          * the oldest first.
01249          */
01250         if (strncmp(cptr2, perspath, strlen(perspath)) == 0 ||
01251             (persfile != NULL &&
01252              strncmp(cptr2, persfile, strlen(persfile)) == 0)) {
01253             DEBUGMSGTL(("read_config:persist", " persist dir: %s\n", cptr2 ));
01254             /*
01255              * limit this to the known storage directory only 
01256              */
01257             for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
01258                 snprintf(configfile, sizeof(configfile),
01259                          "%s/%s.%d.conf", cptr2,
01260                          ctmp->fileHeader, j);
01261                 configfile[ sizeof(configfile)-1 ] = 0;
01262                 if (stat(configfile, &statbuf) != 0) {
01263                     /*
01264                      * file not there, continue 
01265                      */
01266                     break;
01267                 } else {
01268                     /*
01269                      * backup exists, read it 
01270                      */
01271                     DEBUGMSGTL(("read_config_files",
01272                                 "old config file found: %s, parsing\n",
01273                                 configfile));
01274                     if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
01275                         ret = SNMPERR_SUCCESS;
01276                 }
01277             }
01278         }
01279         snprintf(configfile, sizeof(configfile),
01280                  "%s/%s.conf", cptr2, ctmp->fileHeader);
01281         configfile[ sizeof(configfile)-1 ] = 0;
01282         if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
01283             ret = SNMPERR_SUCCESS;
01284         snprintf(configfile, sizeof(configfile),
01285                  "%s/%s.local.conf", cptr2, ctmp->fileHeader);
01286         configfile[ sizeof(configfile)-1 ] = 0;
01287         if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
01288             ret = SNMPERR_SUCCESS;
01289 
01290         if(done)
01291             break;
01292 
01293         cptr2 = ++cptr1;
01294     }
01295     SNMP_FREE(envconfpath);
01296     return ret;
01297 }
01298 
01299 /*******************************************************************-o-******
01300  * read_config_files
01301  *
01302  * Parameters:
01303  *      when    == PREMIB_CONFIG, NORMAL_CONFIG  -or-  EITHER_CONFIG
01304  *
01305  *
01306  * Traverse the list of config file types, performing the following actions
01307  * for each --
01308  *
01309  * First, build a search path for config files.  If the contents of 
01310  * environment variable SNMPCONFPATH are NULL, then use the following
01311  * path list (where the last entry exists only if HOME is non-null):
01312  *
01313  *      SNMPSHAREPATH:SNMPLIBPATH:${HOME}/.snmp
01314  *
01315  * Then, In each of these directories, read config files by the name of:
01316  *
01317  *      <dir>/<fileHeader>.conf         -AND-
01318  *      <dir>/<fileHeader>.local.conf
01319  *
01320  * where <fileHeader> is taken from the config file type structure.
01321  *
01322  *
01323  * PREMIB_CONFIG causes free_config() to be invoked prior to any other action.
01324  *
01325  *
01326  * EXITs if any 'config_errors' are logged while parsing config file lines.
01327  *
01328  * Return SNMPERR_SUCCESS if any config files are processed
01329  * Return SNMPERR_GENERR if _no_ config files are processed
01330  *    Whether this is actually an error is left to the application
01331  */
01332 int
01333 read_config_files_of_type(int when, struct config_files *ctmp)
01334 {
01335     const char     *confpath, *persfile, *envconfpath;
01336     char           *perspath;
01337     int             ret = SNMPERR_GENERR;
01338 
01339     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01340                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
01341         || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01342                                   NETSNMP_DS_LIB_DISABLE_CONFIG_LOAD)
01343         || (NULL == ctmp)) return ret;
01344 
01345     /*
01346      * these shouldn't change
01347      */
01348     confpath = get_configuration_directory();
01349     persfile = netsnmp_getenv("SNMP_PERSISTENT_FILE");
01350     envconfpath = netsnmp_getenv("SNMPCONFPATH");
01351 
01352 
01353         /*
01354          * read the config files. strdup() the result of
01355          * get_persistent_directory() to avoid that parsing the "persistentDir"
01356          * keyword transforms the perspath pointer into a dangling pointer.
01357          */
01358         perspath = strdup(get_persistent_directory());
01359         if (envconfpath == NULL) {
01360             /*
01361              * read just the config files (no persistent stuff), since
01362              * persistent path can change via conf file. Then get the
01363              * current persistent directory, and read files there.
01364              */
01365             if ( read_config_files_in_path(confpath, ctmp, when, perspath,
01366                                       persfile) == SNMPERR_SUCCESS )
01367                 ret = SNMPERR_SUCCESS;
01368             free(perspath);
01369             perspath = strdup(get_persistent_directory());
01370             if ( read_config_files_in_path(perspath, ctmp, when, perspath,
01371                                       persfile) == SNMPERR_SUCCESS )
01372                 ret = SNMPERR_SUCCESS;
01373         }
01374         else {
01375             /*
01376              * only read path specified by user
01377              */
01378             if ( read_config_files_in_path(envconfpath, ctmp, when, perspath,
01379                                       persfile) == SNMPERR_SUCCESS )
01380                 ret = SNMPERR_SUCCESS;
01381         }
01382         free(perspath);
01383         return ret;
01384 }
01385 
01386 /*
01387  * Return SNMPERR_SUCCESS if any config files are processed
01388  * Return SNMPERR_GENERR if _no_ config files are processed
01389  *    Whether this is actually an error is left to the application
01390  */
01391 int
01392 read_config_files(int when) {
01393 
01394     struct config_files *ctmp = config_files;
01395     int                  ret  = SNMPERR_GENERR;
01396 
01397     config_errors = 0;
01398 
01399     if (when == PREMIB_CONFIG)
01400         free_config();
01401 
01402     /*
01403      * read all config file types 
01404      */
01405     for (; ctmp != NULL; ctmp = ctmp->next) {
01406         if ( read_config_files_of_type(when, ctmp) == SNMPERR_SUCCESS )
01407             ret = SNMPERR_SUCCESS;
01408     }
01409 
01410     if (config_errors) {
01411         snmp_log(LOG_ERR, "net-snmp: %d error(s) in config file(s)\n",
01412                  config_errors);
01413     }
01414     return ret;
01415 }
01416 
01417 void
01418 read_config_print_usage(const char *lead)
01419 {
01420     struct config_files *ctmp = config_files;
01421     struct config_line *ltmp;
01422 
01423     if (lead == NULL)
01424         lead = "";
01425 
01426     for (ctmp = config_files; ctmp != NULL; ctmp = ctmp->next) {
01427         snmp_log(LOG_INFO, "%sIn %s.conf and %s.local.conf:\n", lead,
01428                  ctmp->fileHeader, ctmp->fileHeader);
01429         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next) {
01430             DEBUGIF("read_config_usage") {
01431                 if (ltmp->config_time == PREMIB_CONFIG)
01432                     DEBUGMSG(("read_config_usage", "*"));
01433                 else
01434                     DEBUGMSG(("read_config_usage", " "));
01435             }
01436             if (ltmp->help) {
01437                 snmp_log(LOG_INFO, "%s%s%-24s %s\n", lead, lead,
01438                          ltmp->config_token, ltmp->help);
01439             } else {
01440                 DEBUGIF("read_config_usage") {
01441                     snmp_log(LOG_INFO, "%s%s%-24s [NO HELP]\n", lead, lead,
01442                              ltmp->config_token);
01443                 }
01444             }
01445         }
01446     }
01447 }
01448 
01462 void
01463 read_config_store(const char *type, const char *line)
01464 {
01465 #ifdef NETSNMP_PERSISTENT_DIRECTORY
01466     char            file[512], *filep;
01467     FILE           *fout;
01468 #ifdef NETSNMP_PERSISTENT_MASK
01469     mode_t          oldmask;
01470 #endif
01471 
01472     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01473                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
01474      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01475                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD)) return;
01476 
01477     /*
01478      * store configuration directives in the following order of preference:
01479      * 1. ENV variable SNMP_PERSISTENT_FILE
01480      * 2. configured <NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf
01481      */
01482     if ((filep = netsnmp_getenv("SNMP_PERSISTENT_FILE")) == NULL) {
01483         snprintf(file, sizeof(file),
01484                  "%s/%s.conf", get_persistent_directory(), type);
01485         file[ sizeof(file)-1 ] = 0;
01486         filep = file;
01487     }
01488 #ifdef NETSNMP_PERSISTENT_MASK
01489     oldmask = umask(NETSNMP_PERSISTENT_MASK);
01490 #endif
01491     if (mkdirhier(filep, NETSNMP_AGENT_DIRECTORY_MODE, 1)) {
01492         snmp_log(LOG_ERR,
01493                  "Failed to create the persistent directory for %s\n",
01494                  file);
01495     }
01496     if ((fout = fopen(filep, "a")) != NULL) {
01497         fprintf(fout, "%s", line);
01498         if (line[strlen(line)] != '\n')
01499             fprintf(fout, "\n");
01500         DEBUGMSGTL(("read_config:store", "storing: %s\n", line));
01501         fclose(fout);
01502     } else {
01503         snmp_log(LOG_ERR, "read_config_store open failure on %s\n", filep);
01504     }
01505 #ifdef NETSNMP_PERSISTENT_MASK
01506     umask(oldmask);
01507 #endif
01508 
01509 #endif
01510 }                               /* end read_config_store() */
01511 
01512 void
01513 read_app_config_store(const char *line)
01514 {
01515     read_config_store(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01516                                             NETSNMP_DS_LIB_APPTYPE), line);
01517 }
01518 
01519 
01520 
01521 
01522 /*******************************************************************-o-******
01523  * snmp_save_persistent
01524  *
01525  * Parameters:
01526  *      *type
01527  *      
01528  *
01529  * Save the file "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf" into a backup copy
01530  * called "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.%d.conf", which %d is an
01531  * incrementing number on each call, but less than NETSNMP_MAX_PERSISTENT_BACKUPS.
01532  *
01533  * Should be called just before all persistent information is supposed to be
01534  * written to move aside the existing persistent cache.
01535  * snmp_clean_persistent should then be called afterward all data has been
01536  * saved to remove these backup files.
01537  *
01538  * Note: on an rename error, the files are removed rather than saved.
01539  *
01540  */
01541 void
01542 snmp_save_persistent(const char *type)
01543 {
01544     char            file[512], fileold[SPRINT_MAX_LEN];
01545     struct stat     statbuf;
01546     int             j;
01547 
01548     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01549                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
01550      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01551                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE)) return;
01552 
01553     DEBUGMSGTL(("snmp_save_persistent", "saving %s files...\n", type));
01554     snprintf(file, sizeof(file),
01555              "%s/%s.conf", get_persistent_directory(), type);
01556     file[ sizeof(file)-1 ] = 0;
01557     if (stat(file, &statbuf) == 0) {
01558         for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
01559             snprintf(fileold, sizeof(fileold),
01560                      "%s/%s.%d.conf", get_persistent_directory(), type, j);
01561             fileold[ sizeof(fileold)-1 ] = 0;
01562             if (stat(fileold, &statbuf) != 0) {
01563                 DEBUGMSGTL(("snmp_save_persistent",
01564                             " saving old config file: %s -> %s.\n", file,
01565                             fileold));
01566                 if (rename(file, fileold)) {
01567                     snmp_log(LOG_ERR, "Cannot rename %s to %s\n", file, fileold);
01568                      /* moving it failed, try nuking it, as leaving
01569                       * it around is very bad. */
01570                     if (unlink(file) == -1)
01571                         snmp_log(LOG_ERR, "Cannot unlink %s\n", file);
01572                 }
01573                 break;
01574             }
01575         }
01576     }
01577     /*
01578      * save a warning header to the top of the new file 
01579      */
01580     snprintf(fileold, sizeof(fileold),
01581             "%s%s# Please save normal configuration tokens for %s in SNMPCONFPATH/%s.conf.\n# Only \"createUser\" tokens should be placed here by %s administrators.\n%s",
01582             "#\n# net-snmp (or ucd-snmp) persistent data file.\n#\n############################################################################\n# STOP STOP STOP STOP STOP STOP STOP STOP STOP \n",
01583             "#\n#          **** DO NOT EDIT THIS FILE ****\n#\n# STOP STOP STOP STOP STOP STOP STOP STOP STOP \n############################################################################\n#\n# DO NOT STORE CONFIGURATION ENTRIES HERE.\n",
01584             type, type, type,
01585             "# (Did I mention: do not edit this file?)\n#\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
01586     fileold[ sizeof(fileold)-1 ] = 0;
01587     read_config_store(type, fileold);
01588 }
01589 
01590 
01591 /*******************************************************************-o-******
01592  * snmp_clean_persistent
01593  *
01594  * Parameters:
01595  *      *type
01596  *      
01597  *
01598  * Unlink all backup files called "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.%d.conf".
01599  *
01600  * Should be called just after we successfull dumped the last of the
01601  * persistent data, to remove the backup copies of previous storage dumps.
01602  *
01603  * XXX  Worth overwriting with random bytes first?  This would
01604  *      ensure that the data is destroyed, even a buffer containing the
01605  *      data persists in memory or swap.  Only important if secrets
01606  *      will be stored here.
01607  */
01608 void
01609 snmp_clean_persistent(const char *type)
01610 {
01611     char            file[512];
01612     struct stat     statbuf;
01613     int             j;
01614 
01615     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01616                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
01617      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01618                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE)) return;
01619 
01620     DEBUGMSGTL(("snmp_clean_persistent", "cleaning %s files...\n", type));
01621     snprintf(file, sizeof(file),
01622              "%s/%s.conf", get_persistent_directory(), type);
01623     file[ sizeof(file)-1 ] = 0;
01624     if (stat(file, &statbuf) == 0) {
01625         for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
01626             snprintf(file, sizeof(file),
01627                      "%s/%s.%d.conf", get_persistent_directory(), type, j);
01628             file[ sizeof(file)-1 ] = 0;
01629             if (stat(file, &statbuf) == 0) {
01630                 DEBUGMSGTL(("snmp_clean_persistent",
01631                             " removing old config file: %s\n", file));
01632                 if (unlink(file) == -1)
01633                     snmp_log(LOG_ERR, "Cannot unlink %s\n", file);
01634             }
01635         }
01636     }
01637 }
01638 
01639 
01640 
01641 
01642 /*
01643  * config_perror: prints a warning string associated with a file and
01644  * line number of a .conf file and increments the error count. 
01645  */
01646 static void
01647 config_vlog(int level, const char *levelmsg, const char *str, va_list args)
01648 {
01649     char tmpbuf[256];
01650     char* buf = tmpbuf;
01651     int len = snprintf(tmpbuf, sizeof(tmpbuf), "%s: line %d: %s: %s\n",
01652                        curfilename, linecount, levelmsg, str);
01653     if (len >= (int)sizeof(tmpbuf)) {
01654         buf = (char*)malloc(len + 1);
01655         sprintf(buf, "%s: line %d: %s: %s\n",
01656                 curfilename, linecount, levelmsg, str);
01657     }
01658     snmp_vlog(level, buf, args);
01659     if (buf != tmpbuf)
01660         free(buf);
01661 }
01662 
01663 void
01664 netsnmp_config_error(const char *str, ...)
01665 {
01666     va_list args;
01667     va_start(args, str);
01668     config_vlog(LOG_ERR, "Error", str, args);
01669     va_end(args);
01670     config_errors++;
01671 }
01672 
01673 void
01674 netsnmp_config_warn(const char *str, ...)
01675 {
01676     va_list args;
01677     va_start(args, str);
01678     config_vlog(LOG_WARNING, "Warning", str, args);
01679     va_end(args);
01680 }
01681 
01682 void
01683 config_perror(const char *str)
01684 {
01685     netsnmp_config_error("%s", str);
01686 }
01687 
01688 void
01689 config_pwarn(const char *str)
01690 {
01691     netsnmp_config_warn("%s", str);
01692 }
01693 
01694 /*
01695  * skip all white spaces and return 1 if found something either end of
01696  * line or a comment character 
01697  */
01698 char           *
01699 skip_white(char *ptr)
01700 {
01701     return NETSNMP_REMOVE_CONST(char *, skip_white_const(ptr));
01702 }
01703 
01704 const char     *
01705 skip_white_const(const char *ptr)
01706 {
01707     if (ptr == NULL)
01708         return (NULL);
01709     while (*ptr != 0 && isspace((unsigned char)*ptr))
01710         ptr++;
01711     if (*ptr == 0 || *ptr == '#')
01712         return (NULL);
01713     return (ptr);
01714 }
01715 
01716 char           *
01717 skip_not_white(char *ptr)
01718 {
01719     return NETSNMP_REMOVE_CONST(char *, skip_not_white_const(ptr));
01720 }
01721 
01722 const char     *
01723 skip_not_white_const(const char *ptr)
01724 {
01725     if (ptr == NULL)
01726         return (NULL);
01727     while (*ptr != 0 && !isspace((unsigned char)*ptr))
01728         ptr++;
01729     if (*ptr == 0 || *ptr == '#')
01730         return (NULL);
01731     return (ptr);
01732 }
01733 
01734 char           *
01735 skip_token(char *ptr)
01736 {
01737     return NETSNMP_REMOVE_CONST(char *, skip_token_const(ptr));
01738 }
01739 
01740 const char     *
01741 skip_token_const(const char *ptr)
01742 {
01743     ptr = skip_white_const(ptr);
01744     ptr = skip_not_white_const(ptr);
01745     ptr = skip_white_const(ptr);
01746     return (ptr);
01747 }
01748 
01749 /*
01750  * copy_word
01751  * copies the next 'token' from 'from' into 'to', maximum len-1 characters.
01752  * currently a token is anything seperate by white space
01753  * or within quotes (double or single) (i.e. "the red rose" 
01754  * is one token, \"the red rose\" is three tokens)
01755  * a '\' character will allow a quote character to be treated
01756  * as a regular character 
01757  * It returns a pointer to first non-white space after the end of the token
01758  * being copied or to 0 if we reach the end.
01759  * Note: Partially copied words (greater than len) still returns a !NULL ptr
01760  * Note: partially copied words are, however, null terminated.
01761  */
01762 
01763 char           *
01764 copy_nword(char *from, char *to, int len)
01765 {
01766     return NETSNMP_REMOVE_CONST(char *, copy_nword_const(from, to, len));
01767 }
01768 
01769 const char           *
01770 copy_nword_const(const char *from, char *to, int len)
01771 {
01772     char            quote;
01773     if (!from || !to)
01774         return NULL;
01775     if ((*from == '\"') || (*from == '\'')) {
01776         quote = *(from++);
01777         while ((*from != quote) && (*from != 0)) {
01778             if ((*from == '\\') && (*(from + 1) != 0)) {
01779                 if (len > 0) {  /* don't copy beyond len bytes */
01780                     *to++ = *(from + 1);
01781                     if (--len == 0)
01782                         *(to - 1) = '\0';       /* null protect the last spot */
01783                 }
01784                 from = from + 2;
01785             } else {
01786                 if (len > 0) {  /* don't copy beyond len bytes */
01787                     *to++ = *from++;
01788                     if (--len == 0)
01789                         *(to - 1) = '\0';       /* null protect the last spot */
01790                 } else
01791                     from++;
01792             }
01793         }
01794         if (*from == 0) {
01795             DEBUGMSGTL(("read_config_copy_word",
01796                         "no end quote found in config string\n"));
01797         } else
01798             from++;
01799     } else {
01800         while (*from != 0 && !isspace((unsigned char)(*from))) {
01801             if ((*from == '\\') && (*(from + 1) != 0)) {
01802                 if (len > 0) {  /* don't copy beyond len bytes */
01803                     *to++ = *(from + 1);
01804                     if (--len == 0)
01805                         *(to - 1) = '\0';       /* null protect the last spot */
01806                 }
01807                 from = from + 2;
01808             } else {
01809                 if (len > 0) {  /* don't copy beyond len bytes */
01810                     *to++ = *from++;
01811                     if (--len == 0)
01812                         *(to - 1) = '\0';       /* null protect the last spot */
01813                 } else
01814                     from++;
01815             }
01816         }
01817     }
01818     if (len > 0)
01819         *to = 0;
01820     from = skip_white_const(from);
01821     return (from);
01822 }                               /* copy_nword */
01823 
01824 /*
01825  * copy_word
01826  * copies the next 'token' from 'from' into 'to'.
01827  * currently a token is anything seperate by white space
01828  * or within quotes (double or single) (i.e. "the red rose" 
01829  * is one token, \"the red rose\" is three tokens)
01830  * a '\' character will allow a quote character to be treated
01831  * as a regular character 
01832  * It returns a pointer to first non-white space after the end of the token
01833  * being copied or to 0 if we reach the end.
01834  */
01835 
01836 static int      have_warned = 0;
01837 char           *
01838 copy_word(char *from, char *to)
01839 {
01840     if (!have_warned) {
01841         snmp_log(LOG_INFO,
01842                  "copy_word() called.  Use copy_nword() instead.\n");
01843         have_warned = 1;
01844     }
01845     return copy_nword(from, to, SPRINT_MAX_LEN);
01846 }                               /* copy_word */
01847 
01848 /*
01849  * read_config_save_octet_string(): saves an octet string as a length
01850  * followed by a string of hex 
01851  */
01852 char           *
01853 read_config_save_octet_string(char *saveto, u_char * str, size_t len)
01854 {
01855     int             i;
01856     u_char         *cp;
01857 
01858     /*
01859      * is everything easily printable 
01860      */
01861     for (i = 0, cp = str; i < (int) len && cp &&
01862          (isalpha(*cp) || isdigit(*cp) || *cp == ' '); cp++, i++);
01863 
01864     if (len != 0 && i == (int) len) {
01865         *saveto++ = '"';
01866         memcpy(saveto, str, len);
01867         saveto += len;
01868         *saveto++ = '"';
01869         *saveto = '\0';
01870     } else {
01871         if (str != NULL) {
01872             sprintf(saveto, "0x");
01873             saveto += 2;
01874             for (i = 0; i < (int) len; i++) {
01875                 sprintf(saveto, "%02x", str[i]);
01876                 saveto = saveto + 2;
01877             }
01878         } else {
01879             sprintf(saveto, "\"\"");
01880             saveto += 2;
01881         }
01882     }
01883     return saveto;
01884 }
01885 
01904 char           *
01905 read_config_read_octet_string(const char *readfrom, u_char ** str,
01906                               size_t * len)
01907 {
01908     return NETSNMP_REMOVE_CONST(char *,
01909                read_config_read_octet_string_const(readfrom, str, len));
01910 }
01911 
01912 const char     *
01913 read_config_read_octet_string_const(const char *readfrom, u_char ** str,
01914                                     size_t * len)
01915 {
01916     u_char         *cptr;
01917     const char     *cptr1;
01918     u_int           tmp;
01919     size_t          i, ilen;
01920 
01921     if (readfrom == NULL || str == NULL || len == NULL)
01922         return NULL;
01923 
01924     if (strncasecmp(readfrom, "0x", 2) == 0) {
01925         /*
01926          * A hex string submitted. How long? 
01927          */
01928         readfrom += 2;
01929         cptr1 = skip_not_white_const(readfrom);
01930         if (cptr1)
01931             ilen = (cptr1 - readfrom);
01932         else
01933             ilen = strlen(readfrom);
01934 
01935         if (ilen % 2) {
01936             snmp_log(LOG_WARNING,"invalid hex string: wrong length\n");
01937             DEBUGMSGTL(("read_config_read_octet_string",
01938                         "invalid hex string: wrong length"));
01939             return NULL;
01940         }
01941         ilen = ilen / 2;
01942 
01943         /*
01944          * malloc data space if needed (+1 for good measure) 
01945          */
01946         if (*str == NULL) {
01947             *str = (u_char *) malloc(ilen + 1);
01948             if (!*str)
01949                 return NULL;
01950         } else {
01951             /*
01952              * require caller to have +1, and bail if not enough space.
01953              */
01954             if (ilen >= *len) {
01955                 snmp_log(LOG_WARNING,"buffer too small to read octet string (%lu < %lu)\n",
01956                          (unsigned long)*len, (unsigned long)ilen);
01957                 DEBUGMSGTL(("read_config_read_octet_string",
01958                             "buffer too small (%lu < %lu)", (unsigned long)*len, (unsigned long)ilen));
01959                 *len = 0;
01960                 cptr1 = skip_not_white_const(readfrom);
01961                 return skip_white_const(cptr1);
01962             }
01963         }
01964 
01965         /*
01966          * copy validated data 
01967          */
01968         cptr = *str;
01969         for (i = 0; i < ilen; i++) {
01970             if (1 == sscanf(readfrom, "%2x", &tmp))
01971                 *cptr++ = (u_char) tmp;
01972             else {
01973                 /*
01974                  * we may lose memory, but don't know caller's buffer XX free(cptr); 
01975                  */
01976                 return (NULL);
01977             }
01978             readfrom += 2;
01979         }
01980         /*
01981          * Terminate the output buffer.
01982          */
01983         *cptr++ = '\0';
01984         *len = ilen;
01985         readfrom = skip_white_const(readfrom);
01986     } else {
01987         /*
01988          * Normal string 
01989          */
01990 
01991         /*
01992          * malloc string space if needed (including NULL terminator) 
01993          */
01994         if (*str == NULL) {
01995             char            buf[SNMP_MAXBUF];
01996             readfrom = copy_nword_const(readfrom, buf, sizeof(buf));
01997 
01998             *len = strlen(buf);
01999             *str = (u_char *) malloc(*len + 1);
02000             if (*str == NULL)
02001                 return NULL;
02002             memcpy(*str, buf, *len + 1);
02003         } else {
02004             readfrom = copy_nword_const(readfrom, (char *) *str, *len);
02005             if (*len)
02006                 *len = strlen((char *) *str);
02007         }
02008     }
02009 
02010     return readfrom;
02011 }
02012 
02013 /*
02014  * read_config_save_objid(): saves an objid as a numerical string 
02015  */
02016 char           *
02017 read_config_save_objid(char *saveto, oid * objid, size_t len)
02018 {
02019     int             i;
02020 
02021     if (len == 0) {
02022         strcat(saveto, "NULL");
02023         saveto += strlen(saveto);
02024         return saveto;
02025     }
02026 
02027     /*
02028      * in case len=0, this makes it easier to read it back in 
02029      */
02030     for (i = 0; i < (int) len; i++) {
02031         sprintf(saveto, ".%" NETSNMP_PRIo "d", objid[i]);
02032         saveto += strlen(saveto);
02033     }
02034     return saveto;
02035 }
02036 
02037 /*
02038  * read_config_read_objid(): reads an objid from a format saved by the above 
02039  */
02040 char           *
02041 read_config_read_objid(char *readfrom, oid ** objid, size_t * len)
02042 {
02043     return NETSNMP_REMOVE_CONST(char *,
02044              read_config_read_objid_const(readfrom, objid, len));
02045 }
02046 
02047 const char     *
02048 read_config_read_objid_const(const char *readfrom, oid ** objid, size_t * len)
02049 {
02050 
02051     if (objid == NULL || readfrom == NULL || len == NULL)
02052         return NULL;
02053 
02054     if (*objid == NULL) {
02055         *len = 0;
02056         if ((*objid = (oid *) malloc(MAX_OID_LEN * sizeof(oid))) == NULL)
02057             return NULL;
02058         *len = MAX_OID_LEN;
02059     }
02060 
02061     if (strncmp(readfrom, "NULL", 4) == 0) {
02062         /*
02063          * null length oid 
02064          */
02065         *len = 0;
02066     } else {
02067         /*
02068          * qualify the string for read_objid 
02069          */
02070         char            buf[SPRINT_MAX_LEN];
02071         copy_nword_const(readfrom, buf, sizeof(buf));
02072 
02073         if (!read_objid(buf, *objid, len)) {
02074             DEBUGMSGTL(("read_config_read_objid", "Invalid OID"));
02075             *len = 0;
02076             return NULL;
02077         }
02078     }
02079 
02080     readfrom = skip_token_const(readfrom);
02081     return readfrom;
02082 }
02083 
02109 char           *
02110 read_config_read_data(int type, char *readfrom, void *dataptr,
02111                       size_t * len)
02112 {
02113     int            *intp;
02114     char          **charpp;
02115     oid           **oidpp;
02116     unsigned int   *uintp;
02117 
02118     if (dataptr && readfrom)
02119         switch (type) {
02120         case ASN_INTEGER:
02121             intp = (int *) dataptr;
02122             *intp = atoi(readfrom);
02123             readfrom = skip_token(readfrom);
02124             return readfrom;
02125 
02126         case ASN_TIMETICKS:
02127         case ASN_UNSIGNED:
02128             uintp = (unsigned int *) dataptr;
02129             *uintp = strtoul(readfrom, NULL, 0);
02130             readfrom = skip_token(readfrom);
02131             return readfrom;
02132 
02133         case ASN_IPADDRESS:
02134             intp = (int *) dataptr;
02135             *intp = inet_addr(readfrom);
02136             if ((*intp == -1) &&
02137                 (strncmp(readfrom, "255.255.255.255", 15) != 0))
02138                 return NULL;
02139             readfrom = skip_token(readfrom);
02140             return readfrom;
02141 
02142         case ASN_OCTET_STR:
02143         case ASN_BIT_STR:
02144             charpp = (char **) dataptr;
02145             return read_config_read_octet_string(readfrom,
02146                                                  (u_char **) charpp, len);
02147 
02148         case ASN_OBJECT_ID:
02149             oidpp = (oid **) dataptr;
02150             return read_config_read_objid(readfrom, oidpp, len);
02151 
02152         default:
02153             DEBUGMSGTL(("read_config_read_data", "Fail: Unknown type: %d",
02154                         type));
02155             return NULL;
02156         }
02157     return NULL;
02158 }
02159 
02160 /*
02161  * read_config_read_memory():
02162  * 
02163  * similar to read_config_read_data, but expects a generic memory
02164  * pointer rather than a specific type of pointer.  Len is expected to
02165  * be the amount of available memory.
02166  */
02167 char           *
02168 read_config_read_memory(int type, char *readfrom,
02169                         char *dataptr, size_t * len)
02170 {
02171     int            *intp;
02172     unsigned int   *uintp;
02173     char            buf[SPRINT_MAX_LEN];
02174 
02175     if (!dataptr || !readfrom)
02176         return NULL;
02177 
02178     switch (type) {
02179     case ASN_INTEGER:
02180         if (*len < sizeof(int))
02181             return NULL;
02182         intp = (int *) dataptr;
02183         readfrom = copy_nword(readfrom, buf, sizeof(buf));
02184         *intp = atoi(buf);
02185         *len = sizeof(int);
02186         return readfrom;
02187 
02188     case ASN_COUNTER:
02189     case ASN_TIMETICKS:
02190     case ASN_UNSIGNED:
02191         if (*len < sizeof(unsigned int))
02192             return NULL;
02193         uintp = (unsigned int *) dataptr;
02194         readfrom = copy_nword(readfrom, buf, sizeof(buf));
02195         *uintp = strtoul(buf, NULL, 0);
02196         *len = sizeof(unsigned int);
02197         return readfrom;
02198 
02199     case ASN_IPADDRESS:
02200         if (*len < sizeof(int))
02201             return NULL;
02202         intp = (int *) dataptr;
02203         readfrom = copy_nword(readfrom, buf, sizeof(buf));
02204         *intp = inet_addr(buf);
02205         if ((*intp == -1) && (strcmp(buf, "255.255.255.255") != 0))
02206             return NULL;
02207         *len = sizeof(int);
02208         return readfrom;
02209 
02210     case ASN_OCTET_STR:
02211     case ASN_BIT_STR:
02212     case ASN_PRIV_IMPLIED_OCTET_STR:
02213         return read_config_read_octet_string(readfrom,
02214                                              (u_char **) & dataptr, len);
02215 
02216     case ASN_PRIV_IMPLIED_OBJECT_ID:
02217     case ASN_OBJECT_ID:
02218         readfrom =
02219             read_config_read_objid(readfrom, (oid **) & dataptr, len);
02220         *len *= sizeof(oid);
02221         return readfrom;
02222 
02223     case ASN_COUNTER64:
02224         if (*len < sizeof(U64))
02225             return NULL;
02226         *len = sizeof(U64);
02227         read64((U64 *) dataptr, readfrom);
02228         readfrom = skip_token(readfrom);
02229         return readfrom;
02230     }
02231 
02232     DEBUGMSGTL(("read_config_read_memory", "Fail: Unknown type: %d", type));
02233     return NULL;
02234 }
02235 
02263 char           *
02264 read_config_store_data(int type, char *storeto, void *dataptr, size_t * len)
02265 {
02266     return read_config_store_data_prefix(' ', type, storeto, dataptr,
02267                                                          (len ? *len : 0));
02268 }
02269 
02270 char           *
02271 read_config_store_data_prefix(char prefix, int type, char *storeto,
02272                               void *dataptr, size_t len)
02273 {
02274     int            *intp;
02275     u_char        **charpp;
02276     unsigned int   *uintp;
02277     struct in_addr  in;
02278     oid           **oidpp;
02279 
02280     if (dataptr && storeto)
02281         switch (type) {
02282         case ASN_INTEGER:
02283             intp = (int *) dataptr;
02284             sprintf(storeto, "%c%d", prefix, *intp);
02285             return (storeto + strlen(storeto));
02286 
02287         case ASN_TIMETICKS:
02288         case ASN_UNSIGNED:
02289             uintp = (unsigned int *) dataptr;
02290             sprintf(storeto, "%c%u", prefix, *uintp);
02291             return (storeto + strlen(storeto));
02292 
02293         case ASN_IPADDRESS:
02294             in.s_addr = *(unsigned int *) dataptr; 
02295             sprintf(storeto, "%c%s", prefix, inet_ntoa(in));
02296             return (storeto + strlen(storeto));
02297 
02298         case ASN_OCTET_STR:
02299         case ASN_BIT_STR:
02300             *storeto++ = prefix;
02301             charpp = (u_char **) dataptr;
02302             return read_config_save_octet_string(storeto, *charpp, len);
02303 
02304         case ASN_OBJECT_ID:
02305             *storeto++ = prefix;
02306             oidpp = (oid **) dataptr;
02307             return read_config_save_objid(storeto, *oidpp, len);
02308 
02309         default:
02310             DEBUGMSGTL(("read_config_store_data_prefix",
02311                         "Fail: Unknown type: %d", type));
02312             return NULL;
02313         }
02314     return NULL;
02315 }
02316 
02319 #ifdef READ_CONFIG_UNIT_TEST
02320 
02321 #define NETSNMP_USE_ASSERT 1
02322 #include <net-snmp/libary/snmp_assert.h>
02323 
02324 int
02325 read64(U64 * i64, const char *str)
02326 {
02327     netsnmp_assert(0);
02328 }
02329 
02330 int
02331 snmp_get_do_debugging(void)
02332 {
02333     return 0;
02334 }
02335 
02336 int
02337 debug_is_token_registered(const char *token)
02338 {
02339     netsnmp_assert(0);
02340 }
02341 
02342 void
02343 debugmsg(const char *token, const char *format, ...)
02344 {
02345     netsnmp_assert(0);
02346 }
02347 
02348 void
02349 debugmsgtoken(const char *token, const char *format, ...)
02350 {
02351     netsnmp_assert(0);
02352 }
02353 
02354 int
02355 snmp_log(int priority, const char *format, ...)
02356 {
02357 #if 0
02358     va_list         ap;
02359 
02360     va_start(ap, format);
02361     vprintf(format, ap);
02362     va_end(ap);
02363 #endif
02364     return 0;
02365 }
02366 
02367 void
02368 snmp_log_perror(const char *s)
02369 {
02370     netsnmp_assert(0);
02371 }
02372 
02373 int
02374 snmp_vlog(int priority, const char *format, va_list ap)
02375 {
02376     netsnmp_assert(0);
02377 }
02378 
02379 int
02380 netsnmp_ds_set_boolean(int storeid, int which, int value)
02381 {
02382     netsnmp_assert(0);
02383 }
02384 
02385 int
02386 netsnmp_ds_get_boolean(int storeid, int which)
02387 {
02388     netsnmp_assert(0);
02389 }
02390 
02391 int
02392 netsnmp_ds_set_string(int storeid, int which, const char *value)
02393 {
02394     netsnmp_assert(0);
02395 }
02396 
02397 char           *
02398 netsnmp_ds_get_string(int storeid, int which)
02399 {
02400     netsnmp_assert(0);
02401 }
02402 
02403 char           *
02404 netsnmp_getenv(const char *name)
02405 {
02406     netsnmp_assert(0);
02407 }
02408 
02409 int
02410 snmp_call_callbacks(int major, int minor, void *caller_arg)
02411 {
02412     netsnmp_assert(0);
02413 }
02414 
02415 int
02416 mkdirhier(const char *pathname, mode_t mode, int skiplast)
02417 {
02418     netsnmp_assert(0);
02419 }
02420 
02421 int
02422 read_objid(const char *input, oid * output, size_t * out_len)
02423 {
02424     netsnmp_assert(0);
02425 }
02426 
02427 struct read_config_testcase {
02428     /*
02429      * inputs 
02430      */
02431     const char     *(*pf) (const char * readfrom, u_char ** str,
02432                            size_t * len);
02433     const char     *readfrom;
02434     size_t          obuf_len;
02435 
02436     /*
02437      * expected outputs 
02438      */
02439     size_t          expected_offset;
02440     const u_char   *expected_output;
02441     size_t          expected_len;
02442 };
02443 
02444 static const u_char obuf1[] = { 1, 0, 2 };
02445 static const u_char obuf2[] = { 'a', 'b', 'c', 0 };
02446 static const u_char obuf3[] = { 1, 3, 2 };
02447 
02448 static const struct read_config_testcase test_input[] = {
02449     { &read_config_read_octet_string_const, "",           1, -1, NULL,  0 },
02450     { &read_config_read_octet_string_const, "0x0",        1, -1, NULL,  1 },
02451     { &read_config_read_octet_string_const, "0x0 0",      1, -1, NULL,  1 },
02452 
02453     { &read_config_read_octet_string_const, "0x010002",   1, -1, NULL,  0 },
02454     { &read_config_read_octet_string_const, "0x010002",   2, -1, NULL,  0 },
02455     { &read_config_read_octet_string_const, "0x010002",   3, -1, obuf1, 0 },
02456     { &read_config_read_octet_string_const, "0x010002",   4, -1, obuf1, 3 },
02457     { &read_config_read_octet_string_const, "0x010002 0", 4,  9, obuf1, 3 },
02458     { &read_config_read_octet_string_const, "0x010002",   0, -1, obuf1, 3 },
02459 
02460     { &read_config_read_octet_string_const, "abc",        1, -1, NULL,  0 },
02461     { &read_config_read_octet_string_const, "abc z",      1,  4, NULL,  0 },
02462     { &read_config_read_octet_string_const, "abc",        2, -1, NULL,  1 },
02463     { &read_config_read_octet_string_const, "abc",        3, -1, obuf2, 2 },
02464     { &read_config_read_octet_string_const, "abc",        4, -1, obuf2, 3 },
02465     { &read_config_read_octet_string_const, "abc z",      4,  4, obuf2, 3 },
02466     { &read_config_read_octet_string_const, "abc",        0, -1, obuf2, 3 },
02467 };
02468 
02469 int
02470 main(int argc, char **argv)
02471 {
02472     int             failure_count = 0;
02473     unsigned int    i, j;
02474 
02475     printf("Start of unit test.\n");
02476     for (i = 0; i < sizeof(test_input) / sizeof(test_input[0]); i++) {
02477         const struct read_config_testcase *const p = &test_input[i];
02478         size_t          len = p->obuf_len;
02479         u_char         *str = len > 0 ? malloc(len) : NULL;
02480         const char     *result;
02481         size_t          offset;
02482 
02483         printf("Test %d ...\n", i);
02484         fflush(stdout);
02485         result = (p->pf) (p->readfrom, &str, &len);
02486         offset = result ? result - p->readfrom : -1;
02487         if (offset != p->expected_offset) {
02488             failure_count++;
02489             printf("test %d: expected offset %zd, got offset %" NETSNMP_PRIz "d\n",
02490                    i, p->expected_offset, offset);
02491         } else if (len != p->expected_len) {
02492             failure_count++;
02493             printf("test %d: expected length %d, got length %d\n",
02494                    i, p->expected_len, len);
02495         } else if (len >= 0 && p->expected_output
02496                    && memcmp(str, p->expected_output, len) != 0
02497                    && p->expected_output[len] == 0) {
02498             failure_count++;
02499             printf("test %d: output buffer mismatch\n", i);
02500             printf("Expected: ");
02501             for (j = 0; j < p->expected_len; ++j)
02502                 printf("%02x ", p->expected_output[j]);
02503             printf("\nActual:   ");
02504             for (j = 0; j < len; ++j)
02505                 printf("%02x ", str[j]);
02506             printf("\n");
02507         }
02508 
02509         if (str)
02510             free(str);
02511     }
02512     if (failure_count == 0)
02513         printf("All %d tests passed.\n", i);
02514     return 0;
02515 }
02516 #endif                          /* READ_CONFIG_UNIT_TEST */
02517 
02518 /*
02519  * Local variables:
02520  * c-basic-offset: 4
02521  * indent-tabs-mode: nil
02522  * compile-command: "gcc -Wall -Werror -DREAD_CONFIG_UNIT_TEST=1 -O1 -I../include -g -o read_config-unit-test read_config.c && ./read_config-unit-test && valgrind --leak-check=full ./read_config-unit-test"
02523  * End:
02524  */