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