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