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