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