]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Fix my errors in specifying the configuration defaults:
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2  *   Main configuration file parser for Bacula Directors,
3  *    some parts may be split into separate files such as
4  *    the schedule configuration (run_config.c).
5  *
6  *   Note, the configuration file parser consists of three parts
7  *
8  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
9  *
10  *   2. The generic config  scanner in lib/parse_config.c and
11  *      lib/parse_config.h.
12  *      These files contain the parser code, some utility
13  *      routines, and the common store routines (name, int,
14  *      string).
15  *
16  *   3. The daemon specific file, which contains the Resource
17  *      definitions as well as any specific store routines
18  *      for the resource records.
19  *
20  *     Kern Sibbald, January MM
21  *
22  *     Version $Id$
23  */
24 /*
25    Copyright (C) 2000-2005 Kern Sibbald
26
27    This program is free software; you can redistribute it and/or
28    modify it under the terms of the GNU General Public License
29    version 2 as amended with additional clauses defined in the
30    file LICENSE in the main source directory.
31
32    This program is distributed in the hope that it will be useful,
33    but WITHOUT ANY WARRANTY; without even the implied warranty of
34    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
35    the file LICENSE for additional details.
36
37  */
38
39 #include "bacula.h"
40 #include "dird.h"
41
42 /* Define the first and last resource ID record
43  * types. Note, these should be unique for each
44  * daemon though not a requirement.
45  */
46 int r_first = R_FIRST;
47 int r_last  = R_LAST;
48 static RES *sres_head[R_LAST - R_FIRST + 1];
49 RES **res_head = sres_head;
50
51 /* Imported subroutines */
52 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
53 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
54 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
55
56
57 /* Forward referenced subroutines */
58
59 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
60 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
61 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
62 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
63 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
64
65
66 /* We build the current resource here as we are
67  * scanning the resource configuration definition,
68  * then move it to allocated memory when the resource
69  * scan is complete.
70  */
71 URES res_all;
72 int  res_all_size = sizeof(res_all);
73
74
75 /* Definition of records permitted within each
76  * resource with the routine to process the record
77  * information.  NOTE! quoted names must be in lower case.
78  */
79 /*
80  *    Director Resource
81  *
82  *   name          handler     value                 code flags    default_value
83  */
84 static RES_ITEM dir_items[] = {
85    {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
86    {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
87    {"messages",    store_res,      ITEM(res_dir.messages), R_MSGS, 0, 0},
88    {"dirport",     store_addresses_port,    ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
89    {"diraddress",  store_addresses_address, ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
90    {"diraddresses",store_addresses,         ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
91    {"queryfile",   store_dir,      ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
92    {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
93    {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
94    {"piddirectory",store_dir,     ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
95    {"subsysdirectory", store_dir,  ITEM(res_dir.subsys_directory), 0, 0, 0},
96    {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
97    {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
98    {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
99    {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
100    {"tlsenable",            store_yesno,     ITEM(res_dir.tls_enable), 0, 0, 0},
101    {"tlsrequire",           store_yesno,     ITEM(res_dir.tls_require), 0, 0, 0},
102    {"tlsverifypeer",        store_yesno,     ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, 1},
103    {"tlscacertificatefile", store_dir,       ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
104    {"tlscacertificatedir",  store_dir,       ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
105    {"tlscertificate",       store_dir,       ITEM(res_dir.tls_certfile), 0, 0, 0},
106    {"tlskey",               store_dir,       ITEM(res_dir.tls_keyfile), 0, 0, 0},
107    {"tlsdhfile",            store_dir,       ITEM(res_dir.tls_dhfile), 0, 0, 0},
108    {"tlsallowedcn",         store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
109    {NULL, NULL, NULL, 0, 0, 0}
110 };
111
112 /*
113  *    Console Resource
114  *
115  *   name          handler     value                 code flags    default_value
116  */
117 static RES_ITEM con_items[] = {
118    {"name",        store_name,     ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
119    {"description", store_str,      ITEM(res_con.hdr.desc), 0, 0, 0},
120    {"password",    store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
121    {"jobacl",      store_acl,      ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
122    {"clientacl",   store_acl,      ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
123    {"storageacl",  store_acl,      ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
124    {"scheduleacl", store_acl,      ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
125    {"runacl",      store_acl,      ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
126    {"poolacl",     store_acl,      ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
127    {"commandacl",  store_acl,      ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
128    {"filesetacl",  store_acl,      ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
129    {"catalogacl",  store_acl,      ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
130    {"tlsenable",            store_yesno,     ITEM(res_con.tls_enable), 0, 0, 0},
131    {"tlsrequire",           store_yesno,     ITEM(res_con.tls_require), 0, 0, 0},
132    {"tlsverifypeer",        store_yesno,     ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, 1},
133    {"tlscacertificatefile", store_dir,       ITEM(res_con.tls_ca_certfile), 0, 0, 0},
134    {"tlscacertificatedir",  store_dir,       ITEM(res_con.tls_ca_certdir), 0, 0, 0},
135    {"tlscertificate",       store_dir,       ITEM(res_con.tls_certfile), 0, 0, 0},
136    {"tlskey",               store_dir,       ITEM(res_con.tls_keyfile), 0, 0, 0},
137    {"tlsdhfile",            store_dir,       ITEM(res_con.tls_dhfile), 0, 0, 0},
138    {"tlsallowedcn",         store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
139    {NULL, NULL, NULL, 0, 0, 0}
140 };
141
142
143 /*
144  *    Client or File daemon resource
145  *
146  *   name          handler     value                 code flags    default_value
147  */
148
149 static RES_ITEM cli_items[] = {
150    {"name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
151    {"description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
152    {"address",  store_str,        ITEM(res_client.address),  0, ITEM_REQUIRED, 0},
153    {"fdaddress",  store_str,      ITEM(res_client.address),  0, 0, 0},
154    {"fdport",   store_pint,       ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
155    {"password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
156    {"fdpassword", store_password,   ITEM(res_client.password), 0, 0, 0},
157    {"catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, ITEM_REQUIRED, 0},
158    {"fileretention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
159    {"jobretention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*180},
160    {"autoprune", store_yesno,     ITEM(res_client.AutoPrune), 1, ITEM_DEFAULT, 1},
161    {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
162    {"tlsenable",            store_yesno,     ITEM(res_client.tls_enable), 0, 0, 0},
163    {"tlsrequire",           store_yesno,     ITEM(res_client.tls_require), 0, 0, 0},
164    {"tlscacertificatefile", store_dir,       ITEM(res_client.tls_ca_certfile), 0, 0, 0},
165    {"tlscacertificatedir",  store_dir,       ITEM(res_client.tls_ca_certdir), 0, 0, 0},
166    {"tlscertificate",       store_dir,       ITEM(res_client.tls_certfile), 0, 0, 0},
167    {"tlskey",               store_dir,       ITEM(res_client.tls_keyfile), 0, 0, 0},
168    {NULL, NULL, NULL, 0, 0, 0}
169 };
170
171 /* Storage daemon resource
172  *
173  *   name          handler     value                 code flags    default_value
174  */
175 static RES_ITEM store_items[] = {
176    {"name",        store_name,     ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
177    {"description", store_str,      ITEM(res_store.hdr.desc),   0, 0, 0},
178    {"sdport",      store_pint,     ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
179    {"address",     store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
180    {"sdaddress",   store_str,      ITEM(res_store.address),    0, 0, 0},
181    {"password",    store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 0},
182    {"sdpassword",  store_password, ITEM(res_store.password),   0, 0, 0},
183    {"device",      store_device,   ITEM(res_store.device),     R_DEVICE, ITEM_REQUIRED, 0},
184    {"mediatype",   store_strname,  ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
185    {"autochanger", store_yesno,    ITEM(res_store.autochanger), 1, ITEM_DEFAULT, 0},
186    {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
187    {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
188    {"tlsenable",            store_yesno,     ITEM(res_store.tls_enable), 0, 0, 0},
189    {"tlsrequire",           store_yesno,     ITEM(res_store.tls_require), 0, 0, 0},
190    {"tlscacertificatefile", store_dir,       ITEM(res_store.tls_ca_certfile), 0, 0, 0},
191    {"tlscacertificatedir",  store_dir,       ITEM(res_store.tls_ca_certdir), 0, 0, 0},
192    {"tlscertificate",       store_dir,       ITEM(res_store.tls_certfile), 0, 0, 0},
193    {"tlskey",               store_dir,       ITEM(res_store.tls_keyfile), 0, 0, 0},
194    {NULL, NULL, NULL, 0, 0, 0}
195 };
196
197 /*
198  *    Catalog Resource Directives
199  *
200  *   name          handler     value                 code flags    default_value
201  */
202 static RES_ITEM cat_items[] = {
203    {"name",     store_name,     ITEM(res_cat.hdr.name),    0, ITEM_REQUIRED, 0},
204    {"description", store_str,   ITEM(res_cat.hdr.desc),    0, 0, 0},
205    {"address",  store_str,      ITEM(res_cat.db_address),  0, 0, 0},
206    {"dbaddress", store_str,     ITEM(res_cat.db_address),  0, 0, 0},
207    {"dbport",   store_pint,     ITEM(res_cat.db_port),      0, 0, 0},
208    /* keep this password as store_str for the moment */
209    {"password", store_str,      ITEM(res_cat.db_password), 0, 0, 0},
210    {"dbpassword", store_str,    ITEM(res_cat.db_password), 0, 0, 0},
211    {"user",     store_str,      ITEM(res_cat.db_user),     0, 0, 0},
212    {"dbname",   store_str,      ITEM(res_cat.db_name),     0, ITEM_REQUIRED, 0},
213    {"dbsocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0},
214    /* Turned off for the moment */
215    {"multipleconnections", store_yesno, ITEM(res_cat.mult_db_connections), 0, 0, 0},
216    {NULL, NULL, NULL, 0, 0, 0}
217 };
218
219 /*
220  *    Job Resource Directives
221  *
222  *   name          handler     value                 code flags    default_value
223  */
224 RES_ITEM job_items[] = {
225    {"name",      store_name,    ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
226    {"description", store_str,   ITEM(res_job.hdr.desc), 0, 0, 0},
227    {"type",      store_jobtype, ITEM(res_job.JobType),  0, ITEM_REQUIRED, 0},
228    {"level",     store_level,   ITEM(res_job.JobLevel),    0, 0, 0},
229    {"messages",  store_res,     ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
230    {"storage",   store_alist_res, ITEM(res_job.storage),  R_STORAGE, ITEM_REQUIRED, 0},
231    {"pool",      store_res,     ITEM(res_job.pool),     R_POOL, ITEM_REQUIRED, 0},
232    {"fullbackuppool",  store_res, ITEM(res_job.full_pool),   R_POOL, 0, 0},
233    {"incrementalbackuppool",  store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
234    {"differentialbackuppool", store_res, ITEM(res_job.dif_pool), R_POOL, 0, 0},
235    {"client",    store_res,     ITEM(res_job.client),   R_CLIENT, ITEM_REQUIRED, 0},
236    {"fileset",   store_res,     ITEM(res_job.fileset),  R_FILESET, ITEM_REQUIRED, 0},
237    {"schedule",  store_res,     ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
238    {"verifyjob", store_res,     ITEM(res_job.verify_job), R_JOB, 0, 0},
239    {"jobdefs",   store_res,     ITEM(res_job.jobdefs),    R_JOBDEFS, 0, 0},
240    {"run",       store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
241    {"where",    store_dir,      ITEM(res_job.RestoreWhere), 0, 0, 0},
242    {"bootstrap",store_dir,      ITEM(res_job.RestoreBootstrap), 0, 0, 0},
243    {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
244    {"replace",  store_replace,  ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
245    {"maxruntime",   store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
246    {"fullmaxwaittime",  store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
247    {"incrementalmaxwaittime",  store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
248    {"differentialmaxwaittime",  store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
249    {"maxwaittime",  store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
250    {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
251    {"jobretention", store_time, ITEM(res_job.JobRetention),  0, 0, 0},
252    {"prefixlinks", store_yesno, ITEM(res_job.PrefixLinks), 1, ITEM_DEFAULT, 0},
253    {"prunejobs",   store_yesno, ITEM(res_job.PruneJobs), 1, ITEM_DEFAULT, 0},
254    {"prunefiles",  store_yesno, ITEM(res_job.PruneFiles), 1, ITEM_DEFAULT, 0},
255    {"prunevolumes",store_yesno, ITEM(res_job.PruneVolumes), 1, ITEM_DEFAULT, 0},
256    {"spoolattributes",store_yesno, ITEM(res_job.SpoolAttributes), 1, ITEM_DEFAULT, 0},
257    {"spooldata",   store_yesno, ITEM(res_job.spool_data), 1, ITEM_DEFAULT, 0},
258    {"rerunfailedlevels",   store_yesno, ITEM(res_job.rerun_failed_levels), 1, ITEM_DEFAULT, 0},
259    {"prefermountedvolumes", store_yesno, ITEM(res_job.PreferMountedVolumes), 1, ITEM_DEFAULT, 1},
260    {"runbeforejob", store_str,  ITEM(res_job.RunBeforeJob), 0, 0, 0},
261    {"runafterjob",  store_str,  ITEM(res_job.RunAfterJob),  0, 0, 0},
262    {"runafterfailedjob",  store_str,  ITEM(res_job.RunAfterFailedJob),  0, 0, 0},
263    {"clientrunbeforejob", store_str,  ITEM(res_job.ClientRunBeforeJob), 0, 0, 0},
264    {"clientrunafterjob",  store_str,  ITEM(res_job.ClientRunAfterJob),  0, 0, 0},
265    {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
266    {"rescheduleonerror", store_yesno, ITEM(res_job.RescheduleOnError), 1, ITEM_DEFAULT, 0},
267    {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
268    {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
269    {"priority",   store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
270    {"writepartafterjob",   store_yesno, ITEM(res_job.write_part_after_job), 1, ITEM_DEFAULT, 0},
271    {NULL, NULL, NULL, 0, 0, 0}
272 };
273
274 /* FileSet resource
275  *
276  *   name          handler     value                 code flags    default_value
277  */
278 static RES_ITEM fs_items[] = {
279    {"name",        store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
280    {"description", store_str,  ITEM(res_fs.hdr.desc), 0, 0, 0},
281    {"include",     store_inc,  NULL,                  0, ITEM_NO_EQUALS, 0},
282    {"exclude",     store_inc,  NULL,                  1, ITEM_NO_EQUALS, 0},
283    {"ignorefilesetchanges", store_yesno, ITEM(res_fs.ignore_fs_changes), 1, ITEM_DEFAULT, 0},
284    {"enablevss",   store_yesno, ITEM(res_fs.enable_vss), 1, ITEM_DEFAULT, 0},
285    {NULL,          NULL,       NULL,                  0, 0, 0}
286 };
287
288 /* Schedule -- see run_conf.c */
289 /* Schedule
290  *
291  *   name          handler     value                 code flags    default_value
292  */
293 static RES_ITEM sch_items[] = {
294    {"name",     store_name,  ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
295    {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
296    {"run",      store_run,   ITEM(res_sch.run),      0, 0, 0},
297    {NULL, NULL, NULL, 0, 0, 0}
298 };
299
300 /* Pool resource
301  *
302  *   name             handler     value                        code flags default_value
303  */
304 static RES_ITEM pool_items[] = {
305    {"name",            store_name,    ITEM(res_pool.hdr.name),      0, ITEM_REQUIRED, 0},
306    {"description",     store_str,     ITEM(res_pool.hdr.desc),      0, 0,     0},
307    {"pooltype",        store_strname, ITEM(res_pool.pool_type),     0, ITEM_REQUIRED, 0},
308    {"labelformat",     store_strname, ITEM(res_pool.label_format),  0, 0,     0},
309    {"labeltype",       store_label,   ITEM(res_pool.LabelType),     0, 0,     0},     
310    {"cleaningprefix",  store_strname, ITEM(res_pool.cleaning_prefix), 0, 0,     0},
311    {"usecatalog",      store_yesno,   ITEM(res_pool.use_catalog),    1, ITEM_DEFAULT,  1},
312    {"usevolumeonce",   store_yesno,   ITEM(res_pool.use_volume_once),1, 0,        0},
313    {"purgeoldestvolume", store_yesno, ITEM(res_pool.purge_oldest_volume), 1, 0, 0},
314    {"recycleoldestvolume", store_yesno,  ITEM(res_pool.recycle_oldest_volume), 1, 0, 0},
315    {"recyclecurrentvolume", store_yesno, ITEM(res_pool.recycle_current_volume), 1, 0, 0},
316    {"maximumvolumes",  store_pint,    ITEM(res_pool.max_volumes),   0, 0,        0},
317    {"maximumvolumejobs", store_pint,  ITEM(res_pool.MaxVolJobs),    0, 0,       0},
318    {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles),   0, 0,       0},
319    {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes),   0, 0,       0},
320    {"acceptanyvolume", store_yesno,   ITEM(res_pool.accept_any_volume), 1, ITEM_DEFAULT,     1},
321    {"catalogfiles",    store_yesno,   ITEM(res_pool.catalog_files),  1, ITEM_DEFAULT,  1},
322    {"volumeretention", store_time,    ITEM(res_pool.VolRetention),   0, ITEM_DEFAULT, 60*60*24*365},
323    {"volumeuseduration", store_time,  ITEM(res_pool.VolUseDuration), 0, 0, 0},
324    {"autoprune",       store_yesno,   ITEM(res_pool.AutoPrune), 1, ITEM_DEFAULT, 1},
325    {"recycle",         store_yesno,   ITEM(res_pool.Recycle),     1, ITEM_DEFAULT, 1},
326    {NULL, NULL, NULL, 0, 0, 0}
327 };
328
329 /*
330  * Counter Resource
331  *   name             handler     value                        code flags default_value
332  */
333 static RES_ITEM counter_items[] = {
334    {"name",            store_name,    ITEM(res_counter.hdr.name),        0, ITEM_REQUIRED, 0},
335    {"description",     store_str,     ITEM(res_counter.hdr.desc),        0, 0,     0},
336    {"minimum",         store_int,     ITEM(res_counter.MinValue),        0, ITEM_DEFAULT, 0},
337    {"maximum",         store_pint,    ITEM(res_counter.MaxValue),        0, ITEM_DEFAULT, INT32_MAX},
338    {"wrapcounter",     store_res,     ITEM(res_counter.WrapCounter),     R_COUNTER, 0, 0},
339    {"catalog",         store_res,     ITEM(res_counter.Catalog),         R_CATALOG, 0, 0},
340    {NULL, NULL, NULL, 0, 0, 0}
341 };
342
343
344 /* Message resource */
345 extern RES_ITEM msgs_items[];
346
347 /*
348  * This is the master resource definition.
349  * It must have one item for each of the resources.
350  *
351  *  NOTE!!! keep it in the same order as the R_codes
352  *    or eliminate all resources[rindex].name
353  *
354  *  name             items        rcode        res_head
355  */
356 RES_TABLE resources[] = {
357    {"director",      dir_items,   R_DIRECTOR},
358    {"client",        cli_items,   R_CLIENT},
359    {"job",           job_items,   R_JOB},
360    {"storage",       store_items, R_STORAGE},
361    {"catalog",       cat_items,   R_CATALOG},
362    {"schedule",      sch_items,   R_SCHEDULE},
363    {"fileset",       fs_items,    R_FILESET},
364    {"pool",          pool_items,  R_POOL},
365    {"messages",      msgs_items,  R_MSGS},
366    {"counter",       counter_items, R_COUNTER},
367    {"console",       con_items,   R_CONSOLE},
368    {"jobdefs",       job_items,   R_JOBDEFS},
369    {"device",        NULL,        R_DEVICE},  /* info obtained from SD */
370    {NULL,            NULL,        0}
371 };
372
373
374 /* Keywords (RHS) permitted in Job Level records
375  *
376  *   level_name      level              job_type
377  */
378 struct s_jl joblevels[] = {
379    {"Full",          L_FULL,            JT_BACKUP},
380    {"Base",          L_BASE,            JT_BACKUP},
381    {"Incremental",   L_INCREMENTAL,     JT_BACKUP},
382    {"Differential",  L_DIFFERENTIAL,    JT_BACKUP},
383    {"Since",         L_SINCE,           JT_BACKUP},
384    {"Catalog",       L_VERIFY_CATALOG,  JT_VERIFY},
385    {"InitCatalog",   L_VERIFY_INIT,     JT_VERIFY},
386    {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG,   JT_VERIFY},
387    {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG,   JT_VERIFY},
388    {"Data",          L_VERIFY_DATA,     JT_VERIFY},
389    {" ",             L_NONE,            JT_ADMIN},
390    {" ",             L_NONE,            JT_RESTORE},
391    {NULL,            0,                          0}
392 };
393
394 /* Keywords (RHS) permitted in Job type records
395  *
396  *   type_name       job_type
397  */
398 struct s_jt jobtypes[] = {
399    {"backup",        JT_BACKUP},
400    {"admin",         JT_ADMIN},
401    {"verify",        JT_VERIFY},
402    {"restore",       JT_RESTORE},
403    {NULL,            0}
404 };
405
406
407 /* Options permitted in Restore replace= */
408 struct s_kw ReplaceOptions[] = {
409    {"always",         REPLACE_ALWAYS},
410    {"ifnewer",        REPLACE_IFNEWER},
411    {"ifolder",        REPLACE_IFOLDER},
412    {"never",          REPLACE_NEVER},
413    {NULL,               0}
414 };
415
416 const char *level_to_str(int level)
417 {
418    int i;
419    static char level_no[30];
420    const char *str = level_no;
421
422    bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level);    /* default if not found */
423    for (i=0; joblevels[i].level_name; i++) {
424       if (level == joblevels[i].level) {
425          str = joblevels[i].level_name;
426          break;
427       }
428    }
429    return str;
430 }
431
432 /* Dump contents of resource */
433 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
434 {
435    URES *res = (URES *)reshdr;
436    bool recurse = true;
437    char ed1[100], ed2[100];
438    DEVICE *dev;
439
440    if (res == NULL) {
441       sendit(sock, _("No %s resource defined\n"), res_to_str(type));
442       return;
443    }
444    if (type < 0) {                    /* no recursion */
445       type = - type;
446       recurse = false;
447    }
448    switch (type) {
449    case R_DIRECTOR:
450       sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
451          reshdr->name, res->res_dir.MaxConcurrentJobs,
452          edit_uint64(res->res_dir.FDConnectTimeout, ed1),
453          edit_uint64(res->res_dir.SDConnectTimeout, ed2));
454       if (res->res_dir.query_file) {
455          sendit(sock, _("   query_file=%s\n"), res->res_dir.query_file);
456       }
457       if (res->res_dir.messages) {
458          sendit(sock, _("  --> "));
459          dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
460       }
461       break;
462    case R_CONSOLE:
463 #ifdef HAVE_TLS
464       sendit(sock, _("Console: name=%s SSL=%d\n"),
465          res->res_con.hdr.name, res->res_con.tls_enable);
466 #else
467       sendit(sock, _("Console: name=%s SSL=%d\n"),
468          res->res_con.hdr.name, BNET_TLS_NONE);
469 #endif
470       break;
471    case R_COUNTER:
472       if (res->res_counter.WrapCounter) {
473          sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
474             res->res_counter.hdr.name, res->res_counter.MinValue,
475             res->res_counter.MaxValue, res->res_counter.CurrentValue,
476             res->res_counter.WrapCounter->hdr.name);
477       } else {
478          sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
479             res->res_counter.hdr.name, res->res_counter.MinValue,
480             res->res_counter.MaxValue);
481       }
482       if (res->res_counter.Catalog) {
483          sendit(sock, _("  --> "));
484          dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
485       }
486       break;
487
488    case R_CLIENT:
489       sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
490          res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
491          res->res_client.MaxConcurrentJobs);
492       sendit(sock, _("      JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
493          edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
494          edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
495          res->res_client.AutoPrune);
496       if (res->res_client.catalog) {
497          sendit(sock, _("  --> "));
498          dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
499       }
500       break;
501    case R_DEVICE:
502       dev = &res->res_dev;
503       char ed1[50];
504       sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
505 "      reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
506 "      poolid=%s volname=%s MediaType=%s\n"),
507          dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
508          dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
509          dev->offline, dev->autochanger,
510          edit_uint64(dev->PoolId, ed1),
511          dev->VolumeName, dev->MediaType);
512       break;
513    case R_STORAGE:
514       sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
515 "      DeviceName=%s MediaType=%s StorageId=%s\n"),
516          res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
517          res->res_store.MaxConcurrentJobs,
518          res->res_store.dev_name(),
519          res->res_store.media_type,
520          edit_int64(res->res_store.StorageId, ed1));
521       break;
522    case R_CATALOG:
523       sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
524 "      db_user=%s MutliDBConn=%d\n"),
525          res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
526          res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
527          res->res_cat.mult_db_connections);
528       break;
529    case R_JOB:
530    case R_JOBDEFS:
531       sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d MaxJobs=%u\n"),
532          type == R_JOB ? _("Job") : _("JobDefs"),
533          res->res_job.hdr.name, res->res_job.JobType,
534          level_to_str(res->res_job.JobLevel), res->res_job.Priority,
535          res->res_job.MaxConcurrentJobs);
536       sendit(sock, _("     Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
537           res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
538           edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
539           res->res_job.spool_data, res->res_job.write_part_after_job);
540       if (res->res_job.client) {
541          sendit(sock, _("  --> "));
542          dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
543       }
544       if (res->res_job.fileset) {
545          sendit(sock, _("  --> "));
546          dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
547       }
548       if (res->res_job.schedule) {
549          sendit(sock, _("  --> "));
550          dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
551       }
552       if (res->res_job.RestoreWhere) {
553          sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
554       }
555       if (res->res_job.RestoreBootstrap) {
556          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
557       }
558       if (res->res_job.RunBeforeJob) {
559          sendit(sock, _("  --> RunBefore=%s\n"), NPRT(res->res_job.RunBeforeJob));
560       }
561       if (res->res_job.RunAfterJob) {
562          sendit(sock, _("  --> RunAfter=%s\n"), NPRT(res->res_job.RunAfterJob));
563       }
564       if (res->res_job.RunAfterFailedJob) {
565          sendit(sock, _("  --> RunAfterFailed=%s\n"), NPRT(res->res_job.RunAfterFailedJob));
566       }
567       if (res->res_job.WriteBootstrap) {
568          sendit(sock, _("  --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
569       }
570       if (res->res_job.storage) {
571          STORE *store;
572          foreach_alist(store, res->res_job.storage) {
573             sendit(sock, _("  --> "));
574             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
575          }
576       }
577       if (res->res_job.pool) {
578          sendit(sock, _("  --> "));
579          dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
580       }
581       if (res->res_job.full_pool) {
582          sendit(sock, _("  --> "));
583          dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
584       }
585       if (res->res_job.inc_pool) {
586          sendit(sock, _("  --> "));
587          dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
588       }
589       if (res->res_job.dif_pool) {
590          sendit(sock, _("  --> "));
591          dump_resource(-R_POOL, (RES *)res->res_job.dif_pool, sendit, sock);
592       }
593       if (res->res_job.verify_job) {
594          sendit(sock, _("  --> "));
595          dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
596       }
597       if (res->res_job.run_cmds) {
598          char *runcmd;
599          foreach_alist(runcmd, res->res_job.run_cmds) {
600             sendit(sock, _("  --> Run=%s\n"), runcmd);
601          }
602       }
603       if (res->res_job.messages) {
604          sendit(sock, _("  --> "));
605          dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
606       }
607       break;
608    case R_FILESET:
609    {
610       int i, j, k;
611       sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
612       for (i=0; i<res->res_fs.num_includes; i++) {
613          INCEXE *incexe = res->res_fs.include_items[i];
614          for (j=0; j<incexe->num_opts; j++) {
615             FOPTS *fo = incexe->opts_list[j];
616             sendit(sock, "      O %s\n", fo->opts);
617             for (k=0; k<fo->regex.size(); k++) {
618                sendit(sock, "      R %s\n", fo->regex.get(k));
619             }
620             for (k=0; k<fo->regexdir.size(); k++) {
621                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
622             }
623             for (k=0; k<fo->regexfile.size(); k++) {
624                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
625             }
626             for (k=0; k<fo->wild.size(); k++) {
627                sendit(sock, "      W %s\n", fo->wild.get(k));
628             }
629             for (k=0; k<fo->wilddir.size(); k++) {
630                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
631             }
632             for (k=0; k<fo->wildfile.size(); k++) {
633                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
634             }
635             for (k=0; k<fo->base.size(); k++) {
636                sendit(sock, "      B %s\n", fo->base.get(k));
637             }
638             for (k=0; k<fo->fstype.size(); k++) {
639                sendit(sock, "      X %s\n", fo->fstype.get(k));
640             }
641             if (fo->reader) {
642                sendit(sock, "      D %s\n", fo->reader);
643             }
644             if (fo->writer) {
645                sendit(sock, "      T %s\n", fo->writer);
646             }
647             sendit(sock, "      N\n");
648          }
649          for (j=0; j<incexe->name_list.size(); j++) {
650             sendit(sock, "      I %s\n", incexe->name_list.get(j));
651          }
652          if (incexe->name_list.size()) {
653             sendit(sock, "      N\n");
654          }
655       }
656
657       for (i=0; i<res->res_fs.num_excludes; i++) {
658          INCEXE *incexe = res->res_fs.exclude_items[i];
659          for (j=0; j<incexe->name_list.size(); j++) {
660             sendit(sock, "      E %s\n", incexe->name_list.get(j));
661          }
662          if (incexe->name_list.size()) {
663             sendit(sock, "      N\n");
664          }
665       }
666       break;
667    }
668    case R_SCHEDULE:
669       if (res->res_sch.run) {
670          int i;
671          RUN *run = res->res_sch.run;
672          char buf[1000], num[30];
673          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
674          if (!run) {
675             break;
676          }
677 next_run:
678          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
679          bstrncpy(buf, _("      hour="), sizeof(buf));
680          for (i=0; i<24; i++) {
681             if (bit_is_set(i, run->hour)) {
682                bsnprintf(num, sizeof(num), "%d ", i);
683                bstrncat(buf, num, sizeof(buf));
684             }
685          }
686          bstrncat(buf, "\n", sizeof(buf));
687          sendit(sock, buf);
688          bstrncpy(buf, _("      mday="), sizeof(buf));
689          for (i=0; i<31; i++) {
690             if (bit_is_set(i, run->mday)) {
691                bsnprintf(num, sizeof(num), "%d ", i);
692                bstrncat(buf, num, sizeof(buf));
693             }
694          }
695          bstrncat(buf, "\n", sizeof(buf));
696          sendit(sock, buf);
697          bstrncpy(buf, _("      month="), sizeof(buf));
698          for (i=0; i<12; i++) {
699             if (bit_is_set(i, run->month)) {
700                bsnprintf(num, sizeof(num), "%d ", i);
701                bstrncat(buf, num, sizeof(buf));
702             }
703          }
704          bstrncat(buf, "\n", sizeof(buf));
705          sendit(sock, buf);
706          bstrncpy(buf, _("      wday="), sizeof(buf));
707          for (i=0; i<7; i++) {
708             if (bit_is_set(i, run->wday)) {
709                bsnprintf(num, sizeof(num), "%d ", i);
710                bstrncat(buf, num, sizeof(buf));
711             }
712          }
713          bstrncat(buf, "\n", sizeof(buf));
714          sendit(sock, buf);
715          bstrncpy(buf, _("      wom="), sizeof(buf));
716          for (i=0; i<5; i++) {
717             if (bit_is_set(i, run->wom)) {
718                bsnprintf(num, sizeof(num), "%d ", i);
719                bstrncat(buf, num, sizeof(buf));
720             }
721          }
722          bstrncat(buf, "\n", sizeof(buf));
723          sendit(sock, buf);
724          bstrncpy(buf, _("      woy="), sizeof(buf));
725          for (i=0; i<54; i++) {
726             if (bit_is_set(i, run->woy)) {
727                bsnprintf(num, sizeof(num), "%d ", i);
728                bstrncat(buf, num, sizeof(buf));
729             }
730          }
731          bstrncat(buf, "\n", sizeof(buf));
732          sendit(sock, buf);
733          sendit(sock, _("      mins=%d\n"), run->minute);
734          if (run->pool) {
735             sendit(sock, _("     --> "));
736             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
737          }
738          if (run->storage) {
739             sendit(sock, _("     --> "));
740             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
741          }
742          if (run->msgs) {
743             sendit(sock, _("     --> "));
744             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
745          }
746          /* If another Run record is chained in, go print it */
747          if (run->next) {
748             run = run->next;
749             goto next_run;
750          }
751       } else {
752          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
753       }
754       break;
755    case R_POOL:
756       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
757               res->res_pool.pool_type);
758       sendit(sock, _("      use_cat=%d use_once=%d acpt_any=%d cat_files=%d\n"),
759               res->res_pool.use_catalog, res->res_pool.use_volume_once,
760               res->res_pool.accept_any_volume, res->res_pool.catalog_files);
761       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
762               res->res_pool.max_volumes, res->res_pool.AutoPrune,
763               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
764       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
765               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
766               res->res_pool.Recycle,
767               NPRT(res->res_pool.label_format));
768       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
769               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
770       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d MaxVolJobs=%d MaxVolFiles=%d\n"),
771               res->res_pool.recycle_oldest_volume,
772               res->res_pool.purge_oldest_volume,
773               res->res_pool.MaxVolJobs, res->res_pool.MaxVolFiles);
774       break;
775    case R_MSGS:
776       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
777       if (res->res_msgs.mail_cmd)
778          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
779       if (res->res_msgs.operator_cmd)
780          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
781       break;
782    default:
783       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
784       break;
785    }
786    if (recurse && res->res_dir.hdr.next) {
787       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
788    }
789 }
790
791 /*
792  * Free all the members of an INCEXE structure
793  */
794 static void free_incexe(INCEXE *incexe)
795 {
796    incexe->name_list.destroy();
797    for (int i=0; i<incexe->num_opts; i++) {
798       FOPTS *fopt = incexe->opts_list[i];
799       fopt->regex.destroy();
800       fopt->regexdir.destroy();
801       fopt->regexfile.destroy();
802       fopt->wild.destroy();
803       fopt->wilddir.destroy();
804       fopt->wildfile.destroy();
805       fopt->base.destroy();
806       fopt->fstype.destroy();
807       if (fopt->reader) {
808          free(fopt->reader);
809       }
810       if (fopt->writer) {
811          free(fopt->writer);
812       }
813       free(fopt);
814    }
815    if (incexe->opts_list) {
816       free(incexe->opts_list);
817    }
818    free(incexe);
819 }
820
821 /*
822  * Free memory of resource -- called when daemon terminates.
823  * NB, we don't need to worry about freeing any references
824  * to other resources as they will be freed when that
825  * resource chain is traversed.  Mainly we worry about freeing
826  * allocated strings (names).
827  */
828 void free_resource(RES *sres, int type)
829 {
830    int num;
831    RES *nres;                         /* next resource if linked */
832    URES *res = (URES *)sres;
833
834    if (res == NULL)
835       return;
836
837    /* common stuff -- free the resource name and description */
838    nres = (RES *)res->res_dir.hdr.next;
839    if (res->res_dir.hdr.name) {
840       free(res->res_dir.hdr.name);
841    }
842    if (res->res_dir.hdr.desc) {
843       free(res->res_dir.hdr.desc);
844    }
845
846    switch (type) {
847    case R_DIRECTOR:
848       if (res->res_dir.working_directory) {
849          free(res->res_dir.working_directory);
850       }
851       if (res->res_dir.scripts_directory) {
852          free((char *)res->res_dir.scripts_directory);
853       }
854       if (res->res_dir.pid_directory) {
855          free(res->res_dir.pid_directory);
856       }
857       if (res->res_dir.subsys_directory) {
858          free(res->res_dir.subsys_directory);
859       }
860       if (res->res_dir.password) {
861          free(res->res_dir.password);
862       }
863       if (res->res_dir.query_file) {
864          free(res->res_dir.query_file);
865       }
866       if (res->res_dir.DIRaddrs) {
867          free_addresses(res->res_dir.DIRaddrs);
868       }
869       if (res->res_dir.tls_ctx) { 
870          free_tls_context(res->res_dir.tls_ctx);
871       }
872       if (res->res_dir.tls_ca_certfile) {
873          free(res->res_dir.tls_ca_certfile);
874       }
875       if (res->res_dir.tls_ca_certdir) {
876          free(res->res_dir.tls_ca_certdir);
877       }
878       if (res->res_dir.tls_certfile) {
879          free(res->res_dir.tls_certfile);
880       }
881       if (res->res_dir.tls_keyfile) {
882          free(res->res_dir.tls_keyfile);
883       }
884       if (res->res_dir.tls_dhfile) {
885          free(res->res_dir.tls_dhfile);
886       }
887       if (res->res_dir.tls_allowed_cns) {
888          delete res->res_dir.tls_allowed_cns;
889       }
890       break;
891    case R_DEVICE:
892    case R_COUNTER:
893        break;
894    case R_CONSOLE:
895       if (res->res_con.password) {
896          free(res->res_con.password);
897       }
898       if (res->res_con.tls_ctx) { 
899          free_tls_context(res->res_con.tls_ctx);
900       }
901       if (res->res_con.tls_ca_certfile) {
902          free(res->res_con.tls_ca_certfile);
903       }
904       if (res->res_con.tls_ca_certdir) {
905          free(res->res_con.tls_ca_certdir);
906       }
907       if (res->res_con.tls_certfile) {
908          free(res->res_con.tls_certfile);
909       }
910       if (res->res_con.tls_keyfile) {
911          free(res->res_con.tls_keyfile);
912       }
913       if (res->res_con.tls_dhfile) {
914          free(res->res_con.tls_dhfile);
915       }
916       if (res->res_con.tls_allowed_cns) {
917          delete res->res_con.tls_allowed_cns;
918       }
919       for (int i=0; i<Num_ACL; i++) {
920          if (res->res_con.ACL_lists[i]) {
921             delete res->res_con.ACL_lists[i];
922             res->res_con.ACL_lists[i] = NULL;
923          }
924       }
925       break;
926    case R_CLIENT:
927       if (res->res_client.address) {
928          free(res->res_client.address);
929       }
930       if (res->res_client.password) {
931          free(res->res_client.password);
932       }
933       if (res->res_client.tls_ctx) { 
934          free_tls_context(res->res_client.tls_ctx);
935       }
936       if (res->res_client.tls_ca_certfile) {
937          free(res->res_client.tls_ca_certfile);
938       }
939       if (res->res_client.tls_ca_certdir) {
940          free(res->res_client.tls_ca_certdir);
941       }
942       if (res->res_client.tls_certfile) {
943          free(res->res_client.tls_certfile);
944       }
945       if (res->res_client.tls_keyfile) {
946          free(res->res_client.tls_keyfile);
947       }
948       break;
949    case R_STORAGE:
950       if (res->res_store.address) {
951          free(res->res_store.address);
952       }
953       if (res->res_store.password) {
954          free(res->res_store.password);
955       }
956       if (res->res_store.media_type) {
957          free(res->res_store.media_type);
958       }
959       if (res->res_store.device) {
960          delete res->res_store.device;
961       }
962       if (res->res_store.tls_ctx) { 
963          free_tls_context(res->res_store.tls_ctx);
964       }
965       if (res->res_store.tls_ca_certfile) {
966          free(res->res_store.tls_ca_certfile);
967       }
968       if (res->res_store.tls_ca_certdir) {
969          free(res->res_store.tls_ca_certdir);
970       }
971       if (res->res_store.tls_certfile) {
972          free(res->res_store.tls_certfile);
973       }
974       if (res->res_store.tls_keyfile) {
975          free(res->res_store.tls_keyfile);
976       }
977       break;
978    case R_CATALOG:
979       if (res->res_cat.db_address) {
980          free(res->res_cat.db_address);
981       }
982       if (res->res_cat.db_socket) {
983          free(res->res_cat.db_socket);
984       }
985       if (res->res_cat.db_user) {
986          free(res->res_cat.db_user);
987       }
988       if (res->res_cat.db_name) {
989          free(res->res_cat.db_name);
990       }
991       if (res->res_cat.db_password) {
992          free(res->res_cat.db_password);
993       }
994       break;
995    case R_FILESET:
996       if ((num=res->res_fs.num_includes)) {
997          while (--num >= 0) {
998             free_incexe(res->res_fs.include_items[num]);
999          }
1000          free(res->res_fs.include_items);
1001       }
1002       res->res_fs.num_includes = 0;
1003       if ((num=res->res_fs.num_excludes)) {
1004          while (--num >= 0) {
1005             free_incexe(res->res_fs.exclude_items[num]);
1006          }
1007          free(res->res_fs.exclude_items);
1008       }
1009       res->res_fs.num_excludes = 0;
1010       break;
1011    case R_POOL:
1012       if (res->res_pool.pool_type) {
1013          free(res->res_pool.pool_type);
1014       }
1015       if (res->res_pool.label_format) {
1016          free(res->res_pool.label_format);
1017       }
1018       if (res->res_pool.cleaning_prefix) {
1019          free(res->res_pool.cleaning_prefix);
1020       }
1021       break;
1022    case R_SCHEDULE:
1023       if (res->res_sch.run) {
1024          RUN *nrun, *next;
1025          nrun = res->res_sch.run;
1026          while (nrun) {
1027             next = nrun->next;
1028             free(nrun);
1029             nrun = next;
1030          }
1031       }
1032       break;
1033    case R_JOB:
1034    case R_JOBDEFS:
1035       if (res->res_job.RestoreWhere) {
1036          free(res->res_job.RestoreWhere);
1037       }
1038       if (res->res_job.RestoreBootstrap) {
1039          free(res->res_job.RestoreBootstrap);
1040       }
1041       if (res->res_job.WriteBootstrap) {
1042          free(res->res_job.WriteBootstrap);
1043       }
1044       if (res->res_job.RunBeforeJob) {
1045          free(res->res_job.RunBeforeJob);
1046       }
1047       if (res->res_job.RunAfterJob) {
1048          free(res->res_job.RunAfterJob);
1049       }
1050       if (res->res_job.RunAfterFailedJob) {
1051          free(res->res_job.RunAfterFailedJob);
1052       }
1053       if (res->res_job.ClientRunBeforeJob) {
1054          free(res->res_job.ClientRunBeforeJob);
1055       }
1056       if (res->res_job.ClientRunAfterJob) {
1057          free(res->res_job.ClientRunAfterJob);
1058       }
1059       if (res->res_job.run_cmds) {
1060          delete res->res_job.run_cmds;
1061       }
1062       if (res->res_job.storage) {
1063          delete res->res_job.storage;
1064       }
1065       break;
1066    case R_MSGS:
1067       if (res->res_msgs.mail_cmd) {
1068          free(res->res_msgs.mail_cmd);
1069       }
1070       if (res->res_msgs.operator_cmd) {
1071          free(res->res_msgs.operator_cmd);
1072       }
1073       free_msgs_res((MSGS *)res);  /* free message resource */
1074       res = NULL;
1075       break;
1076    default:
1077       printf(_("Unknown resource type %d in free_resource.\n"), type);
1078    }
1079    /* Common stuff again -- free the resource, recurse to next one */
1080    if (res) {
1081       free(res);
1082    }
1083    if (nres) {
1084       free_resource(nres, type);
1085    }
1086 }
1087
1088 /*
1089  * Save the new resource by chaining it into the head list for
1090  * the resource. If this is pass 2, we update any resource
1091  * pointers because they may not have been defined until
1092  * later in pass 1.
1093  */
1094 void save_resource(int type, RES_ITEM *items, int pass)
1095 {
1096    URES *res;
1097    int rindex = type - r_first;
1098    int i, size = 0;
1099    bool error = false;
1100
1101    /* Check Job requirements after applying JobDefs */
1102    if (type != R_JOB && type != R_JOBDEFS) {
1103       /*
1104        * Ensure that all required items are present
1105        */
1106       for (i=0; items[i].name; i++) {
1107          if (items[i].flags & ITEM_REQUIRED) {
1108             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1109                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1110                     items[i].name, resources[rindex]);
1111             }
1112          }
1113          /* If this triggers, take a look at lib/parse_conf.h */
1114          if (i >= MAX_RES_ITEMS) {
1115             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1116          }
1117       }
1118    } else if (type == R_JOB) {
1119       /*
1120        * Ensure that the name item is present
1121        */
1122       if (items[0].flags & ITEM_REQUIRED) {
1123          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1124              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1125                    items[0].name, resources[rindex]);
1126          }
1127       }
1128    }
1129
1130    /*
1131     * During pass 2 in each "store" routine, we looked up pointers
1132     * to all the resources referrenced in the current resource, now we
1133     * must copy their addresses from the static record to the allocated
1134     * record.
1135     */
1136    if (pass == 2) {
1137       switch (type) {
1138       /* Resources not containing a resource */
1139       case R_CATALOG:
1140       case R_POOL:
1141       case R_MSGS:
1142       case R_FILESET:
1143       case R_DEVICE:
1144          break;
1145
1146       /* Resources containing another resource or alist */
1147       case R_CONSOLE:
1148          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1149             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1150          }
1151          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1152          break;
1153       case R_DIRECTOR:
1154          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1155             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1156          }
1157          res->res_dir.messages = res_all.res_dir.messages;
1158          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1159          break;
1160       case R_STORAGE:
1161          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1162             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1163                   res_all.res_dir.hdr.name);
1164          }
1165          /* we must explicitly copy the device alist pointer */
1166          res->res_store.device   = res_all.res_store.device;
1167          break;
1168       case R_JOB:
1169       case R_JOBDEFS:
1170          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1171             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1172                   res_all.res_dir.hdr.name);
1173          }
1174          res->res_job.messages   = res_all.res_job.messages;
1175          res->res_job.schedule   = res_all.res_job.schedule;
1176          res->res_job.client     = res_all.res_job.client;
1177          res->res_job.fileset    = res_all.res_job.fileset;
1178          res->res_job.storage    = res_all.res_job.storage;
1179          res->res_job.pool       = res_all.res_job.pool;
1180          res->res_job.full_pool  = res_all.res_job.full_pool;
1181          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1182          res->res_job.dif_pool   = res_all.res_job.dif_pool;
1183          res->res_job.verify_job = res_all.res_job.verify_job;
1184          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1185          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1186          break;
1187       case R_COUNTER:
1188          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1189             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1190          }
1191          res->res_counter.Catalog = res_all.res_counter.Catalog;
1192          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1193          break;
1194
1195       case R_CLIENT:
1196          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1197             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1198          }
1199          res->res_client.catalog = res_all.res_client.catalog;
1200          break;
1201       case R_SCHEDULE:
1202          /*
1203           * Schedule is a bit different in that it contains a RUN record
1204           * chain which isn't a "named" resource. This chain was linked
1205           * in by run_conf.c during pass 2, so here we jam the pointer
1206           * into the Schedule resource.
1207           */
1208          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1209             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1210          }
1211          res->res_sch.run = res_all.res_sch.run;
1212          break;
1213       default:
1214          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1215          error = true;
1216          break;
1217       }
1218       /* Note, the resource name was already saved during pass 1,
1219        * so here, we can just release it.
1220        */
1221       if (res_all.res_dir.hdr.name) {
1222          free(res_all.res_dir.hdr.name);
1223          res_all.res_dir.hdr.name = NULL;
1224       }
1225       if (res_all.res_dir.hdr.desc) {
1226          free(res_all.res_dir.hdr.desc);
1227          res_all.res_dir.hdr.desc = NULL;
1228       }
1229       return;
1230    }
1231
1232    /*
1233     * The following code is only executed during pass 1
1234     */
1235    switch (type) {
1236    case R_DIRECTOR:
1237       size = sizeof(DIRRES);
1238       break;
1239    case R_CONSOLE:
1240       size = sizeof(CONRES);
1241       break;
1242    case R_CLIENT:
1243       size =sizeof(CLIENT);
1244       break;
1245    case R_STORAGE:
1246       size = sizeof(STORE);
1247       break;
1248    case R_CATALOG:
1249       size = sizeof(CAT);
1250       break;
1251    case R_JOB:
1252    case R_JOBDEFS:
1253       size = sizeof(JOB);
1254       break;
1255    case R_FILESET:
1256       size = sizeof(FILESET);
1257       break;
1258    case R_SCHEDULE:
1259       size = sizeof(SCHED);
1260       break;
1261    case R_POOL:
1262       size = sizeof(POOL);
1263       break;
1264    case R_MSGS:
1265       size = sizeof(MSGS);
1266       break;
1267    case R_COUNTER:
1268       size = sizeof(COUNTER);
1269       break;
1270    case R_DEVICE:
1271       error = true;
1272       break;
1273    default:
1274       printf(_("Unknown resource type %d in save_resrouce.\n"), type);
1275       error = true; 
1276       break;
1277    }
1278    /* Common */
1279    if (!error) {
1280       res = (URES *)malloc(size);
1281       memcpy(res, &res_all, size);
1282       if (!res_head[rindex]) {
1283          res_head[rindex] = (RES *)res; /* store first entry */
1284          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1285                res->res_dir.hdr.name, rindex);
1286       } else {
1287          RES *next;
1288          if (res->res_dir.hdr.name == NULL) {
1289             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1290                   resources[rindex]);
1291          }   
1292          /* Add new res to end of chain */
1293          for (next=res_head[rindex]; next->next; next=next->next) {
1294             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1295                Emsg2(M_ERROR_TERM, 0,
1296                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1297                   resources[rindex].name, res->res_dir.hdr.name);
1298             }
1299          }
1300          next->next = (RES *)res;
1301          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1302                res->res_dir.hdr.name, rindex, pass);
1303       }
1304    }
1305 }
1306
1307 /*
1308  * Store Device. Note, the resource is created upon the
1309  *  first reference. The details of the resource are obtained
1310  *  later from the SD.
1311  */
1312 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1313 {
1314    int token;
1315    URES *res;
1316    int rindex = R_DEVICE - r_first;
1317    int size = sizeof(DEVICE);
1318    bool found = false;
1319
1320    if (pass == 1) {
1321       token = lex_get_token(lc, T_NAME);
1322       if (!res_head[rindex]) {
1323          res = (URES *)malloc(size);
1324          memset(res, 0, size);
1325          res->res_dev.hdr.name = bstrdup(lc->str);
1326          res_head[rindex] = (RES *)res; /* store first entry */
1327          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1328                res->res_dir.hdr.name, rindex);
1329       } else {
1330          RES *next;
1331          /* See if it is already defined */
1332          for (next=res_head[rindex]; next->next; next=next->next) {
1333             if (strcmp(next->name, lc->str) == 0) {
1334                found = true;
1335                break;
1336             }
1337          }
1338          if (!found) {
1339             res = (URES *)malloc(size);
1340             memset(res, 0, size);
1341             res->res_dev.hdr.name = bstrdup(lc->str);
1342             next->next = (RES *)res;
1343             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1344                res->res_dir.hdr.name, rindex, pass);
1345          }
1346       }
1347
1348       scan_to_eol(lc);
1349       set_bit(index, res_all.hdr.item_present);
1350    } else {
1351       store_alist_res(lc, item, index, pass);
1352    }
1353 }
1354
1355
1356 /*
1357  * Store JobType (backup, verify, restore)
1358  *
1359  */
1360 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1361 {
1362    int token, i;
1363
1364    token = lex_get_token(lc, T_NAME);
1365    /* Store the type both pass 1 and pass 2 */
1366    for (i=0; jobtypes[i].type_name; i++) {
1367       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1368          *(int *)(item->value) = jobtypes[i].job_type;
1369          i = 0;
1370          break;
1371       }
1372    }
1373    if (i != 0) {
1374       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1375    }
1376    scan_to_eol(lc);
1377    set_bit(index, res_all.hdr.item_present);
1378 }
1379
1380 /*
1381  * Store Job Level (Full, Incremental, ...)
1382  *
1383  */
1384 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1385 {
1386    int token, i;
1387
1388    token = lex_get_token(lc, T_NAME);
1389    /* Store the level pass 2 so that type is defined */
1390    for (i=0; joblevels[i].level_name; i++) {
1391       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1392          *(int *)(item->value) = joblevels[i].level;
1393          i = 0;
1394          break;
1395       }
1396    }
1397    if (i != 0) {
1398       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1399    }
1400    scan_to_eol(lc);
1401    set_bit(index, res_all.hdr.item_present);
1402 }
1403
1404
1405 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1406 {
1407    int token, i;
1408    token = lex_get_token(lc, T_NAME);
1409    /* Scan Replacement options */
1410    for (i=0; ReplaceOptions[i].name; i++) {
1411       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1412          *(int *)(item->value) = ReplaceOptions[i].token;
1413          i = 0;
1414          break;
1415       }
1416    }
1417    if (i != 0) {
1418       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1419    }
1420    scan_to_eol(lc);
1421    set_bit(index, res_all.hdr.item_present);
1422 }
1423
1424 /*
1425  * Store ACL (access control list)
1426  *
1427  */
1428 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1429 {
1430    int token;
1431
1432    for (;;) {
1433       token = lex_get_token(lc, T_NAME);
1434       if (pass == 1) {
1435          if (((alist **)item->value)[item->code] == NULL) {
1436             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1437             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1438          }
1439          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1440          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1441       }
1442       token = lex_get_token(lc, T_ALL);
1443       if (token == T_COMMA) {
1444          continue;                    /* get another ACL */
1445       }
1446       break;
1447    }
1448    set_bit(index, res_all.hdr.item_present);
1449 }