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