1 /*
2 * read_config.c
3 */
4 /* Portions of this file are subject to the following copyright(s). See
5 * the Net-SNMP's COPYING file for more details and other copyrights
6 * that may apply:
7 */
8 /*
9 * Portions of this file are copyrighted by:
10 * Copyright � 2003 Sun Microsystems, Inc. All rights reserved.
11 * Use is subject to license terms specified in the COPYING file
12 * distributed with the Net-SNMP package.
13 */
14
15 /** @defgroup read_config parsing various configuration files at run time
16 * @ingroup library
17 *
18 * The read_config related functions are a fairly extensible system of
19 * parsing various configuration files at the run time.
20 *
21 * The idea is that the calling application is able to register
22 * handlers for certain tokens specified in certain types
23 * of files. The read_configs function can then be called
24 * to look for all the files that it has registrations for,
25 * find the first word on each line, and pass the remainder
26 * to the appropriately registered handler.
27 *
28 * For persistent configuration storage you will need to use the
29 * read_config_read_data, read_config_store, and read_config_store_data
30 * APIs in conjunction with first registering a
31 * callback so when the agent shutsdown for whatever reason data is written
32 * to your configuration files. The following explains in more detail the
33 * sequence to make this happen.
34 *
35 * This is the callback registration API, you need to call this API with
36 * the appropriate parameters in order to configure persistent storage needs.
37 *
38 * int snmp_register_callback(int major, int minor,
39 * SNMPCallback *new_callback,
40 * void *arg);
41 *
42 * You will need to set major to SNMP_CALLBACK_LIBRARY, minor to
43 * SNMP_CALLBACK_STORE_DATA. arg is whatever you want.
44 *
45 * Your callback function's prototype is:
46 * int (SNMPCallback) (int majorID, int minorID, void *serverarg,
47 * void *clientarg);
48 *
49 * The majorID, minorID and clientarg are what you passed in the callback
50 * registration above. When the callback is called you have to essentially
51 * transfer all your state from memory to disk. You do this by generating
52 * configuration lines into a buffer. The lines are of the form token
53 * followed by token parameters.
54 *
55 * Finally storing is done using read_config_store(type, buffer);
56 * type is the application name this can be obtained from:
57 *
58 * netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE);
59 *
60 * Now, reading back the data: This is done by registering a config handler
61 * for your token using the register_config_handler function. Your
62 * handler will be invoked and you can parse in the data using the
63 * read_config_read APIs.
64 *
65 * @{
66 */
67 #include <net-snmp/net-snmp-config.h>
68 #include <net-snmp/net-snmp-features.h>
69
70 #include <stdio.h>
71 #include <ctype.h>
72 #if HAVE_STDLIB_H
73 #include <stdlib.h>
74 #endif
75 #if HAVE_STRING_H
76 #include <string.h>
77 #else
78 #include <strings.h>
79 #endif
80 #if HAVE_UNISTD_H
81 #include <unistd.h>
82 #endif
83 #include <sys/types.h>
84 #if HAVE_SYS_PARAM_H
85 #include <sys/param.h>
86 #endif
87 #if TIME_WITH_SYS_TIME
88 # include <sys/time.h>
89 # include <time.h>
90 #else
91 # if HAVE_SYS_TIME_H
92 # include <sys/time.h>
93 # else
94 # include <time.h>
95 # endif
96 #endif
97 #ifdef HAVE_SYS_STAT_H
98 #include <sys/stat.h>
99 #endif
100 #if HAVE_NETINET_IN_H
101 #include <netinet/in.h>
102 #endif
103 #if HAVE_ARPA_INET_H
104 #include <arpa/inet.h>
105 #endif
106 #if HAVE_SYS_SELECT_H
107 #include <sys/select.h>
108 #endif
109 #if HAVE_SYS_SOCKET_H
110 #include <sys/socket.h>
111 #endif
112 #if HAVE_NETDB_H
113 #include <netdb.h>
114 #endif
115 #include <errno.h>
116 #if HAVE_IO_H
117 #include <io.h>
118 #endif
119
120 #if HAVE_DIRENT_H
121 # include <dirent.h>
122 # define NAMLEN(dirent) strlen((dirent)->d_name)
123 #else
124 # define dirent direct
125 # define NAMLEN(dirent) (dirent)->d_namlen
126 # if HAVE_SYS_NDIR_H
127 # include <sys/ndir.h>
128 # endif
129 # if HAVE_SYS_DIR_H
130 # include <sys/dir.h>
131 # endif
132 # if HAVE_NDIR_H
133 # include <ndir.h>
134 # endif
135 #endif
136
137 #include <net-snmp/types.h>
138 #include <net-snmp/output_api.h>
139 #include <net-snmp/config_api.h>
140 #include <net-snmp/library/read_config.h> /* for "internal" definitions */
141 #include <net-snmp/utilities.h>
142
143 #include <net-snmp/library/mib.h>
144 #include <net-snmp/library/parse.h>
145 #include <net-snmp/library/snmp_api.h>
146 #include <net-snmp/library/callback.h>
147
148 netsnmp_feature_child_of(read_config_all, libnetsnmp);
149
150 netsnmp_feature_child_of(unregister_app_config_handler, read_config_all);
151 netsnmp_feature_child_of(read_config_register_app_prenetsnmp_mib_handler, netsnmp_unused);
152
153 static int config_errors;
154
155 struct config_files *config_files = NULL;
156
157
158 static struct config_line *
internal_register_config_handler(const char * type_param,const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help,int when)159 internal_register_config_handler(const char *type_param,
160 const char *token,
161 void (*parser) (const char *, char *),
162 void (*releaser) (void), const char *help,
163 int when)
164 {
165 struct config_files **ctmp = &config_files;
166 struct config_line **ltmp;
167 const char *type = type_param;
168
169 if (type == NULL || *type == '\0') {
170 type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
171 NETSNMP_DS_LIB_APPTYPE);
172 }
173
174 /*
175 * Handle multiple types (recursively)
176 */
177 if (strchr(type, ':')) {
178 struct config_line *ltmp2 = NULL;
179 char buf[STRINGMAX];
180 char *cptr = buf;
181
182 strlcpy(buf, type, STRINGMAX);
183 while (cptr) {
184 char* c = cptr;
185 cptr = strchr(cptr, ':');
186 if(cptr) {
187 *cptr = '\0';
188 ++cptr;
189 }
190 ltmp2 = internal_register_config_handler(c, token, parser,
191 releaser, help, when);
192 }
193 return ltmp2;
194 }
195
196 /*
197 * Find type in current list -OR- create a new file type.
198 */
199 while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
200 ctmp = &((*ctmp)->next);
201 }
202
203 if (*ctmp == NULL) {
204 *ctmp = (struct config_files *)
205 calloc(1, sizeof(struct config_files));
206 if (!*ctmp) {
207 return NULL;
208 }
209
210 (*ctmp)->fileHeader = strdup(type);
211 DEBUGMSGTL(("9:read_config:type", "new type %s\n", type));
212 }
213
214 DEBUGMSGTL(("9:read_config:register_handler", "registering %s %s\n",
215 type, token));
216 /*
217 * Find parser type in current list -OR- create a new
218 * line parser entry.
219 */
220 ltmp = &((*ctmp)->start);
221
222 while (*ltmp != NULL && strcmp((*ltmp)->config_token, token)) {
223 ltmp = &((*ltmp)->next);
224 }
225
226 if (*ltmp == NULL) {
227 *ltmp = (struct config_line *)
228 calloc(1, sizeof(struct config_line));
229 if (!*ltmp) {
230 return NULL;
231 }
232
233 (*ltmp)->config_time = when;
234 (*ltmp)->config_token = strdup(token);
235 if (help != NULL)
236 (*ltmp)->help = strdup(help);
237 }
238
239 /*
240 * Add/Replace the parse/free functions for the given line type
241 * in the given file type.
242 */
243 (*ltmp)->parse_line = parser;
244 (*ltmp)->free_func = releaser;
245
246 return (*ltmp);
247
248 } /* end register_config_handler() */
249
250 struct config_line *
register_prenetsnmp_mib_handler(const char * type,const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help)251 register_prenetsnmp_mib_handler(const char *type,
252 const char *token,
253 void (*parser) (const char *, char *),
254 void (*releaser) (void), const char *help)
255 {
256 return internal_register_config_handler(type, token, parser, releaser,
257 help, PREMIB_CONFIG);
258 }
259
260 #ifndef NETSNMP_FEATURE_REMOVE_READ_CONFIG_REGISTER_APP_PRENETSNMP_MIB_HANDLER
261 struct config_line *
register_app_prenetsnmp_mib_handler(const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help)262 register_app_prenetsnmp_mib_handler(const char *token,
263 void (*parser) (const char *, char *),
264 void (*releaser) (void),
265 const char *help)
266 {
267 return (register_prenetsnmp_mib_handler
268 (NULL, token, parser, releaser, help));
269 }
270 #endif /* NETSNMP_FEATURE_REMOVE_READ_CONFIG_REGISTER_APP_PRENETSNMP_MIB_HANDLER */
271
272 /**
273 * register_config_handler registers handlers for certain tokens specified in
274 * certain types of files.
275 *
276 * Allows a module writer use/register multiple configuration files based off
277 * of the type parameter. A module writer may want to set up multiple
278 * configuration files to separate out related tasks/variables or just for
279 * management of where to put tokens as the module or modules get more complex
280 * in regard to handling token registrations.
281 *
282 * @param type the configuration file used, e.g., if snmp.conf is the
283 * file where the token is located use "snmp" here.
284 * Multiple colon separated tokens might be used.
285 * If NULL or "" then the configuration file used will be
286 * \<application\>.conf.
287 *
288 * @param token the token being parsed from the file. Must be non-NULL.
289 *
290 * @param parser the handler function pointer that use the specified
291 * token and the rest of the line to do whatever is required
292 * Should be non-NULL in order to make use of this API.
293 *
294 * @param releaser if non-NULL, the function specified is called when
295 * unregistering config handler or when configuration
296 * files are re-read.
297 * This function should free any resources allocated by
298 * the token handler function.
299 *
300 * @param help if non-NULL, used to display help information on the
301 * expected arguments after the token.
302 *
303 * @return Pointer to a new config line entry or NULL on error.
304 */
305 struct config_line *
register_config_handler(const char * type,const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help)306 register_config_handler(const char *type,
307 const char *token,
308 void (*parser) (const char *, char *),
309 void (*releaser) (void), const char *help)
310 {
311 return internal_register_config_handler(type, token, parser, releaser,
312 help, NORMAL_CONFIG);
313 }
314
315 struct config_line *
register_const_config_handler(const char * type,const char * token,void (* parser)(const char *,const char *),void (* releaser)(void),const char * help)316 register_const_config_handler(const char *type,
317 const char *token,
318 void (*parser) (const char *, const char *),
319 void (*releaser) (void), const char *help)
320 {
321 return internal_register_config_handler(type, token,
322 (void(*)(const char *, char *))
323 parser, releaser,
324 help, NORMAL_CONFIG);
325 }
326
327 struct config_line *
register_app_config_handler(const char * token,void (* parser)(const char *,char *),void (* releaser)(void),const char * help)328 register_app_config_handler(const char *token,
329 void (*parser) (const char *, char *),
330 void (*releaser) (void), const char *help)
331 {
332 return (register_config_handler(NULL, token, parser, releaser, help));
333 }
334
335
336
337 /**
338 * uregister_config_handler un-registers handlers given a specific type_param
339 * and token.
340 *
341 * @param type_param the configuration file used where the token is located.
342 * Used to lookup the config file entry
343 *
344 * @param token the token that is being unregistered
345 *
346 * @return void
347 */
348 void
unregister_config_handler(const char * type_param,const char * token)349 unregister_config_handler(const char *type_param, const char *token)
350 {
351 struct config_files **ctmp = &config_files;
352 struct config_line **ltmp;
353 const char *type = type_param;
354
355 if (type == NULL || *type == '\0') {
356 type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
357 NETSNMP_DS_LIB_APPTYPE);
358 }
359
360 /*
361 * Handle multiple types (recursively)
362 */
363 if (strchr(type, ':')) {
364 char buf[STRINGMAX];
365 char *cptr = buf;
366
367 strlcpy(buf, type, STRINGMAX);
368 while (cptr) {
369 char* c = cptr;
370 cptr = strchr(cptr, ':');
371 if(cptr) {
372 *cptr = '\0';
373 ++cptr;
374 }
375 unregister_config_handler(c, token);
376 }
377 return;
378 }
379
380 /*
381 * find type in current list
382 */
383 while (*ctmp != NULL && strcmp((*ctmp)->fileHeader, type)) {
384 ctmp = &((*ctmp)->next);
385 }
386
387 if (*ctmp == NULL) {
388 /*
389 * Not found, return.
390 */
391 return;
392 }
393
394 ltmp = &((*ctmp)->start);
395 if (*ltmp == NULL) {
396 /*
397 * Not found, return.
398 */
399 return;
400 }
401 if (strcmp((*ltmp)->config_token, token) == 0) {
402 /*
403 * found it at the top of the list
404 */
405 struct config_line *ltmp2 = (*ltmp)->next;
406 if ((*ltmp)->free_func)
407 (*ltmp)->free_func();
408 SNMP_FREE((*ltmp)->config_token);
409 SNMP_FREE((*ltmp)->help);
410 SNMP_FREE(*ltmp);
411 (*ctmp)->start = ltmp2;
412 return;
413 }
414 while ((*ltmp)->next != NULL
415 && strcmp((*ltmp)->next->config_token, token)) {
416 ltmp = &((*ltmp)->next);
417 }
418 if ((*ltmp)->next != NULL) {
419 struct config_line *ltmp2 = (*ltmp)->next->next;
420 if ((*ltmp)->next->free_func)
421 (*ltmp)->next->free_func();
422 SNMP_FREE((*ltmp)->next->config_token);
423 SNMP_FREE((*ltmp)->next->help);
424 SNMP_FREE((*ltmp)->next);
425 (*ltmp)->next = ltmp2;
426 }
427 }
428
429 #ifndef NETSNMP_FEATURE_REMOVE_UNREGISTER_APP_CONFIG_HANDLER
430 void
unregister_app_config_handler(const char * token)431 unregister_app_config_handler(const char *token)
432 {
433 unregister_config_handler(NULL, token);
434 }
435 #endif /* NETSNMP_FEATURE_REMOVE_UNREGISTER_APP_CONFIG_HANDLER */
436
437 void
unregister_all_config_handlers(void)438 unregister_all_config_handlers(void)
439 {
440 struct config_files *ctmp, *save;
441 struct config_line *ltmp;
442
443 /*
444 * Keep using config_files until there are no more!
445 */
446 for (ctmp = config_files; ctmp;) {
447 for (ltmp = ctmp->start; ltmp; ltmp = ctmp->start) {
448 unregister_config_handler(ctmp->fileHeader,
449 ltmp->config_token);
450 }
451 SNMP_FREE(ctmp->fileHeader);
452 save = ctmp->next;
453 SNMP_FREE(ctmp);
454 ctmp = save;
455 config_files = save;
456 }
457 }
458
459 #ifdef TESTING
460 void
print_config_handlers(void)461 print_config_handlers(void)
462 {
463 struct config_files *ctmp = config_files;
464 struct config_line *ltmp;
465
466 for (; ctmp != NULL; ctmp = ctmp->next) {
467 DEBUGMSGTL(("read_config", "read_conf: %s\n", ctmp->fileHeader));
468 for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
469 DEBUGMSGTL(("read_config", " %s\n",
470 ltmp->config_token));
471 }
472 }
473 #endif
474
475 static unsigned int linecount;
476 static const char *curfilename;
477
478 struct config_line *
read_config_get_handlers(const char * type)479 read_config_get_handlers(const char *type)
480 {
481 struct config_files *ctmp = config_files;
482 for (; ctmp != NULL && strcmp(ctmp->fileHeader, type);
483 ctmp = ctmp->next);
484 if (ctmp)
485 return ctmp->start;
486 return NULL;
487 }
488
489 int
read_config_with_type_when(const char * filename,const char * type,int when)490 read_config_with_type_when(const char *filename, const char *type, int when)
491 {
492 struct config_line *ctmp = read_config_get_handlers(type);
493 if (ctmp)
494 return read_config(filename, ctmp, when);
495 else
496 DEBUGMSGTL(("read_config",
497 "read_config: I have no registrations for type:%s,file:%s\n",
498 type, filename));
499 return SNMPERR_GENERR; /* No config files read */
500 }
501
502 int
read_config_with_type(const char * filename,const char * type)503 read_config_with_type(const char *filename, const char *type)
504 {
505 return read_config_with_type_when(filename, type, EITHER_CONFIG);
506 }
507
508
509 struct config_line *
read_config_find_handler(struct config_line * line_handlers,const char * token)510 read_config_find_handler(struct config_line *line_handlers,
511 const char *token)
512 {
513 struct config_line *lptr;
514
515 for (lptr = line_handlers; lptr != NULL; lptr = lptr->next) {
516 if (!strcasecmp(token, lptr->config_token)) {
517 return lptr;
518 }
519 }
520 return NULL;
521 }
522
523
524 /*
525 * searches a config_line linked list for a match
526 */
527 int
run_config_handler(struct config_line * lptr,const char * token,char * cptr,int when)528 run_config_handler(struct config_line *lptr,
529 const char *token, char *cptr, int when)
530 {
531 char *cp;
532 lptr = read_config_find_handler(lptr, token);
533 if (lptr != NULL) {
534 if (when == EITHER_CONFIG || lptr->config_time == when) {
535 char tmpbuf[1];
536 DEBUGMSGTL(("read_config:parser",
537 "Found a parser. Calling it: %s / %s\n", token,
538 cptr));
539 /*
540 * Make sure cptr is non-null
541 */
542 if (!cptr) {
543 tmpbuf[0] = '\0';
544 cptr = tmpbuf;
545 }
546
547 /*
548 * Stomp on any trailing whitespace
549 */
550 cp = &(cptr[strlen(cptr)-1]);
551 while ((cp > cptr) && isspace((unsigned char)(*cp))) {
552 *(cp--) = '\0';
553 }
554 (*(lptr->parse_line)) (token, cptr);
555 }
556 else
557 DEBUGMSGTL(("9:read_config:parser",
558 "%s handler not registered for this time\n", token));
559 } else if (when != PREMIB_CONFIG &&
560 !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
561 NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
562 netsnmp_config_warn("Unknown token: %s.", token);
563 return SNMPERR_GENERR;
564 }
565 return SNMPERR_SUCCESS;
566 }
567
568 /*
569 * takens an arbitrary string and tries to intepret it based on the
570 * known configuration handlers for all registered types. May produce
571 * inconsistent results when multiple tokens of the same name are
572 * registered under different file types.
573 */
574
575 /*
576 * we allow = delimeters here
577 */
578 #define SNMP_CONFIG_DELIMETERS " \t="
579
580 int
snmp_config_when(char * line,int when)581 snmp_config_when(char *line, int when)
582 {
583 char *cptr, buf[STRINGMAX];
584 struct config_line *lptr = NULL;
585 struct config_files *ctmp = config_files;
586 char *st;
587
588 if (line == NULL) {
589 config_perror("snmp_config() called with a null string.");
590 return SNMPERR_GENERR;
591 }
592
593 strlcpy(buf, line, STRINGMAX);
594 cptr = strtok_r(buf, SNMP_CONFIG_DELIMETERS, &st);
595 if (!cptr) {
596 netsnmp_config_warn("Wrong format: %s", line);
597 return SNMPERR_GENERR;
598 }
599 if (cptr[0] == '[') {
600 if (cptr[strlen(cptr) - 1] != ']') {
601 netsnmp_config_error("no matching ']' for type %s.", cptr + 1);
602 return SNMPERR_GENERR;
603 }
604 cptr[strlen(cptr) - 1] = '\0';
605 lptr = read_config_get_handlers(cptr + 1);
606 if (lptr == NULL) {
607 netsnmp_config_error("No handlers regestered for type %s.",
608 cptr + 1);
609 return SNMPERR_GENERR;
610 }
611 cptr = strtok_r(NULL, SNMP_CONFIG_DELIMETERS, &st);
612 lptr = read_config_find_handler(lptr, cptr);
613 } else {
614 /*
615 * we have to find a token
616 */
617 for (; ctmp != NULL && lptr == NULL; ctmp = ctmp->next)
618 lptr = read_config_find_handler(ctmp->start, cptr);
619 }
620 if (lptr == NULL && netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
621 NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
622 netsnmp_config_warn("Unknown token: %s.", cptr);
623 return SNMPERR_GENERR;
624 }
625
626 /*
627 * use the original string instead since strtok_r messed up the original
628 */
629 line = skip_white(line + (cptr - buf) + strlen(cptr) + 1);
630
631 return (run_config_handler(lptr, cptr, line, when));
632 }
633
634 int
netsnmp_config(char * line)635 netsnmp_config(char *line)
636 {
637 int ret = SNMP_ERR_NOERROR;
638 DEBUGMSGTL(("snmp_config", "remembering line \"%s\"\n", line));
639 netsnmp_config_remember(line); /* always remember it so it's read
640 * processed after a free_config()
641 * call */
642 if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
643 NETSNMP_DS_LIB_HAVE_READ_CONFIG)) {
644 DEBUGMSGTL(("snmp_config", " ... processing it now\n"));
645 ret = snmp_config_when(line, NORMAL_CONFIG);
646 }
647 return ret;
648 }
649
650 void
netsnmp_config_remember_in_list(char * line,struct read_config_memory ** mem)651 netsnmp_config_remember_in_list(char *line,
652 struct read_config_memory **mem)
653 {
654 if (mem == NULL)
655 return;
656
657 while (*mem != NULL)
658 mem = &((*mem)->next);
659
660 *mem = SNMP_MALLOC_STRUCT(read_config_memory);
661 if (*mem != NULL) {
662 if (line)
663 (*mem)->line = strdup(line);
664 }
665 }
666
667 void
netsnmp_config_remember_free_list(struct read_config_memory ** mem)668 netsnmp_config_remember_free_list(struct read_config_memory **mem)
669 {
670 struct read_config_memory *tmpmem;
671 while (*mem) {
672 SNMP_FREE((*mem)->line);
673 tmpmem = (*mem)->next;
674 SNMP_FREE(*mem);
675 *mem = tmpmem;
676 }
677 }
678
679 void
netsnmp_config_process_memory_list(struct read_config_memory ** memp,int when,int clear)680 netsnmp_config_process_memory_list(struct read_config_memory **memp,
681 int when, int clear)
682 {
683
684 struct read_config_memory *mem;
685
686 if (!memp)
687 return;
688
689 mem = *memp;
690
691 while (mem) {
692 DEBUGMSGTL(("read_config:mem", "processing memory: %s\n", mem->line));
693 snmp_config_when(mem->line, when);
694 mem = mem->next;
695 }
696
697 if (clear)
698 netsnmp_config_remember_free_list(memp);
699 }
700
701 /*
702 * default storage location implementation
703 */
704 static struct read_config_memory *memorylist = NULL;
705
706 void
netsnmp_config_remember(char * line)707 netsnmp_config_remember(char *line)
708 {
709 netsnmp_config_remember_in_list(line, &memorylist);
710 }
711
712 void
netsnmp_config_process_memories(void)713 netsnmp_config_process_memories(void)
714 {
715 netsnmp_config_process_memory_list(&memorylist, EITHER_CONFIG, 1);
716 }
717
718 void
netsnmp_config_process_memories_when(int when,int clear)719 netsnmp_config_process_memories_when(int when, int clear)
720 {
721 netsnmp_config_process_memory_list(&memorylist, when, clear);
722 }
723
724 /*******************************************************************-o-******
725 * read_config
726 *
727 * Parameters:
728 * *filename
729 * *line_handler
730 * when
731 *
732 * Read <filename> and process each line in accordance with the list of
733 * <line_handler> functions.
734 *
735 *
736 * For each line in <filename>, search the list of <line_handler>'s
737 * for an entry that matches the first token on the line. This comparison is
738 * case insensitive.
739 *
740 * For each match, check that <when> is the designated time for the
741 * <line_handler> function to be executed before processing the line.
742 *
743 * Returns SNMPERR_SUCCESS if the file is processed successfully.
744 * Returns SNMPERR_GENERR if it cannot.
745 * Note that individual config token errors do not trigger SNMPERR_GENERR
746 * It's only if the whole file cannot be processed for some reason.
747 */
748 int
read_config(const char * filename,struct config_line * line_handler,int when)749 read_config(const char *filename,
750 struct config_line *line_handler, int when)
751 {
752 static int depth = 0;
753 static int files = 0;
754
755 const char * const prev_filename = curfilename;
756 const unsigned int prev_linecount = linecount;
757
758 FILE *ifile;
759 char *line = NULL; /* current line buffer */
760 size_t linesize = 0; /* allocated size of line */
761
762 /* reset file counter when recursion depth is 0 */
763 if (depth == 0)
764 files = 0;
765
766 if ((ifile = fopen(filename, "r")) == NULL) {
767 #ifdef ENOENT
768 if (errno == ENOENT) {
769 DEBUGMSGTL(("read_config", "%s: %s\n", filename,
770 strerror(errno)));
771 } else
772 #endif /* ENOENT */
773 #ifdef EACCES
774 if (errno == EACCES) {
775 DEBUGMSGTL(("read_config", "%s: %s\n", filename,
776 strerror(errno)));
777 } else
778 #endif /* EACCES */
779 {
780 snmp_log_perror(filename);
781 }
782 return SNMPERR_GENERR;
783 }
784
785 #define CONFIG_MAX_FILES 4096
786 if (files > CONFIG_MAX_FILES) {
787 netsnmp_config_error("maximum conf file count (%d) exceeded\n",
788 CONFIG_MAX_FILES);
789 fclose(ifile);
790 return SNMPERR_GENERR;
791 }
792 #define CONFIG_MAX_RECURSE_DEPTH 16
793 if (depth > CONFIG_MAX_RECURSE_DEPTH) {
794 netsnmp_config_error("nested include depth > %d\n",
795 CONFIG_MAX_RECURSE_DEPTH);
796 fclose(ifile);
797 return SNMPERR_GENERR;
798 }
799
800 linecount = 0;
801 curfilename = filename;
802
803 ++files;
804 ++depth;
805
806 DEBUGMSGTL(("read_config:file", "Reading configuration %s (%d)\n",
807 filename, when));
808
809 while (ifile) {
810 size_t linelen = 0; /* strlen of the current line */
811 char *cptr;
812 struct config_line *lptr = line_handler;
813
814 for (;;) {
815 if (linesize <= linelen + 1) {
816 char *tmp = realloc(line, linesize + 256);
817 if (tmp) {
818 line = tmp;
819 linesize += 256;
820 } else {
821 netsnmp_config_error("Failed to allocate memory\n");
822 free(line);
823 fclose(ifile);
824 return SNMPERR_GENERR;
825 }
826 }
827 if (fgets(line + linelen, linesize - linelen, ifile) == NULL) {
828 line[linelen] = '\0';
829 fclose (ifile);
830 ifile = NULL;
831 break;
832 }
833
834 linelen += strlen(line + linelen);
835
836 if (line[linelen - 1] == '\n') {
837 line[linelen - 1] = '\0';
838 break;
839 }
840 }
841
842 ++linecount;
843 DEBUGMSGTL(("9:read_config:line", "%s:%d examining: %s\n",
844 filename, linecount, line));
845 /*
846 * check blank line or # comment
847 */
848 if ((cptr = skip_white(line))) {
849 char token[STRINGMAX];
850
851 cptr = copy_nword(cptr, token, sizeof(token));
852 if (token[0] == '[') {
853 if (token[strlen(token) - 1] != ']') {
854 netsnmp_config_error("no matching ']' for type %s.",
855 &token[1]);
856 continue;
857 }
858 token[strlen(token) - 1] = '\0';
859 lptr = read_config_get_handlers(&token[1]);
860 if (lptr == NULL) {
861 netsnmp_config_error("No handlers regestered for type %s.",
862 &token[1]);
863 continue;
864 }
865 DEBUGMSGTL(("read_config:context",
866 "Switching to new context: %s%s\n",
867 ((cptr) ? "(this line only) " : ""),
868 &token[1]));
869 if (cptr == NULL) {
870 /*
871 * change context permanently
872 */
873 line_handler = lptr;
874 continue;
875 } else {
876 /*
877 * the rest of this line only applies.
878 */
879 cptr = copy_nword(cptr, token, sizeof(token));
880 }
881 } else if ((token[0] == 'i') && (strncasecmp(token,"include", 7 )==0)) {
882 if ( strcasecmp( token, "include" )==0) {
883 if (when != PREMIB_CONFIG &&
884 !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
885 NETSNMP_DS_LIB_NO_TOKEN_WARNINGS)) {
886 netsnmp_config_warn("Ambiguous token '%s' - use 'includeSearch' (or 'includeFile') instead.", token);
887 }
888 continue;
889 } else if ( strcasecmp( token, "includedir" )==0) {
890 DIR *d;
891 struct dirent *entry;
892 char fname[SNMP_MAXPATH];
893 int len;
894
895 if (cptr == NULL) {
896 if (when != PREMIB_CONFIG)
897 netsnmp_config_error("Blank line following %s token.", token);
898 continue;
899 }
900 if ((d=opendir(cptr)) == NULL ) {
901 if (when != PREMIB_CONFIG)
902 netsnmp_config_error("Can't open include dir '%s'.", cptr);
903 continue;
904 }
905 while ((entry = readdir( d )) != NULL ) {
906 if ( entry->d_name[0] != '.') {
907 len = NAMLEN(entry);
908 if ((len > 5) && (strcmp(&(entry->d_name[len-5]),".conf") == 0)) {
909 snprintf(fname, SNMP_MAXPATH, "%s/%s",
910 cptr, entry->d_name);
911 (void)read_config(fname, line_handler, when);
912 }
913 }
914 }
915 closedir(d);
916 continue;
917 } else if ( strcasecmp( token, "includefile" )==0) {
918 char fname[SNMP_MAXPATH], *cp;
919
920 if (cptr == NULL) {
921 if (when != PREMIB_CONFIG)
922 netsnmp_config_error("Blank line following %s token.", token);
923 continue;
924 }
925 if ( cptr[0] == '/' ) {
926 strlcpy(fname, cptr, SNMP_MAXPATH);
927 } else {
928 strlcpy(fname, filename, SNMP_MAXPATH);
929 cp = strrchr(fname, '/');
930 if (!cp)
931 fname[0] = '\0';
932 else
933 *(++cp) = '\0';
934 strlcat(fname, cptr, SNMP_MAXPATH);
935 }
936 if (read_config(fname, line_handler, when) !=
937 SNMPERR_SUCCESS && when != PREMIB_CONFIG)
938 netsnmp_config_error("Included file '%s' not found.",
939 fname);
940 continue;
941 } else if ( strcasecmp( token, "includesearch" )==0) {
942 struct config_files ctmp;
943 int len, ret;
944
945 if (cptr == NULL) {
946 if (when != PREMIB_CONFIG)
947 netsnmp_config_error("Blank line following %s token.", token);
948 continue;
949 }
950 len = strlen(cptr);
951 ctmp.fileHeader = cptr;
952 ctmp.start = line_handler;
953 ctmp.next = NULL;
954 if ((len > 5) && (strcmp(&cptr[len-5],".conf") == 0))
955 cptr[len-5] = 0; /* chop off .conf */
956 ret = read_config_files_of_type(when,&ctmp);
957 if ((len > 5) && (cptr[len-5] == 0))
958 cptr[len-5] = '.'; /* restore .conf */
959 if (( ret != SNMPERR_SUCCESS ) && (when != PREMIB_CONFIG))
960 netsnmp_config_error("Included config '%s' not found.", cptr);
961 continue;
962 } else {
963 lptr = line_handler;
964 }
965 } else {
966 lptr = line_handler;
967 }
968 if (cptr == NULL) {
969 netsnmp_config_error("Blank line following %s token.", token);
970 } else {
971 DEBUGMSGTL(("read_config:line", "%s:%d examining: %s\n",
972 filename, linecount, line));
973 run_config_handler(lptr, token, cptr, when);
974 }
975 }
976 }
977 free(line);
978 linecount = prev_linecount;
979 curfilename = prev_filename;
980 --depth;
981 return SNMPERR_SUCCESS;
982
983 } /* end read_config() */
984
985
986
987 void
free_config(void)988 free_config(void)
989 {
990 struct config_files *ctmp = config_files;
991 struct config_line *ltmp;
992
993 for (; ctmp != NULL; ctmp = ctmp->next)
994 for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next)
995 if (ltmp->free_func)
996 (*(ltmp->free_func)) ();
997 }
998
999 /*
1000 * Return SNMPERR_SUCCESS if any config files are processed
1001 * Return SNMPERR_GENERR if _no_ config files are processed
1002 * Whether this is actually an error is left to the application
1003 */
1004 int
read_configs_optional(const char * optional_config,int when)1005 read_configs_optional(const char *optional_config, int when)
1006 {
1007 char *newp, *cp, *st = NULL;
1008 int ret = SNMPERR_GENERR;
1009 char *type = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1010 NETSNMP_DS_LIB_APPTYPE);
1011
1012 if ((NULL == optional_config) || (NULL == type))
1013 return ret;
1014
1015 DEBUGMSGTL(("read_configs_optional",
1016 "reading optional configuration tokens for %s\n", type));
1017
1018 newp = strdup(optional_config); /* strtok_r messes it up */
1019 if (!newp)
1020 return ret;
1021 cp = strtok_r(newp, ",", &st);
1022 while (cp) {
1023 struct stat statbuf;
1024 if (stat(cp, &statbuf)) {
1025 DEBUGMSGTL(("read_config",
1026 "Optional File \"%s\" does not exist.\n", cp));
1027 snmp_log_perror(cp);
1028 } else {
1029 DEBUGMSGTL(("read_config:opt",
1030 "Reading optional config file: \"%s\"\n", cp));
1031 if ( read_config_with_type_when(cp, type, when) == SNMPERR_SUCCESS )
1032 ret = SNMPERR_SUCCESS;
1033 }
1034 cp = strtok_r(NULL, ",", &st);
1035 }
1036 free(newp);
1037 return ret;
1038 }
1039
1040 void
read_configs(void)1041 read_configs(void)
1042 {
1043 char *optional_config = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1044 NETSNMP_DS_LIB_OPTIONALCONFIG);
1045
1046 snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
1047 SNMP_CALLBACK_PRE_READ_CONFIG, NULL);
1048
1049 DEBUGMSGTL(("read_config", "reading normal configuration tokens\n"));
1050
1051 if ((NULL != optional_config) && (*optional_config == '-')) {
1052 (void)read_configs_optional(++optional_config, NORMAL_CONFIG);
1053 optional_config = NULL; /* clear, so we don't read them twice */
1054 }
1055
1056 (void)read_config_files(NORMAL_CONFIG);
1057
1058 /*
1059 * do this even when the normal above wasn't done
1060 */
1061 if (NULL != optional_config)
1062 (void)read_configs_optional(optional_config, NORMAL_CONFIG);
1063
1064 netsnmp_config_process_memories_when(NORMAL_CONFIG, 1);
1065
1066 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
1067 NETSNMP_DS_LIB_HAVE_READ_CONFIG, 1);
1068 snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
1069 SNMP_CALLBACK_POST_READ_CONFIG, NULL);
1070 }
1071
1072 void
read_premib_configs(void)1073 read_premib_configs(void)
1074 {
1075 char *optional_config = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1076 NETSNMP_DS_LIB_OPTIONALCONFIG);
1077
1078 snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
1079 SNMP_CALLBACK_PRE_PREMIB_READ_CONFIG, NULL);
1080
1081 DEBUGMSGTL(("read_config", "reading premib configuration tokens\n"));
1082
1083 if ((NULL != optional_config) && (*optional_config == '-')) {
1084 (void)read_configs_optional(++optional_config, PREMIB_CONFIG);
1085 optional_config = NULL; /* clear, so we don't read them twice */
1086 }
1087
1088 (void)read_config_files(PREMIB_CONFIG);
1089
1090 if (NULL != optional_config)
1091 (void)read_configs_optional(optional_config, PREMIB_CONFIG);
1092
1093 netsnmp_config_process_memories_when(PREMIB_CONFIG, 0);
1094
1095 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
1096 NETSNMP_DS_LIB_HAVE_READ_PREMIB_CONFIG, 1);
1097 snmp_call_callbacks(SNMP_CALLBACK_LIBRARY,
1098 SNMP_CALLBACK_POST_PREMIB_READ_CONFIG, NULL);
1099 }
1100
1101 /*******************************************************************-o-******
1102 * set_configuration_directory
1103 *
1104 * Parameters:
1105 * char *dir - value of the directory
1106 * Sets the configuration directory. Multiple directories can be
1107 * specified, but need to be seperated by 'ENV_SEPARATOR_CHAR'.
1108 */
1109 void
set_configuration_directory(const char * dir)1110 set_configuration_directory(const char *dir)
1111 {
1112 netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
1113 NETSNMP_DS_LIB_CONFIGURATION_DIR, dir);
1114 }
1115
1116 /*******************************************************************-o-******
1117 * get_configuration_directory
1118 *
1119 * Parameters: -
1120 * Retrieve the configuration directory or directories.
1121 * (For backwards compatibility that is:
1122 * SNMPCONFPATH, SNMPSHAREPATH, SNMPLIBPATH, HOME/.snmp
1123 * First check whether the value is set.
1124 * If not set give it the default value.
1125 * Return the value.
1126 * We always retrieve it new, since we have to do it anyway if it is just set.
1127 */
1128 const char *
get_configuration_directory(void)1129 get_configuration_directory(void)
1130 {
1131 char defaultPath[SPRINT_MAX_LEN];
1132 char *homepath;
1133
1134 if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1135 NETSNMP_DS_LIB_CONFIGURATION_DIR)) {
1136 homepath = netsnmp_getenv("HOME");
1137 snprintf(defaultPath, sizeof(defaultPath), "%s%c%s%c%s%s%s%s",
1138 SNMPCONFPATH, ENV_SEPARATOR_CHAR,
1139 SNMPSHAREPATH, ENV_SEPARATOR_CHAR, SNMPLIBPATH,
1140 ((homepath == NULL) ? "" : ENV_SEPARATOR),
1141 ((homepath == NULL) ? "" : homepath),
1142 ((homepath == NULL) ? "" : "/.snmp"));
1143 defaultPath[ sizeof(defaultPath)-1 ] = 0;
1144 set_configuration_directory(defaultPath);
1145 }
1146 return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1147 NETSNMP_DS_LIB_CONFIGURATION_DIR));
1148 }
1149
1150 /*******************************************************************-o-******
1151 * set_persistent_directory
1152 *
1153 * Parameters:
1154 * char *dir - value of the directory
1155 * Sets the configuration directory.
1156 * No multiple directories may be specified.
1157 * (However, this is not checked)
1158 */
1159 void
set_persistent_directory(const char * dir)1160 set_persistent_directory(const char *dir)
1161 {
1162 netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
1163 NETSNMP_DS_LIB_PERSISTENT_DIR, dir);
1164 }
1165
1166 /*******************************************************************-o-******
1167 * get_persistent_directory
1168 *
1169 * Parameters: -
1170 * Function will retrieve the persisten directory value.
1171 * First check whether the value is set.
1172 * If not set give it the default value.
1173 * Return the value.
1174 * We always retrieve it new, since we have to do it anyway if it is just set.
1175 */
1176 const char *
get_persistent_directory(void)1177 get_persistent_directory(void)
1178 {
1179 if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1180 NETSNMP_DS_LIB_PERSISTENT_DIR)) {
1181 const char *persdir = netsnmp_getenv("SNMP_PERSISTENT_DIR");
1182 if (NULL == persdir)
1183 persdir = NETSNMP_PERSISTENT_DIRECTORY;
1184 set_persistent_directory(persdir);
1185 }
1186 return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1187 NETSNMP_DS_LIB_PERSISTENT_DIR));
1188 }
1189
1190 /*******************************************************************-o-******
1191 * set_temp_file_pattern
1192 *
1193 * Parameters:
1194 * char *pattern - value of the file pattern
1195 * Sets the temp file pattern.
1196 * Multiple patterns may not be specified.
1197 * (However, this is not checked)
1198 */
1199 void
set_temp_file_pattern(const char * pattern)1200 set_temp_file_pattern(const char *pattern)
1201 {
1202 netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
1203 NETSNMP_DS_LIB_TEMP_FILE_PATTERN, pattern);
1204 }
1205
1206 /*******************************************************************-o-******
1207 * get_temp_file_pattern
1208 *
1209 * Parameters: -
1210 * Function will retrieve the temp file pattern value.
1211 * First check whether the value is set.
1212 * If not set give it the default value.
1213 * Return the value.
1214 * We always retrieve it new, since we have to do it anyway if it is just set.
1215 */
1216 const char *
get_temp_file_pattern(void)1217 get_temp_file_pattern(void)
1218 {
1219 if (NULL == netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1220 NETSNMP_DS_LIB_TEMP_FILE_PATTERN)) {
1221 set_temp_file_pattern(NETSNMP_TEMP_FILE_PATTERN);
1222 }
1223 return (netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1224 NETSNMP_DS_LIB_TEMP_FILE_PATTERN));
1225 }
1226
1227 /**
1228 * utility routine for read_config_files
1229 *
1230 * Return SNMPERR_SUCCESS if any config files are processed
1231 * Return SNMPERR_GENERR if _no_ config files are processed
1232 * Whether this is actually an error is left to the application
1233 */
1234 static int
read_config_files_in_path(const char * path,struct config_files * ctmp,int when,const char * perspath,const char * persfile)1235 read_config_files_in_path(const char *path, struct config_files *ctmp,
1236 int when, const char *perspath, const char *persfile)
1237 {
1238 int done, j;
1239 char configfile[300];
1240 char *cptr1, *cptr2, *envconfpath;
1241 struct stat statbuf;
1242 int ret = SNMPERR_GENERR;
1243
1244 if ((NULL == path) || (NULL == ctmp))
1245 return SNMPERR_GENERR;
1246
1247 envconfpath = strdup(path);
1248
1249 DEBUGMSGTL(("read_config:path", " config path used for %s:%s (persistent path:%s)\n",
1250 ctmp->fileHeader, envconfpath, perspath));
1251 cptr1 = cptr2 = envconfpath;
1252 done = 0;
1253 while ((!done) && (*cptr2 != 0)) {
1254 while (*cptr1 != 0 && *cptr1 != ENV_SEPARATOR_CHAR)
1255 cptr1++;
1256 if (*cptr1 == 0)
1257 done = 1;
1258 else
1259 *cptr1 = 0;
1260
1261 DEBUGMSGTL(("read_config:dir", " config dir: %s\n", cptr2 ));
1262 if (stat(cptr2, &statbuf) != 0) {
1263 /*
1264 * Directory not there, continue
1265 */
1266 DEBUGMSGTL(("read_config:dir", " Directory not present: %s\n", cptr2 ));
1267 cptr2 = ++cptr1;
1268 continue;
1269 }
1270 #ifdef S_ISDIR
1271 if (!S_ISDIR(statbuf.st_mode)) {
1272 /*
1273 * Not a directory, continue
1274 */
1275 DEBUGMSGTL(("read_config:dir", " Not a directory: %s\n", cptr2 ));
1276 cptr2 = ++cptr1;
1277 continue;
1278 }
1279 #endif
1280
1281 /*
1282 * for proper persistent storage retrieval, we need to read old backup
1283 * copies of the previous storage files. If the application in
1284 * question has died without the proper call to snmp_clean_persistent,
1285 * then we read all the configuration files we can, starting with
1286 * the oldest first.
1287 */
1288 if (strncmp(cptr2, perspath, strlen(perspath)) == 0 ||
1289 (persfile != NULL &&
1290 strncmp(cptr2, persfile, strlen(persfile)) == 0)) {
1291 DEBUGMSGTL(("read_config:persist", " persist dir: %s\n", cptr2 ));
1292 /*
1293 * limit this to the known storage directory only
1294 */
1295 for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
1296 snprintf(configfile, sizeof(configfile),
1297 "%s/%s.%d.conf", cptr2,
1298 ctmp->fileHeader, j);
1299 configfile[ sizeof(configfile)-1 ] = 0;
1300 if (stat(configfile, &statbuf) != 0) {
1301 /*
1302 * file not there, continue
1303 */
1304 break;
1305 } else {
1306 /*
1307 * backup exists, read it
1308 */
1309 DEBUGMSGTL(("read_config_files",
1310 "old config file found: %s, parsing\n",
1311 configfile));
1312 if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
1313 ret = SNMPERR_SUCCESS;
1314 }
1315 }
1316 }
1317 snprintf(configfile, sizeof(configfile),
1318 "%s/%s.conf", cptr2, ctmp->fileHeader);
1319 configfile[ sizeof(configfile)-1 ] = 0;
1320 if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
1321 ret = SNMPERR_SUCCESS;
1322 snprintf(configfile, sizeof(configfile),
1323 "%s/%s.local.conf", cptr2, ctmp->fileHeader);
1324 configfile[ sizeof(configfile)-1 ] = 0;
1325 if (read_config(configfile, ctmp->start, when) == SNMPERR_SUCCESS)
1326 ret = SNMPERR_SUCCESS;
1327
1328 if(done)
1329 break;
1330
1331 cptr2 = ++cptr1;
1332 }
1333 SNMP_FREE(envconfpath);
1334 return ret;
1335 }
1336
1337 /*******************************************************************-o-******
1338 * read_config_files
1339 *
1340 * Parameters:
1341 * when == PREMIB_CONFIG, NORMAL_CONFIG -or- EITHER_CONFIG
1342 *
1343 *
1344 * Traverse the list of config file types, performing the following actions
1345 * for each --
1346 *
1347 * First, build a search path for config files. If the contents of
1348 * environment variable SNMPCONFPATH are NULL, then use the following
1349 * path list (where the last entry exists only if HOME is non-null):
1350 *
1351 * SNMPSHAREPATH:SNMPLIBPATH:${HOME}/.snmp
1352 *
1353 * Then, In each of these directories, read config files by the name of:
1354 *
1355 * <dir>/<fileHeader>.conf -AND-
1356 * <dir>/<fileHeader>.local.conf
1357 *
1358 * where <fileHeader> is taken from the config file type structure.
1359 *
1360 *
1361 * PREMIB_CONFIG causes free_config() to be invoked prior to any other action.
1362 *
1363 *
1364 * EXITs if any 'config_errors' are logged while parsing config file lines.
1365 *
1366 * Return SNMPERR_SUCCESS if any config files are processed
1367 * Return SNMPERR_GENERR if _no_ config files are processed
1368 * Whether this is actually an error is left to the application
1369 */
1370 int
read_config_files_of_type(int when,struct config_files * ctmp)1371 read_config_files_of_type(int when, struct config_files *ctmp)
1372 {
1373 const char *confpath, *persfile, *envconfpath;
1374 char *perspath;
1375 int ret = SNMPERR_GENERR;
1376
1377 if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1378 NETSNMP_DS_LIB_DONT_PERSIST_STATE)
1379 || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1380 NETSNMP_DS_LIB_DISABLE_CONFIG_LOAD)
1381 || (NULL == ctmp)) return ret;
1382
1383 /*
1384 * these shouldn't change
1385 */
1386 confpath = get_configuration_directory();
1387 persfile = netsnmp_getenv("SNMP_PERSISTENT_FILE");
1388 envconfpath = netsnmp_getenv("SNMPCONFPATH");
1389
1390
1391 /*
1392 * read the config files. strdup() the result of
1393 * get_persistent_directory() to avoid that parsing the "persistentDir"
1394 * keyword transforms the perspath pointer into a dangling pointer.
1395 */
1396 perspath = strdup(get_persistent_directory());
1397 if (envconfpath == NULL) {
1398 /*
1399 * read just the config files (no persistent stuff), since
1400 * persistent path can change via conf file. Then get the
1401 * current persistent directory, and read files there.
1402 */
1403 if ( read_config_files_in_path(confpath, ctmp, when, perspath,
1404 persfile) == SNMPERR_SUCCESS )
1405 ret = SNMPERR_SUCCESS;
1406 free(perspath);
1407 perspath = strdup(get_persistent_directory());
1408 if ( read_config_files_in_path(perspath, ctmp, when, perspath,
1409 persfile) == SNMPERR_SUCCESS )
1410 ret = SNMPERR_SUCCESS;
1411 }
1412 else {
1413 /*
1414 * only read path specified by user
1415 */
1416 if ( read_config_files_in_path(envconfpath, ctmp, when, perspath,
1417 persfile) == SNMPERR_SUCCESS )
1418 ret = SNMPERR_SUCCESS;
1419 }
1420 free(perspath);
1421 return ret;
1422 }
1423
1424 /*
1425 * Return SNMPERR_SUCCESS if any config files are processed
1426 * Return SNMPERR_GENERR if _no_ config files are processed
1427 * Whether this is actually an error is left to the application
1428 */
1429 int
read_config_files(int when)1430 read_config_files(int when) {
1431
1432 struct config_files *ctmp = config_files;
1433 int ret = SNMPERR_GENERR;
1434
1435 config_errors = 0;
1436
1437 if (when == PREMIB_CONFIG)
1438 free_config();
1439
1440 /*
1441 * read all config file types
1442 */
1443 for (; ctmp != NULL; ctmp = ctmp->next) {
1444 if ( read_config_files_of_type(when, ctmp) == SNMPERR_SUCCESS )
1445 ret = SNMPERR_SUCCESS;
1446 }
1447
1448 if (config_errors) {
1449 snmp_log(LOG_ERR, "net-snmp: %d error(s) in config file(s)\n",
1450 config_errors);
1451 }
1452 return ret;
1453 }
1454
1455 void
read_config_print_usage(const char * lead)1456 read_config_print_usage(const char *lead)
1457 {
1458 struct config_files *ctmp = config_files;
1459 struct config_line *ltmp;
1460
1461 if (lead == NULL)
1462 lead = "";
1463
1464 for (ctmp = config_files; ctmp != NULL; ctmp = ctmp->next) {
1465 snmp_log(LOG_INFO, "%sIn %s.conf and %s.local.conf:\n", lead,
1466 ctmp->fileHeader, ctmp->fileHeader);
1467 for (ltmp = ctmp->start; ltmp != NULL; ltmp = ltmp->next) {
1468 DEBUGIF("read_config_usage") {
1469 if (ltmp->config_time == PREMIB_CONFIG)
1470 DEBUGMSG(("read_config_usage", "*"));
1471 else
1472 DEBUGMSG(("read_config_usage", " "));
1473 }
1474 if (ltmp->help) {
1475 snmp_log(LOG_INFO, "%s%s%-24s %s\n", lead, lead,
1476 ltmp->config_token, ltmp->help);
1477 } else {
1478 DEBUGIF("read_config_usage") {
1479 snmp_log(LOG_INFO, "%s%s%-24s [NO HELP]\n", lead, lead,
1480 ltmp->config_token);
1481 }
1482 }
1483 }
1484 }
1485 }
1486
1487 /**
1488 * read_config_store intended for use by applications to store permenant
1489 * configuration information generated by sets or persistent counters.
1490 * Appends line to a file named either ENV(SNMP_PERSISTENT_FILE) or
1491 * "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf".
1492 * Adds a trailing newline to the stored file if necessary.
1493 *
1494 * @param type is the application name
1495 * @param line is the configuration line written to the application name's
1496 * configuration file
1497 *
1498 * @return void
1499 */
1500 void
read_config_store(const char * type,const char * line)1501 read_config_store(const char *type, const char *line)
1502 {
1503 #ifdef NETSNMP_PERSISTENT_DIRECTORY
1504 char file[512], *filep;
1505 FILE *fout;
1506 #ifdef NETSNMP_PERSISTENT_MASK
1507 mode_t oldmask;
1508 #endif
1509
1510 if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1511 NETSNMP_DS_LIB_DONT_PERSIST_STATE)
1512 || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1513 NETSNMP_DS_LIB_DISABLE_PERSISTENT_LOAD)) return;
1514
1515 /*
1516 * store configuration directives in the following order of preference:
1517 * 1. ENV variable SNMP_PERSISTENT_FILE
1518 * 2. configured <NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf
1519 */
1520 if ((filep = netsnmp_getenv("SNMP_PERSISTENT_FILE")) == NULL) {
1521 snprintf(file, sizeof(file),
1522 "%s/%s.conf", get_persistent_directory(), type);
1523 file[ sizeof(file)-1 ] = 0;
1524 filep = file;
1525 }
1526 #ifdef NETSNMP_PERSISTENT_MASK
1527 oldmask = umask(NETSNMP_PERSISTENT_MASK);
1528 #endif
1529 if (mkdirhier(filep, NETSNMP_AGENT_DIRECTORY_MODE, 1)) {
1530 snmp_log(LOG_ERR,
1531 "Failed to create the persistent directory for %s\n",
1532 file);
1533 }
1534 if ((fout = fopen(filep, "a")) != NULL) {
1535 fprintf(fout, "%s", line);
1536 if (line[strlen(line)] != '\n')
1537 fprintf(fout, "\n");
1538 DEBUGMSGTL(("read_config:store", "storing: %s\n", line));
1539 fflush(fout);
1540 #if defined(HAVE_FSYNC)
1541 fsync(fileno(fout));
1542 #elif defined(HAVE__GET_OSFHANDLE)
1543 {
1544 int fd;
1545 HANDLE h;
1546
1547 fd = fileno(fout);
1548 netsnmp_assert(fd != -1);
1549 /*
1550 * Use size_t instead of uintptr_t because not all supported
1551 * Windows compilers support uintptr_t.
1552 */
1553 h = (HANDLE)(size_t)_get_osfhandle(fd);
1554 netsnmp_assert(h != INVALID_HANDLE_VALUE);
1555 FlushFileBuffers(h);
1556 }
1557 #endif
1558 fclose(fout);
1559 } else {
1560 if (strcmp(NETSNMP_APPLICATION_CONFIG_TYPE, type) != 0) {
1561 /*
1562 * Ignore this error in client utilities, they can run with random
1563 * UID/GID and typically cannot write to /var. Error message just
1564 * confuses people.
1565 */
1566 snmp_log(LOG_ERR, "read_config_store open failure on %s\n", filep);
1567 }
1568 }
1569 #ifdef NETSNMP_PERSISTENT_MASK
1570 umask(oldmask);
1571 #endif
1572
1573 #endif
1574 } /* end read_config_store() */
1575
1576 void
read_app_config_store(const char * line)1577 read_app_config_store(const char *line)
1578 {
1579 read_config_store(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
1580 NETSNMP_DS_LIB_APPTYPE), line);
1581 }
1582
1583
1584
1585
1586 /*******************************************************************-o-******
1587 * snmp_save_persistent
1588 *
1589 * Parameters:
1590 * *type
1591 *
1592 *
1593 * Save the file "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.conf" into a backup copy
1594 * called "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.%d.conf", which %d is an
1595 * incrementing number on each call, but less than NETSNMP_MAX_PERSISTENT_BACKUPS.
1596 *
1597 * Should be called just before all persistent information is supposed to be
1598 * written to move aside the existing persistent cache.
1599 * snmp_clean_persistent should then be called afterward all data has been
1600 * saved to remove these backup files.
1601 *
1602 * Note: on an rename error, the files are removed rather than saved.
1603 *
1604 */
1605 void
snmp_save_persistent(const char * type)1606 snmp_save_persistent(const char *type)
1607 {
1608 char file[512], fileold[SPRINT_MAX_LEN];
1609 struct stat statbuf;
1610 int j;
1611
1612 if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1613 NETSNMP_DS_LIB_DONT_PERSIST_STATE)
1614 || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1615 NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE)) return;
1616
1617 DEBUGMSGTL(("snmp_save_persistent", "saving %s files...\n", type));
1618 snprintf(file, sizeof(file),
1619 "%s/%s.conf", get_persistent_directory(), type);
1620 file[ sizeof(file)-1 ] = 0;
1621 if (stat(file, &statbuf) == 0) {
1622 for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
1623 snprintf(fileold, sizeof(fileold),
1624 "%s/%s.%d.conf", get_persistent_directory(), type, j);
1625 fileold[ sizeof(fileold)-1 ] = 0;
1626 if (stat(fileold, &statbuf) != 0) {
1627 DEBUGMSGTL(("snmp_save_persistent",
1628 " saving old config file: %s -> %s.\n", file,
1629 fileold));
1630 if (rename(file, fileold)) {
1631 snmp_log(LOG_ERR, "Cannot rename %s to %s\n", file, fileold);
1632 /* moving it failed, try nuking it, as leaving
1633 * it around is very bad. */
1634 if (unlink(file) == -1)
1635 snmp_log(LOG_ERR, "Cannot unlink %s\n", file);
1636 }
1637 break;
1638 }
1639 }
1640 }
1641 /*
1642 * save a warning header to the top of the new file
1643 */
1644 snprintf(fileold, sizeof(fileold),
1645 "%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",
1646 "#\n# net-snmp (or ucd-snmp) persistent data file.\n#\n############################################################################\n# STOP STOP STOP STOP STOP STOP STOP STOP STOP \n",
1647 "#\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",
1648 type, type, type,
1649 "# (Did I mention: do not edit this file?)\n#\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
1650 fileold[ sizeof(fileold)-1 ] = 0;
1651 read_config_store(type, fileold);
1652 }
1653
1654
1655 /*******************************************************************-o-******
1656 * snmp_clean_persistent
1657 *
1658 * Parameters:
1659 * *type
1660 *
1661 *
1662 * Unlink all backup files called "<NETSNMP_PERSISTENT_DIRECTORY>/<type>.%d.conf".
1663 *
1664 * Should be called just after we successfull dumped the last of the
1665 * persistent data, to remove the backup copies of previous storage dumps.
1666 *
1667 * XXX Worth overwriting with random bytes first? This would
1668 * ensure that the data is destroyed, even a buffer containing the
1669 * data persists in memory or swap. Only important if secrets
1670 * will be stored here.
1671 */
1672 void
snmp_clean_persistent(const char * type)1673 snmp_clean_persistent(const char *type)
1674 {
1675 char file[512];
1676 struct stat statbuf;
1677 int j;
1678
1679 if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1680 NETSNMP_DS_LIB_DONT_PERSIST_STATE)
1681 || netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
1682 NETSNMP_DS_LIB_DISABLE_PERSISTENT_SAVE)) return;
1683
1684 DEBUGMSGTL(("snmp_clean_persistent", "cleaning %s files...\n", type));
1685 snprintf(file, sizeof(file),
1686 "%s/%s.conf", get_persistent_directory(), type);
1687 file[ sizeof(file)-1 ] = 0;
1688 if (stat(file, &statbuf) == 0) {
1689 for (j = 0; j <= NETSNMP_MAX_PERSISTENT_BACKUPS; j++) {
1690 snprintf(file, sizeof(file),
1691 "%s/%s.%d.conf", get_persistent_directory(), type, j);
1692 file[ sizeof(file)-1 ] = 0;
1693 if (stat(file, &statbuf) == 0) {
1694 DEBUGMSGTL(("snmp_clean_persistent",
1695 " removing old config file: %s\n", file));
1696 if (unlink(file) == -1)
1697 snmp_log(LOG_ERR, "Cannot unlink %s\n", file);
1698 }
1699 }
1700 }
1701 }
1702
1703
1704
1705
1706 /*
1707 * config_perror: prints a warning string associated with a file and
1708 * line number of a .conf file and increments the error count.
1709 */
1710 static void
config_vlog(int level,const char * levelmsg,const char * str,va_list args)1711 config_vlog(int level, const char *levelmsg, const char *str, va_list args)
1712 {
1713 char tmpbuf[256];
1714 char* buf = tmpbuf;
1715 int len = snprintf(tmpbuf, sizeof(tmpbuf), "%s: line %d: %s: %s\n",
1716 curfilename, linecount, levelmsg, str);
1717 if (len >= (int)sizeof(tmpbuf)) {
1718 buf = (char*)malloc(len + 1);
1719 sprintf(buf, "%s: line %d: %s: %s\n",
1720 curfilename, linecount, levelmsg, str);
1721 }
1722 snmp_vlog(level, buf, args);
1723 if (buf != tmpbuf)
1724 free(buf);
1725 }
1726
1727 void
netsnmp_config_error(const char * str,...)1728 netsnmp_config_error(const char *str, ...)
1729 {
1730 va_list args;
1731 va_start(args, str);
1732 config_vlog(LOG_ERR, "Error", str, args);
1733 va_end(args);
1734 config_errors++;
1735 }
1736
1737 void
netsnmp_config_warn(const char * str,...)1738 netsnmp_config_warn(const char *str, ...)
1739 {
1740 va_list args;
1741 va_start(args, str);
1742 config_vlog(LOG_WARNING, "Warning", str, args);
1743 va_end(args);
1744 }
1745
1746 void
config_perror(const char * str)1747 config_perror(const char *str)
1748 {
1749 netsnmp_config_error("%s", str);
1750 }
1751
1752 void
config_pwarn(const char * str)1753 config_pwarn(const char *str)
1754 {
1755 netsnmp_config_warn("%s", str);
1756 }
1757
1758 /*
1759 * skip all white spaces and return 1 if found something either end of
1760 * line or a comment character
1761 */
1762 char *
skip_white(char * ptr)1763 skip_white(char *ptr)
1764 {
1765 return NETSNMP_REMOVE_CONST(char *, skip_white_const(ptr));
1766 }
1767
1768 const char *
skip_white_const(const char * ptr)1769 skip_white_const(const char *ptr)
1770 {
1771 if (ptr == NULL)
1772 return (NULL);
1773 while (*ptr != 0 && isspace((unsigned char)*ptr))
1774 ptr++;
1775 if (*ptr == 0 || *ptr == '#')
1776 return (NULL);
1777 return (ptr);
1778 }
1779
1780 char *
skip_not_white(char * ptr)1781 skip_not_white(char *ptr)
1782 {
1783 return NETSNMP_REMOVE_CONST(char *, skip_not_white_const(ptr));
1784 }
1785
1786 const char *
skip_not_white_const(const char * ptr)1787 skip_not_white_const(const char *ptr)
1788 {
1789 if (ptr == NULL)
1790 return (NULL);
1791 while (*ptr != 0 && !isspace((unsigned char)*ptr))
1792 ptr++;
1793 if (*ptr == 0 || *ptr == '#')
1794 return (NULL);
1795 return (ptr);
1796 }
1797
1798 char *
skip_token(char * ptr)1799 skip_token(char *ptr)
1800 {
1801 return NETSNMP_REMOVE_CONST(char *, skip_token_const(ptr));
1802 }
1803
1804 const char *
skip_token_const(const char * ptr)1805 skip_token_const(const char *ptr)
1806 {
1807 ptr = skip_white_const(ptr);
1808 ptr = skip_not_white_const(ptr);
1809 ptr = skip_white_const(ptr);
1810 return (ptr);
1811 }
1812
1813 /*
1814 * copy_word
1815 * copies the next 'token' from 'from' into 'to', maximum len-1 characters.
1816 * currently a token is anything seperate by white space
1817 * or within quotes (double or single) (i.e. "the red rose"
1818 * is one token, \"the red rose\" is three tokens)
1819 * a '\' character will allow a quote character to be treated
1820 * as a regular character
1821 * It returns a pointer to first non-white space after the end of the token
1822 * being copied or to 0 if we reach the end.
1823 * Note: Partially copied words (greater than len) still returns a !NULL ptr
1824 * Note: partially copied words are, however, null terminated.
1825 */
1826
1827 char *
copy_nword(char * from,char * to,int len)1828 copy_nword(char *from, char *to, int len)
1829 {
1830 return NETSNMP_REMOVE_CONST(char *, copy_nword_const(from, to, len));
1831 }
1832
1833 const char *
copy_nword_const(const char * from,char * to,int len)1834 copy_nword_const(const char *from, char *to, int len)
1835 {
1836 char quote;
1837 if (!from || !to)
1838 return NULL;
1839 if ((*from == '\"') || (*from == '\'')) {
1840 quote = *(from++);
1841 while ((*from != quote) && (*from != 0)) {
1842 if ((*from == '\\') && (*(from + 1) != 0)) {
1843 if (len > 0) { /* don't copy beyond len bytes */
1844 *to++ = *(from + 1);
1845 if (--len == 0)
1846 *(to - 1) = '\0'; /* null protect the last spot */
1847 }
1848 from = from + 2;
1849 } else {
1850 if (len > 0) { /* don't copy beyond len bytes */
1851 *to++ = *from++;
1852 if (--len == 0)
1853 *(to - 1) = '\0'; /* null protect the last spot */
1854 } else
1855 from++;
1856 }
1857 }
1858 if (*from == 0) {
1859 DEBUGMSGTL(("read_config_copy_word",
1860 "no end quote found in config string\n"));
1861 } else
1862 from++;
1863 } else {
1864 while (*from != 0 && !isspace((unsigned char)(*from))) {
1865 if ((*from == '\\') && (*(from + 1) != 0)) {
1866 if (len > 0) { /* don't copy beyond len bytes */
1867 *to++ = *(from + 1);
1868 if (--len == 0)
1869 *(to - 1) = '\0'; /* null protect the last spot */
1870 }
1871 from = from + 2;
1872 } else {
1873 if (len > 0) { /* don't copy beyond len bytes */
1874 *to++ = *from++;
1875 if (--len == 0)
1876 *(to - 1) = '\0'; /* null protect the last spot */
1877 } else
1878 from++;
1879 }
1880 }
1881 }
1882 if (len > 0)
1883 *to = 0;
1884 from = skip_white_const(from);
1885 return (from);
1886 } /* copy_nword */
1887
1888 /*
1889 * copy_word
1890 * copies the next 'token' from 'from' into 'to'.
1891 * currently a token is anything seperate by white space
1892 * or within quotes (double or single) (i.e. "the red rose"
1893 * is one token, \"the red rose\" is three tokens)
1894 * a '\' character will allow a quote character to be treated
1895 * as a regular character
1896 * It returns a pointer to first non-white space after the end of the token
1897 * being copied or to 0 if we reach the end.
1898 */
1899
1900 static int have_warned = 0;
1901 char *
copy_word(char * from,char * to)1902 copy_word(char *from, char *to)
1903 {
1904 if (!have_warned) {
1905 snmp_log(LOG_INFO,
1906 "copy_word() called. Use copy_nword() instead.\n");
1907 have_warned = 1;
1908 }
1909 return copy_nword(from, to, SPRINT_MAX_LEN);
1910 } /* copy_word */
1911
1912 /**
1913 * Stores an quoted version of the first len bytes from str into saveto.
1914 *
1915 * If all octets in str are in the set [[:alnum:] ] then the quotation
1916 * is to enclose the string in quotation marks ("str") otherwise the
1917 * quotation is to prepend the string 0x and then add the hex representation
1918 * of all characters from str (0x737472)
1919 *
1920 * @param[in] saveto pointer to output stream, is assumed to be big enough.
1921 * @param[in] str pointer of the data that is to be stored.
1922 * @param[in] len length of the data that is to be stored.
1923 * @return A pointer to saveto after str is added to it.
1924 */
1925 char *
read_config_save_octet_string(char * saveto,const u_char * str,size_t len)1926 read_config_save_octet_string(char *saveto, const u_char * str, size_t len)
1927 {
1928 size_t i;
1929 const u_char *cp;
1930
1931 /*
1932 * is everything easily printable
1933 */
1934 for (i = 0, cp = str; i < len && cp &&
1935 (isalpha(*cp) || isdigit(*cp) || *cp == ' '); cp++, i++);
1936
1937 if (len != 0 && i == len) {
1938 *saveto++ = '"';
1939 memcpy(saveto, str, len);
1940 saveto += len;
1941 *saveto++ = '"';
1942 *saveto = '\0';
1943 } else {
1944 if (str != NULL) {
1945 sprintf(saveto, "0x");
1946 saveto += 2;
1947 for (i = 0; i < len; i++) {
1948 sprintf(saveto, "%02x", str[i]);
1949 saveto = saveto + 2;
1950 }
1951 } else {
1952 sprintf(saveto, "\"\"");
1953 saveto += 2;
1954 }
1955 }
1956 return saveto;
1957 }
1958
1959 /**
1960 * Reads an octet string that was saved by the
1961 * read_config_save_octet_string() function.
1962 *
1963 * @param[in] readfrom Pointer to the input data to be parsed.
1964 * @param[in,out] str Pointer to the output buffer pointer. The data
1965 * written to the output buffer will be '\0'-terminated. If *str == NULL,
1966 * an output buffer will be allocated that is one byte larger than the
1967 * data stored.
1968 * @param[in,out] len If str != NULL, *len is the size of the buffer *str
1969 * points at. If str == NULL, the value passed via *len is ignored.
1970 * Before this function returns the number of bytes read will be stored
1971 * in *len. If a buffer overflow occurs, *len will be set to 0.
1972 *
1973 * @return A pointer to the next character in the input to be parsed if
1974 * parsing succeeded; NULL when the end of the input string has been reached
1975 * or if an error occurred.
1976 */
1977 char *
read_config_read_octet_string(const char * readfrom,u_char ** str,size_t * len)1978 read_config_read_octet_string(const char *readfrom, u_char ** str,
1979 size_t * len)
1980 {
1981 return NETSNMP_REMOVE_CONST(char *,
1982 read_config_read_octet_string_const(readfrom, str, len));
1983 }
1984
1985 const char *
read_config_read_octet_string_const(const char * readfrom,u_char ** str,size_t * len)1986 read_config_read_octet_string_const(const char *readfrom, u_char ** str,
1987 size_t * len)
1988 {
1989 u_char *cptr;
1990 const char *cptr1;
1991 u_int tmp;
1992 size_t i, ilen;
1993
1994 if (readfrom == NULL || str == NULL || len == NULL)
1995 return NULL;
1996
1997 if (strncasecmp(readfrom, "0x", 2) == 0) {
1998 /*
1999 * A hex string submitted. How long?
2000 */
2001 readfrom += 2;
2002 cptr1 = skip_not_white_const(readfrom);
2003 if (cptr1)
2004 ilen = (cptr1 - readfrom);
2005 else
2006 ilen = strlen(readfrom);
2007
2008 if (ilen % 2) {
2009 snmp_log(LOG_WARNING,"invalid hex string: wrong length\n");
2010 DEBUGMSGTL(("read_config_read_octet_string",
2011 "invalid hex string: wrong length"));
2012 return NULL;
2013 }
2014 ilen = ilen / 2;
2015
2016 /*
2017 * malloc data space if needed (+1 for good measure)
2018 */
2019 if (*str == NULL) {
2020 *str = (u_char *) malloc(ilen + 1);
2021 if (!*str)
2022 return NULL;
2023 } else {
2024 /*
2025 * require caller to have +1, and bail if not enough space.
2026 */
2027 if (ilen >= *len) {
2028 snmp_log(LOG_WARNING,"buffer too small to read octet string (%lu < %lu)\n",
2029 (unsigned long)*len, (unsigned long)ilen);
2030 DEBUGMSGTL(("read_config_read_octet_string",
2031 "buffer too small (%lu < %lu)", (unsigned long)*len, (unsigned long)ilen));
2032 *len = 0;
2033 cptr1 = skip_not_white_const(readfrom);
2034 return skip_white_const(cptr1);
2035 }
2036 }
2037
2038 /*
2039 * copy validated data
2040 */
2041 cptr = *str;
2042 for (i = 0; i < ilen; i++) {
2043 if (1 == sscanf(readfrom, "%2x", &tmp))
2044 *cptr++ = (u_char) tmp;
2045 else {
2046 /*
2047 * we may lose memory, but don't know caller's buffer XX free(cptr);
2048 */
2049 return (NULL);
2050 }
2051 readfrom += 2;
2052 }
2053 /*
2054 * Terminate the output buffer.
2055 */
2056 *cptr++ = '\0';
2057 *len = ilen;
2058 readfrom = skip_white_const(readfrom);
2059 } else {
2060 /*
2061 * Normal string
2062 */
2063
2064 /*
2065 * malloc string space if needed (including NULL terminator)
2066 */
2067 if (*str == NULL) {
2068 char buf[SNMP_MAXBUF];
2069 readfrom = copy_nword_const(readfrom, buf, sizeof(buf));
2070
2071 *len = strlen(buf);
2072 *str = (u_char *) malloc(*len + 1);
2073 if (*str == NULL)
2074 return NULL;
2075 memcpy(*str, buf, *len + 1);
2076 } else {
2077 readfrom = copy_nword_const(readfrom, (char *) *str, *len);
2078 if (*len)
2079 *len = strlen((char *) *str);
2080 }
2081 }
2082
2083 return readfrom;
2084 }
2085
2086 /*
2087 * read_config_save_objid(): saves an objid as a numerical string
2088 */
2089 char *
read_config_save_objid(char * saveto,oid * objid,size_t len)2090 read_config_save_objid(char *saveto, oid * objid, size_t len)
2091 {
2092 int i;
2093
2094 if (len == 0) {
2095 strcat(saveto, "NULL");
2096 saveto += strlen(saveto);
2097 return saveto;
2098 }
2099
2100 /*
2101 * in case len=0, this makes it easier to read it back in
2102 */
2103 for (i = 0; i < (int) len; i++) {
2104 sprintf(saveto, ".%" NETSNMP_PRIo "d", objid[i]);
2105 saveto += strlen(saveto);
2106 }
2107 return saveto;
2108 }
2109
2110 /*
2111 * read_config_read_objid(): reads an objid from a format saved by the above
2112 */
2113 char *
read_config_read_objid(char * readfrom,oid ** objid,size_t * len)2114 read_config_read_objid(char *readfrom, oid ** objid, size_t * len)
2115 {
2116 return NETSNMP_REMOVE_CONST(char *,
2117 read_config_read_objid_const(readfrom, objid, len));
2118 }
2119
2120 const char *
read_config_read_objid_const(const char * readfrom,oid ** objid,size_t * len)2121 read_config_read_objid_const(const char *readfrom, oid ** objid, size_t * len)
2122 {
2123
2124 if (objid == NULL || readfrom == NULL || len == NULL)
2125 return NULL;
2126
2127 if (*objid == NULL) {
2128 *len = 0;
2129 if ((*objid = (oid *) malloc(MAX_OID_LEN * sizeof(oid))) == NULL)
2130 return NULL;
2131 *len = MAX_OID_LEN;
2132 }
2133
2134 if (strncmp(readfrom, "NULL", 4) == 0) {
2135 /*
2136 * null length oid
2137 */
2138 *len = 0;
2139 } else {
2140 /*
2141 * qualify the string for read_objid
2142 */
2143 char buf[SPRINT_MAX_LEN];
2144 copy_nword_const(readfrom, buf, sizeof(buf));
2145
2146 if (!read_objid(buf, *objid, len)) {
2147 DEBUGMSGTL(("read_config_read_objid", "Invalid OID"));
2148 *len = 0;
2149 return NULL;
2150 }
2151 }
2152
2153 readfrom = skip_token_const(readfrom);
2154 return readfrom;
2155 }
2156
2157 /**
2158 * read_config_read_data reads data of a given type from a token(s) on a
2159 * configuration line. The supported types are:
2160 *
2161 * - ASN_INTEGER
2162 * - ASN_TIMETICKS
2163 * - ASN_UNSIGNED
2164 * - ASN_OCTET_STR
2165 * - ASN_BIT_STR
2166 * - ASN_OBJECT_ID
2167 *
2168 * @param type the asn data type to be read in.
2169 *
2170 * @param readfrom the configuration line data to be read.
2171 *
2172 * @param dataptr an allocated pointer expected to match the type being read
2173 * (int *, u_int *, char **, oid **)
2174 *
2175 * @param len is the length of an asn oid or octet/bit string, not required
2176 * for the asn integer, unsigned integer, and timeticks types
2177 *
2178 * @return the next token in the configuration line. NULL if none left or
2179 * if an unknown type.
2180 *
2181 */
2182 char *
read_config_read_data(int type,char * readfrom,void * dataptr,size_t * len)2183 read_config_read_data(int type, char *readfrom, void *dataptr,
2184 size_t * len)
2185 {
2186 int *intp;
2187 char **charpp;
2188 oid **oidpp;
2189 unsigned int *uintp;
2190
2191 if (dataptr && readfrom)
2192 switch (type) {
2193 case ASN_INTEGER:
2194 intp = (int *) dataptr;
2195 *intp = atoi(readfrom);
2196 readfrom = skip_token(readfrom);
2197 return readfrom;
2198
2199 case ASN_TIMETICKS:
2200 case ASN_UNSIGNED:
2201 uintp = (unsigned int *) dataptr;
2202 *uintp = strtoul(readfrom, NULL, 0);
2203 readfrom = skip_token(readfrom);
2204 return readfrom;
2205
2206 case ASN_IPADDRESS:
2207 intp = (int *) dataptr;
2208 *intp = inet_addr(readfrom);
2209 if ((*intp == -1) &&
2210 (strncmp(readfrom, "255.255.255.255", 15) != 0))
2211 return NULL;
2212 readfrom = skip_token(readfrom);
2213 return readfrom;
2214
2215 case ASN_OCTET_STR:
2216 case ASN_BIT_STR:
2217 charpp = (char **) dataptr;
2218 return read_config_read_octet_string(readfrom,
2219 (u_char **) charpp, len);
2220
2221 case ASN_OBJECT_ID:
2222 oidpp = (oid **) dataptr;
2223 return read_config_read_objid(readfrom, oidpp, len);
2224
2225 default:
2226 DEBUGMSGTL(("read_config_read_data", "Fail: Unknown type: %d",
2227 type));
2228 return NULL;
2229 }
2230 return NULL;
2231 }
2232
2233 /*
2234 * read_config_read_memory():
2235 *
2236 * similar to read_config_read_data, but expects a generic memory
2237 * pointer rather than a specific type of pointer. Len is expected to
2238 * be the amount of available memory.
2239 */
2240 char *
read_config_read_memory(int type,char * readfrom,char * dataptr,size_t * len)2241 read_config_read_memory(int type, char *readfrom,
2242 char *dataptr, size_t * len)
2243 {
2244 int *intp;
2245 unsigned int *uintp;
2246 char buf[SPRINT_MAX_LEN];
2247
2248 if (!dataptr || !readfrom)
2249 return NULL;
2250
2251 switch (type) {
2252 case ASN_INTEGER:
2253 if (*len < sizeof(int))
2254 return NULL;
2255 intp = (int *) dataptr;
2256 readfrom = copy_nword(readfrom, buf, sizeof(buf));
2257 *intp = atoi(buf);
2258 *len = sizeof(int);
2259 return readfrom;
2260
2261 case ASN_COUNTER:
2262 case ASN_TIMETICKS:
2263 case ASN_UNSIGNED:
2264 if (*len < sizeof(unsigned int))
2265 return NULL;
2266 uintp = (unsigned int *) dataptr;
2267 readfrom = copy_nword(readfrom, buf, sizeof(buf));
2268 *uintp = strtoul(buf, NULL, 0);
2269 *len = sizeof(unsigned int);
2270 return readfrom;
2271
2272 case ASN_IPADDRESS:
2273 if (*len < sizeof(int))
2274 return NULL;
2275 intp = (int *) dataptr;
2276 readfrom = copy_nword(readfrom, buf, sizeof(buf));
2277 *intp = inet_addr(buf);
2278 if ((*intp == -1) && (strcmp(buf, "255.255.255.255") != 0))
2279 return NULL;
2280 *len = sizeof(int);
2281 return readfrom;
2282
2283 case ASN_OCTET_STR:
2284 case ASN_BIT_STR:
2285 case ASN_PRIV_IMPLIED_OCTET_STR:
2286 return read_config_read_octet_string(readfrom,
2287 (u_char **) & dataptr, len);
2288
2289 case ASN_PRIV_IMPLIED_OBJECT_ID:
2290 case ASN_OBJECT_ID:
2291 readfrom =
2292 read_config_read_objid(readfrom, (oid **) & dataptr, len);
2293 *len *= sizeof(oid);
2294 return readfrom;
2295
2296 case ASN_COUNTER64:
2297 if (*len < sizeof(struct counter64))
2298 return NULL;
2299 *len = sizeof(struct counter64);
2300 read64((struct counter64 *) dataptr, readfrom);
2301 readfrom = skip_token(readfrom);
2302 return readfrom;
2303 }
2304
2305 DEBUGMSGTL(("read_config_read_memory", "Fail: Unknown type: %d", type));
2306 return NULL;
2307 }
2308
2309 /**
2310 * read_config_store_data stores data of a given type to a configuration line
2311 * into the storeto buffer.
2312 * Calls read_config_store_data_prefix with the prefix parameter set to a char
2313 * space. The supported types are:
2314 *
2315 * - ASN_INTEGER
2316 * - ASN_TIMETICKS
2317 * - ASN_UNSIGNED
2318 * - ASN_OCTET_STR
2319 * - ASN_BIT_STR
2320 * - ASN_OBJECT_ID
2321 *
2322 * @param type the asn data type to be stored
2323 *
2324 * @param storeto a pre-allocated char buffer which will contain the data
2325 * to be stored
2326 *
2327 * @param dataptr contains the value to be stored, the supported pointers:
2328 * (int *, u_int *, char **, oid **)
2329 *
2330 * @param len is the length of the value to be stored
2331 * (not required for the asn integer, unsigned integer,
2332 * and timeticks types)
2333 *
2334 * @return character pointer to the end of the line. NULL if an unknown type.
2335 */
2336 char *
read_config_store_data(int type,char * storeto,void * dataptr,size_t * len)2337 read_config_store_data(int type, char *storeto, void *dataptr, size_t * len)
2338 {
2339 return read_config_store_data_prefix(' ', type, storeto, dataptr,
2340 (len ? *len : 0));
2341 }
2342
2343 char *
read_config_store_data_prefix(char prefix,int type,char * storeto,void * dataptr,size_t len)2344 read_config_store_data_prefix(char prefix, int type, char *storeto,
2345 void *dataptr, size_t len)
2346 {
2347 int *intp;
2348 u_char **charpp;
2349 unsigned int *uintp;
2350 struct in_addr in;
2351 oid **oidpp;
2352
2353 if (dataptr && storeto)
2354 switch (type) {
2355 case ASN_INTEGER:
2356 intp = (int *) dataptr;
2357 sprintf(storeto, "%c%d", prefix, *intp);
2358 return (storeto + strlen(storeto));
2359
2360 case ASN_TIMETICKS:
2361 case ASN_UNSIGNED:
2362 uintp = (unsigned int *) dataptr;
2363 sprintf(storeto, "%c%u", prefix, *uintp);
2364 return (storeto + strlen(storeto));
2365
2366 case ASN_IPADDRESS:
2367 in.s_addr = *(unsigned int *) dataptr;
2368 sprintf(storeto, "%c%s", prefix, inet_ntoa(in));
2369 return (storeto + strlen(storeto));
2370
2371 case ASN_OCTET_STR:
2372 case ASN_BIT_STR:
2373 *storeto++ = prefix;
2374 charpp = (u_char **) dataptr;
2375 return read_config_save_octet_string(storeto, *charpp, len);
2376
2377 case ASN_OBJECT_ID:
2378 *storeto++ = prefix;
2379 oidpp = (oid **) dataptr;
2380 return read_config_save_objid(storeto, *oidpp, len);
2381
2382 default:
2383 DEBUGMSGTL(("read_config_store_data_prefix",
2384 "Fail: Unknown type: %d", type));
2385 return NULL;
2386 }
2387 return NULL;
2388 }
2389
2390 /** @} */
2391