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