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