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