net-snmp  5.4.1
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 
00069 #include <stdio.h>
00070 #include <ctype.h>
00071 #if HAVE_STDLIB_H
00072 #include <stdlib.h>
00073 #endif
00074 #if HAVE_STRING_H
00075 #include <string.h>
00076 #else
00077 #include <strings.h>
00078 #endif
00079 #if HAVE_UNISTD_H
00080 #include <unistd.h>
00081 #endif
00082 #include <sys/types.h>
00083 #if HAVE_SYS_PARAM_H
00084 #include <sys/param.h>
00085 #endif
00086 #if TIME_WITH_SYS_TIME
00087 # ifdef WIN32
00088 #  include <sys/timeb.h>
00089 # else
00090 #  include <sys/time.h>
00091 # endif
00092 # include <time.h>
00093 #else
00094 # if HAVE_SYS_TIME_H
00095 #  include <sys/time.h>
00096 # else
00097 #  include <time.h>
00098 # endif
00099 #endif
00100 #ifdef HAVE_SYS_STAT_H
00101 #include <sys/stat.h>
00102 #endif
00103 #if HAVE_NETINET_IN_H
00104 #include <netinet/in.h>
00105 #endif
00106 #if HAVE_ARPA_INET_H
00107 #include <arpa/inet.h>
00108 #endif
00109 #if HAVE_SYS_SELECT_H
00110 #include <sys/select.h>
00111 #endif
00112 #if HAVE_WINSOCK_H
00113 #include <winsock.h>
00114 #endif
00115 #if HAVE_SYS_SOCKET_H
00116 #include <sys/socket.h>
00117 #endif
00118 #if HAVE_NETDB_H
00119 #include <netdb.h>
00120 #endif
00121 #include <errno.h>
00122 
00123 #if HAVE_DMALLOC_H
00124 #include <dmalloc.h>
00125 #endif
00126 
00127 #include <net-snmp/types.h>
00128 #include <net-snmp/output_api.h>
00129 #include <net-snmp/config_api.h>
00130 #include <net-snmp/library/read_config.h>       /* for "internal" definitions */
00131 #include <net-snmp/utilities.h>
00132 
00133 #include <net-snmp/library/mib.h>
00134 #include <net-snmp/library/parse.h>
00135 #include <net-snmp/library/snmp_api.h>
00136 #include <net-snmp/library/callback.h>
00137 
00138 static int      config_errors;
00139 
00140 struct config_files *config_files = NULL;
00141 
00142 static struct config_line *
00143 internal_register_config_handler(const char *type_param,
00144                                  const char *token,
00145                                  void (*parser) (const char *, char *),
00146                                  void (*releaser) (void), const char *help,
00147                                  int when)
00148 {
00149     struct config_files **ctmp = &config_files;
00150     struct config_line  **ltmp;
00151     const char           *type = type_param;
00152 
00153     if (type == NULL || *type == '\0') {
00154         type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00155                                      NETSNMP_DS_LIB_APPTYPE);
00156     }
00157 
00158     /*
00159      * Handle multiple types (recursively)
00160      */
00161     if (strchr(type, ':')) {
00162         struct config_line *ltmp2 = NULL;
00163         char                buf[STRINGMAX];
00164         char               *cptr = buf;
00165         strncpy(buf, type, STRINGMAX - 1);
00166         buf[STRINGMAX - 1] = '\0';
00167         while (cptr) {
00168             char* c = cptr;
00169             cptr = strchr(cptr, ':');
00170             if(cptr) {
00171                 *cptr = '\0';
00172                 ++cptr;
00173             }
00174             ltmp2 = register_config_handler(c, token, parser, releaser, help);
00175         }
00176         return ltmp2;
00177     }
00178     
00179     /*
00180      * Find type in current list  -OR-  create a new file type.
00181      */
00182     while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
00183         ctmp = &((*ctmp)->next);
00184     }
00185 
00186     if (*ctmp == NULL) {
00187         *ctmp = (struct config_files *)
00188             calloc(1, sizeof(struct config_files));
00189         if (!*ctmp) {
00190             return NULL;
00191         }
00192 
00193         (*ctmp)->fileHeader = strdup(type);
00194     }
00195 
00196     /*
00197      * Find parser type in current list  -OR-  create a new
00198      * line parser entry.
00199      */
00200     ltmp = &((*ctmp)->start);
00201 
00202     while (*ltmp != NULL && strcmp((*ltmp)->config_token, token)) {
00203         ltmp = &((*ltmp)->next);
00204     }
00205 
00206     if (*ltmp == NULL) {
00207         *ltmp = (struct config_line *)
00208             calloc(1, sizeof(struct config_line));
00209         if (!*ltmp) {
00210             return NULL;
00211         }
00212 
00213         (*ltmp)->config_time = when;
00214         (*ltmp)->config_token = strdup(token);
00215         if (help != NULL)
00216             (*ltmp)->help = strdup(help);
00217     }
00218 
00219     /*
00220      * Add/Replace the parse/free functions for the given line type
00221      * in the given file type.
00222      */
00223     (*ltmp)->parse_line = parser;
00224     (*ltmp)->free_func = releaser;
00225 
00226     return (*ltmp);
00227 
00228 }                               /* end register_config_handler() */
00229 
00230 struct config_line *
00231 register_prenetsnmp_mib_handler(const char *type,
00232                                 const char *token,
00233                                 void (*parser) (const char *, char *),
00234                                 void (*releaser) (void), const char *help)
00235 {
00236     return internal_register_config_handler(type, token, parser, releaser,
00237                                             help, PREMIB_CONFIG);
00238 }
00239 
00240 struct config_line *
00241 register_app_prenetsnmp_mib_handler(const char *token,
00242                                     void (*parser) (const char *, char *),
00243                                     void (*releaser) (void),
00244                                     const char *help)
00245 {
00246     return (register_prenetsnmp_mib_handler
00247             (NULL, token, parser, releaser, help));
00248 }
00249 
00282 struct config_line *
00283 register_config_handler(const char *type,
00284                         const char *token,
00285                         void (*parser) (const char *, char *),
00286                         void (*releaser) (void), const char *help)
00287 {
00288     return internal_register_config_handler(type, token, parser, releaser,
00289                                             help, NORMAL_CONFIG);
00290 }
00291 
00292 struct config_line *
00293 register_app_config_handler(const char *token,
00294                             void (*parser) (const char *, char *),
00295                             void (*releaser) (void), const char *help)
00296 {
00297     return (register_config_handler(NULL, token, parser, releaser, help));
00298 }
00299 
00300 
00301 
00313 void
00314 unregister_config_handler(const char *type_param, const char *token)
00315 {
00316     struct config_files **ctmp = &config_files;
00317     struct config_line  **ltmp;
00318     const char           *type = type_param;
00319 
00320     if (type == NULL || *type == '\0') {
00321         type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00322                                      NETSNMP_DS_LIB_APPTYPE);
00323     }
00324 
00325     /*
00326      * Handle multiple types (recursively)
00327      */
00328     if (strchr(type, ':')) {
00329         char                buf[STRINGMAX];
00330         char               *cptr = buf;
00331         strncpy(buf, type, STRINGMAX - 1);
00332         buf[STRINGMAX - 1] = '\0';
00333         while (cptr) {
00334             char* c = cptr;
00335             cptr = strchr(cptr, ':');
00336             if(cptr) {
00337                 *cptr = '\0';
00338                 ++cptr;
00339             }
00340             unregister_config_handler(c, token);
00341         }
00342         return;
00343     }
00344     
00345     /*
00346      * find type in current list 
00347      */
00348     while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
00349         ctmp = &((*ctmp)->next);
00350     }
00351 
00352     if (*ctmp == NULL) {
00353         /*
00354          * Not found, return. 
00355          */
00356         return;
00357     }
00358 
00359     ltmp = &((*ctmp)->start);
00360     if (*ltmp == NULL) {
00361         /*
00362          * Not found, return. 
00363          */
00364         return;
00365     }
00366     if (strcmp((*ltmp)->config_token, token) == 0) {
00367         /*
00368          * found it at the top of the list 
00369          */
00370         struct config_line *ltmp2 = (*ltmp)->next;
00371         SNMP_FREE((*ltmp)->config_token);
00372         SNMP_FREE((*ltmp)->help);
00373         SNMP_FREE(*ltmp);
00374         (*ctmp)->start = ltmp2;
00375         return;
00376     }
00377     while ((*ltmp)->next != NULL
00378            && strcmp((*ltmp)->next->config_token, token)) {
00379         ltmp = &((*ltmp)->next);
00380     }
00381     if ((*ltmp)->next != NULL) {
00382         struct config_line *ltmp2 = (*ltmp)->next->next;
00383         SNMP_FREE((*ltmp)->next->config_token);
00384         SNMP_FREE((*ltmp)->next->help);
00385         SNMP_FREE((*ltmp)->next);
00386         (*ltmp)->next = ltmp2;
00387     }
00388 }
00389 
00390 void
00391 unregister_app_config_handler(const char *token)
00392 {
00393     unregister_config_handler(NULL, token);
00394 }
00395 
00396 void
00397 unregister_all_config_handlers()
00398 {
00399     struct config_files *ctmp, *save;
00400     struct config_line *ltmp;
00401 
00402     free_config();
00403 
00404     /*
00405      * Keep using config_files until there are no more! 
00406      */
00407     for (ctmp = config_files; ctmp;) {
00408         for (ltmp = ctmp->start; ltmp; ltmp = ctmp->start) {
00409             unregister_config_handler(ctmp->fileHeader,
00410                                       ltmp->config_token);
00411         }
00412         SNMP_FREE(ctmp->fileHeader);
00413         save = ctmp->next;
00414         SNMP_FREE(ctmp);
00415         ctmp = save;
00416         config_files = save;
00417     }
00418 }
00419 
00420 #ifdef TESTING
00421 void
00422 print_config_handlers(void)
00423 {
00424     struct config_files *ctmp = config_files;
00425     struct config_line *ltmp;
00426 
00427     for (; ctmp != NULL; ctmp = ctmp->next) {
00428         DEBUGMSGTL(("read_config", "read_conf: %s\n", ctmp->fileHeader));
00429         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
00430             DEBUGMSGTL(("read_config", "                   %s\n",
00431                         ltmp->config_token));
00432     }
00433 }
00434 #endif
00435 
00436 int             linecount;
00437 const char     *curfilename;
00438 
00439 struct config_line *
00440 read_config_get_handlers(const char *type)
00441 {
00442     struct config_files *ctmp = config_files;
00443     for (; ctmp != NULL && strcmp(ctmp->fileHeader, type);
00444          ctmp = ctmp->next);
00445     if (ctmp)
00446         return ctmp->start;
00447     return NULL;
00448 }
00449 
00450 void
00451 read_config_with_type_when(const char *filename, const char *type, int when)
00452 {
00453     struct config_line *ctmp = read_config_get_handlers(type);
00454     if (ctmp)
00455         read_config(filename, ctmp, when);
00456     else
00457         DEBUGMSGTL(("read_config",
00458                     "read_config: I have no registrations for type:%s,file:%s\n",
00459                     type, filename));
00460 }
00461 
00462 void
00463 read_config_with_type(const char *filename, const char *type)
00464 {
00465     read_config_with_type_when(filename, type, EITHER_CONFIG);
00466 }
00467 
00468 
00469 struct config_line *
00470 read_config_find_handler(struct config_line *line_handlers,
00471                          const char *token)
00472 {
00473     struct config_line *lptr;
00474 
00475     for (lptr = line_handlers; lptr != NULL; lptr = lptr->next) {
00476         if (!strcasecmp(token, lptr->config_token)) {
00477             return lptr;
00478         }
00479     }
00480     return NULL;
00481 }
00482 
00483 
00484 /*
00485  * searches a config_line linked list for a match 
00486  */
00487 int
00488 run_config_handler(struct config_line *lptr,
00489                    const char *token, char *cptr, int when)
00490 {
00491     char            tmpbuf[STRINGMAX];
00492     char           *cp;
00493     lptr = read_config_find_handler(lptr, token);
00494     if (lptr != NULL) {
00495         if (when == EITHER_CONFIG || lptr->config_time == when) {
00496             DEBUGMSGTL(("read_config",
00497                         "Found a parser.  Calling it: %s / %s\n", token,
00498                         cptr));
00499             /*
00500              * Stomp on any trailing whitespace
00501              */
00502             cp = &(cptr[strlen(cptr)-1]);
00503             while (isspace(*cp)) {
00504                 *(cp--) = '\0';
00505             }
00506             (*(lptr->parse_line)) (token, cptr);
00507         }
00508     } else if (when != PREMIB_CONFIG && 
00509                !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
00510                                        NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
00511         snprintf(tmpbuf, sizeof(tmpbuf), "Unknown token: %s.", token);
00512         tmpbuf[ sizeof(tmpbuf)-1 ] = 0;
00513         config_pwarn(tmpbuf);
00514         return SNMPERR_GENERR;
00515     }
00516     return SNMPERR_SUCCESS;
00517 }
00518 
00519 /*
00520  * takens an arbitrary string and tries to intepret it based on the
00521  * known configuration handlers for all registered types.  May produce
00522  * inconsistent results when multiple tokens of the same name are
00523  * registered under different file types. 
00524  */
00525 
00526 /*
00527  * we allow = delimeters here 
00528  */
00529 #define SNMP_CONFIG_DELIMETERS " \t="
00530 
00531 int
00532 snmp_config_when(char *line, int when)
00533 {
00534     char           *cptr, buf[STRINGMAX], tmpbuf[STRINGMAX];
00535     struct config_line *lptr = NULL;
00536     struct config_files *ctmp = config_files;
00537     char           *st;
00538 
00539     if (line == NULL) {
00540         config_perror("snmp_config() called with a null string.");
00541         return SNMPERR_GENERR;
00542     }
00543 
00544     strncpy(buf, line, STRINGMAX);
00545     buf[STRINGMAX - 1] = '\0';
00546     cptr = strtok_r(buf, SNMP_CONFIG_DELIMETERS, &st);
00547     if (cptr && cptr[0] == '[') {
00548         if (cptr[strlen(cptr) - 1] != ']') {
00549             snprintf(tmpbuf, sizeof(tmpbuf),
00550                     "no matching ']' for type %s.",
00551                     cptr + 1);
00552             tmpbuf[ sizeof(tmpbuf)-1 ] = 0;
00553             config_perror(tmpbuf);
00554             return SNMPERR_GENERR;
00555         }
00556         cptr[strlen(cptr) - 1] = '\0';
00557         lptr = read_config_get_handlers(cptr + 1);
00558         if (lptr == NULL) {
00559             snprintf(tmpbuf,  sizeof(tmpbuf),
00560                      "No handlers regestered for type %s.",
00561                     cptr + 1);
00562             tmpbuf[ sizeof(tmpbuf)-1 ] = 0;
00563             config_perror(tmpbuf);
00564             return SNMPERR_GENERR;
00565         }
00566         cptr = strtok_r(NULL, SNMP_CONFIG_DELIMETERS, &st);
00567         lptr = read_config_find_handler(lptr, cptr);
00568     } else {
00569         /*
00570          * we have to find a token 
00571          */
00572         for (; ctmp != NULL && lptr == NULL; ctmp = ctmp->next)
00573             lptr = read_config_find_handler(ctmp->start, cptr);
00574     }
00575     if (lptr == NULL && netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
00576                                           NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
00577         snprintf(tmpbuf, sizeof(tmpbuf), "Unknown token: %s.", cptr);
00578         tmpbuf[ sizeof(tmpbuf)-1 ] = 0;
00579         config_pwarn(tmpbuf);
00580         return SNMPERR_GENERR;
00581     }
00582 
00583     /*
00584      * use the original string instead since strtok_r messed up the original 
00585      */
00586     line = skip_white(line + (cptr - buf) + strlen(cptr) + 1);
00587 
00588     return (run_config_handler(lptr, cptr, line, when));
00589 }
00590 
00591 int
00592 netsnmp_config(char *line)
00593 {
00594     int             ret = SNMP_ERR_NOERROR;
00595     DEBUGMSGTL(("snmp_config", "remembering line \"%s\"\n", line));
00596     netsnmp_config_remember(line);      /* always remember it so it's read
00597                                          * processed after a free_config()
00598                                          * call */
00599     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
00600                                NETSNMP_DS_LIB_HAVE_READ_CONFIG)) {
00601         DEBUGMSGTL(("snmp_config", "  ... processing it now\n"));
00602         ret = snmp_config_when(line, NORMAL_CONFIG);
00603     }
00604     return ret;
00605 }
00606 
00607 void
00608 netsnmp_config_remember_in_list(char *line,
00609                                 struct read_config_memory **mem)
00610 {
00611     if (mem == NULL)
00612         return;
00613 
00614     while (*mem != NULL)
00615         mem = &((*mem)->next);
00616 
00617     *mem = SNMP_MALLOC_STRUCT(read_config_memory);
00618     if (line)
00619         (*mem)->line = strdup(line);
00620 }
00621 
00622 void
00623 netsnmp_config_remember_free_list(struct read_config_memory **mem)
00624 {
00625     struct read_config_memory *tmpmem;
00626     while (*mem) {
00627         SNMP_FREE((*mem)->line);
00628         tmpmem = (*mem)->next;
00629         SNMP_FREE(*mem);
00630         *mem = tmpmem;
00631     }
00632 }
00633 
00634 void
00635 netsnmp_config_process_memory_list(struct read_config_memory **memp,
00636                                    int when, int clear)
00637 {
00638 
00639     struct read_config_memory *mem;
00640 
00641     if (!memp)
00642         return;
00643 
00644     mem = *memp;
00645 
00646     while (mem) {
00647         DEBUGMSGTL(("read_config", "processing memory: %s\n", mem->line));
00648         snmp_config_when(mem->line, when);
00649         mem = mem->next;
00650     }
00651 
00652     if (clear)
00653         netsnmp_config_remember_free_list(memp);
00654 }
00655 
00656 /*
00657  * default storage location implementation 
00658  */
00659 static struct read_config_memory *memorylist = NULL;
00660 
00661 void
00662 netsnmp_config_remember(char *line)
00663 {
00664     netsnmp_config_remember_in_list(line, &memorylist);
00665 }
00666 
00667 void
00668 netsnmp_config_process_memories(void)
00669 {
00670     netsnmp_config_process_memory_list(&memorylist, EITHER_CONFIG, 1);
00671 }
00672 
00673 void
00674 netsnmp_config_process_memories_when(int when, int clear)
00675 {
00676     netsnmp_config_process_memory_list(&memorylist, when, clear);
00677 }
00678 
00679 /*******************************************************************-o-******
00680  * read_config
00681  *
00682  * Parameters:
00683  *      *filename
00684  *      *line_handler
00685  *       when
00686  *
00687  * Read <filename> and process each line in accordance with the list of
00688  * <line_handler> functions.
00689  *
00690  *
00691  * For each line in <filename>, search the list of <line_handler>'s 
00692  * for an entry that matches the first token on the line.  This comparison is
00693  * case insensitive.
00694  *
00695  * For each match, check that <when> is the designated time for the
00696  * <line_handler> function to be executed before processing the line.
00697  */
00698 void
00699 read_config(const char *filename,
00700             struct config_line *line_handler, int when)
00701 {
00702 
00703     FILE           *ifile;
00704     char            line[STRINGMAX], token[STRINGMAX], tmpbuf[STRINGMAX];
00705     char           *cptr;
00706     int             i;
00707     struct config_line *lptr;
00708 
00709     linecount = 0;
00710     curfilename = filename;
00711 
00712     if ((ifile = fopen(filename, "r")) == NULL) {
00713 #ifdef ENOENT
00714         if (errno == ENOENT) {
00715             DEBUGMSGTL(("read_config", "%s: %s\n", filename,
00716                         strerror(errno)));
00717         } else
00718 #endif                          /* ENOENT */
00719 #ifdef EACCES
00720         if (errno == EACCES) {
00721             DEBUGMSGTL(("read_config", "%s: %s\n", filename,
00722                         strerror(errno)));
00723         } else
00724 #endif                          /* EACCES */
00725 #if defined(ENOENT) || defined(EACCES)
00726         {
00727             snmp_log_perror(filename);
00728         }
00729 #else                           /* defined(ENOENT) || defined(EACCES) */
00730             snmp_log_perror(filename);
00731 #endif                          /* ENOENT */
00732         return;
00733     } else {
00734         DEBUGMSGTL(("read_config", "Reading configuration %s\n",
00735                     filename));
00736     }
00737 
00738     while (fgets(line, sizeof(line), ifile) != NULL) {
00739         lptr = line_handler;
00740         linecount++;
00741         cptr = line;
00742         i = strlen(line) - 1;
00743         if (line[i] == '\n')
00744             line[i] = 0;
00745         /*
00746          * check blank line or # comment 
00747          */
00748         if ((cptr = skip_white(cptr))) {
00749             cptr = copy_nword(cptr, token, sizeof(token));
00750             if (token[0] == '[') {
00751                 if (token[strlen(token) - 1] != ']') {
00752                     snprintf(tmpbuf, sizeof(tmpbuf),
00753                             "no matching ']' for type %s.",
00754                             &token[1]);
00755                     tmpbuf[ sizeof(tmpbuf)-1 ] = 0;
00756                     config_perror(tmpbuf);
00757                     continue;
00758                 }
00759                 token[strlen(token) - 1] = '\0';
00760                 lptr = read_config_get_handlers(&token[1]);
00761                 if (lptr == NULL) {
00762                     snprintf(tmpbuf, sizeof(tmpbuf),
00763                             "No handlers regestered for type %s.",
00764                             &token[1]);
00765                     tmpbuf[ sizeof(tmpbuf)-1 ] = 0;
00766                     config_perror(tmpbuf);
00767                     continue;
00768                 }
00769                 DEBUGMSGTL(("read_config",
00770                             "Switching to new context: %s%s\n",
00771                             ((cptr) ? "(this line only) " : ""),
00772                             &token[1]));
00773                 if (cptr == NULL) {
00774                     /*
00775                      * change context permanently 
00776                      */
00777                     line_handler = lptr;
00778                     continue;
00779                 } else {
00780                     /*
00781                      * the rest of this line only applies. 
00782                      */
00783                     cptr = copy_nword(cptr, token, sizeof(token));
00784                 }
00785             } else {
00786                 lptr = line_handler;
00787             }
00788             if (cptr == NULL) {
00789                 snprintf(tmpbuf, sizeof(tmpbuf),
00790                         "Blank line following %s token.", token);
00791                 tmpbuf[ sizeof(tmpbuf)-1 ] = 0;
00792                 config_perror(tmpbuf);
00793             } else {
00794                 DEBUGMSGTL(("read_config", "%s:%d examining: %s\n",
00795                             filename, linecount, line));
00796                 run_config_handler(lptr, token, cptr, when);
00797             }
00798         }
00799     }
00800     fclose(ifile);
00801     return;
00802 
00803 }                               /* end read_config() */
00804 
00805 
00806 
00807 void
00808 free_config(void)
00809 {
00810     struct config_files *ctmp = config_files;
00811     struct config_line *ltmp;
00812 
00813     for (; ctmp != NULL; ctmp = ctmp->next)
00814         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
00815             if (ltmp->free_func)
00816                 (*(ltmp->free_func)) ();
00817 }
00818 
00819 void
00820 read_configs_optional(const char *optional_config, int when)
00821 {
00822     char *newp, *cp, *st = NULL;
00823     char *type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00824                                        NETSNMP_DS_LIB_APPTYPE);
00825 
00826     if ((NULL == optional_config) || (NULL == type))
00827         return;
00828 
00829     DEBUGMSGTL(("read_configs_optional",
00830                 "reading optional configuration tokens for %s\n", type));
00831     
00832     newp = strdup(optional_config);      /* strtok_r messes it up */
00833     cp = strtok_r(newp, ",", &st);
00834     while (cp) {
00835         struct stat     statbuf;
00836         if (stat(cp, &statbuf)) {
00837             DEBUGMSGTL(("read_config",
00838                         "Optional File \"%s\" does not exist.\n", cp));
00839             snmp_log_perror(cp);
00840         } else {
00841             DEBUGMSGTL(("read_config",
00842                         "Reading optional config file: \"%s\"\n", cp));
00843             read_config_with_type_when(cp, type, when);
00844         }
00845         cp = strtok_r(NULL, ",", &st);
00846     }
00847     free(newp);
00848     
00849 }
00850 
00851 void
00852 read_configs(void)
00853 {
00854     char *optional_config = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00855                                                NETSNMP_DS_LIB_OPTIONALCONFIG);
00856 
00857     DEBUGMSGTL(("read_config", "reading normal configuration tokens\n"));
00858 
00859     if ((NULL != optional_config) && (*optional_config == '-')) {
00860         read_configs_optional(++optional_config, NORMAL_CONFIG);
00861         optional_config = NULL; /* clear, so we don't read them twice */
00862     }
00863 
00864     read_config_files(NORMAL_CONFIG);
00865 
00866     /*
00867      * do this even when the normal above wasn't done 
00868      */
00869     if (NULL != optional_config)
00870         read_configs_optional(optional_config, NORMAL_CONFIG);
00871 
00872     netsnmp_config_process_memories_when(NORMAL_CONFIG, 1);
00873 
00874     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, 
00875                            NETSNMP_DS_LIB_HAVE_READ_CONFIG, 1);
00876     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
00877                         SNMP_CALLBACK_POST_READ_CONFIG, NULL);
00878 }
00879 
00880 void
00881 read_premib_configs(void)
00882 {
00883     char *optional_config = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00884                                                NETSNMP_DS_LIB_OPTIONALCONFIG);
00885 
00886     DEBUGMSGTL(("read_config", "reading premib configuration tokens\n"));
00887 
00888     if ((NULL != optional_config) && (*optional_config == '-')) {
00889         read_configs_optional(++optional_config, PREMIB_CONFIG);
00890         optional_config = NULL; /* clear, so we don't read them twice */
00891     }
00892 
00893     read_config_files(PREMIB_CONFIG);
00894 
00895     if (NULL != optional_config)
00896         read_configs_optional(optional_config, PREMIB_CONFIG);
00897 
00898     netsnmp_config_process_memories_when(PREMIB_CONFIG, 0);
00899 
00900     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, 
00901                            NETSNMP_DS_LIB_HAVE_READ_PREMIB_CONFIG, 1);
00902     snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
00903                         SNMP_CALLBACK_POST_PREMIB_READ_CONFIG, NULL);
00904 }
00905 
00906 /*******************************************************************-o-******
00907  * set_configuration_directory
00908  *
00909  * Parameters:
00910  *      char *dir - value of the directory
00911  * Sets the configuration directory. Multiple directories can be
00912  * specified, but need to be seperated by 'ENV_SEPARATOR_CHAR'.
00913  */
00914 void
00915 set_configuration_directory(const char *dir)
00916 {
00917     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, 
00918                           NETSNMP_DS_LIB_CONFIGURATION_DIR, dir);
00919 }
00920 
00921 /*******************************************************************-o-******
00922  * get_configuration_directory
00923  *
00924  * Parameters: -
00925  * Retrieve the configuration directory or directories.
00926  * (For backwards compatibility that is:
00927  *       SNMPCONFPATH, SNMPSHAREPATH, SNMPLIBPATH, HOME/.snmp
00928  * First check whether the value is set.
00929  * If not set give it the default value.
00930  * Return the value.
00931  * We always retrieve it new, since we have to do it anyway if it is just set.
00932  */
00933 const char     *
00934 get_configuration_directory()
00935 {
00936     char            defaultPath[SPRINT_MAX_LEN];
00937     char           *homepath;
00938 
00939     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00940                                       NETSNMP_DS_LIB_CONFIGURATION_DIR)) {
00941         homepath = netsnmp_getenv("HOME");
00942         snprintf(defaultPath, sizeof(defaultPath), "%s%c%s%c%s%s%s%s",
00943                 SNMPCONFPATH, ENV_SEPARATOR_CHAR,
00944                 SNMPSHAREPATH, ENV_SEPARATOR_CHAR, SNMPLIBPATH,
00945                 ((homepath == NULL) ? "" : ENV_SEPARATOR),
00946                 ((homepath == NULL) ? "" : homepath),
00947                 ((homepath == NULL) ? "" : "/.snmp"));
00948         defaultPath[ sizeof(defaultPath)-1 ] = 0;
00949         set_configuration_directory(defaultPath);
00950     }
00951     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00952                                   NETSNMP_DS_LIB_CONFIGURATION_DIR));
00953 }
00954 
00955 /*******************************************************************-o-******
00956  * set_persistent_directory
00957  *
00958  * Parameters:
00959  *      char *dir - value of the directory
00960  * Sets the configuration directory. 
00961  * No multiple directories may be specified.
00962  * (However, this is not checked)
00963  */
00964 void
00965 set_persistent_directory(const char *dir)
00966 {
00967     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, 
00968                           NETSNMP_DS_LIB_PERSISTENT_DIR, dir);
00969 }
00970 
00971 /*******************************************************************-o-******
00972  * get_persistent_directory
00973  *
00974  * Parameters: -
00975  * Function will retrieve the persisten directory value.
00976  * First check whether the value is set.
00977  * If not set give it the default value.
00978  * Return the value. 
00979  * We always retrieve it new, since we have to do it anyway if it is just set.
00980  */
00981 const char     *
00982 get_persistent_directory()
00983 {
00984     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00985                                       NETSNMP_DS_LIB_PERSISTENT_DIR)) {
00986         const char *persdir = netsnmp_getenv("SNMP_PERSISTENT_DIR");
00987         if (NULL == persdir)
00988             persdir = NETSNMP_PERSISTENT_DIRECTORY;
00989         set_persistent_directory(persdir);
00990     }
00991     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
00992                                   NETSNMP_DS_LIB_PERSISTENT_DIR));
00993 }
00994 
00995 /*******************************************************************-o-******
00996  * set_temp_file_pattern
00997  *
00998  * Parameters:
00999  *      char *pattern - value of the file pattern
01000  * Sets the temp file pattern. 
01001  * Multiple patterns may not be specified.
01002  * (However, this is not checked)
01003  */
01004 void
01005 set_temp_file_pattern(const char *pattern)
01006 {
01007     netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, 
01008                           NETSNMP_DS_LIB_TEMP_FILE_PATTERN, pattern);
01009 }
01010 
01011 /*******************************************************************-o-******
01012  * get_temp_file_pattern
01013  *
01014  * Parameters: -
01015  * Function will retrieve the temp file pattern value.
01016  * First check whether the value is set.
01017  * If not set give it the default value.
01018  * Return the value. 
01019  * We always retrieve it new, since we have to do it anyway if it is just set.
01020  */
01021 const char     *
01022 get_temp_file_pattern()
01023 {
01024     if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01025                                       NETSNMP_DS_LIB_TEMP_FILE_PATTERN)) {
01026         set_temp_file_pattern(NETSNMP_TEMP_FILE_PATTERN);
01027     }
01028     return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01029                                   NETSNMP_DS_LIB_TEMP_FILE_PATTERN));
01030 }
01031 
01035 static void
01036 read_config_files_in_path(const char *path, struct config_files *ctmp,
01037                           int when, const char *perspath, const char *persfile)
01038 {
01039     int             done, j;
01040     char            configfile[300];
01041     char           *cptr1, *cptr2, *envconfpath;
01042     struct stat     statbuf;
01043 
01044     if ((NULL == path) || (NULL == ctmp))
01045         return;
01046 
01047     envconfpath = strdup(path);
01048 
01049     DEBUGMSGTL(("read_config", " config path used for %s:%s (persistent path:%s)\n",
01050                 ctmp->fileHeader, envconfpath, perspath));
01051     cptr1 = cptr2 = envconfpath;
01052     done = 0;
01053     while ((*cptr2 != 0) && (!done)) {
01054         while (*cptr1 != 0 && *cptr1 != ENV_SEPARATOR_CHAR)
01055             cptr1++;
01056         if (*cptr1 == 0)
01057             done = 1;
01058         else
01059             *cptr1 = 0;
01060 
01061         DEBUGMSGTL(("read_config", " config dir: %s\n", cptr2 ));
01062         if (stat(cptr2, &statbuf) != 0) {
01063             /*
01064              * Directory not there, continue 
01065              */
01066             DEBUGMSGTL(("read_config", " Directory not present: %s\n", cptr2 ));
01067             cptr2 = ++cptr1;
01068             continue;
01069         }
01070 #ifdef S_ISDIR
01071         if (!S_ISDIR(statbuf.st_mode)) {
01072             /*
01073              * Not a directory, continue 
01074              */
01075             DEBUGMSGTL(("read_config", " Not a directory: %s\n", cptr2 ));
01076             cptr2 = ++cptr1;
01077             continue;
01078         }
01079 #endif
01080 
01081         /*
01082          * for proper persistent storage retrival, we need to read old backup
01083          * copies of the previous storage files.  If the application in
01084          * question has died without the proper call to snmp_clean_persistent,
01085          * then we read all the configuration files we can, starting with
01086          * the oldest first.
01087          */
01088         if (strncmp(cptr2, perspath, strlen(perspath)) == 0 ||
01089             (persfile != NULL &&
01090              strncmp(cptr2, persfile, strlen(persfile)) == 0)) {
01091             /*
01092              * limit this to the known storage directory only 
01093              */
01094             for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
01095                 snprintf(configfile, sizeof(configfile),
01096                          "%s/%s.%d.conf", cptr2,
01097                          ctmp->fileHeader, j);
01098                 configfile[ sizeof(configfile)-1 ] = 0;
01099                 if (stat(configfile, &statbuf) != 0) {
01100                     /*
01101                      * file not there, continue 
01102                      */
01103                     break;
01104                 } else {
01105                     /*
01106                      * backup exists, read it 
01107                      */
01108                     DEBUGMSGTL(("read_config_files",
01109                                 "old config file found: %s, parsing\n",
01110                                 configfile));
01111                     read_config(configfile, ctmp->start, when);
01112                 }
01113             }
01114         }
01115         snprintf(configfile, sizeof(configfile),
01116                  "%s/%s.conf", cptr2, ctmp->fileHeader);
01117         configfile[ sizeof(configfile)-1 ] = 0;
01118         read_config(configfile, ctmp->start, when);
01119         snprintf(configfile, sizeof(configfile),
01120                  "%s/%s.local.conf", cptr2, ctmp->fileHeader);
01121         configfile[ sizeof(configfile)-1 ] = 0;
01122         read_config(configfile, ctmp->start, when);
01123 
01124         if(done)
01125             break;
01126 
01127         cptr2 = ++cptr1;
01128     }
01129     SNMP_FREE(envconfpath);
01130 }
01131 
01132 /*******************************************************************-o-******
01133  * read_config_files
01134  *
01135  * Parameters:
01136  *      when    == PREMIB_CONFIG, NORMAL_CONFIG  -or-  EITHER_CONFIG
01137  *
01138  *
01139  * Traverse the list of config file types, performing the following actions
01140  * for each --
01141  *
01142  * First, build a search path for config files.  If the contents of 
01143  * environment variable SNMPCONFPATH are NULL, then use the following
01144  * path list (where the last entry exists only if HOME is non-null):
01145  *
01146  *      SNMPSHAREPATH:SNMPLIBPATH:${HOME}/.snmp
01147  *
01148  * Then, In each of these directories, read config files by the name of:
01149  *
01150  *      <dir>/<fileHeader>.conf         -AND-
01151  *      <dir>/<fileHeader>.local.conf
01152  *
01153  * where <fileHeader> is taken from the config file type structure.
01154  *
01155  *
01156  * PREMIB_CONFIG causes free_config() to be invoked prior to any other action.
01157  *
01158  *
01159  * EXITs if any 'config_errors' are logged while parsing config file lines.
01160  */
01161 void
01162 read_config_files(int when)
01163 {
01164     const char     *confpath, *perspath, *persfile, *envconfpath;
01165     struct config_files *ctmp = config_files;
01166 
01167     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01168                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
01169      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01170                                NETSNMP_DS_LIB_DISABLE_CONFIG_LOAD)) return;
01171 
01172     config_errors = 0;
01173 
01174     if (when == PREMIB_CONFIG)
01175         free_config();
01176 
01177     /*
01178      * these shouldn't change
01179      */
01180     confpath = get_configuration_directory();
01181     persfile = netsnmp_getenv("SNMP_PERSISTENT_FILE");
01182     envconfpath = netsnmp_getenv("SNMPCONFPATH");
01183 
01184     /*
01185      * read all config file types 
01186      */
01187     for (; ctmp != NULL; ctmp = ctmp->next) {
01188 
01189         /*
01190          * read the config files 
01191          */
01192         perspath = get_persistent_directory();
01193         if (envconfpath == NULL) {
01194             /*
01195              * read just the config files (no persistent stuff), since
01196              * persistent path can change via conf file. Then get the
01197              * current persistent directory, and read files there.
01198              */
01199             read_config_files_in_path(confpath, ctmp, when, perspath,
01200                                       persfile);
01201             perspath = get_persistent_directory();
01202             read_config_files_in_path(perspath, ctmp, when, perspath,
01203                                       persfile);
01204         }
01205         else {
01206             /*
01207              * only read path specified by user
01208              */
01209             read_config_files_in_path(envconfpath, ctmp, when, perspath,
01210                                       persfile);
01211         }
01212     }
01213 
01214     if (config_errors) {
01215         snmp_log(LOG_ERR, "net-snmp: %d error(s) in config file(s)\n",
01216                  config_errors);
01217     }
01218 }
01219 
01220 void
01221 read_config_print_usage(const char *lead)
01222 {
01223     struct config_files *ctmp = config_files;
01224     struct config_line *ltmp;
01225 
01226     if (lead == NULL)
01227         lead = "";
01228 
01229     for (ctmp = config_files; ctmp != NULL; ctmp = ctmp->next) {
01230         snmp_log(LOG_INFO, "%sIn %s.conf and %s.local.conf:\n", lead,
01231                  ctmp->fileHeader, ctmp->fileHeader);
01232         for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next) {
01233             DEBUGIF("read_config_usage") {
01234                 if (ltmp->config_time == PREMIB_CONFIG)
01235                     DEBUGMSG(("read_config_usage", "*"));
01236                 else
01237                     DEBUGMSG(("read_config_usage", " "));
01238             }
01239             if (ltmp->help) {
01240                 snmp_log(LOG_INFO, "%s%s%-24s %s\n", lead, lead,
01241                          ltmp->config_token, ltmp->help);
01242             } else {
01243                 DEBUGIF("read_config_usage") {
01244                     snmp_log(LOG_INFO, "%s%s%-24s [NO HELP]\n", lead, lead,
01245                              ltmp->config_token);
01246                 }
01247             }
01248         }
01249     }
01250 }
01251 
01265 void
01266 read_config_store(const char *type, const char *line)
01267 {
01268 #ifdef NETSNMP_PERSISTENT_DIRECTORY
01269     char            file[512], *filep;
01270     FILE           *fout;
01271 #ifdef NETSNMP_PERSISTENT_MASK
01272     mode_t          oldmask;
01273 #endif
01274 
01275     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01276                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
01277      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01278                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD)) return;
01279 
01280     /*
01281      * store configuration directives in the following order of preference:
01282      * 1. ENV variable SNMP_PERSISTENT_FILE
01283      * 2. configured <NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf
01284      */
01285     if ((filep = netsnmp_getenv("SNMP_PERSISTENT_FILE")) == NULL) {
01286         snprintf(file, sizeof(file),
01287                  "%s/%s.conf", get_persistent_directory(), type);
01288         file[ sizeof(file)-1 ] = 0;
01289         filep = file;
01290     }
01291 #ifdef NETSNMP_PERSISTENT_MASK
01292     oldmask = umask(NETSNMP_PERSISTENT_MASK);
01293 #endif
01294     if (mkdirhier(filep, NETSNMP_AGENT_DIRECTORY_MODE, 1)) {
01295         snmp_log(LOG_ERR,
01296                  "Failed to create the persistent directory for %s\n",
01297                  file);
01298     }
01299     if ((fout = fopen(filep, "a")) != NULL) {
01300         fprintf(fout, "%s", line);
01301         if (line[strlen(line)] != '\n')
01302             fprintf(fout, "\n");
01303         DEBUGMSGTL(("read_config", "storing: %s\n", line));
01304         fclose(fout);
01305     } else {
01306         snmp_log(LOG_ERR, "read_config_store open failure on %s\n", filep);
01307     }
01308 #ifdef NETSNMP_PERSISTENT_MASK
01309     umask(oldmask);
01310 #endif
01311 
01312 #endif
01313 }                               /* end read_config_store() */
01314 
01315 void
01316 read_app_config_store(const char *line)
01317 {
01318     read_config_store(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 
01319                                             NETSNMP_DS_LIB_APPTYPE), line);
01320 }
01321 
01322 
01323 
01324 
01325 /*******************************************************************-o-******
01326  * snmp_save_persistent
01327  *
01328  * Parameters:
01329  *      *type
01330  *      
01331  *
01332  * Save the file "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf" into a backup copy
01333  * called "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.%d.conf", which %d is an
01334  * incrementing number on each call, but less than NETSNMP_MAX_PERSISTENT_BACKUPS.
01335  *
01336  * Should be called just before all persistent information is supposed to be
01337  * written to move aside the existing persistent cache.
01338  * snmp_clean_persistent should then be called afterward all data has been
01339  * saved to remove these backup files.
01340  *
01341  * Note: on an rename error, the files are removed rather than saved.
01342  *
01343  */
01344 void
01345 snmp_save_persistent(const char *type)
01346 {
01347     char            file[512], fileold[SPRINT_MAX_LEN];
01348     struct stat     statbuf;
01349     int             j;
01350 
01351     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01352                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
01353      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01354                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE)) return;
01355 
01356     DEBUGMSGTL(("snmp_save_persistent", "saving %s files...\n", type));
01357     snprintf(file, sizeof(file),
01358              "%s/%s.conf", get_persistent_directory(), type);
01359     file[ sizeof(file)-1 ] = 0;
01360     if (stat(file, &statbuf) == 0) {
01361         for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
01362             snprintf(fileold, sizeof(fileold),
01363                      "%s/%s.%d.conf", get_persistent_directory(), type, j);
01364             fileold[ sizeof(fileold)-1 ] = 0;
01365             if (stat(fileold, &statbuf) != 0) {
01366                 DEBUGMSGTL(("snmp_save_persistent",
01367                             " saving old config file: %s -> %s.\n", file,
01368                             fileold));
01369                 if (rename(file, fileold)) {
01370                     snmp_log(LOG_ERR, "Cannot rename %s to %s\n", file, fileold);
01371                      /* moving it failed, try nuking it, as leaving
01372                       * it around is very bad. */
01373                     if (unlink(file) == -1)
01374                         snmp_log(LOG_ERR, "Cannot unlink %s\n", file);
01375                 }
01376                 break;
01377             }
01378         }
01379     }
01380     /*
01381      * save a warning header to the top of the new file 
01382      */
01383     snprintf(fileold, sizeof(fileold),
01384             "#\n# net-snmp (or ucd-snmp) persistent data file.\n#\n############################################################################\n# STOP STOP STOP STOP STOP STOP STOP STOP STOP \n#\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# Please save normal configuration tokens for %s in SNMPCONFPATH/%s.conf.\n# Only \"createUser\" tokens should be placed here by %s administrators.\n# (Did I mention: do not edit this file?)\n#\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
01385             type, type, type);
01386     fileold[ sizeof(fileold)-1 ] = 0;
01387     read_config_store(type, fileold);
01388 }
01389 
01390 
01391 /*******************************************************************-o-******
01392  * snmp_clean_persistent
01393  *
01394  * Parameters:
01395  *      *type
01396  *      
01397  *
01398  * Unlink all backup files called "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.%d.conf".
01399  *
01400  * Should be called just after we successfull dumped the last of the
01401  * persistent data, to remove the backup copies of previous storage dumps.
01402  *
01403  * XXX  Worth overwriting with random bytes first?  This would
01404  *      ensure that the data is destroyed, even a buffer containing the
01405  *      data persists in memory or swap.  Only important if secrets
01406  *      will be stored here.
01407  */
01408 void
01409 snmp_clean_persistent(const char *type)
01410 {
01411     char            file[512];
01412     struct stat     statbuf;
01413     int             j;
01414 
01415     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01416                                NETSNMP_DS_LIB_DONT_PERSIST_STATE)
01417      || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
01418                                NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE)) return;
01419 
01420     DEBUGMSGTL(("snmp_clean_persistent", "cleaning %s files...\n", type));
01421     snprintf(file, sizeof(file),
01422              "%s/%s.conf", get_persistent_directory(), type);
01423     file[ sizeof(file)-1 ] = 0;
01424     if (stat(file, &statbuf) == 0) {
01425         for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
01426             snprintf(file, sizeof(file),
01427                      "%s/%s.%d.conf", get_persistent_directory(), type, j);
01428             file[ sizeof(file)-1 ] = 0;
01429             if (stat(file, &statbuf) == 0) {
01430                 DEBUGMSGTL(("snmp_clean_persistent",
01431                             " removing old config file: %s\n", file));
01432                 if (unlink(file) == -1)
01433                     snmp_log(LOG_ERR, "Cannot unlink %s\n", file);
01434             }
01435         }
01436     }
01437 }
01438 
01439 
01440 
01441 
01442 /*
01443  * config_perror: prints a warning string associated with a file and
01444  * line number of a .conf file and increments the error count. 
01445  */
01446 void
01447 config_perror(const char *str)
01448 {
01449     snmp_log(LOG_ERR, "%s: line %d: Error: %s\n", curfilename, linecount,
01450              str);
01451     config_errors++;
01452 }
01453 
01454 void
01455 config_pwarn(const char *str)
01456 {
01457     snmp_log(LOG_WARNING, "%s: line %d: Warning: %s\n", curfilename,
01458              linecount, str);
01459 }
01460 
01461 /*
01462  * skip all white spaces and return 1 if found something either end of
01463  * line or a comment character 
01464  */
01465 char           *
01466 skip_white(char *ptr)
01467 {
01468     if (ptr == NULL)
01469         return (NULL);
01470     while (*ptr != 0 && isspace(*ptr))
01471         ptr++;
01472     if (*ptr == 0 || *ptr == '#')
01473         return (NULL);
01474     return (ptr);
01475 }
01476 
01477 char           *
01478 skip_not_white(char *ptr)
01479 {
01480     if (ptr == NULL)
01481         return (NULL);
01482     while (*ptr != 0 && !isspace(*ptr))
01483         ptr++;
01484     if (*ptr == 0 || *ptr == '#')
01485         return (NULL);
01486     return (ptr);
01487 }
01488 
01489 char           *
01490 skip_token(char *ptr)
01491 {
01492     ptr = skip_white(ptr);
01493     ptr = skip_not_white(ptr);
01494     ptr = skip_white(ptr);
01495     return (ptr);
01496 }
01497 
01498 /*
01499  * copy_word
01500  * copies the next 'token' from 'from' into 'to', maximum len-1 characters.
01501  * currently a token is anything seperate by white space
01502  * or within quotes (double or single) (i.e. "the red rose" 
01503  * is one token, \"the red rose\" is three tokens)
01504  * a '\' character will allow a quote character to be treated
01505  * as a regular character 
01506  * It returns a pointer to first non-white space after the end of the token
01507  * being copied or to 0 if we reach the end.
01508  * Note: Partially copied words (greater than len) still returns a !NULL ptr
01509  * Note: partially copied words are, however, null terminated.
01510  */
01511 
01512 char           *
01513 copy_nword(char *from, char *to, int len)
01514 {
01515     char            quote;
01516     if (!from || !to)
01517         return NULL;
01518     if ((*from == '\"') || (*from == '\'')) {
01519         quote = *(from++);
01520         while ((*from != quote) && (*from != 0)) {
01521             if ((*from == '\\') && (*(from + 1) != 0)) {
01522                 if (len > 0) {  /* don't copy beyond len bytes */
01523                     *to++ = *(from + 1);
01524                     if (--len == 0)
01525                         *(to - 1) = '\0';       /* null protect the last spot */
01526                 }
01527                 from = from + 2;
01528             } else {
01529                 if (len > 0) {  /* don't copy beyond len bytes */
01530                     *to++ = *from++;
01531                     if (--len == 0)
01532                         *(to - 1) = '\0';       /* null protect the last spot */
01533                 } else
01534                     from++;
01535             }
01536         }
01537         if (*from == 0) {
01538             DEBUGMSGTL(("read_config_copy_word",
01539                         "no end quote found in config string\n"));
01540         } else
01541             from++;
01542     } else {
01543         while (*from != 0 && !isspace(*from)) {
01544             if ((*from == '\\') && (*(from + 1) != 0)) {
01545                 if (len > 0) {  /* don't copy beyond len bytes */
01546                     *to++ = *(from + 1);
01547                     if (--len == 0)
01548                         *(to - 1) = '\0';       /* null protect the last spot */
01549                 }
01550                 from = from + 2;
01551             } else {
01552                 if (len > 0) {  /* don't copy beyond len bytes */
01553                     *to++ = *from++;
01554                     if (--len == 0)
01555                         *(to - 1) = '\0';       /* null protect the last spot */
01556                 } else
01557                     from++;
01558             }
01559         }
01560     }
01561     if (len > 0)
01562         *to = 0;
01563     from = skip_white(from);
01564     return (from);
01565 }                               /* copy_word */
01566 
01567 /*
01568  * copy_word
01569  * copies the next 'token' from 'from' into 'to'.
01570  * currently a token is anything seperate by white space
01571  * or within quotes (double or single) (i.e. "the red rose" 
01572  * is one token, \"the red rose\" is three tokens)
01573  * a '\' character will allow a quote character to be treated
01574  * as a regular character 
01575  * It returns a pointer to first non-white space after the end of the token
01576  * being copied or to 0 if we reach the end.
01577  */
01578 
01579 static int      have_warned = 0;
01580 char           *
01581 copy_word(char *from, char *to)
01582 {
01583     if (!have_warned) {
01584         snmp_log(LOG_INFO,
01585                  "copy_word() called.  Use copy_nword() instead.\n");
01586         have_warned = 1;
01587     }
01588     return copy_nword(from, to, SPRINT_MAX_LEN);
01589 }                               /* copy_word */
01590 
01591 /*
01592  * read_config_save_octet_string(): saves an octet string as a length
01593  * followed by a string of hex 
01594  */
01595 char           *
01596 read_config_save_octet_string(char *saveto, u_char * str, size_t len)
01597 {
01598     int             i;
01599     u_char         *cp;
01600 
01601     /*
01602      * is everything easily printable 
01603      */
01604     for (i = 0, cp = str; i < (int) len && cp &&
01605          (isalpha(*cp) || isdigit(*cp) || *cp == ' '); cp++, i++);
01606 
01607     if (len != 0 && i == (int) len) {
01608         *saveto++ = '"';
01609         memcpy(saveto, str, len);
01610         saveto += len;
01611         *saveto++ = '"';
01612         *saveto = '\0';
01613     } else {
01614         if (str != NULL) {
01615             sprintf(saveto, "0x");
01616             saveto += 2;
01617             for (i = 0; i < (int) len; i++) {
01618                 sprintf(saveto, "%02x", str[i]);
01619                 saveto = saveto + 2;
01620             }
01621         } else {
01622             sprintf(saveto, "\"\"");
01623             saveto += 2;
01624         }
01625     }
01626     return saveto;
01627 }
01628 
01629 /*
01630  * read_config_read_octet_string(): reads an octet string that was
01631  * saved by the read_config_save_octet_string() function 
01632  */
01633 char           *
01634 read_config_read_octet_string(char *readfrom, u_char ** str, size_t * len)
01635 {
01636     u_char         *cptr = NULL;
01637     char           *cptr1;
01638     u_int           tmp;
01639     int             i;
01640     size_t          ilen;
01641 
01642     if (readfrom == NULL || str == NULL)
01643         return NULL;
01644 
01645     if (strncasecmp(readfrom, "0x", 2) == 0) {
01646         /*
01647          * A hex string submitted. How long? 
01648          */
01649         readfrom += 2;
01650         cptr1 = skip_not_white(readfrom);
01651         if (cptr1)
01652             ilen = (cptr1 - readfrom);
01653         else
01654             ilen = strlen(readfrom);
01655 
01656         if (ilen % 2) {
01657             snmp_log(LOG_WARNING,"invalid hex string: wrong length\n");
01658             DEBUGMSGTL(("read_config_read_octet_string",
01659                         "invalid hex string: wrong length"));
01660             return NULL;
01661         }
01662         ilen = ilen / 2;
01663 
01664         /*
01665          * malloc data space if needed (+1 for good measure) 
01666          */
01667         if (*str == NULL) {
01668             if ((cptr = (u_char *) malloc(ilen + 1)) == NULL) {
01669                 return NULL;
01670             }
01671             *str = cptr;
01672         } else {
01673             /*
01674              * don't require caller to have +1 for good measure, and 
01675              * bail if not enough space.
01676              */
01677             if (ilen > *len) {
01678                 snmp_log(LOG_WARNING,"buffer too small to read octet string (%d < %d)\n",
01679                          *len, ilen);
01680                 DEBUGMSGTL(("read_config_read_octet_string",
01681                             "buffer too small (%lu < %lu)", (unsigned long)*len, (unsigned long)ilen));
01682                 cptr1 = skip_not_white(readfrom);
01683                 return skip_white(cptr1);
01684             }
01685             cptr = *str;
01686         }
01687         *len = ilen;
01688 
01689         /*
01690          * copy validated data 
01691          */
01692         for (i = 0; i < (int) *len; i++) {
01693             if (1 == sscanf(readfrom, "%2x", &tmp))
01694                 *cptr++ = (u_char) tmp;
01695             else {
01696                 /*
01697                  * we may lose memory, but don't know caller's buffer XX free(cptr); 
01698                  */
01699                 return (NULL);
01700             }
01701             readfrom += 2;
01702         }
01703         /*
01704          * only null terminate if we have the space
01705          */
01706         if (ilen > *len) {
01707             ilen = *len-1;
01708             *cptr++ = '\0';
01709         }
01710         readfrom = skip_white(readfrom);
01711     } else {
01712         /*
01713          * Normal string 
01714          */
01715 
01716         /*
01717          * malloc string space if needed (including NULL terminator) 
01718          */
01719         if (*str == NULL) {
01720             char            buf[SNMP_MAXBUF];
01721             readfrom = copy_nword(readfrom, buf, sizeof(buf));
01722 
01723             *len = strlen(buf);
01724             if ((cptr = (u_char *) malloc(*len + 1)) == NULL)
01725                 return NULL;
01726             *str = cptr;
01727             if (cptr) {
01728                 memcpy(cptr, buf, *len + 1);
01729             }
01730         } else {
01731             readfrom = copy_nword(readfrom, (char *) *str, *len);
01732             *len = strlen((char *) *str);
01733         }
01734     }
01735 
01736     return readfrom;
01737 }
01738 
01739 
01740 /*
01741  * read_config_save_objid(): saves an objid as a numerical string 
01742  */
01743 char           *
01744 read_config_save_objid(char *saveto, oid * objid, size_t len)
01745 {
01746     int             i;
01747 
01748     if (len == 0) {
01749         strcat(saveto, "NULL");
01750         saveto += strlen(saveto);
01751         return saveto;
01752     }
01753 
01754     /*
01755      * in case len=0, this makes it easier to read it back in 
01756      */
01757     for (i = 0; i < (int) len; i++) {
01758         sprintf(saveto, ".%ld", objid[i]);
01759         saveto += strlen(saveto);
01760     }
01761     return saveto;
01762 }
01763 
01764 /*
01765  * read_config_read_objid(): reads an objid from a format saved by the above 
01766  */
01767 char           *
01768 read_config_read_objid(char *readfrom, oid ** objid, size_t * len)
01769 {
01770 
01771     if (objid == NULL || readfrom == NULL || len == NULL)
01772         return NULL;
01773 
01774     if (*objid == NULL) {
01775         *len = 0;
01776         if ((*objid = (oid *) malloc(MAX_OID_LEN * sizeof(oid))) == NULL)
01777             return NULL;
01778         *len = MAX_OID_LEN;
01779     }
01780 
01781     if (strncmp(readfrom, "NULL", 4) == 0) {
01782         /*
01783          * null length oid 
01784          */
01785         *len = 0;
01786     } else {
01787         /*
01788          * qualify the string for read_objid 
01789          */
01790         char            buf[SPRINT_MAX_LEN];
01791         copy_nword(readfrom, buf, sizeof(buf));
01792 
01793         if (!read_objid(buf, *objid, len)) {
01794             DEBUGMSGTL(("read_config_read_objid", "Invalid OID"));
01795             *len = 0;
01796             return NULL;
01797         }
01798     }
01799 
01800     readfrom = skip_token(readfrom);
01801     return readfrom;
01802 }
01803 
01829 char           *
01830 read_config_read_data(int type, char *readfrom, void *dataptr,
01831                       size_t * len)
01832 {
01833     int            *intp;
01834     char          **charpp;
01835     oid           **oidpp;
01836     unsigned int   *uintp;
01837 
01838     if (dataptr && readfrom)
01839         switch (type) {
01840         case ASN_INTEGER:
01841             intp = (int *) dataptr;
01842             *intp = atoi(readfrom);
01843             readfrom = skip_token(readfrom);
01844             return readfrom;
01845 
01846         case ASN_TIMETICKS:
01847         case ASN_UNSIGNED:
01848             uintp = (unsigned int *) dataptr;
01849             *uintp = strtoul(readfrom, NULL, 0);
01850             readfrom = skip_token(readfrom);
01851             return readfrom;
01852 
01853         case ASN_IPADDRESS:
01854             intp = (int *) dataptr;
01855             *intp = inet_addr(readfrom);
01856             if ((*intp == -1) &&
01857                 (strncmp(readfrom, "255.255.255.255", 15) != 0))
01858                 return NULL;
01859             readfrom = skip_token(readfrom);
01860             return readfrom;
01861 
01862         case ASN_OCTET_STR:
01863         case ASN_BIT_STR:
01864             charpp = (char **) dataptr;
01865             return read_config_read_octet_string(readfrom,
01866                                                  (u_char **) charpp, len);
01867 
01868         case ASN_OBJECT_ID:
01869             oidpp = (oid **) dataptr;
01870             return read_config_read_objid(readfrom, oidpp, len);
01871 
01872         default:
01873             DEBUGMSGTL(("read_config_read_data", "Fail: Unknown type: %d",
01874                         type));
01875             return NULL;
01876         }
01877     return NULL;
01878 }
01879 
01880 /*
01881  * read_config_read_memory():
01882  * 
01883  * similar to read_config_read_data, but expects a generic memory
01884  * pointer rather than a specific type of pointer.  Len is expected to
01885  * be the amount of available memory.
01886  */
01887 char           *
01888 read_config_read_memory(int type, char *readfrom,
01889                         char *dataptr, size_t * len)
01890 {
01891     int            *intp;
01892     unsigned int   *uintp;
01893     char            buf[SPRINT_MAX_LEN];
01894 
01895     if (!dataptr || !readfrom)
01896         return NULL;
01897 
01898     switch (type) {
01899     case ASN_INTEGER:
01900         if (*len < sizeof(int))
01901             return NULL;
01902         intp = (int *) dataptr;
01903         readfrom = copy_nword(readfrom, buf, sizeof(buf));
01904         *intp = atoi(buf);
01905         *len = sizeof(int);
01906         return readfrom;
01907 
01908     case ASN_COUNTER:
01909     case ASN_TIMETICKS:
01910     case ASN_UNSIGNED:
01911         if (*len < sizeof(unsigned int))
01912             return NULL;
01913         uintp = (unsigned int *) dataptr;
01914         readfrom = copy_nword(readfrom, buf, sizeof(buf));
01915         *uintp = strtoul(buf, NULL, 0);
01916         *len = sizeof(unsigned int);
01917         return readfrom;
01918 
01919     case ASN_IPADDRESS:
01920         if (*len < sizeof(int))
01921             return NULL;
01922         intp = (int *) dataptr;
01923         readfrom = copy_nword(readfrom, buf, sizeof(buf));
01924         *intp = inet_addr(buf);
01925         if ((*intp == -1) && (strcmp(buf, "255.255.255.255") != 0))
01926             return NULL;
01927         *len = sizeof(int);
01928         return readfrom;
01929 
01930     case ASN_OCTET_STR:
01931     case ASN_BIT_STR:
01932     case ASN_PRIV_IMPLIED_OCTET_STR:
01933         return read_config_read_octet_string(readfrom,
01934                                              (u_char **) & dataptr, len);
01935 
01936     case ASN_PRIV_IMPLIED_OBJECT_ID:
01937     case ASN_OBJECT_ID:
01938         readfrom =
01939             read_config_read_objid(readfrom, (oid **) & dataptr, len);
01940         *len *= sizeof(oid);
01941         return readfrom;
01942 
01943     case ASN_COUNTER64:
01944     {
01945         if (*len < sizeof(U64))
01946             return NULL;
01947         *len = sizeof(U64);
01948         read64((U64 *) dataptr, readfrom);
01949         readfrom = skip_token(readfrom);
01950         return readfrom;
01951     }
01952 
01953     default:
01954         DEBUGMSGTL(("read_config_read_memory", "Fail: Unknown type: %d",
01955                     type));
01956         return NULL;
01957     }
01958     return NULL;
01959 }
01960 
01988 char           *
01989 read_config_store_data(int type, char *storeto, void *dataptr, size_t * len)
01990 {
01991     return read_config_store_data_prefix(' ', type, storeto, dataptr,
01992                                                          (len ? *len : 0));
01993 }
01994 
01995 char           *
01996 read_config_store_data_prefix(char prefix, int type, char *storeto,
01997                               void *dataptr, size_t len)
01998 {
01999     int            *intp;
02000     u_char        **charpp;
02001     unsigned int   *uintp;
02002     struct in_addr  in;
02003     oid           **oidpp;
02004 
02005     if (dataptr && storeto)
02006         switch (type) {
02007         case ASN_INTEGER:
02008             intp = (int *) dataptr;
02009             sprintf(storeto, "%c%d", prefix, *intp);
02010             return (storeto + strlen(storeto));
02011 
02012         case ASN_TIMETICKS:
02013         case ASN_UNSIGNED:
02014             uintp = (unsigned int *) dataptr;
02015             sprintf(storeto, "%c%u", prefix, *uintp);
02016             return (storeto + strlen(storeto));
02017 
02018         case ASN_IPADDRESS:
02019             in.s_addr = *(unsigned int *) dataptr; 
02020             sprintf(storeto, "%c%s", prefix, inet_ntoa(in));
02021             return (storeto + strlen(storeto));
02022 
02023         case ASN_OCTET_STR:
02024         case ASN_BIT_STR:
02025             *storeto++ = prefix;
02026             charpp = (u_char **) dataptr;
02027             return read_config_save_octet_string(storeto, *charpp, len);
02028 
02029         case ASN_OBJECT_ID:
02030             *storeto++ = prefix;
02031             oidpp = (oid **) dataptr;
02032             return read_config_save_objid(storeto, *oidpp, len);
02033 
02034         default:
02035             DEBUGMSGTL(("read_config_store_data_prefix",
02036                         "Fail: Unknown type: %d", type));
02037             return NULL;
02038         }
02039     return NULL;
02040 }