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