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