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