]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
kes Fix %g in filename returned by SQL for browse tree reported by
[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 and included
11    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 && !res->res_job.RegexWhere) {
619            sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
620       }
621       if (res->res_job.RegexWhere) {
622            sendit(sock, _("  --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
623       }
624       if (res->res_job.RestoreBootstrap) {
625          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
626       }
627       if (res->res_job.WriteBootstrap) {
628          sendit(sock, _("  --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
629       }
630       if (res->res_job.storage) {
631          STORE *store;
632          foreach_alist(store, res->res_job.storage) {
633             sendit(sock, _("  --> "));
634             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
635          }
636       }
637       if (res->res_job.RunScripts) {
638         RUNSCRIPT *script;
639         foreach_alist(script, res->res_job.RunScripts) {
640            sendit(sock, _(" --> RunScript\n"));
641            sendit(sock, _("  --> Command=%s\n"), NPRT(script->command));
642            sendit(sock, _("  --> Target=%s\n"),  NPRT(script->target));
643            sendit(sock, _("  --> RunOnSuccess=%u\n"),  script->on_success);
644            sendit(sock, _("  --> RunOnFailure=%u\n"),  script->on_failure);
645            sendit(sock, _("  --> FailJobOnError=%u\n"),  script->fail_on_error);
646            sendit(sock, _("  --> RunWhen=%u\n"),  script->when);
647         }
648       }
649       if (res->res_job.pool) {
650          sendit(sock, _("  --> "));
651          dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
652       }
653       if (res->res_job.full_pool) {
654          sendit(sock, _("  --> "));
655          dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
656       }
657       if (res->res_job.inc_pool) {
658          sendit(sock, _("  --> "));
659          dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
660       }
661       if (res->res_job.diff_pool) {
662          sendit(sock, _("  --> "));
663          dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
664       }
665       if (res->res_job.verify_job) {
666          sendit(sock, _("  --> "));
667          dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
668       }
669       if (res->res_job.run_cmds) {
670          char *runcmd;
671          foreach_alist(runcmd, res->res_job.run_cmds) {
672             sendit(sock, _("  --> Run=%s\n"), runcmd);
673          }
674       }
675       if (res->res_job.selection_pattern) {
676          sendit(sock, _("  --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
677       }
678       if (res->res_job.messages) {
679          sendit(sock, _("  --> "));
680          dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
681       }
682       break;
683    case R_FILESET:
684    {
685       int i, j, k;
686       sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
687       for (i=0; i<res->res_fs.num_includes; i++) {
688          INCEXE *incexe = res->res_fs.include_items[i];
689          for (j=0; j<incexe->num_opts; j++) {
690             FOPTS *fo = incexe->opts_list[j];
691             sendit(sock, "      O %s\n", fo->opts);
692
693             bool enhanced_wild = false;
694             for (k=0; fo->opts[k]!='\0'; k++) {
695                if (fo->opts[k]=='W') {
696                   enhanced_wild = true;
697                   break;
698                }
699             }
700
701             for (k=0; k<fo->regex.size(); k++) {
702                sendit(sock, "      R %s\n", fo->regex.get(k));
703             }
704             for (k=0; k<fo->regexdir.size(); k++) {
705                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
706             }
707             for (k=0; k<fo->regexfile.size(); k++) {
708                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
709             }
710             for (k=0; k<fo->wild.size(); k++) {
711                sendit(sock, "      W %s\n", fo->wild.get(k));
712             }
713             for (k=0; k<fo->wilddir.size(); k++) {
714                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
715             }
716             for (k=0; k<fo->wildfile.size(); k++) {
717                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
718             }
719             for (k=0; k<fo->wildbase.size(); k++) {
720                sendit(sock, "      W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
721             }
722             for (k=0; k<fo->base.size(); k++) {
723                sendit(sock, "      B %s\n", fo->base.get(k));
724             }
725             for (k=0; k<fo->fstype.size(); k++) {
726                sendit(sock, "      X %s\n", fo->fstype.get(k));
727             }
728             for (k=0; k<fo->drivetype.size(); k++) {
729                sendit(sock, "      XD %s\n", fo->drivetype.get(k));
730             }
731             if (fo->reader) {
732                sendit(sock, "      D %s\n", fo->reader);
733             }
734             if (fo->writer) {
735                sendit(sock, "      T %s\n", fo->writer);
736             }
737             sendit(sock, "      N\n");
738          }
739          for (j=0; j<incexe->name_list.size(); j++) {
740             sendit(sock, "      I %s\n", incexe->name_list.get(j));
741          }
742          if (incexe->name_list.size()) {
743             sendit(sock, "      N\n");
744          }
745       }
746
747       for (i=0; i<res->res_fs.num_excludes; i++) {
748          INCEXE *incexe = res->res_fs.exclude_items[i];
749          for (j=0; j<incexe->name_list.size(); j++) {
750             sendit(sock, "      E %s\n", incexe->name_list.get(j));
751          }
752          if (incexe->name_list.size()) {
753             sendit(sock, "      N\n");
754          }
755       }
756       break;
757    }
758    case R_SCHEDULE:
759       if (res->res_sch.run) {
760          int i;
761          RUN *run = res->res_sch.run;
762          char buf[1000], num[30];
763          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
764          if (!run) {
765             break;
766          }
767 next_run:
768          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
769          bstrncpy(buf, _("      hour="), sizeof(buf));
770          for (i=0; i<24; i++) {
771             if (bit_is_set(i, run->hour)) {
772                bsnprintf(num, sizeof(num), "%d ", i);
773                bstrncat(buf, num, sizeof(buf));
774             }
775          }
776          bstrncat(buf, "\n", sizeof(buf));
777          sendit(sock, buf);
778          bstrncpy(buf, _("      mday="), sizeof(buf));
779          for (i=0; i<31; i++) {
780             if (bit_is_set(i, run->mday)) {
781                bsnprintf(num, sizeof(num), "%d ", i);
782                bstrncat(buf, num, sizeof(buf));
783             }
784          }
785          bstrncat(buf, "\n", sizeof(buf));
786          sendit(sock, buf);
787          bstrncpy(buf, _("      month="), sizeof(buf));
788          for (i=0; i<12; i++) {
789             if (bit_is_set(i, run->month)) {
790                bsnprintf(num, sizeof(num), "%d ", i);
791                bstrncat(buf, num, sizeof(buf));
792             }
793          }
794          bstrncat(buf, "\n", sizeof(buf));
795          sendit(sock, buf);
796          bstrncpy(buf, _("      wday="), sizeof(buf));
797          for (i=0; i<7; i++) {
798             if (bit_is_set(i, run->wday)) {
799                bsnprintf(num, sizeof(num), "%d ", i);
800                bstrncat(buf, num, sizeof(buf));
801             }
802          }
803          bstrncat(buf, "\n", sizeof(buf));
804          sendit(sock, buf);
805          bstrncpy(buf, _("      wom="), sizeof(buf));
806          for (i=0; i<5; i++) {
807             if (bit_is_set(i, run->wom)) {
808                bsnprintf(num, sizeof(num), "%d ", i);
809                bstrncat(buf, num, sizeof(buf));
810             }
811          }
812          bstrncat(buf, "\n", sizeof(buf));
813          sendit(sock, buf);
814          bstrncpy(buf, _("      woy="), sizeof(buf));
815          for (i=0; i<54; i++) {
816             if (bit_is_set(i, run->woy)) {
817                bsnprintf(num, sizeof(num), "%d ", i);
818                bstrncat(buf, num, sizeof(buf));
819             }
820          }
821          bstrncat(buf, "\n", sizeof(buf));
822          sendit(sock, buf);
823          sendit(sock, _("      mins=%d\n"), run->minute);
824          if (run->pool) {
825             sendit(sock, _("     --> "));
826             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
827          }
828          if (run->storage) {
829             sendit(sock, _("     --> "));
830             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
831          }
832          if (run->msgs) {
833             sendit(sock, _("     --> "));
834             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
835          }
836          /* If another Run record is chained in, go print it */
837          if (run->next) {
838             run = run->next;
839             goto next_run;
840          }
841       } else {
842          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
843       }
844       break;
845    case R_POOL:
846       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
847               res->res_pool.pool_type);
848       sendit(sock, _("      use_cat=%d use_once=%d cat_files=%d\n"),
849               res->res_pool.use_catalog, res->res_pool.use_volume_once,
850               res->res_pool.catalog_files);
851       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
852               res->res_pool.max_volumes, res->res_pool.AutoPrune,
853               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
854       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
855               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
856               res->res_pool.Recycle,
857               NPRT(res->res_pool.label_format));
858       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
859               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
860       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d\n"), 
861               res->res_pool.recycle_oldest_volume,
862               res->res_pool.purge_oldest_volume);
863       sendit(sock, _("      MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
864               res->res_pool.MaxVolJobs, 
865               res->res_pool.MaxVolFiles,
866               edit_uint64(res->res_pool.MaxVolFiles, ed1));
867       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
868               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
869               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
870               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
871       if (res->res_pool.NextPool) {
872          sendit(sock, _("      NextPool=%s\n"), res->res_pool.NextPool->name());
873       }
874       if (res->res_pool.RecyclePool) {
875          sendit(sock, _("      RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
876       }
877       if (res->res_pool.storage) {
878          STORE *store;
879          foreach_alist(store, res->res_pool.storage) {
880             sendit(sock, _("  --> "));
881             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
882          }
883       }
884       break;
885    case R_MSGS:
886       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
887       if (res->res_msgs.mail_cmd)
888          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
889       if (res->res_msgs.operator_cmd)
890          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
891       break;
892    default:
893       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
894       break;
895    }
896    if (recurse && res->res_dir.hdr.next) {
897       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
898    }
899 }
900
901 /*
902  * Free all the members of an INCEXE structure
903  */
904 static void free_incexe(INCEXE *incexe)
905 {
906    incexe->name_list.destroy();
907    for (int i=0; i<incexe->num_opts; i++) {
908       FOPTS *fopt = incexe->opts_list[i];
909       fopt->regex.destroy();
910       fopt->regexdir.destroy();
911       fopt->regexfile.destroy();
912       fopt->wild.destroy();
913       fopt->wilddir.destroy();
914       fopt->wildfile.destroy();
915       fopt->wildbase.destroy();
916       fopt->base.destroy();
917       fopt->fstype.destroy();
918       fopt->drivetype.destroy();
919       if (fopt->reader) {
920          free(fopt->reader);
921       }
922       if (fopt->writer) {
923          free(fopt->writer);
924       }
925       free(fopt);
926    }
927    if (incexe->opts_list) {
928       free(incexe->opts_list);
929    }
930    free(incexe);
931 }
932
933 /*
934  * Free memory of resource -- called when daemon terminates.
935  * NB, we don't need to worry about freeing any references
936  * to other resources as they will be freed when that
937  * resource chain is traversed.  Mainly we worry about freeing
938  * allocated strings (names).
939  */
940 void free_resource(RES *sres, int type)
941 {
942    int num;
943    RES *nres;                         /* next resource if linked */
944    URES *res = (URES *)sres;
945
946    if (res == NULL)
947       return;
948
949    /* common stuff -- free the resource name and description */
950    nres = (RES *)res->res_dir.hdr.next;
951    if (res->res_dir.hdr.name) {
952       free(res->res_dir.hdr.name);
953    }
954    if (res->res_dir.hdr.desc) {
955       free(res->res_dir.hdr.desc);
956    }
957
958    switch (type) {
959    case R_DIRECTOR:
960       if (res->res_dir.working_directory) {
961          free(res->res_dir.working_directory);
962       }
963       if (res->res_dir.scripts_directory) {
964          free((char *)res->res_dir.scripts_directory);
965       }
966       if (res->res_dir.pid_directory) {
967          free(res->res_dir.pid_directory);
968       }
969       if (res->res_dir.subsys_directory) {
970          free(res->res_dir.subsys_directory);
971       }
972       if (res->res_dir.password) {
973          free(res->res_dir.password);
974       }
975       if (res->res_dir.query_file) {
976          free(res->res_dir.query_file);
977       }
978       if (res->res_dir.DIRaddrs) {
979          free_addresses(res->res_dir.DIRaddrs);
980       }
981       if (res->res_dir.tls_ctx) { 
982          free_tls_context(res->res_dir.tls_ctx);
983       }
984       if (res->res_dir.tls_ca_certfile) {
985          free(res->res_dir.tls_ca_certfile);
986       }
987       if (res->res_dir.tls_ca_certdir) {
988          free(res->res_dir.tls_ca_certdir);
989       }
990       if (res->res_dir.tls_certfile) {
991          free(res->res_dir.tls_certfile);
992       }
993       if (res->res_dir.tls_keyfile) {
994          free(res->res_dir.tls_keyfile);
995       }
996       if (res->res_dir.tls_dhfile) {
997          free(res->res_dir.tls_dhfile);
998       }
999       if (res->res_dir.tls_allowed_cns) {
1000          delete res->res_dir.tls_allowed_cns;
1001       }
1002       break;
1003    case R_DEVICE:
1004    case R_COUNTER:
1005        break;
1006    case R_CONSOLE:
1007       if (res->res_con.password) {
1008          free(res->res_con.password);
1009       }
1010       if (res->res_con.tls_ctx) { 
1011          free_tls_context(res->res_con.tls_ctx);
1012       }
1013       if (res->res_con.tls_ca_certfile) {
1014          free(res->res_con.tls_ca_certfile);
1015       }
1016       if (res->res_con.tls_ca_certdir) {
1017          free(res->res_con.tls_ca_certdir);
1018       }
1019       if (res->res_con.tls_certfile) {
1020          free(res->res_con.tls_certfile);
1021       }
1022       if (res->res_con.tls_keyfile) {
1023          free(res->res_con.tls_keyfile);
1024       }
1025       if (res->res_con.tls_dhfile) {
1026          free(res->res_con.tls_dhfile);
1027       }
1028       if (res->res_con.tls_allowed_cns) {
1029          delete res->res_con.tls_allowed_cns;
1030       }
1031       for (int i=0; i<Num_ACL; i++) {
1032          if (res->res_con.ACL_lists[i]) {
1033             delete res->res_con.ACL_lists[i];
1034             res->res_con.ACL_lists[i] = NULL;
1035          }
1036       }
1037       break;
1038    case R_CLIENT:
1039       if (res->res_client.address) {
1040          free(res->res_client.address);
1041       }
1042       if (res->res_client.password) {
1043          free(res->res_client.password);
1044       }
1045       if (res->res_client.tls_ctx) { 
1046          free_tls_context(res->res_client.tls_ctx);
1047       }
1048       if (res->res_client.tls_ca_certfile) {
1049          free(res->res_client.tls_ca_certfile);
1050       }
1051       if (res->res_client.tls_ca_certdir) {
1052          free(res->res_client.tls_ca_certdir);
1053       }
1054       if (res->res_client.tls_certfile) {
1055          free(res->res_client.tls_certfile);
1056       }
1057       if (res->res_client.tls_keyfile) {
1058          free(res->res_client.tls_keyfile);
1059       }
1060       if (res->res_client.tls_allowed_cns) {
1061          delete res->res_client.tls_allowed_cns;
1062       }
1063       break;
1064    case R_STORAGE:
1065       if (res->res_store.address) {
1066          free(res->res_store.address);
1067       }
1068       if (res->res_store.password) {
1069          free(res->res_store.password);
1070       }
1071       if (res->res_store.media_type) {
1072          free(res->res_store.media_type);
1073       }
1074       if (res->res_store.device) {
1075          delete res->res_store.device;
1076       }
1077       if (res->res_store.tls_ctx) { 
1078          free_tls_context(res->res_store.tls_ctx);
1079       }
1080       if (res->res_store.tls_ca_certfile) {
1081          free(res->res_store.tls_ca_certfile);
1082       }
1083       if (res->res_store.tls_ca_certdir) {
1084          free(res->res_store.tls_ca_certdir);
1085       }
1086       if (res->res_store.tls_certfile) {
1087          free(res->res_store.tls_certfile);
1088       }
1089       if (res->res_store.tls_keyfile) {
1090          free(res->res_store.tls_keyfile);
1091       }
1092       break;
1093    case R_CATALOG:
1094       if (res->res_cat.db_address) {
1095          free(res->res_cat.db_address);
1096       }
1097       if (res->res_cat.db_socket) {
1098          free(res->res_cat.db_socket);
1099       }
1100       if (res->res_cat.db_user) {
1101          free(res->res_cat.db_user);
1102       }
1103       if (res->res_cat.db_name) {
1104          free(res->res_cat.db_name);
1105       }
1106       if (res->res_cat.db_password) {
1107          free(res->res_cat.db_password);
1108       }
1109       break;
1110    case R_FILESET:
1111       if ((num=res->res_fs.num_includes)) {
1112          while (--num >= 0) {
1113             free_incexe(res->res_fs.include_items[num]);
1114          }
1115          free(res->res_fs.include_items);
1116       }
1117       res->res_fs.num_includes = 0;
1118       if ((num=res->res_fs.num_excludes)) {
1119          while (--num >= 0) {
1120             free_incexe(res->res_fs.exclude_items[num]);
1121          }
1122          free(res->res_fs.exclude_items);
1123       }
1124       res->res_fs.num_excludes = 0;
1125       break;
1126    case R_POOL:
1127       if (res->res_pool.pool_type) {
1128          free(res->res_pool.pool_type);
1129       }
1130       if (res->res_pool.label_format) {
1131          free(res->res_pool.label_format);
1132       }
1133       if (res->res_pool.cleaning_prefix) {
1134          free(res->res_pool.cleaning_prefix);
1135       }
1136       if (res->res_pool.storage) {
1137          delete res->res_pool.storage;
1138       }
1139       break;
1140    case R_SCHEDULE:
1141       if (res->res_sch.run) {
1142          RUN *nrun, *next;
1143          nrun = res->res_sch.run;
1144          while (nrun) {
1145             next = nrun->next;
1146             free(nrun);
1147             nrun = next;
1148          }
1149       }
1150       break;
1151    case R_JOB:
1152    case R_JOBDEFS:
1153       if (res->res_job.RestoreWhere) {
1154          free(res->res_job.RestoreWhere);
1155       }
1156       if (res->res_job.RegexWhere) {
1157          free(res->res_job.RegexWhere);
1158       }
1159       if (res->res_job.strip_prefix) {
1160          free(res->res_job.strip_prefix);
1161       }
1162       if (res->res_job.add_prefix) {
1163          free(res->res_job.add_prefix);
1164       }
1165       if (res->res_job.add_suffix) {
1166          free(res->res_job.add_suffix);
1167       }
1168       if (res->res_job.RestoreBootstrap) {
1169          free(res->res_job.RestoreBootstrap);
1170       }
1171       if (res->res_job.WriteBootstrap) {
1172          free(res->res_job.WriteBootstrap);
1173       }
1174       if (res->res_job.selection_pattern) {
1175          free(res->res_job.selection_pattern);
1176       }
1177       if (res->res_job.run_cmds) {
1178          delete res->res_job.run_cmds;
1179       }
1180       if (res->res_job.storage) {
1181          delete res->res_job.storage;
1182       }
1183       if (res->res_job.RunScripts) {
1184          free_runscripts(res->res_job.RunScripts);
1185          delete res->res_job.RunScripts;
1186       }
1187       break;
1188    case R_MSGS:
1189       if (res->res_msgs.mail_cmd) {
1190          free(res->res_msgs.mail_cmd);
1191       }
1192       if (res->res_msgs.operator_cmd) {
1193          free(res->res_msgs.operator_cmd);
1194       }
1195       free_msgs_res((MSGS *)res);  /* free message resource */
1196       res = NULL;
1197       break;
1198    default:
1199       printf(_("Unknown resource type %d in free_resource.\n"), type);
1200    }
1201    /* Common stuff again -- free the resource, recurse to next one */
1202    if (res) {
1203       free(res);
1204    }
1205    if (nres) {
1206       free_resource(nres, type);
1207    }
1208 }
1209
1210 /*
1211  * Save the new resource by chaining it into the head list for
1212  * the resource. If this is pass 2, we update any resource
1213  * pointers because they may not have been defined until
1214  * later in pass 1.
1215  */
1216 void save_resource(int type, RES_ITEM *items, int pass)
1217 {
1218    URES *res;
1219    int rindex = type - r_first;
1220    int i, size = 0;
1221    bool error = false;
1222
1223    /* Check Job requirements after applying JobDefs */
1224    if (type != R_JOB && type != R_JOBDEFS) {
1225       /*
1226        * Ensure that all required items are present
1227        */
1228       for (i=0; items[i].name; i++) {
1229          if (items[i].flags & ITEM_REQUIRED) {
1230             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1231                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1232                     items[i].name, resources[rindex]);
1233             }
1234          }
1235          /* If this triggers, take a look at lib/parse_conf.h */
1236          if (i >= MAX_RES_ITEMS) {
1237             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1238          }
1239       }
1240    } else if (type == R_JOB) {
1241       /*
1242        * Ensure that the name item is present
1243        */
1244       if (items[0].flags & ITEM_REQUIRED) {
1245          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1246              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1247                    items[0].name, resources[rindex]);
1248          }
1249       }
1250    }
1251
1252    /*
1253     * During pass 2 in each "store" routine, we looked up pointers
1254     * to all the resources referrenced in the current resource, now we
1255     * must copy their addresses from the static record to the allocated
1256     * record.
1257     */
1258    if (pass == 2) {
1259       switch (type) {
1260       /* Resources not containing a resource */
1261       case R_CATALOG:
1262       case R_MSGS:
1263       case R_FILESET:
1264       case R_DEVICE:
1265          break;
1266
1267       /*
1268        * Resources containing another resource or alist. First
1269        *  look up the resource which contains another resource. It
1270        *  was written during pass 1.  Then stuff in the pointers to
1271        *  the resources it contains, which were inserted this pass.
1272        *  Finally, it will all be stored back.
1273        */
1274       case R_POOL:
1275          /* Find resource saved in pass 1 */
1276          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1277             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1278          }
1279          /* Explicitly copy resource pointers from this pass (res_all) */
1280          res->res_pool.NextPool = res_all.res_pool.NextPool;
1281          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1282          res->res_pool.storage    = res_all.res_pool.storage;
1283          break;
1284       case R_CONSOLE:
1285          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1286             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1287          }
1288          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1289          break;
1290       case R_DIRECTOR:
1291          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1292             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1293          }
1294          res->res_dir.messages = res_all.res_dir.messages;
1295          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1296          break;
1297       case R_STORAGE:
1298          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1299             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1300                   res_all.res_dir.hdr.name);
1301          }
1302          /* we must explicitly copy the device alist pointer */
1303          res->res_store.device   = res_all.res_store.device;
1304          break;
1305       case R_JOB:
1306       case R_JOBDEFS:
1307          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1308             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1309                   res_all.res_dir.hdr.name);
1310          }
1311          res->res_job.messages   = res_all.res_job.messages;
1312          res->res_job.schedule   = res_all.res_job.schedule;
1313          res->res_job.client     = res_all.res_job.client;
1314          res->res_job.fileset    = res_all.res_job.fileset;
1315          res->res_job.storage    = res_all.res_job.storage;
1316          res->res_job.pool       = res_all.res_job.pool;
1317          res->res_job.full_pool  = res_all.res_job.full_pool;
1318          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1319          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1320          res->res_job.verify_job = res_all.res_job.verify_job;
1321          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1322          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1323          res->res_job.RunScripts = res_all.res_job.RunScripts;
1324
1325          /* TODO: JobDefs where/regexwhere doesn't work well (but this
1326           * is not very useful) 
1327           * We have to set_bit(index, res_all.hdr.item_present);
1328           * or something like that
1329           */
1330
1331          /* we take RegexWhere before all other options */
1332          if (!res->res_job.RegexWhere 
1333              &&
1334              (res->res_job.strip_prefix ||
1335               res->res_job.add_suffix   ||
1336               res->res_job.add_prefix))
1337          {
1338             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1339                                                    res->res_job.add_prefix,
1340                                                    res->res_job.add_suffix);
1341             res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1342             bregexp_build_where(res->res_job.RegexWhere, len,
1343                                 res->res_job.strip_prefix,
1344                                 res->res_job.add_prefix,
1345                                 res->res_job.add_suffix);
1346             /* TODO: test bregexp */
1347          }
1348
1349          if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1350             free(res->res_job.RestoreWhere);
1351             res->res_job.RestoreWhere = NULL;
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->fail_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->fail_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  {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1786  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1787  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
1788  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
1789  {NULL, NULL, {0}, 0, 0, 0}
1790 };
1791
1792 /*
1793  * Store RunScript info
1794  *
1795  *  Note, when this routine is called, we are inside a Job
1796  *  resource.  We treat the RunScript like a sort of
1797  *  mini-resource within the Job resource.
1798  */
1799 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1800 {
1801    int token, i;
1802    alist **runscripts = (alist **)(item->value) ;
1803
1804    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1805
1806    res_runscript.reset_default();      /* setting on_success, on_failure, fail_on_error */
1807    
1808    token = lex_get_token(lc, T_SKIP_EOL);
1809    
1810    if (token != T_BOB) {
1811       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1812    }
1813    
1814    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1815       if (token == T_EOB) {
1816         break;
1817       }
1818       if (token != T_IDENTIFIER) {
1819         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1820       }
1821       for (i=0; runscript_items[i].name; i++) {
1822         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1823            token = lex_get_token(lc, T_SKIP_EOL);
1824            if (token != T_EQUALS) {
1825               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1826            }
1827            
1828            /* Call item handler */
1829            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1830            i = -1;
1831            break;
1832         }
1833       }
1834       
1835       if (i >=0) {
1836         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1837       }
1838    }
1839
1840    if (pass == 2) {
1841       if (res_runscript.command == NULL) {
1842          scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1843                    "command", "runscript");
1844       }
1845
1846       /* run on client by default */
1847       if (res_runscript.target == NULL) {
1848          res_runscript.set_target("%c");
1849       }
1850
1851       RUNSCRIPT *script = new_runscript();
1852       memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1853       
1854       if (*runscripts == NULL) {
1855         *runscripts = New(alist(10, not_owned_by_alist));
1856       }
1857       
1858       (*runscripts)->append(script);
1859       script->debug();
1860    }
1861
1862    scan_to_eol(lc);
1863    set_bit(index, res_all.hdr.item_present);
1864 }