]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
ebl use RegexWhere instead of where_use_regexp in job conf
[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    {"regexwhere",    store_str,   ITEM(res_job.RegexWhere), 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.RegexWhere) {
1158          free(res->res_job.RegexWhere);
1159       }
1160       if (res->res_job.strip_prefix) {
1161          free(res->res_job.strip_prefix);
1162       }
1163       if (res->res_job.add_prefix) {
1164          free(res->res_job.add_prefix);
1165       }
1166       if (res->res_job.add_suffix) {
1167          free(res->res_job.add_suffix);
1168       }
1169       if (res->res_job.RestoreBootstrap) {
1170          free(res->res_job.RestoreBootstrap);
1171       }
1172       if (res->res_job.WriteBootstrap) {
1173          free(res->res_job.WriteBootstrap);
1174       }
1175       if (res->res_job.selection_pattern) {
1176          free(res->res_job.selection_pattern);
1177       }
1178       if (res->res_job.run_cmds) {
1179          delete res->res_job.run_cmds;
1180       }
1181       if (res->res_job.storage) {
1182          delete res->res_job.storage;
1183       }
1184       if (res->res_job.RunScripts) {
1185          free_runscripts(res->res_job.RunScripts);
1186          delete res->res_job.RunScripts;
1187       }
1188       break;
1189    case R_MSGS:
1190       if (res->res_msgs.mail_cmd) {
1191          free(res->res_msgs.mail_cmd);
1192       }
1193       if (res->res_msgs.operator_cmd) {
1194          free(res->res_msgs.operator_cmd);
1195       }
1196       free_msgs_res((MSGS *)res);  /* free message resource */
1197       res = NULL;
1198       break;
1199    default:
1200       printf(_("Unknown resource type %d in free_resource.\n"), type);
1201    }
1202    /* Common stuff again -- free the resource, recurse to next one */
1203    if (res) {
1204       free(res);
1205    }
1206    if (nres) {
1207       free_resource(nres, type);
1208    }
1209 }
1210
1211 /*
1212  * Save the new resource by chaining it into the head list for
1213  * the resource. If this is pass 2, we update any resource
1214  * pointers because they may not have been defined until
1215  * later in pass 1.
1216  */
1217 void save_resource(int type, RES_ITEM *items, int pass)
1218 {
1219    URES *res;
1220    int rindex = type - r_first;
1221    int i, size = 0;
1222    bool error = false;
1223
1224    /* Check Job requirements after applying JobDefs */
1225    if (type != R_JOB && type != R_JOBDEFS) {
1226       /*
1227        * Ensure that all required items are present
1228        */
1229       for (i=0; items[i].name; i++) {
1230          if (items[i].flags & ITEM_REQUIRED) {
1231             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1232                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1233                     items[i].name, resources[rindex]);
1234             }
1235          }
1236          /* If this triggers, take a look at lib/parse_conf.h */
1237          if (i >= MAX_RES_ITEMS) {
1238             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1239          }
1240       }
1241    } else if (type == R_JOB) {
1242       /*
1243        * Ensure that the name item is present
1244        */
1245       if (items[0].flags & ITEM_REQUIRED) {
1246          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1247              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1248                    items[0].name, resources[rindex]);
1249          }
1250       }
1251    }
1252
1253    /*
1254     * During pass 2 in each "store" routine, we looked up pointers
1255     * to all the resources referrenced in the current resource, now we
1256     * must copy their addresses from the static record to the allocated
1257     * record.
1258     */
1259    if (pass == 2) {
1260       switch (type) {
1261       /* Resources not containing a resource */
1262       case R_CATALOG:
1263       case R_MSGS:
1264       case R_FILESET:
1265       case R_DEVICE:
1266          break;
1267
1268       /*
1269        * Resources containing another resource or alist. First
1270        *  look up the resource which contains another resource. It
1271        *  was written during pass 1.  Then stuff in the pointers to
1272        *  the resources it contains, which were inserted this pass.
1273        *  Finally, it will all be stored back.
1274        */
1275       case R_POOL:
1276          /* Find resource saved in pass 1 */
1277          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1278             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1279          }
1280          /* Explicitly copy resource pointers from this pass (res_all) */
1281          res->res_pool.NextPool = res_all.res_pool.NextPool;
1282          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1283          res->res_pool.storage    = res_all.res_pool.storage;
1284          break;
1285       case R_CONSOLE:
1286          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1287             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1288          }
1289          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1290          break;
1291       case R_DIRECTOR:
1292          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1293             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1294          }
1295          res->res_dir.messages = res_all.res_dir.messages;
1296          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1297          break;
1298       case R_STORAGE:
1299          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1300             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1301                   res_all.res_dir.hdr.name);
1302          }
1303          /* we must explicitly copy the device alist pointer */
1304          res->res_store.device   = res_all.res_store.device;
1305          break;
1306       case R_JOB:
1307       case R_JOBDEFS:
1308          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1309             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1310                   res_all.res_dir.hdr.name);
1311          }
1312          res->res_job.messages   = res_all.res_job.messages;
1313          res->res_job.schedule   = res_all.res_job.schedule;
1314          res->res_job.client     = res_all.res_job.client;
1315          res->res_job.fileset    = res_all.res_job.fileset;
1316          res->res_job.storage    = res_all.res_job.storage;
1317          res->res_job.pool       = res_all.res_job.pool;
1318          res->res_job.full_pool  = res_all.res_job.full_pool;
1319          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1320          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1321          res->res_job.verify_job = res_all.res_job.verify_job;
1322          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1323          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1324          res->res_job.RunScripts = res_all.res_job.RunScripts;
1325
1326          if (res->res_job.strip_prefix ||
1327              res->res_job.add_suffix   ||
1328              res->res_job.add_prefix)
1329          {
1330             if (res->res_job.RestoreWhere) {
1331                free(res->res_job.RestoreWhere);
1332             }
1333             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1334                                                    res->res_job.add_prefix,
1335                                                    res->res_job.add_suffix);
1336             res->res_job.RestoreWhere = (char *) bmalloc (len * sizeof(char));
1337             bregexp_build_where(res->res_job.RestoreWhere, len,
1338                                 res->res_job.strip_prefix,
1339                                 res->res_job.add_prefix,
1340                                 res->res_job.add_suffix);
1341             res->res_job.where_use_regexp = true;
1342
1343             /* TODO: test bregexp */
1344          }
1345
1346          if (res->res_job.RegexWhere) {
1347             if (res->res_job.RestoreWhere) {
1348                free(res->res_job.RestoreWhere);
1349             }
1350             res->res_job.RestoreWhere = bstrdup(res->res_job.RegexWhere);
1351             res->res_job.where_use_regexp = true;
1352          }
1353
1354          break;
1355       case R_COUNTER:
1356          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1357             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1358          }
1359          res->res_counter.Catalog = res_all.res_counter.Catalog;
1360          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1361          break;
1362
1363       case R_CLIENT:
1364          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1365             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1366          }
1367          res->res_client.catalog = res_all.res_client.catalog;
1368          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1369          break;
1370       case R_SCHEDULE:
1371          /*
1372           * Schedule is a bit different in that it contains a RUN record
1373           * chain which isn't a "named" resource. This chain was linked
1374           * in by run_conf.c during pass 2, so here we jam the pointer
1375           * into the Schedule resource.
1376           */
1377          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1378             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1379          }
1380          res->res_sch.run = res_all.res_sch.run;
1381          break;
1382       default:
1383          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1384          error = true;
1385          break;
1386       }
1387       /* Note, the resource name was already saved during pass 1,
1388        * so here, we can just release it.
1389        */
1390       if (res_all.res_dir.hdr.name) {
1391          free(res_all.res_dir.hdr.name);
1392          res_all.res_dir.hdr.name = NULL;
1393       }
1394       if (res_all.res_dir.hdr.desc) {
1395          free(res_all.res_dir.hdr.desc);
1396          res_all.res_dir.hdr.desc = NULL;
1397       }
1398       return;
1399    }
1400
1401    /*
1402     * The following code is only executed during pass 1
1403     */
1404    switch (type) {
1405    case R_DIRECTOR:
1406       size = sizeof(DIRRES);
1407       break;
1408    case R_CONSOLE:
1409       size = sizeof(CONRES);
1410       break;
1411    case R_CLIENT:
1412       size =sizeof(CLIENT);
1413       break;
1414    case R_STORAGE:
1415       size = sizeof(STORE);
1416       break;
1417    case R_CATALOG:
1418       size = sizeof(CAT);
1419       break;
1420    case R_JOB:
1421    case R_JOBDEFS:
1422       size = sizeof(JOB);
1423       break;
1424    case R_FILESET:
1425       size = sizeof(FILESET);
1426       break;
1427    case R_SCHEDULE:
1428       size = sizeof(SCHED);
1429       break;
1430    case R_POOL:
1431       size = sizeof(POOL);
1432       break;
1433    case R_MSGS:
1434       size = sizeof(MSGS);
1435       break;
1436    case R_COUNTER:
1437       size = sizeof(COUNTER);
1438       break;
1439    case R_DEVICE:
1440       error = true;
1441       break;
1442    default:
1443       printf(_("Unknown resource type %d in save_resource.\n"), type);
1444       error = true; 
1445       break;
1446    }
1447    /* Common */
1448    if (!error) {
1449       res = (URES *)malloc(size);
1450       memcpy(res, &res_all, size);
1451       if (!res_head[rindex]) {
1452          res_head[rindex] = (RES *)res; /* store first entry */
1453          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1454                res->res_dir.hdr.name, rindex);
1455       } else {
1456          RES *next, *last;
1457          if (res->res_dir.hdr.name == NULL) {
1458             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1459                   resources[rindex]);
1460          }   
1461          /* Add new res to end of chain */
1462          for (last=next=res_head[rindex]; next; next=next->next) {
1463             last = next;
1464             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1465                Emsg2(M_ERROR_TERM, 0,
1466                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1467                   resources[rindex].name, res->res_dir.hdr.name);
1468             }
1469          }
1470          last->next = (RES *)res;
1471          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1472                res->res_dir.hdr.name, rindex, pass);
1473       }
1474    }
1475 }
1476
1477 /*
1478  * Store Device. Note, the resource is created upon the
1479  *  first reference. The details of the resource are obtained
1480  *  later from the SD.
1481  */
1482 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1483 {
1484    int token;
1485    URES *res;
1486    int rindex = R_DEVICE - r_first;
1487    int size = sizeof(DEVICE);
1488    bool found = false;
1489
1490    if (pass == 1) {
1491       token = lex_get_token(lc, T_NAME);
1492       if (!res_head[rindex]) {
1493          res = (URES *)malloc(size);
1494          memset(res, 0, size);
1495          res->res_dev.hdr.name = bstrdup(lc->str);
1496          res_head[rindex] = (RES *)res; /* store first entry */
1497          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1498                res->res_dir.hdr.name, rindex);
1499       } else {
1500          RES *next;
1501          /* See if it is already defined */
1502          for (next=res_head[rindex]; next->next; next=next->next) {
1503             if (strcmp(next->name, lc->str) == 0) {
1504                found = true;
1505                break;
1506             }
1507          }
1508          if (!found) {
1509             res = (URES *)malloc(size);
1510             memset(res, 0, size);
1511             res->res_dev.hdr.name = bstrdup(lc->str);
1512             next->next = (RES *)res;
1513             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1514                res->res_dir.hdr.name, rindex, pass);
1515          }
1516       }
1517
1518       scan_to_eol(lc);
1519       set_bit(index, res_all.hdr.item_present);
1520    } else {
1521       store_alist_res(lc, item, index, pass);
1522    }
1523 }
1524
1525 /*
1526  * Store JobType (backup, verify, restore)
1527  *
1528  */
1529 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1530 {
1531    int token, i;
1532
1533    token = lex_get_token(lc, T_NAME);
1534    /* Store the type both pass 1 and pass 2 */
1535    for (i=0; migtypes[i].type_name; i++) {
1536       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1537          *(int *)(item->value) = migtypes[i].job_type;
1538          i = 0;
1539          break;
1540       }
1541    }
1542    if (i != 0) {
1543       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1544    }
1545    scan_to_eol(lc);
1546    set_bit(index, res_all.hdr.item_present);
1547 }
1548
1549
1550
1551 /*
1552  * Store JobType (backup, verify, restore)
1553  *
1554  */
1555 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1556 {
1557    int token, i;
1558
1559    token = lex_get_token(lc, T_NAME);
1560    /* Store the type both pass 1 and pass 2 */
1561    for (i=0; jobtypes[i].type_name; i++) {
1562       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1563          *(int *)(item->value) = jobtypes[i].job_type;
1564          i = 0;
1565          break;
1566       }
1567    }
1568    if (i != 0) {
1569       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1570    }
1571    scan_to_eol(lc);
1572    set_bit(index, res_all.hdr.item_present);
1573 }
1574
1575 /*
1576  * Store Job Level (Full, Incremental, ...)
1577  *
1578  */
1579 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1580 {
1581    int token, i;
1582
1583    token = lex_get_token(lc, T_NAME);
1584    /* Store the level pass 2 so that type is defined */
1585    for (i=0; joblevels[i].level_name; i++) {
1586       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1587          *(int *)(item->value) = joblevels[i].level;
1588          i = 0;
1589          break;
1590       }
1591    }
1592    if (i != 0) {
1593       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1594    }
1595    scan_to_eol(lc);
1596    set_bit(index, res_all.hdr.item_present);
1597 }
1598
1599
1600 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1601 {
1602    int token, i;
1603    token = lex_get_token(lc, T_NAME);
1604    /* Scan Replacement options */
1605    for (i=0; ReplaceOptions[i].name; i++) {
1606       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1607          *(int *)(item->value) = ReplaceOptions[i].token;
1608          i = 0;
1609          break;
1610       }
1611    }
1612    if (i != 0) {
1613       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1614    }
1615    scan_to_eol(lc);
1616    set_bit(index, res_all.hdr.item_present);
1617 }
1618
1619 /*
1620  * Store ACL (access control list)
1621  *
1622  */
1623 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1624 {
1625    int token;
1626
1627    for (;;) {
1628       token = lex_get_token(lc, T_STRING);
1629       if (pass == 1) {
1630          if (((alist **)item->value)[item->code] == NULL) {
1631             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1632             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1633          }
1634          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1635          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1636       }
1637       token = lex_get_token(lc, T_ALL);
1638       if (token == T_COMMA) {
1639          continue;                    /* get another ACL */
1640       }
1641       break;
1642    }
1643    set_bit(index, res_all.hdr.item_present);
1644 }
1645
1646 /* We build RunScripts items here */
1647 static RUNSCRIPT res_runscript;
1648
1649 /* Store a runscript->when in a bit field */
1650 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1651 {
1652    lex_get_token(lc, T_NAME);
1653
1654    if (strcasecmp(lc->str, "before") == 0) {
1655       *(int *)(item->value) = SCRIPT_Before ;
1656    } else if (strcasecmp(lc->str, "after") == 0) {
1657       *(int *)(item->value) = SCRIPT_After;
1658    } else if (strcasecmp(lc->str, "always") == 0) {
1659       *(int *)(item->value) = SCRIPT_Any;
1660    } else {
1661       scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1662    }
1663    scan_to_eol(lc);
1664 }
1665
1666 /* Store a runscript->target
1667  * 
1668  */
1669 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1670 {
1671    lex_get_token(lc, T_STRING);
1672
1673    if (pass == 2) {
1674       if (strcmp(lc->str, "%c") == 0) {
1675          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1676       } else if (strcasecmp(lc->str, "yes") == 0) {
1677          ((RUNSCRIPT*) item->value)->set_target("%c");
1678       } else if (strcasecmp(lc->str, "no") == 0) {
1679          ((RUNSCRIPT*) item->value)->set_target("");
1680       } else {
1681          RES *res = GetResWithName(R_CLIENT, lc->str);
1682          if (res == NULL) {
1683             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1684                       lc->str, lc->line_no, lc->line);
1685          }
1686
1687          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1688       }
1689    }
1690    scan_to_eol(lc);
1691 }
1692
1693 /* Store a runscript->command in a bit field
1694  * 
1695  */
1696 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1697 {
1698    lex_get_token(lc, T_STRING);
1699
1700    if (pass == 2) {
1701       ((RUNSCRIPT*) item->value)->set_command(lc->str);
1702    }
1703    scan_to_eol(lc);
1704 }
1705
1706 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1707 {
1708    lex_get_token(lc, T_STRING);
1709    alist **runscripts = (alist **)(item->value) ;
1710
1711    if (pass == 2) {
1712       RUNSCRIPT *script = new_runscript();
1713
1714       script->set_command(lc->str);
1715
1716       /* TODO: remove all script->old_proto with bacula 1.42 */
1717
1718       if (strcmp(item->name, "runbeforejob") == 0) {
1719          script->when = SCRIPT_Before;
1720          script->abort_on_error = true;
1721          script->set_target("");
1722
1723       } else if (strcmp(item->name, "runafterjob") == 0) {
1724          script->when = SCRIPT_After;
1725          script->on_success = true;
1726          script->on_failure = false;
1727          script->set_target("");
1728          
1729       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1730          script->old_proto = true;
1731          script->when = SCRIPT_After;
1732          script->set_target("%c");
1733          script->on_success = true;
1734          script->on_failure = false;
1735
1736       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1737          script->old_proto = true;
1738          script->when = SCRIPT_Before;
1739          script->set_target("%c");
1740          script->abort_on_error = true;
1741
1742       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1743          script->when = SCRIPT_After;
1744          script->on_failure = true;
1745          script->on_success = false;
1746          script->set_target("");
1747       }
1748
1749       if (*runscripts == NULL) {
1750         *runscripts = New(alist(10, not_owned_by_alist));
1751       }
1752       
1753       (*runscripts)->append(script);
1754       script->debug();
1755    }
1756
1757    scan_to_eol(lc);
1758 }
1759
1760 /* Store a bool in a bit field without modifing res_all.hdr 
1761  * We can also add an option to store_bool to skip res_all.hdr
1762  */
1763 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1764 {
1765    lex_get_token(lc, T_NAME);
1766    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1767       *(bool *)(item->value) = true;
1768    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1769       *(bool *)(item->value) = false;
1770    } else {
1771       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1772    }
1773    scan_to_eol(lc);
1774 }
1775
1776 /*
1777  * new RunScript items
1778  *   name             handler              value                             code flags default_value
1779  */
1780 static RES_ITEM runscript_items[] = {
1781  {"command",        store_runscript_cmd,  {(char **)&res_runscript},           0,  ITEM_REQUIRED, 0}, 
1782  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, 
1783  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
1784  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
1785  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.abort_on_error},0, 0, 0},
1786  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
1787  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
1788  {NULL, NULL, {0}, 0, 0, 0}
1789 };
1790
1791 /*
1792  * Store RunScript info
1793  *
1794  *  Note, when this routine is called, we are inside a Job
1795  *  resource.  We treat the RunScript like a sort of
1796  *  mini-resource within the Job resource.
1797  */
1798 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1799 {
1800    int token, i;
1801    alist **runscripts = (alist **)(item->value) ;
1802
1803    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1804
1805    res_runscript.reset_default();      /* setting on_success, on_failure, abort_on_error */
1806    
1807    token = lex_get_token(lc, T_SKIP_EOL);
1808    
1809    if (token != T_BOB) {
1810       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1811    }
1812    
1813    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1814       if (token == T_EOB) {
1815         break;
1816       }
1817       if (token != T_IDENTIFIER) {
1818         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1819       }
1820       for (i=0; runscript_items[i].name; i++) {
1821         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1822            token = lex_get_token(lc, T_SKIP_EOL);
1823            if (token != T_EQUALS) {
1824               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1825            }
1826            
1827            /* Call item handler */
1828            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1829            i = -1;
1830            break;
1831         }
1832       }
1833       
1834       if (i >=0) {
1835         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1836       }
1837    }
1838
1839    if (pass == 2) {
1840       if (res_runscript.command == NULL) {
1841          scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1842                    "command", "runscript");
1843       }
1844
1845       /* run on client by default */
1846       if (res_runscript.target == NULL) {
1847          res_runscript.set_target("%c");
1848       }
1849
1850       RUNSCRIPT *script = new_runscript();
1851       memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1852       
1853       if (*runscripts == NULL) {
1854         *runscripts = New(alist(10, not_owned_by_alist));
1855       }
1856       
1857       (*runscripts)->append(script);
1858       script->debug();
1859    }
1860
1861    scan_to_eol(lc);
1862    set_bit(index, res_all.hdr.item_present);
1863 }