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