1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2020 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 * Master Configuration routines.
21 *
22 * This file contains the common parts of the Bacula
23 * configuration routines.
24 *
25 * Note, the configuration file parser consists of three parts
26 *
27 * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
28 *
29 * 2. The generic config scanner in lib/parse_conf.c and
30 * lib/parse_conf.h.
31 * These files contain the parser code, some utility
32 * routines, and the common store routines (name, int,
33 * string, time, int64, size, ...).
34 *
35 * 3. The daemon specific file, which contains the Resource
36 * definitions as well as any specific store routines
37 * for the resource records.
38 *
39 * N.B. This is a two pass parser, so if you malloc() a string
40 * in a "store" routine, you must ensure to do it during
41 * only one of the two passes, or to free it between.
42 * Also, note that the resource record is malloced and
43 * saved in save_resource() during pass 1. Anything that
44 * you want saved after pass two (e.g. resource pointers)
45 * must explicitly be done in save_resource. Take a look
46 * at the Job resource in src/dird/dird_conf.c to see how
47 * it is done.
48 *
49 * Kern Sibbald, January MM
50 *
51 */
52
53
54 #include "bacula.h"
55
56 #if defined(HAVE_WIN32)
57 #include "shlobj.h"
58 #else
59 #define MAX_PATH 1024
60 #endif
61
62 /*
63 * Define the Union of all the common resource structure definitions.
64 */
65 union URES {
66 MSGS res_msgs;
67 RES hdr;
68 COLLECTOR res_collector;
69 };
70
71 #if defined(_MSC_VER)
72 // work around visual studio name mangling preventing external linkage since res_all
73 // is declared as a different type when instantiated.
74 extern "C" URES res_all;
75 #else
76 extern URES res_all;
77 #endif
78
79 extern brwlock_t res_lock; /* resource lock */
80
81
82 /* Forward referenced subroutines */
83 static void scan_types(LEX *lc, MSGS *msg, int dest, char *where, char *cmd);
84 static const char *get_default_configdir();
85
86 /* Common Resource definitions */
87
88 /*
89 * Message resource directives
90 * Note: keep all store_mesgs last in the list as they are all
91 * output in json as a list.
92 * Also, the list store_msgs item must have flags set to ITEM_LAST
93 * so that the list editor (bjson.c) knows when to stop.
94 *
95 * name handler value code flags default_value
96 */
97 RES_ITEM msgs_items[] = {
98 {"Name", store_name, ITEM(res_msgs.hdr.name), 0, ITEM_REQUIRED, 0},
99 {"Description", store_str, ITEM(res_msgs.hdr.desc), 0, 0, 0},
100 {"MailCommand", store_str, ITEM(res_msgs.mail_cmd), 0, ITEM_ALLOW_DUPS, 0},
101 {"OperatorCommand", store_str, ITEM(res_msgs.operator_cmd), 0, ITEM_ALLOW_DUPS, 0},
102 /* See comments above */
103 {"Syslog", store_msgs, ITEM(res_msgs), MD_SYSLOG, 0, 0},
104 {"Mail", store_msgs, ITEM(res_msgs), MD_MAIL, 0, 0},
105 {"MailOnError", store_msgs, ITEM(res_msgs), MD_MAIL_ON_ERROR, 0, 0},
106 {"MailOnSuccess", store_msgs, ITEM(res_msgs), MD_MAIL_ON_SUCCESS, 0, 0},
107 {"File", store_msgs, ITEM(res_msgs), MD_FILE, 0, 0},
108 {"Append", store_msgs, ITEM(res_msgs), MD_APPEND, 0, 0},
109 {"Stdout", store_msgs, ITEM(res_msgs), MD_STDOUT, 0, 0},
110 {"Stderr", store_msgs, ITEM(res_msgs), MD_STDERR, 0, 0},
111 {"Director", store_msgs, ITEM(res_msgs), MD_DIRECTOR, 0, 0},
112 {"Console", store_msgs, ITEM(res_msgs), MD_CONSOLE, 0, 0},
113 {"Operator", store_msgs, ITEM(res_msgs), MD_OPERATOR, 0, 0},
114 {"Catalog", store_msgs, ITEM(res_msgs), MD_CATALOG, ITEM_LAST, 0},
115 {NULL, NULL, {0}, 0, 0, 0}
116 };
117
118 /*
119 * Statistics resource directives
120 *
121 * name handler value code flags default_value
122 */
123 RES_ITEM collector_items[] = {
124 {"Name", store_name, ITEM(res_collector.hdr.name), 0, ITEM_REQUIRED, 0},
125 {"Description", store_str, ITEM(res_collector.hdr.desc), 0, 0, 0},
126 {"Prefix", store_str, ITEM(res_collector.prefix), 0, 0, 0},
127 {"Metrics", store_alist_str, ITEM(res_collector.metrics), 0, 0, 0}, /* default all */
128 {"Interval", store_time, ITEM(res_collector.interval), 0, ITEM_DEFAULT, 5*60}, /* default 5 min */
129 {"Port", store_pint32, ITEM(res_collector.port), 0, 0, 0},
130 {"Host", store_str, ITEM(res_collector.host), 0, 0, 0},
131 {"Type", store_coll_type, ITEM(res_collector.type), 0, ITEM_REQUIRED, 0},
132 {"File", store_str, ITEM(res_collector.file), 0, 0, 0},
133 {"MangleMetric", store_bool, ITEM(res_collector.mangle_name), 0, 0, 0},
134 {NULL, NULL, {0}, 0, 0, 0}
135 };
136
137 /* Various message types */
138 s_kw msg_types[] = {
139 {"Debug", M_DEBUG}, /* Keep 1st place */
140 {"Saved", M_SAVED}, /* Keep 2nd place */
141 {"Abort", M_ABORT},
142 {"Fatal", M_FATAL},
143 {"Error", M_ERROR},
144 {"Warning", M_WARNING},
145 {"Info", M_INFO},
146 {"NotSaved", M_NOTSAVED},
147 {"Skipped", M_SKIPPED},
148 {"Mount", M_MOUNT},
149 {"Terminate", M_TERM},
150 {"Restored", M_RESTORED},
151 {"Security", M_SECURITY},
152 {"Alert", M_ALERT},
153 {"VolMgmt", M_VOLMGMT},
154 {"ErrorTerm", M_ERROR_TERM},
155 {"All", M_MAX+1},
156 {NULL, 0}
157 };
158
159
160 /*
161 * Tape Label types permitted in Pool records
162 *
163 * tape label label code = token
164 */
165 s_kw tapelabels[] = {
166 {"Bacula", B_BACULA_LABEL},
167 {"ANSI", B_ANSI_LABEL},
168 {"IBM", B_IBM_LABEL},
169 {NULL, 0}
170 };
171
172
173 /*
174 * Keywords (RHS) permitted in Statistics type records
175 *
176 * type_name backend_type
177 */
178 s_collt collectortypes[] = {
179 {"CSV", COLLECTOR_BACKEND_CSV},
180 {"Graphite", COLLECTOR_BACKEND_Graphite},
181 {NULL, 0}
182 };
183
184
185 /* Simply print a message */
prtmsg(void * sock,const char * fmt,...)186 static void prtmsg(void *sock, const char *fmt, ...)
187 {
188 va_list arg_ptr;
189
190 va_start(arg_ptr, fmt);
191 vfprintf(stdout, fmt, arg_ptr);
192 va_end(arg_ptr);
193 }
194
res_to_str(int rcode)195 const char *res_to_str(int rcode)
196 {
197 if (rcode < r_first || rcode > r_last) {
198 return _("***UNKNOWN***");
199 } else {
200 return resources[rcode-r_first].name;
201 }
202 }
203
204 /*
205 * Create a new res_head pointer to a list of res_heads
206 */
init_res_head(RES_HEAD *** rhead,int32_t rfirst,int32_t rlast)207 void CONFIG::init_res_head(RES_HEAD ***rhead, int32_t rfirst, int32_t rlast)
208 {
209 int num = rlast - rfirst + 1;
210 RES *res = NULL;
211 RES_HEAD **rh;
212 rh = *rhead = (RES_HEAD **)malloc(num * sizeof(RES_HEAD));
213 for (int i=0; i<num; i++) {
214 rh[i] = (RES_HEAD *)malloc(sizeof(RES_HEAD));
215 rh[i]->res_list = New(rblist(res, &res->link));
216 rh[i]->first = NULL;
217 rh[i]->last = NULL;
218 }
219 }
220
221 /*
222 * Insert the resource in res_all into the
223 * resource list.
224 */
insert_res(int rindex,int size)225 bool CONFIG::insert_res(int rindex, int size)
226 {
227 RES *res;
228 rblist *list = m_res_head[rindex]->res_list;
229 res = (RES *)malloc(size);
230 memcpy(res, m_res_all, size);
231 if (list->empty()) {
232 list->insert(res, res_compare);
233 m_res_head[rindex]->first = res;
234 m_res_head[rindex]->last = res;
235 } else {
236 RES *item, *prev;
237 prev = m_res_head[rindex]->last;
238 item = (RES *)list->insert(res, res_compare);
239 if (item != res) {
240 Mmsg(m_errmsg, _("Attempt to define second \"%s\" resource named \"%s\" is not permitted.\n"),
241 resources[rindex].name, ((URES *)res)->hdr.name);
242 return false;
243 }
244 prev->res_next = res;
245 m_res_head[rindex]->last = res;
246 }
247 Dmsg2(900, _("Inserted res: %s index=%d\n"), ((URES *)res)->hdr.name, rindex);
248 return true;
249 }
250
251 /*
252 * Initialize the static structure to zeros, then
253 * apply all the default values.
254 */
init_resource0(CONFIG * config,int type,RES_ITEM * items,int pass)255 static void init_resource0(CONFIG *config, int type, RES_ITEM *items, int pass)
256 {
257 int i;
258 int rindex = type - r_first;
259
260 memset(config->m_res_all, 0, config->m_res_all_size);
261 res_all.hdr.rcode = type;
262 res_all.hdr.refcnt = 1;
263
264 /* Set defaults in each item */
265 for (i=0; items[i].name; i++) {
266 Dmsg3(900, "Item=%s def=%s defval=%d\n", items[i].name,
267 (items[i].flags & ITEM_DEFAULT) ? "yes" : "no",
268 items[i].default_value);
269 if (items[i].flags & ITEM_DEFAULT && items[i].default_value != 0) {
270 if (items[i].handler == store_bit) {
271 *(uint32_t *)(items[i].value) |= items[i].code;
272 } else if (items[i].handler == store_bool) {
273 *(bool *)(items[i].value) = items[i].default_value != 0;
274 } else if (items[i].handler == store_pint32 ||
275 items[i].handler == store_int32 ||
276 items[i].handler == store_size32) {
277 *(uint32_t *)(items[i].value) = items[i].default_value;
278 } else if (items[i].handler == store_int64) {
279 *(int64_t *)(items[i].value) = items[i].default_value;
280 } else if (items[i].handler == store_size64) {
281 *(uint64_t *)(items[i].value) = (uint64_t)items[i].default_value;
282 } else if (items[i].handler == store_speed) {
283 *(uint64_t *)(items[i].value) = (uint64_t)items[i].default_value;
284 } else if (items[i].handler == store_time) {
285 *(utime_t *)(items[i].value) = (utime_t)items[i].default_value;
286 } else if (pass == 1 && items[i].handler == store_addresses) {
287 init_default_addresses((dlist**)items[i].value, items[i].default_value);
288 }
289 }
290 /* If this triggers, take a look at lib/parse_conf.h */
291 if (i >= MAX_RES_ITEMS) {
292 Emsg1(M_ERROR_TERM, 0, _("Too many directives in \"%s\" resource\n"), resources[rindex].name);
293 }
294 }
295 }
296
297 /* Initialize a resouce with default values */
init_resource(CONFIG * config,uint32_t type,void * res,int size)298 bool init_resource(CONFIG *config, uint32_t type, void *res, int size)
299 {
300 RES_ITEM *items;
301 for (int i=0; resources[i].name; i++) {
302 if (resources[i].rcode == type) {
303 items = resources[i].items;
304 if (!items) {
305 return false;
306 }
307 init_resource0(config, type, items, 1);
308 memcpy(res, config->m_res_all, size);
309 return true;
310 }
311 }
312 return false;
313 }
314
315 /*
316 * Dump each resource of type
317 */
dump_each_resource(int type,void sendit (void * sock,const char * fmt,...),void * sock)318 void dump_each_resource(int type, void sendit(void *sock, const char *fmt, ...), void *sock)
319 {
320 RES *res = NULL;
321
322 if (type < 0) { /* no recursion */
323 type = -type;
324 }
325 foreach_res(res, type) {
326 dump_resource(-type, res, sendit, sock);
327 }
328 }
329
330
331 /* Store Messages Destination information */
store_msgs(LEX * lc,RES_ITEM * item,int index,int pass)332 void store_msgs(LEX *lc, RES_ITEM *item, int index, int pass)
333 {
334 int token;
335 char *cmd;
336 POOLMEM *dest;
337 int dest_len;
338
339 Dmsg2(900, "store_msgs pass=%d code=%d\n", pass, item->code);
340 if (pass == 1) {
341 switch (item->code) {
342 case MD_STDOUT:
343 case MD_STDERR:
344 case MD_SYSLOG: /* syslog */
345 case MD_CONSOLE:
346 case MD_CATALOG:
347 scan_types(lc, (MSGS *)(item->value), item->code, NULL, NULL);
348 break;
349 case MD_OPERATOR: /* send to operator */
350 case MD_DIRECTOR: /* send to Director */
351 case MD_MAIL: /* mail */
352 case MD_MAIL_ON_ERROR: /* mail if Job errors */
353 case MD_MAIL_ON_SUCCESS: /* mail if Job succeeds */
354 if (item->code == MD_OPERATOR) {
355 cmd = res_all.res_msgs.operator_cmd;
356 } else {
357 cmd = res_all.res_msgs.mail_cmd;
358 }
359 dest = get_pool_memory(PM_MESSAGE);
360 dest[0] = 0;
361 dest_len = 0;
362 /* Pick up comma separated list of destinations */
363 for ( ;; ) {
364 token = lex_get_token(lc, T_NAME); /* scan destination */
365 dest = check_pool_memory_size(dest, dest_len + lc->str_len + 2);
366 if (dest[0] != 0) {
367 pm_strcat(dest, " "); /* separate multiple destinations with space */
368 dest_len++;
369 }
370 pm_strcat(dest, lc->str);
371 dest_len += lc->str_len;
372 Dmsg2(900, "store_msgs newdest=%s: dest=%s:\n", lc->str, NPRT(dest));
373 token = lex_get_token(lc, T_SKIP_EOL);
374 if (token == T_COMMA) {
375 continue; /* get another destination */
376 }
377 if (token != T_EQUALS) {
378 scan_err1(lc, _("expected an =, got: %s"), lc->str);
379 return;
380 }
381 break;
382 }
383 Dmsg1(900, "mail_cmd=%s\n", NPRT(cmd));
384 scan_types(lc, (MSGS *)(item->value), item->code, dest, cmd);
385 free_pool_memory(dest);
386 Dmsg0(900, "done with dest codes\n");
387 break;
388
389 case MD_FILE: /* file */
390 case MD_APPEND: /* append */
391 dest = get_pool_memory(PM_MESSAGE);
392 /* Pick up a single destination */
393 token = lex_get_token(lc, T_NAME); /* scan destination */
394 pm_strcpy(dest, lc->str);
395 dest_len = lc->str_len;
396 token = lex_get_token(lc, T_SKIP_EOL);
397 Dmsg1(900, "store_msgs dest=%s:\n", NPRT(dest));
398 if (token != T_EQUALS) {
399 scan_err1(lc, _("expected an =, got: %s"), lc->str);
400 return;
401 }
402 scan_types(lc, (MSGS *)(item->value), item->code, dest, NULL);
403 free_pool_memory(dest);
404 Dmsg0(900, "done with dest codes\n");
405 break;
406
407 default:
408 scan_err1(lc, _("Unknown item code: %d\n"), item->code);
409 return;
410 }
411 }
412 scan_to_eol(lc);
413 set_bit(index, res_all.hdr.item_present);
414 Dmsg0(900, "Done store_msgs\n");
415 }
416
417 /*
418 * Scan for message types and add them to the message
419 * destination. The basic job here is to connect message types
420 * (WARNING, ERROR, FATAL, INFO, ...) with an appropriate
421 * destination (MAIL, FILE, OPERATOR, ...)
422 */
scan_types(LEX * lc,MSGS * msg,int dest_code,char * where,char * cmd)423 static void scan_types(LEX *lc, MSGS *msg, int dest_code, char *where, char *cmd)
424 {
425 int i;
426 bool found, is_not;
427 int msg_type = 0;
428 char *str;
429
430 for ( ;; ) {
431 lex_get_token(lc, T_NAME); /* expect at least one type */
432 found = false;
433 if (lc->str[0] == '!') {
434 is_not = true;
435 str = &lc->str[1];
436 } else {
437 is_not = false;
438 str = &lc->str[0];
439 }
440 for (i=0; msg_types[i].name; i++) {
441 if (strcasecmp(str, msg_types[i].name) == 0) {
442 msg_type = msg_types[i].token;
443 found = true;
444 break;
445 }
446 }
447 if (!found) {
448 scan_err1(lc, _("message type: %s not found"), str);
449 return;
450 }
451
452 if (msg_type == M_MAX+1) { /* all? */
453 for (i=2; i<=M_MAX; i++) { /* yes set all types except Debug and Saved */
454 add_msg_dest(msg, dest_code, msg_types[i].token, where, cmd);
455 }
456 } else if (is_not) {
457 rem_msg_dest(msg, dest_code, msg_type, where);
458 } else {
459 add_msg_dest(msg, dest_code, msg_type, where, cmd);
460 }
461 if (lc->ch != ',') {
462 break;
463 }
464 Dmsg0(900, "call lex_get_token() to eat comma\n");
465 lex_get_token(lc, T_ALL); /* eat comma */
466 }
467 Dmsg0(900, "Done scan_types()\n");
468 }
469
470
471 /*
472 * This routine is ONLY for resource names
473 * Store a name at specified address.
474 */
store_name(LEX * lc,RES_ITEM * item,int index,int pass)475 void store_name(LEX *lc, RES_ITEM *item, int index, int pass)
476 {
477 POOLMEM *msg = get_pool_memory(PM_EMSG);
478
479 lex_get_token(lc, T_NAME);
480 if (!is_name_valid(lc->str, &msg)) {
481 scan_err1(lc, "%s\n", msg);
482 return;
483 }
484 free_pool_memory(msg);
485 /* Store the name both pass 1 and pass 2 */
486 if (*(item->value)) {
487 scan_err5(lc, _("Attempt to redefine \"%s\" from \"%s\" to \"%s\" referenced on line %d : %s\n"),
488 item->name, *(item->value), lc->str, lc->line_no, lc->line);
489 return;
490 }
491 *(item->value) = bstrdup(lc->str);
492 scan_to_eol(lc);
493 set_bit(index, res_all.hdr.item_present);
494 }
495
496
497 /*
498 * Store a name string at specified address
499 * A name string is limited to MAX_RES_NAME_LENGTH
500 */
store_strname(LEX * lc,RES_ITEM * item,int index,int pass)501 void store_strname(LEX *lc, RES_ITEM *item, int index, int pass)
502 {
503 lex_get_token(lc, T_NAME);
504 /* Store the name */
505 if (pass == 1) {
506 if (*(item->value)) {
507 scan_err5(lc, _("Attempt to redefine \"%s\" from \"%s\" to \"%s\" referenced on line %d : %s\n"),
508 item->name, *(item->value), lc->str, lc->line_no, lc->line);
509 return;
510 }
511 *(item->value) = bstrdup(lc->str);
512 }
513 scan_to_eol(lc);
514 set_bit(index, res_all.hdr.item_present);
515 }
516
517 /* Store a string at specified address */
store_str(LEX * lc,RES_ITEM * item,int index,int pass)518 void store_str(LEX *lc, RES_ITEM *item, int index, int pass)
519 {
520 lex_get_token(lc, T_STRING);
521 if (pass == 1) {
522 if (*(item->value) && (item->flags & ITEM_ALLOW_DUPS)) {
523 free(*(item->value));
524 *(item->value) = NULL;
525 }
526 if (*(item->value)) {
527 scan_err5(lc, _("Attempt to redefine \"%s\" from \"%s\" to \"%s\" referenced on line %d : %s\n"),
528 item->name, *(item->value), lc->str, lc->line_no, lc->line);
529 return;
530 }
531 *(item->value) = bstrdup(lc->str);
532 }
533 scan_to_eol(lc);
534 set_bit(index, res_all.hdr.item_present);
535 }
536
537 /*
538 * Store a directory name at specified address. Note, we do
539 * shell expansion except if the string begins with a vertical
540 * bar (i.e. it will likely be passed to the shell later).
541 */
store_dir(LEX * lc,RES_ITEM * item,int index,int pass)542 void store_dir(LEX *lc, RES_ITEM *item, int index, int pass)
543 {
544 lex_get_token(lc, T_STRING);
545 if (pass == 1) {
546 if (lc->str[0] != '|') {
547 do_shell_expansion(lc->str, sizeof_pool_memory(lc->str));
548 }
549 #ifdef STANDARDIZED_DIRECTORY_USAGE
550 // TODO ASX we should store all directory without the ending slash to
551 // avoid the need of testing its presence
552 int len=strlen(lc->str);
553 if (len>0 && IsPathSeparator(lc->str[len-1])) {
554 lc->str[len-1]='\0';
555 }
556 #endif
557 if (*(item->value)) {
558 scan_err5(lc, _("Attempt to redefine \"%s\" from \"%s\" to \"%s\" referenced on line %d : %s\n"),
559 item->name, *(item->value), lc->str, lc->line_no, lc->line);
560 return;
561 }
562 *(item->value) = bstrdup(lc->str);
563 }
564 scan_to_eol(lc);
565 set_bit(index, res_all.hdr.item_present);
566 }
567
568
569 /* Store a password specified address in MD5 coding */
store_password(LEX * lc,RES_ITEM * item,int index,int pass)570 void store_password(LEX *lc, RES_ITEM *item, int index, int pass)
571 {
572 unsigned int i, j;
573 struct MD5Context md5c;
574 unsigned char digest[CRYPTO_DIGEST_MD5_SIZE];
575 char sig[100];
576
577 if (lc->options & LOPT_NO_MD5) {
578 store_str(lc, item, index, pass);
579
580 } else {
581 lex_get_token(lc, T_STRING);
582 if (pass == 1) {
583 MD5Init(&md5c);
584 MD5Update(&md5c, (unsigned char *) (lc->str), lc->str_len);
585 MD5Final(digest, &md5c);
586 for (i = j = 0; i < sizeof(digest); i++) {
587 sprintf(&sig[j], "%02x", digest[i]);
588 j += 2;
589 }
590 if (*(item->value)) {
591 scan_err5(lc, _("Attempt to redefine \"%s\" from \"%s\" to \"%s\" referenced on line %d : %s\n"),
592 item->name, *(item->value), lc->str, lc->line_no, lc->line);
593 return;
594 }
595 *(item->value) = bstrdup(sig);
596 }
597 scan_to_eol(lc);
598 set_bit(index, res_all.hdr.item_present);
599 }
600 }
601
602
603 /* Store a resource at specified address.
604 * If we are in pass 2, do a lookup of the
605 * resource.
606 */
store_res(LEX * lc,RES_ITEM * item,int index,int pass)607 void store_res(LEX *lc, RES_ITEM *item, int index, int pass)
608 {
609 RES *res;
610
611 lex_get_token(lc, T_NAME);
612 if (pass == 2) {
613 res = GetResWithName(item->code, lc->str);
614 if (res == NULL) {
615 scan_err3(lc, _("Could not find config Resource \"%s\" referenced on line %d : %s\n"),
616 lc->str, lc->line_no, lc->line);
617 return;
618 }
619 if (*(item->value)) {
620 scan_err3(lc, _("Attempt to redefine resource \"%s\" referenced on line %d : %s\n"),
621 item->name, lc->line_no, lc->line);
622 return;
623 }
624 *(item->value) = (char *)res;
625 }
626 scan_to_eol(lc);
627 set_bit(index, res_all.hdr.item_present);
628 }
629
630 /*
631 * Store a resource pointer in an alist. default_value indicates how many
632 * times this routine can be called -- i.e. how many alists
633 * there are.
634 * If we are in pass 2, do a lookup of the
635 * resource.
636 */
store_alist_res(LEX * lc,RES_ITEM * item,int index,int pass)637 void store_alist_res(LEX *lc, RES_ITEM *item, int index, int pass)
638 {
639 RES *res;
640 int count = item->default_value;
641 int i = 0;
642 alist *list;
643
644 if (pass == 2) {
645 if (count == 0) { /* always store in item->value */
646 i = 0;
647 if ((item->value)[i] == NULL) {
648 list = New(alist(10, not_owned_by_alist));
649 } else {
650 list = (alist *)(item->value)[i];
651 }
652 } else {
653 /* Find empty place to store this directive */
654 while ((item->value)[i] != NULL && i++ < count) { }
655 if (i >= count) {
656 scan_err4(lc, _("Too many %s directives. Max. is %d. line %d: %s\n"),
657 lc->str, count, lc->line_no, lc->line);
658 return;
659 }
660 list = New(alist(10, not_owned_by_alist));
661 }
662
663 for (;;) {
664 lex_get_token(lc, T_NAME); /* scan next item */
665 res = GetResWithName(item->code, lc->str);
666 if (res == NULL) {
667 scan_err3(lc, _("Could not find config Resource \"%s\" referenced on line %d : %s\n"),
668 lc->str, lc->line_no, lc->line);
669 return;
670 }
671 Dmsg5(900, "Append %p to alist %p size=%d i=%d %s\n",
672 res, list, list->size(), i, item->name);
673 list->append(res);
674 (item->value)[i] = (char *)list;
675 if (lc->ch != ',') { /* if no other item follows */
676 if (!lex_check_eol(lc)) {
677 /* found garbage at the end of the line */
678 scan_err3(lc, _("Found unexpected characters resource list in Directive \"%s\" at the end of line %d : %s\n"),
679 item->name, lc->line_no, lc->line);
680 }
681 break; /* get out */
682 }
683 lex_get_token(lc, T_ALL); /* eat comma */
684 }
685 }
686 scan_to_eol(lc);
687 set_bit(index, res_all.hdr.item_present);
688 }
689
690
691 /*
692 * Store a string in an alist.
693 */
store_alist_str(LEX * lc,RES_ITEM * item,int index,int pass)694 void store_alist_str(LEX *lc, RES_ITEM *item, int index, int pass)
695 {
696 alist *list;
697
698 if (pass == 2) {
699 if (*(item->value) == NULL) {
700 list = New(alist(10, owned_by_alist));
701 *(item->value) = (char *)list;
702 } else {
703 list = (alist *)(*(item->value));
704 }
705 for (;;) {
706 lex_get_token(lc, T_STRING); /* scan next item */
707 Dmsg4(900, "Append %s to alist 0x%p size=%d %s\n",
708 lc->str, list, list->size(), item->name);
709 list->append(bstrdup(lc->str));
710 if (lc->ch != ',') { /* if no other item follows */
711 if (!lex_check_eol(lc)) {
712 /* found garbage at the end of the line */
713 scan_err3(lc, _("Found unexpected characters in resource list in Directive \"%s\" at the end of line %d : %s\n"),
714 item->name, lc->line_no, lc->line);
715 }
716 break; /* get out */
717 }
718 lex_get_token(lc, T_ALL); /* eat comma */
719 }
720 }
721 scan_to_eol(lc);
722 set_bit(index, res_all.hdr.item_present);
723 }
724
725
726
727 /*
728 * Store default values for Resource from xxxDefs
729 * If we are in pass 2, do a lookup of the
730 * resource and store everything not explicitly set
731 * in main resource.
732 *
733 * Note, here item points to the main resource (e.g. Job, not
734 * the jobdefs, which we look up).
735 */
store_defs(LEX * lc,RES_ITEM * item,int index,int pass)736 void store_defs(LEX *lc, RES_ITEM *item, int index, int pass)
737 {
738 RES *res;
739
740 lex_get_token(lc, T_NAME);
741 if (pass == 2) {
742 Dmsg2(900, "Code=%d name=%s\n", item->code, lc->str);
743 res = GetResWithName(item->code, lc->str);
744 if (res == NULL) {
745 scan_err3(lc, _("Missing config Resource \"%s\" referenced on line %d : %s\n"),
746 lc->str, lc->line_no, lc->line);
747 return;
748 }
749 }
750 scan_to_eol(lc);
751 }
752
753
754
755 /* Store an integer at specified address */
store_int32(LEX * lc,RES_ITEM * item,int index,int pass)756 void store_int32(LEX *lc, RES_ITEM *item, int index, int pass)
757 {
758 lex_get_token(lc, T_INT32);
759 *(uint32_t *)(item->value) = lc->int32_val;
760 scan_to_eol(lc);
761 set_bit(index, res_all.hdr.item_present);
762 }
763
764 /* Store a positive integer at specified address */
store_pint32(LEX * lc,RES_ITEM * item,int index,int pass)765 void store_pint32(LEX *lc, RES_ITEM *item, int index, int pass)
766 {
767 lex_get_token(lc, T_PINT32);
768 *(uint32_t *)(item->value) = lc->pint32_val;
769 scan_to_eol(lc);
770 set_bit(index, res_all.hdr.item_present);
771 }
772
773
774 /* Store an 64 bit integer at specified address */
store_int64(LEX * lc,RES_ITEM * item,int index,int pass)775 void store_int64(LEX *lc, RES_ITEM *item, int index, int pass)
776 {
777 lex_get_token(lc, T_INT64);
778 *(int64_t *)(item->value) = lc->int64_val;
779 scan_to_eol(lc);
780 set_bit(index, res_all.hdr.item_present);
781 }
782
783 enum store_unit_type {
784 STORE_SIZE,
785 STORE_SPEED
786 } ;
787
788 /*
789 * This routine stores either a 32 or a 64 bit value (size32)
790 * and either a size (in bytes) or a speed (bytes per second).
791 */
store_int_unit(LEX * lc,RES_ITEM * item,int index,int pass,bool size32,enum store_unit_type type)792 static void store_int_unit(LEX *lc, RES_ITEM *item, int index, int pass,
793 bool size32, enum store_unit_type type)
794 {
795 int token;
796 uint64_t uvalue;
797 char bsize[500];
798
799 Dmsg0(900, "Enter store_unit\n");
800 token = lex_get_token(lc, T_SKIP_EOL);
801 errno = 0;
802 switch (token) {
803 case T_NUMBER:
804 case T_IDENTIFIER:
805 case T_UNQUOTED_STRING:
806 bstrncpy(bsize, lc->str, sizeof(bsize)); /* save first part */
807 /* if terminated by space, scan and get modifier */
808 while (lc->ch == ' ') {
809 token = lex_get_token(lc, T_ALL);
810 switch (token) {
811 case T_NUMBER:
812 case T_IDENTIFIER:
813 case T_UNQUOTED_STRING:
814 bstrncat(bsize, lc->str, sizeof(bsize));
815 break;
816 }
817 }
818 if (type == STORE_SIZE) {
819 if (!size_to_uint64(bsize, strlen(bsize), &uvalue)) {
820 scan_err1(lc, _("expected a size number, got: %s"), lc->str);
821 return;
822 }
823 } else {
824 if (!speed_to_uint64(bsize, strlen(bsize), &uvalue)) {
825 scan_err1(lc, _("expected a speed number, got: %s"), lc->str);
826 return;
827 }
828 }
829 if (size32) {
830 *(uint32_t *)(item->value) = (uint32_t)uvalue;
831 } else {
832 *(uint64_t *)(item->value) = uvalue;
833 }
834 break;
835 default:
836 scan_err2(lc, _("expected a %s, got: %s"),
837 (type == STORE_SIZE)?_("size"):_("speed"), lc->str);
838 return;
839 }
840 if (token != T_EOL) {
841 scan_to_eol(lc);
842 }
843 set_bit(index, res_all.hdr.item_present);
844 Dmsg0(900, "Leave store_unit\n");
845 }
846
847 /* Store a size in bytes */
store_size32(LEX * lc,RES_ITEM * item,int index,int pass)848 void store_size32(LEX *lc, RES_ITEM *item, int index, int pass)
849 {
850 store_int_unit(lc, item, index, pass, true /* 32 bit */, STORE_SIZE);
851 }
852
853 /* Store a size in bytes */
store_size64(LEX * lc,RES_ITEM * item,int index,int pass)854 void store_size64(LEX *lc, RES_ITEM *item, int index, int pass)
855 {
856 store_int_unit(lc, item, index, pass, false /* not 32 bit */, STORE_SIZE);
857 }
858
859 /* Store a speed in bytes/s */
store_speed(LEX * lc,RES_ITEM * item,int index,int pass)860 void store_speed(LEX *lc, RES_ITEM *item, int index, int pass)
861 {
862 store_int_unit(lc, item, index, pass, false /* 64 bit */, STORE_SPEED);
863 }
864
865 /* Store a time period in seconds */
store_time(LEX * lc,RES_ITEM * item,int index,int pass)866 void store_time(LEX *lc, RES_ITEM *item, int index, int pass)
867 {
868 int token;
869 utime_t utime;
870 char period[500];
871
872 token = lex_get_token(lc, T_SKIP_EOL);
873 errno = 0;
874 switch (token) {
875 case T_NUMBER:
876 case T_IDENTIFIER:
877 case T_UNQUOTED_STRING:
878 bstrncpy(period, lc->str, sizeof(period)); /* get first part */
879 /* if terminated by space, scan and get modifier */
880 while (lc->ch == ' ') {
881 token = lex_get_token(lc, T_ALL);
882 switch (token) {
883 case T_NUMBER:
884 case T_IDENTIFIER:
885 case T_UNQUOTED_STRING:
886 bstrncat(period, lc->str, sizeof(period));
887 break;
888 }
889 }
890 if (!duration_to_utime(period, &utime)) {
891 scan_err1(lc, _("expected a time period, got: %s"), period);
892 return;
893 }
894 *(utime_t *)(item->value) = utime;
895 break;
896 default:
897 scan_err1(lc, _("expected a time period, got: %s"), lc->str);
898 return;
899 }
900 if (token != T_EOL) {
901 scan_to_eol(lc);
902 }
903 set_bit(index, res_all.hdr.item_present);
904 }
905
906
907 /* Store a yes/no in a bit field */
store_bit(LEX * lc,RES_ITEM * item,int index,int pass)908 void store_bit(LEX *lc, RES_ITEM *item, int index, int pass)
909 {
910 lex_get_token(lc, T_NAME);
911 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
912 *(uint32_t *)(item->value) |= item->code;
913 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
914 *(uint32_t *)(item->value) &= ~(item->code);
915 } else {
916 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
917 return;
918 }
919 scan_to_eol(lc);
920 set_bit(index, res_all.hdr.item_present);
921 }
922
923 /* Store a bool in a bit field */
store_bool(LEX * lc,RES_ITEM * item,int index,int pass)924 void store_bool(LEX *lc, RES_ITEM *item, int index, int pass)
925 {
926 lex_get_token(lc, T_NAME);
927 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
928 *(bool *)(item->value) = true;
929 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
930 *(bool *)(item->value) = false;
931 } else {
932 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
933 return;
934 }
935 scan_to_eol(lc);
936 set_bit(index, res_all.hdr.item_present);
937 }
938
939
940 /*
941 * Store Tape Label Type (Bacula, ANSI, IBM)
942 *
943 */
store_label(LEX * lc,RES_ITEM * item,int index,int pass)944 void store_label(LEX *lc, RES_ITEM *item, int index, int pass)
945 {
946 int i;
947
948 lex_get_token(lc, T_NAME);
949 /* Store the label pass 2 so that type is defined */
950 for (i=0; tapelabels[i].name; i++) {
951 if (strcasecmp(lc->str, tapelabels[i].name) == 0) {
952 *(uint32_t *)(item->value) = tapelabels[i].token;
953 i = 0;
954 break;
955 }
956 }
957 if (i != 0) {
958 scan_err1(lc, _("Expected a Tape Label keyword, got: %s"), lc->str);
959 return;
960 }
961 scan_to_eol(lc);
962 set_bit(index, res_all.hdr.item_present);
963 }
964
965 /*
966 * Store Statistics Type (CSV, Graphite - only supported)
967 */
store_coll_type(LEX * lc,RES_ITEM * item,int index,int pass)968 void store_coll_type(LEX *lc, RES_ITEM *item, int index, int pass)
969 {
970 int i;
971
972 lex_get_token(lc, T_NAME);
973 /* Store the type both pass 1 and pass 2 */
974 for (i = 0; collectortypes[i].type_name; i++) {
975 if (strcasecmp(lc->str, collectortypes[i].type_name) == 0) {
976 *(int32_t *)(item->value) = collectortypes[i].coll_type;
977 i = 0;
978 break;
979 }
980 }
981 if (i != 0) {
982 scan_err1(lc, _("Expected a Statistics backend type keyword, got: %s"), lc->str);
983 }
984 scan_to_eol(lc);
985 set_bit(index, res_all.hdr.item_present);
986 }
987
988 /* Parser state */
989 enum parse_state {
990 p_none,
991 p_resource
992 };
993
init(const char * cf,LEX_ERROR_HANDLER * scan_error,int32_t err_type,void * vres_all,int32_t res_all_size,int32_t r_first,int32_t r_last,RES_TABLE * resources,RES_HEAD *** res_head)994 void CONFIG::init(
995 const char *cf,
996 LEX_ERROR_HANDLER *scan_error,
997 int32_t err_type,
998 void *vres_all,
999 int32_t res_all_size,
1000 int32_t r_first,
1001 int32_t r_last,
1002 RES_TABLE *resources,
1003 RES_HEAD ***res_head)
1004 {
1005 m_cf = cf;
1006 m_scan_error = scan_error;
1007 m_err_type = err_type;
1008 m_res_all = vres_all;
1009 m_res_all_size = res_all_size;
1010 m_r_first = r_first;
1011 m_r_last = r_last;
1012 m_resources = resources;
1013 init_res_head(res_head, r_first, r_last);
1014 m_res_head = *res_head;
1015 }
1016
1017 /*********************************************************************
1018 *
1019 * Parse configuration file
1020 *
1021 * Return 0 if reading failed, 1 otherwise
1022 * Note, the default behavior unless you have set an alternate
1023 * scan_error handler is to die on an error.
1024 */
parse_config()1025 bool CONFIG::parse_config()
1026 {
1027 LEX *lc = NULL;
1028 int token, i, pass;
1029 int res_type = 0;
1030 enum parse_state state = p_none;
1031 RES_ITEM *items = NULL;
1032 int level = 0;
1033 static bool first = true;
1034 int errstat;
1035 const char *cf = m_cf;
1036 LEX_ERROR_HANDLER *scan_error = m_scan_error;
1037 int err_type = m_err_type;
1038 //HPKT hpkt;
1039
1040 if (first && (errstat=rwl_init(&res_lock)) != 0) {
1041 berrno be;
1042 Jmsg1(NULL, M_ABORT, 0, _("Unable to initialize resource lock. ERR=%s\n"),
1043 be.bstrerror(errstat));
1044 }
1045 first = false;
1046
1047 char *full_path = (char *)alloca(MAX_PATH + 1);
1048
1049 if (!find_config_file(cf, full_path, MAX_PATH +1)) {
1050 Jmsg0(NULL, M_ABORT, 0, _("Config filename too long.\n"));
1051 }
1052 cf = full_path;
1053
1054 /* Make two passes. The first builds the name symbol table,
1055 * and the second picks up the items.
1056 */
1057 Dmsg0(900, "Enter parse_config()\n");
1058 for (pass=1; pass <= 2; pass++) {
1059 Dmsg1(900, "parse_config pass %d\n", pass);
1060 if ((lc = lex_open_file(lc, cf, scan_error)) == NULL) {
1061 berrno be;
1062 /* We must create a lex packet to print the error */
1063 lc = (LEX *)malloc(sizeof(LEX));
1064 memset(lc, 0, sizeof(LEX));
1065 lc->str = get_memory(5000);
1066 if (scan_error) {
1067 lc->scan_error = scan_error;
1068 } else {
1069 lex_set_default_error_handler(lc);
1070 }
1071 lex_set_error_handler_error_type(lc, err_type) ;
1072 pm_strcpy(lc->str, cf);
1073 lc->fname = lc->str;
1074 scan_err2(lc, _("Cannot open config file \"%s\": %s\n"),
1075 lc->str, be.bstrerror());
1076 free_pool_memory(lc->str);
1077 free(lc);
1078 return 0;
1079 }
1080 if (!m_encode_pass) {
1081 lex_store_clear_passwords(lc);
1082 }
1083 lex_set_error_handler_error_type(lc, err_type) ;
1084 while ((token=lex_get_token(lc, T_ALL)) != T_EOF) {
1085 Dmsg3(900, "parse state=%d pass=%d got token=%s\n", state, pass,
1086 lex_tok_to_str(token));
1087 switch (state) {
1088 case p_none:
1089 if (token == T_EOL) {
1090 break;
1091 } else if (token == T_UTF8_BOM) {
1092 /* We can assume the file is UTF-8 as we have seen a UTF-8 BOM */
1093 break;
1094 } else if (token == T_UTF16_BOM) {
1095 scan_err0(lc, _("Currently we cannot handle UTF-16 source files. "
1096 "Please convert the conf file to UTF-8\n"));
1097 goto bail_out;
1098 } else if (token != T_IDENTIFIER) {
1099 scan_err1(lc, _("Expected a Resource name identifier, got: %s"), lc->str);
1100 goto bail_out;
1101 }
1102 for (i=0; resources[i].name; i++) {
1103 if (strcasecmp(resources[i].name, lc->str) == 0) {
1104 items = resources[i].items;
1105 if (!items) {
1106 break;
1107 }
1108 state = p_resource;
1109 res_type = resources[i].rcode;
1110 init_resource0(this, res_type, items, pass);
1111 break;
1112 }
1113 }
1114 if (state == p_none) {
1115 scan_err1(lc, _("expected resource name, got: %s"), lc->str);
1116 goto bail_out;
1117 }
1118 break;
1119 case p_resource:
1120 switch (token) {
1121 case T_BOB:
1122 level++;
1123 break;
1124 case T_IDENTIFIER:
1125 if (level != 1) {
1126 scan_err1(lc, _("not in resource definition: %s"), lc->str);
1127 goto bail_out;
1128 }
1129 for (i=0; items[i].name; i++) {
1130 //hpkt.pass = pass;
1131 //hpkt.ritem = &items[i];
1132 //hpkt.edbuf = NULL;
1133 //hpkt.index = i;
1134 //hpkt.lc = lc;
1135 //hpkt.hfunc = HF_STORE;
1136 if (strcasecmp(items[i].name, lc->str) == 0) {
1137 /* If the ITEM_NO_EQUALS flag is set we do NOT
1138 * scan for = after the keyword */
1139 if (!(items[i].flags & ITEM_NO_EQUALS)) {
1140 token = lex_get_token(lc, T_SKIP_EOL);
1141 Dmsg1 (900, "in T_IDENT got token=%s\n", lex_tok_to_str(token));
1142 if (token != T_EQUALS) {
1143 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1144 goto bail_out;
1145 }
1146 }
1147 Dmsg1(800, "calling handler for %s\n", items[i].name);
1148 /* Call item handler */
1149 items[i].handler(lc, &items[i], i, pass);
1150 i = -1;
1151 break;
1152 }
1153 }
1154 if (i >= 0) {
1155 Dmsg2(900, "level=%d id=%s\n", level, lc->str);
1156 Dmsg1(900, "Keyword = %s\n", lc->str);
1157 scan_err1(lc, _("Keyword \"%s\" not permitted in this resource.\n"
1158 "Perhaps you left the trailing brace off of the previous resource."), lc->str);
1159 goto bail_out;
1160 }
1161 break;
1162
1163 case T_EOB:
1164 level--;
1165 state = p_none;
1166 Dmsg0(900, "T_EOB => define new resource\n");
1167 if (res_all.hdr.name == NULL) {
1168 scan_err0(lc, _("Name not specified for resource"));
1169 goto bail_out;
1170 }
1171 if (!save_resource(this, res_type, items, pass)) { /* save resource */
1172 scan_err1(lc, "%s", m_errmsg);
1173 goto bail_out;
1174 }
1175 break;
1176
1177 case T_EOL:
1178 break;
1179
1180 default:
1181 scan_err2(lc, _("unexpected token %d %s in resource definition"),
1182 token, lex_tok_to_str(token));
1183 goto bail_out;
1184 }
1185 break;
1186 default:
1187 scan_err1(lc, _("Unknown parser state %d\n"), state);
1188 goto bail_out;
1189 }
1190 }
1191 if (state != p_none) {
1192 scan_err0(lc, _("End of conf file reached with unclosed resource."));
1193 goto bail_out;
1194 }
1195 if (chk_dbglvl(900) && pass == 2) {
1196 int i;
1197 for (i=m_r_first; i<=m_r_last; i++) {
1198 dump_each_resource(i, prtmsg, NULL);
1199 }
1200 }
1201 lc = lex_close_file(lc);
1202 }
1203 Dmsg0(900, "Leave parse_config()\n");
1204 return 1;
1205 bail_out:
1206 if (lc) {
1207 lc = lex_close_file(lc);
1208 }
1209 return 0;
1210 }
1211
get_default_configdir()1212 const char *get_default_configdir()
1213 {
1214 return SYSCONFDIR;
1215 }
1216
1217 #ifdef xxx_not_used
1218 HRESULT hr;
1219 static char szConfigDir[MAX_PATH + 1] = { 0 };
1220 if (!p_SHGetFolderPath) {
1221 bstrncpy(szConfigDir, DEFAULT_CONFIGDIR, sizeof(szConfigDir));
1222 return szConfigDir;
1223 }
1224 if (szConfigDir[0] == '\0') {
1225 hr = p_SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szConfigDir);
1226 if (SUCCEEDED(hr)) {
1227 bstrncat(szConfigDir, "\\Bacula", sizeof(szConfigDir));
1228 } else {
1229 bstrncpy(szConfigDir, DEFAULT_CONFIGDIR, sizeof(szConfigDir));
1230 }
1231 }
1232 return szConfigDir;
1233 #endif
1234
1235
1236 /*
1237 * Returns false on error
1238 * true on OK, with full_path set to where config file should be
1239 */
1240 bool
find_config_file(const char * config_file,char * full_path,int max_path)1241 find_config_file(const char *config_file, char *full_path, int max_path)
1242 {
1243 int file_length = strlen(config_file) + 1;
1244
1245 /* If a full path specified, use it */
1246 if (first_path_separator(config_file) != NULL) {
1247 if (file_length > max_path) {
1248 return false;
1249 }
1250 bstrncpy(full_path, config_file, file_length);
1251 return true;
1252 }
1253
1254 /* config_file is default file name, now find default dir */
1255 const char *config_dir = get_default_configdir();
1256 int dir_length = strlen(config_dir);
1257
1258 if ((dir_length + 1 + file_length) > max_path) {
1259 return false;
1260 }
1261
1262 memcpy(full_path, config_dir, dir_length + 1);
1263
1264 if (!IsPathSeparator(full_path[dir_length - 1])) {
1265 full_path[dir_length++] = '/';
1266 }
1267
1268 memcpy(&full_path[dir_length], config_file, file_length);
1269
1270 return true;
1271 }
1272
1273 /*********************************************************************
1274 *
1275 * Free configuration resources
1276 *
1277 */
free_all_resources()1278 void CONFIG::free_all_resources()
1279 {
1280 RES *next, *res;
1281 if (m_res_head == NULL) {
1282 return;
1283 }
1284 /* Walk down chain of res_heads */
1285 for (int i=m_r_first; i<=m_r_last; i++) {
1286 if (m_res_head[i-m_r_first]) {
1287 next = m_res_head[i-m_r_first]->first;
1288 Dmsg2(500, "i=%d, next=%p\n", i, next);
1289 /* Walk down resource chain freeing them */
1290 for ( ; next; ) {
1291 res = next;
1292 next = res->res_next;
1293 free_resource(res, i);
1294 }
1295 free(m_res_head[i-m_r_first]->res_list);
1296 free(m_res_head[i-m_r_first]);
1297 m_res_head[i-m_r_first] = NULL;
1298 }
1299 }
1300 }
1301
CONFIG()1302 CONFIG::CONFIG()
1303 : m_cf(NULL),
1304 m_scan_error(NULL),
1305 m_err_type(0),
1306 m_res_all(NULL),
1307 m_res_all_size(0),
1308 m_encode_pass(true),
1309 m_r_first(0),
1310 m_r_last(0),
1311 m_resources(NULL),
1312 m_res_head(NULL)
1313 {
1314 m_errmsg = get_pool_memory(PM_EMSG);
1315 *m_errmsg = 0;
1316 }
1317
~CONFIG()1318 CONFIG::~CONFIG() {
1319 free_all_resources();
1320 free_pool_memory(m_errmsg);
1321 }
1322
encode_password(bool a)1323 void CONFIG::encode_password(bool a)
1324 {
1325 m_encode_pass = a;
1326 }
1327