]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
c87d79d21e6c12a416dc18d37b22fbde01bcda25
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Main configuration file parser for Bacula Directors,
30  *    some parts may be split into separate files such as
31  *    the schedule configuration (run_config.c).
32  *
33  *   Note, the configuration file parser consists of three parts
34  *
35  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
36  *
37  *   2. The generic config  scanner in lib/parse_config.c and
38  *      lib/parse_config.h.
39  *      These files contain the parser code, some utility
40  *      routines, and the common store routines (name, int,
41  *      string).
42  *
43  *   3. The daemon specific file, which contains the Resource
44  *      definitions as well as any specific store routines
45  *      for the resource records.
46  *
47  *     Kern Sibbald, January MM
48  *
49  *     Version $Id$
50  */
51
52
53 #include "bacula.h"
54 #include "dird.h"
55
56 /* Define the first and last resource ID record
57  * types. Note, these should be unique for each
58  * daemon though not a requirement.
59  */
60 int r_first = R_FIRST;
61 int r_last  = R_LAST;
62 static RES *sres_head[R_LAST - R_FIRST + 1];
63 RES **res_head = sres_head;
64
65 /* Imported subroutines */
66 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
67 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
68 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
69
70
71 /* Forward referenced subroutines */
72
73 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
74 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
75 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
76 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
77 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
79 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
80 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
81 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
82 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
83
84 /* We build the current resource here as we are
85  * scanning the resource configuration definition,
86  * then move it to allocated memory when the resource
87  * scan is complete.
88  */
89 #if defined(_MSC_VER)
90 extern "C" { // work around visual compiler mangling variables
91     URES res_all;
92 }
93 #else
94 URES res_all;
95 #endif
96 int  res_all_size = sizeof(res_all);
97
98
99 /* Definition of records permitted within each
100  * resource with the routine to process the record
101  * information.  NOTE! quoted names must be in lower case.
102  */
103 /*
104  *    Director Resource
105  *
106  *   name          handler     value                 code flags    default_value
107  */
108 static RES_ITEM dir_items[] = {
109    {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
110    {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
111    {"messages",    store_res,      ITEM(res_dir.messages), R_MSGS, 0, 0},
112    {"dirport",     store_addresses_port,    ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
113    {"diraddress",  store_addresses_address, ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
114    {"diraddresses",store_addresses,         ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
115    {"queryfile",   store_dir,      ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
116    {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
117    {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
118    {"piddirectory",store_dir,     ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
119    {"subsysdirectory", store_dir,  ITEM(res_dir.subsys_directory), 0, 0, 0},
120    {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
121    {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
122    {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
123    {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
124    {"heartbeatinterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 0},
125    {"tlsenable",            store_bool,      ITEM(res_dir.tls_enable), 0, 0, 0},
126    {"tlsrequire",           store_bool,      ITEM(res_dir.tls_require), 0, 0, 0},
127    {"tlsverifypeer",        store_bool,      ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
128    {"tlscacertificatefile", store_dir,       ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
129    {"tlscacertificatedir",  store_dir,       ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
130    {"tlscertificate",       store_dir,       ITEM(res_dir.tls_certfile), 0, 0, 0},
131    {"tlskey",               store_dir,       ITEM(res_dir.tls_keyfile), 0, 0, 0},
132    {"tlsdhfile",            store_dir,       ITEM(res_dir.tls_dhfile), 0, 0, 0},
133    {"tlsallowedcn",         store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
134    {NULL, NULL, {0}, 0, 0, 0}
135 };
136
137 /*
138  *    Console Resource
139  *
140  *   name          handler     value                 code flags    default_value
141  */
142 static RES_ITEM con_items[] = {
143    {"name",        store_name,     ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
144    {"description", store_str,      ITEM(res_con.hdr.desc), 0, 0, 0},
145    {"password",    store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
146    {"jobacl",      store_acl,      ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
147    {"clientacl",   store_acl,      ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
148    {"storageacl",  store_acl,      ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
149    {"scheduleacl", store_acl,      ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
150    {"runacl",      store_acl,      ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
151    {"poolacl",     store_acl,      ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
152    {"commandacl",  store_acl,      ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
153    {"filesetacl",  store_acl,      ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
154    {"catalogacl",  store_acl,      ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
155    {"whereacl",    store_acl,      ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
156    {"tlsenable",            store_bool,      ITEM(res_con.tls_enable), 0, 0, 0},
157    {"tlsrequire",           store_bool,      ITEM(res_con.tls_require), 0, 0, 0},
158    {"tlsverifypeer",        store_bool,      ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
159    {"tlscacertificatefile", store_dir,       ITEM(res_con.tls_ca_certfile), 0, 0, 0},
160    {"tlscacertificatedir",  store_dir,       ITEM(res_con.tls_ca_certdir), 0, 0, 0},
161    {"tlscertificate",       store_dir,       ITEM(res_con.tls_certfile), 0, 0, 0},
162    {"tlskey",               store_dir,       ITEM(res_con.tls_keyfile), 0, 0, 0},
163    {"tlsdhfile",            store_dir,       ITEM(res_con.tls_dhfile), 0, 0, 0},
164    {"tlsallowedcn",         store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
165    {NULL, NULL, {0}, 0, 0, 0}
166 };
167
168
169 /*
170  *    Client or File daemon resource
171  *
172  *   name          handler     value                 code flags    default_value
173  */
174
175 static RES_ITEM cli_items[] = {
176    {"name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
177    {"description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
178    {"address",  store_str,        ITEM(res_client.address),  0, ITEM_REQUIRED, 0},
179    {"fdaddress",  store_str,      ITEM(res_client.address),  0, 0, 0},
180    {"fdport",   store_pint,       ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
181    {"password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
182    {"fdpassword", store_password,   ITEM(res_client.password), 0, 0, 0},
183    {"catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, ITEM_REQUIRED, 0},
184    {"fileretention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
185    {"jobretention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*180},
186    {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0},
187    {"autoprune", store_bool,      ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
188    {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
189    {"tlsenable",            store_bool,      ITEM(res_client.tls_enable), 0, 0, 0},
190    {"tlsrequire",           store_bool,      ITEM(res_client.tls_require), 0, 0, 0},
191    {"tlscacertificatefile", store_dir,       ITEM(res_client.tls_ca_certfile), 0, 0, 0},
192    {"tlscacertificatedir",  store_dir,       ITEM(res_client.tls_ca_certdir), 0, 0, 0},
193    {"tlscertificate",       store_dir,       ITEM(res_client.tls_certfile), 0, 0, 0},
194    {"tlskey",               store_dir,       ITEM(res_client.tls_keyfile), 0, 0, 0},
195    {"tlsallowedcn",         store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
196    {NULL, NULL, {0}, 0, 0, 0}
197 };
198
199 /* Storage daemon resource
200  *
201  *   name          handler     value                 code flags    default_value
202  */
203 static RES_ITEM store_items[] = {
204    {"name",        store_name,     ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
205    {"description", store_str,      ITEM(res_store.hdr.desc),   0, 0, 0},
206    {"sdport",      store_pint,     ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
207    {"address",     store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
208    {"sdaddress",   store_str,      ITEM(res_store.address),    0, 0, 0},
209    {"password",    store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 0},
210    {"sdpassword",  store_password, ITEM(res_store.password),   0, 0, 0},
211    {"device",      store_device,   ITEM(res_store.device),     R_DEVICE, ITEM_REQUIRED, 0},
212    {"mediatype",   store_strname,  ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
213    {"autochanger", store_bool,     ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
214    {"enabled",     store_bool,     ITEM(res_store.enabled),     0, ITEM_DEFAULT, true},
215    {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
216    {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
217    {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
218    {"tlsenable",            store_bool,      ITEM(res_store.tls_enable), 0, 0, 0},
219    {"tlsrequire",           store_bool,      ITEM(res_store.tls_require), 0, 0, 0},
220    {"tlscacertificatefile", store_dir,       ITEM(res_store.tls_ca_certfile), 0, 0, 0},
221    {"tlscacertificatedir",  store_dir,       ITEM(res_store.tls_ca_certdir), 0, 0, 0},
222    {"tlscertificate",       store_dir,       ITEM(res_store.tls_certfile), 0, 0, 0},
223    {"tlskey",               store_dir,       ITEM(res_store.tls_keyfile), 0, 0, 0},
224    {NULL, NULL, {0}, 0, 0, 0}
225 };
226
227 /*
228  *    Catalog Resource Directives
229  *
230  *   name          handler     value                 code flags    default_value
231  */
232 static RES_ITEM cat_items[] = {
233    {"name",     store_name,     ITEM(res_cat.hdr.name),    0, ITEM_REQUIRED, 0},
234    {"description", store_str,   ITEM(res_cat.hdr.desc),    0, 0, 0},
235    {"address",  store_str,      ITEM(res_cat.db_address),  0, 0, 0},
236    {"dbaddress", store_str,     ITEM(res_cat.db_address),  0, 0, 0},
237    {"dbport",   store_pint,     ITEM(res_cat.db_port),      0, 0, 0},
238    /* keep this password as store_str for the moment */
239    {"password", store_str,      ITEM(res_cat.db_password), 0, 0, 0},
240    {"dbpassword", store_str,    ITEM(res_cat.db_password), 0, 0, 0},
241    {"user",     store_str,      ITEM(res_cat.db_user),     0, 0, 0},
242    {"dbname",   store_str,      ITEM(res_cat.db_name),     0, ITEM_REQUIRED, 0},
243    {"dbsocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0},
244    /* Turned off for the moment */
245    {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
246    {NULL, NULL, {0}, 0, 0, 0}
247 };
248
249 /*
250  *    Job Resource Directives
251  *
252  *   name          handler     value                 code flags    default_value
253  */
254 RES_ITEM job_items[] = {
255    {"name",      store_name,    ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
256    {"description", store_str,   ITEM(res_job.hdr.desc), 0, 0, 0},
257    {"type",      store_jobtype, ITEM(res_job.JobType),  0, ITEM_REQUIRED, 0},
258    {"level",     store_level,   ITEM(res_job.JobLevel),    0, 0, 0},
259    {"messages",  store_res,     ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
260    {"storage",   store_alist_res, ITEM(res_job.storage),  R_STORAGE, 0, 0},
261    {"pool",      store_res,     ITEM(res_job.pool),     R_POOL, ITEM_REQUIRED, 0},
262    {"fullbackuppool",  store_res, ITEM(res_job.full_pool),   R_POOL, 0, 0},
263    {"incrementalbackuppool",  store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
264    {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
265    {"client",    store_res,     ITEM(res_job.client),   R_CLIENT, ITEM_REQUIRED, 0},
266    {"fileset",   store_res,     ITEM(res_job.fileset),  R_FILESET, ITEM_REQUIRED, 0},
267    {"schedule",  store_res,     ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
268    {"verifyjob", store_res,     ITEM(res_job.verify_job), R_JOB, 0, 0},
269    {"jobtoverify", store_res,   ITEM(res_job.verify_job), R_JOB, 0, 0},
270    {"jobdefs",   store_res,     ITEM(res_job.jobdefs),    R_JOBDEFS, 0, 0},
271    {"run",       store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
272    /* Root of where to restore files */
273    {"where",    store_dir,      ITEM(res_job.RestoreWhere), 0, 0, 0},
274    {"regexwhere",    store_str,   ITEM(res_job.RegexWhere), 0, 0, 0},
275    {"stripprefix",    store_str,  ITEM(res_job.strip_prefix), 0, 0, 0},
276    {"addprefix",    store_str,  ITEM(res_job.add_prefix), 0, 0, 0},
277    {"addsuffix",    store_str,  ITEM(res_job.add_suffix), 0, 0, 0},
278    /* Where to find bootstrap during restore */
279    {"bootstrap",store_dir,      ITEM(res_job.RestoreBootstrap), 0, 0, 0},
280    /* Where to write bootstrap file during backup */
281    {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
282    {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
283    {"replace",  store_replace,  ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
284    {"maxruntime",   store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
285    {"fullmaxwaittime",  store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
286    {"incrementalmaxwaittime",  store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
287    {"differentialmaxwaittime",  store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
288    {"maxwaittime",  store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
289    {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
290    {"jobretention", store_time, ITEM(res_job.JobRetention),  0, 0, 0},
291    {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
292    {"prunejobs",   store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
293    {"prunefiles",  store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
294    {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
295    {"enabled",     store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
296    {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
297    {"spooldata",   store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
298    {"rerunfailedlevels",   store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
299    {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
300    {"runbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
301    {"runafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
302    {"runafterfailedjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
303    {"clientrunbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
304    {"clientrunafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
305    {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
306    {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
307    {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
308    {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
309    {"priority",   store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
310    {"writepartafterjob",   store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
311    {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
312    {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
313    {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
314    {NULL, NULL, {0}, 0, 0, 0}
315 };
316
317 /* FileSet resource
318  *
319  *   name          handler     value                 code flags    default_value
320  */
321 static RES_ITEM fs_items[] = {
322    {"name",        store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
323    {"description", store_str,  ITEM(res_fs.hdr.desc), 0, 0, 0},
324    {"include",     store_inc,  {0},                   0, ITEM_NO_EQUALS, 0},
325    {"exclude",     store_inc,  {0},                   1, ITEM_NO_EQUALS, 0},
326    {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
327    {"enablevss",   store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
328    {NULL,          NULL,       {0},                  0, 0, 0}
329 };
330
331 /* Schedule -- see run_conf.c */
332 /* Schedule
333  *
334  *   name          handler     value                 code flags    default_value
335  */
336 static RES_ITEM sch_items[] = {
337    {"name",     store_name,  ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
338    {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
339    {"run",      store_run,   ITEM(res_sch.run),      0, 0, 0},
340    {NULL, NULL, {0}, 0, 0, 0}
341 };
342
343 /* Pool resource
344  *
345  *   name             handler     value                        code flags default_value
346  */
347 static RES_ITEM pool_items[] = {
348    {"name",            store_name,    ITEM(res_pool.hdr.name),      0, ITEM_REQUIRED, 0},
349    {"description",     store_str,     ITEM(res_pool.hdr.desc),      0, 0,     0},
350    {"pooltype",        store_strname, ITEM(res_pool.pool_type),     0, ITEM_REQUIRED, 0},
351    {"labelformat",     store_strname, ITEM(res_pool.label_format),  0, 0,     0},
352    {"labeltype",       store_label,   ITEM(res_pool.LabelType),     0, 0,     0},     
353    {"cleaningprefix",  store_strname, ITEM(res_pool.cleaning_prefix), 0, 0,   0},
354    {"usecatalog",      store_bool,    ITEM(res_pool.use_catalog),    0, ITEM_DEFAULT, true},
355    {"usevolumeonce",   store_bool,    ITEM(res_pool.use_volume_once), 0, 0,   0},
356    {"purgeoldestvolume", store_bool,  ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
357    {"recycleoldestvolume", store_bool,  ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
358    {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
359    {"maximumvolumes",  store_pint,    ITEM(res_pool.max_volumes),   0, 0,        0},
360    {"maximumvolumejobs", store_pint,  ITEM(res_pool.MaxVolJobs),    0, 0,       0},
361    {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles),   0, 0,       0},
362    {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes),   0, 0,       0},
363    {"catalogfiles",    store_bool,    ITEM(res_pool.catalog_files),  0, ITEM_DEFAULT, true},
364    {"volumeretention", store_time,    ITEM(res_pool.VolRetention),   0, ITEM_DEFAULT, 60*60*24*365},
365    {"volumeuseduration", store_time,  ITEM(res_pool.VolUseDuration), 0, 0, 0},
366    {"migrationtime",  store_time,     ITEM(res_pool.MigrationTime), 0, 0, 0},
367    {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
368    {"migrationlowbytes", store_size,  ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
369    {"nextpool",      store_res,       ITEM(res_pool.NextPool), R_POOL, 0, 0},
370    {"storage",       store_alist_res, ITEM(res_pool.storage),  R_STORAGE, 0, 0},
371    {"autoprune",       store_bool,    ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
372    {"recycle",         store_bool,    ITEM(res_pool.Recycle),   0, ITEM_DEFAULT, true},
373    {"recyclepool",     store_res,     ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
374    {NULL, NULL, {0}, 0, 0, 0}
375 };
376
377 /*
378  * Counter Resource
379  *   name             handler     value                        code flags default_value
380  */
381 static RES_ITEM counter_items[] = {
382    {"name",            store_name,    ITEM(res_counter.hdr.name),        0, ITEM_REQUIRED, 0},
383    {"description",     store_str,     ITEM(res_counter.hdr.desc),        0, 0,     0},
384    {"minimum",         store_int,     ITEM(res_counter.MinValue),        0, ITEM_DEFAULT, 0},
385    {"maximum",         store_pint,    ITEM(res_counter.MaxValue),        0, ITEM_DEFAULT, INT32_MAX},
386    {"wrapcounter",     store_res,     ITEM(res_counter.WrapCounter),     R_COUNTER, 0, 0},
387    {"catalog",         store_res,     ITEM(res_counter.Catalog),         R_CATALOG, 0, 0},
388    {NULL, NULL, {0}, 0, 0, 0}
389 };
390
391
392 /* Message resource */
393 extern RES_ITEM msgs_items[];
394
395 /*
396  * This is the master resource definition.
397  * It must have one item for each of the resources.
398  *
399  *  NOTE!!! keep it in the same order as the R_codes
400  *    or eliminate all resources[rindex].name
401  *
402  *  name             items        rcode        res_head
403  */
404 RES_TABLE resources[] = {
405    {"director",      dir_items,   R_DIRECTOR},
406    {"client",        cli_items,   R_CLIENT},
407    {"job",           job_items,   R_JOB},
408    {"storage",       store_items, R_STORAGE},
409    {"catalog",       cat_items,   R_CATALOG},
410    {"schedule",      sch_items,   R_SCHEDULE},
411    {"fileset",       fs_items,    R_FILESET},
412    {"pool",          pool_items,  R_POOL},
413    {"messages",      msgs_items,  R_MSGS},
414    {"counter",       counter_items, R_COUNTER},
415    {"console",       con_items,   R_CONSOLE},
416    {"jobdefs",       job_items,   R_JOBDEFS},
417    {"device",        NULL,        R_DEVICE},  /* info obtained from SD */
418    {NULL,            NULL,        0}
419 };
420
421
422 /* Keywords (RHS) permitted in Job Level records
423  *
424  *   level_name      level              job_type
425  */
426 struct s_jl joblevels[] = {
427    {"Full",          L_FULL,            JT_BACKUP},
428    {"Base",          L_BASE,            JT_BACKUP},
429    {"Incremental",   L_INCREMENTAL,     JT_BACKUP},
430    {"Differential",  L_DIFFERENTIAL,    JT_BACKUP},
431    {"Since",         L_SINCE,           JT_BACKUP},
432    {"Catalog",       L_VERIFY_CATALOG,  JT_VERIFY},
433    {"InitCatalog",   L_VERIFY_INIT,     JT_VERIFY},
434    {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG,   JT_VERIFY},
435    {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG,   JT_VERIFY},
436    {"Data",          L_VERIFY_DATA,     JT_VERIFY},
437    {" ",             L_NONE,            JT_ADMIN},
438    {" ",             L_NONE,            JT_RESTORE},
439    {NULL,            0,                          0}
440 };
441
442 /* Keywords (RHS) permitted in Job type records
443  *
444  *   type_name       job_type
445  */
446 struct s_jt jobtypes[] = {
447    {"backup",        JT_BACKUP},
448    {"admin",         JT_ADMIN},
449    {"verify",        JT_VERIFY},
450    {"restore",       JT_RESTORE},
451    {"migrate",       JT_MIGRATE},
452    {NULL,            0}
453 };
454
455
456 /* Keywords (RHS) permitted in Selection type records
457  *
458  *   type_name       job_type
459  */
460 struct s_jt migtypes[] = {
461    {"smallestvolume",   MT_SMALLEST_VOL},
462    {"oldestvolume",     MT_OLDEST_VOL},
463    {"pooloccupancy",    MT_POOL_OCCUPANCY},
464    {"pooltime",         MT_POOL_TIME},
465    {"client",           MT_CLIENT},
466    {"volume",           MT_VOLUME},
467    {"job",              MT_JOB},
468    {"sqlquery",         MT_SQLQUERY},
469    {NULL,            0}
470 };
471
472
473
474 /* Options permitted in Restore replace= */
475 struct s_kw ReplaceOptions[] = {
476    {"always",         REPLACE_ALWAYS},
477    {"ifnewer",        REPLACE_IFNEWER},
478    {"ifolder",        REPLACE_IFOLDER},
479    {"never",          REPLACE_NEVER},
480    {NULL,               0}
481 };
482
483 const char *level_to_str(int level)
484 {
485    int i;
486    static char level_no[30];
487    const char *str = level_no;
488
489    bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level);    /* default if not found */
490    for (i=0; joblevels[i].level_name; i++) {
491       if (level == joblevels[i].level) {
492          str = joblevels[i].level_name;
493          break;
494       }
495    }
496    return str;
497 }
498
499 /* Dump contents of resource */
500 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
501 {
502    URES *res = (URES *)reshdr;
503    bool recurse = true;
504    char ed1[100], ed2[100], ed3[100];
505    DEVICE *dev;
506
507    if (res == NULL) {
508       sendit(sock, _("No %s resource defined\n"), res_to_str(type));
509       return;
510    }
511    if (type < 0) {                    /* no recursion */
512       type = - type;
513       recurse = false;
514    }
515    switch (type) {
516    case R_DIRECTOR:
517       sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
518          reshdr->name, res->res_dir.MaxConcurrentJobs,
519          edit_uint64(res->res_dir.FDConnectTimeout, ed1),
520          edit_uint64(res->res_dir.SDConnectTimeout, ed2));
521       if (res->res_dir.query_file) {
522          sendit(sock, _("   query_file=%s\n"), res->res_dir.query_file);
523       }
524       if (res->res_dir.messages) {
525          sendit(sock, _("  --> "));
526          dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
527       }
528       break;
529    case R_CONSOLE:
530       sendit(sock, _("Console: name=%s SSL=%d\n"),
531          res->res_con.hdr.name, res->res_con.tls_enable);
532       break;
533    case R_COUNTER:
534       if (res->res_counter.WrapCounter) {
535          sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
536             res->res_counter.hdr.name, res->res_counter.MinValue,
537             res->res_counter.MaxValue, res->res_counter.CurrentValue,
538             res->res_counter.WrapCounter->hdr.name);
539       } else {
540          sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
541             res->res_counter.hdr.name, res->res_counter.MinValue,
542             res->res_counter.MaxValue);
543       }
544       if (res->res_counter.Catalog) {
545          sendit(sock, _("  --> "));
546          dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
547       }
548       break;
549
550    case R_CLIENT:
551       sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
552          res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
553          res->res_client.MaxConcurrentJobs);
554       sendit(sock, _("      JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
555          edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
556          edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
557          res->res_client.AutoPrune);
558       if (res->res_client.catalog) {
559          sendit(sock, _("  --> "));
560          dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
561       }
562       break;
563    case R_DEVICE:
564       dev = &res->res_dev;
565       char ed1[50];
566       sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
567 "      reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
568 "      poolid=%s volname=%s MediaType=%s\n"),
569          dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
570          dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
571          dev->offline, dev->autochanger,
572          edit_uint64(dev->PoolId, ed1),
573          dev->VolumeName, dev->MediaType);
574       break;
575    case R_STORAGE:
576       sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
577 "      DeviceName=%s MediaType=%s StorageId=%s\n"),
578          res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
579          res->res_store.MaxConcurrentJobs,
580          res->res_store.dev_name(),
581          res->res_store.media_type,
582          edit_int64(res->res_store.StorageId, ed1));
583       break;
584    case R_CATALOG:
585       sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
586 "      db_user=%s MutliDBConn=%d\n"),
587          res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
588          res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
589          res->res_cat.mult_db_connections);
590       break;
591    case R_JOB:
592    case R_JOBDEFS:
593       sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
594          type == R_JOB ? _("Job") : _("JobDefs"),
595          res->res_job.hdr.name, res->res_job.JobType,
596          level_to_str(res->res_job.JobLevel), res->res_job.Priority,
597          res->res_job.enabled);
598       sendit(sock, _("     MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
599          res->res_job.MaxConcurrentJobs, 
600          res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
601          edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
602          res->res_job.spool_data, res->res_job.write_part_after_job);
603       if (res->res_job.JobType == JT_MIGRATE) {
604          sendit(sock, _("     SelectionType=%d\n"), res->res_job.selection_type);
605       }
606       if (res->res_job.client) {
607          sendit(sock, _("  --> "));
608          dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
609       }
610       if (res->res_job.fileset) {
611          sendit(sock, _("  --> "));
612          dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
613       }
614       if (res->res_job.schedule) {
615          sendit(sock, _("  --> "));
616          dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
617       }
618       if (res->res_job.RestoreWhere && !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, _("  --> AbortJobOnError=%u\n"),  script->abort_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          if (!res->res_job.RegexWhere 
1326              &&
1327              (res->res_job.strip_prefix ||
1328               res->res_job.add_suffix   ||
1329               res->res_job.add_prefix))
1330          {
1331             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1332                                                    res->res_job.add_prefix,
1333                                                    res->res_job.add_suffix);
1334             res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1335             bregexp_build_where(res->res_job.RegexWhere, len,
1336                                 res->res_job.strip_prefix,
1337                                 res->res_job.add_prefix,
1338                                 res->res_job.add_suffix);
1339             /* TODO: test bregexp */
1340          }
1341
1342          if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1343             free(res->res_job.RestoreWhere);
1344             res->res_job.RestoreWhere = NULL;
1345          }
1346
1347          break;
1348       case R_COUNTER:
1349          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1350             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1351          }
1352          res->res_counter.Catalog = res_all.res_counter.Catalog;
1353          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1354          break;
1355
1356       case R_CLIENT:
1357          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1358             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1359          }
1360          res->res_client.catalog = res_all.res_client.catalog;
1361          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1362          break;
1363       case R_SCHEDULE:
1364          /*
1365           * Schedule is a bit different in that it contains a RUN record
1366           * chain which isn't a "named" resource. This chain was linked
1367           * in by run_conf.c during pass 2, so here we jam the pointer
1368           * into the Schedule resource.
1369           */
1370          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1371             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1372          }
1373          res->res_sch.run = res_all.res_sch.run;
1374          break;
1375       default:
1376          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1377          error = true;
1378          break;
1379       }
1380       /* Note, the resource name was already saved during pass 1,
1381        * so here, we can just release it.
1382        */
1383       if (res_all.res_dir.hdr.name) {
1384          free(res_all.res_dir.hdr.name);
1385          res_all.res_dir.hdr.name = NULL;
1386       }
1387       if (res_all.res_dir.hdr.desc) {
1388          free(res_all.res_dir.hdr.desc);
1389          res_all.res_dir.hdr.desc = NULL;
1390       }
1391       return;
1392    }
1393
1394    /*
1395     * The following code is only executed during pass 1
1396     */
1397    switch (type) {
1398    case R_DIRECTOR:
1399       size = sizeof(DIRRES);
1400       break;
1401    case R_CONSOLE:
1402       size = sizeof(CONRES);
1403       break;
1404    case R_CLIENT:
1405       size =sizeof(CLIENT);
1406       break;
1407    case R_STORAGE:
1408       size = sizeof(STORE);
1409       break;
1410    case R_CATALOG:
1411       size = sizeof(CAT);
1412       break;
1413    case R_JOB:
1414    case R_JOBDEFS:
1415       size = sizeof(JOB);
1416       break;
1417    case R_FILESET:
1418       size = sizeof(FILESET);
1419       break;
1420    case R_SCHEDULE:
1421       size = sizeof(SCHED);
1422       break;
1423    case R_POOL:
1424       size = sizeof(POOL);
1425       break;
1426    case R_MSGS:
1427       size = sizeof(MSGS);
1428       break;
1429    case R_COUNTER:
1430       size = sizeof(COUNTER);
1431       break;
1432    case R_DEVICE:
1433       error = true;
1434       break;
1435    default:
1436       printf(_("Unknown resource type %d in save_resource.\n"), type);
1437       error = true; 
1438       break;
1439    }
1440    /* Common */
1441    if (!error) {
1442       res = (URES *)malloc(size);
1443       memcpy(res, &res_all, size);
1444       if (!res_head[rindex]) {
1445          res_head[rindex] = (RES *)res; /* store first entry */
1446          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1447                res->res_dir.hdr.name, rindex);
1448       } else {
1449          RES *next, *last;
1450          if (res->res_dir.hdr.name == NULL) {
1451             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1452                   resources[rindex]);
1453          }   
1454          /* Add new res to end of chain */
1455          for (last=next=res_head[rindex]; next; next=next->next) {
1456             last = next;
1457             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1458                Emsg2(M_ERROR_TERM, 0,
1459                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1460                   resources[rindex].name, res->res_dir.hdr.name);
1461             }
1462          }
1463          last->next = (RES *)res;
1464          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1465                res->res_dir.hdr.name, rindex, pass);
1466       }
1467    }
1468 }
1469
1470 /*
1471  * Store Device. Note, the resource is created upon the
1472  *  first reference. The details of the resource are obtained
1473  *  later from the SD.
1474  */
1475 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1476 {
1477    int token;
1478    URES *res;
1479    int rindex = R_DEVICE - r_first;
1480    int size = sizeof(DEVICE);
1481    bool found = false;
1482
1483    if (pass == 1) {
1484       token = lex_get_token(lc, T_NAME);
1485       if (!res_head[rindex]) {
1486          res = (URES *)malloc(size);
1487          memset(res, 0, size);
1488          res->res_dev.hdr.name = bstrdup(lc->str);
1489          res_head[rindex] = (RES *)res; /* store first entry */
1490          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1491                res->res_dir.hdr.name, rindex);
1492       } else {
1493          RES *next;
1494          /* See if it is already defined */
1495          for (next=res_head[rindex]; next->next; next=next->next) {
1496             if (strcmp(next->name, lc->str) == 0) {
1497                found = true;
1498                break;
1499             }
1500          }
1501          if (!found) {
1502             res = (URES *)malloc(size);
1503             memset(res, 0, size);
1504             res->res_dev.hdr.name = bstrdup(lc->str);
1505             next->next = (RES *)res;
1506             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1507                res->res_dir.hdr.name, rindex, pass);
1508          }
1509       }
1510
1511       scan_to_eol(lc);
1512       set_bit(index, res_all.hdr.item_present);
1513    } else {
1514       store_alist_res(lc, item, index, pass);
1515    }
1516 }
1517
1518 /*
1519  * Store JobType (backup, verify, restore)
1520  *
1521  */
1522 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1523 {
1524    int token, i;
1525
1526    token = lex_get_token(lc, T_NAME);
1527    /* Store the type both pass 1 and pass 2 */
1528    for (i=0; migtypes[i].type_name; i++) {
1529       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1530          *(int *)(item->value) = migtypes[i].job_type;
1531          i = 0;
1532          break;
1533       }
1534    }
1535    if (i != 0) {
1536       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1537    }
1538    scan_to_eol(lc);
1539    set_bit(index, res_all.hdr.item_present);
1540 }
1541
1542
1543
1544 /*
1545  * Store JobType (backup, verify, restore)
1546  *
1547  */
1548 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1549 {
1550    int token, i;
1551
1552    token = lex_get_token(lc, T_NAME);
1553    /* Store the type both pass 1 and pass 2 */
1554    for (i=0; jobtypes[i].type_name; i++) {
1555       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1556          *(int *)(item->value) = jobtypes[i].job_type;
1557          i = 0;
1558          break;
1559       }
1560    }
1561    if (i != 0) {
1562       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1563    }
1564    scan_to_eol(lc);
1565    set_bit(index, res_all.hdr.item_present);
1566 }
1567
1568 /*
1569  * Store Job Level (Full, Incremental, ...)
1570  *
1571  */
1572 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1573 {
1574    int token, i;
1575
1576    token = lex_get_token(lc, T_NAME);
1577    /* Store the level pass 2 so that type is defined */
1578    for (i=0; joblevels[i].level_name; i++) {
1579       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1580          *(int *)(item->value) = joblevels[i].level;
1581          i = 0;
1582          break;
1583       }
1584    }
1585    if (i != 0) {
1586       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1587    }
1588    scan_to_eol(lc);
1589    set_bit(index, res_all.hdr.item_present);
1590 }
1591
1592
1593 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1594 {
1595    int token, i;
1596    token = lex_get_token(lc, T_NAME);
1597    /* Scan Replacement options */
1598    for (i=0; ReplaceOptions[i].name; i++) {
1599       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1600          *(int *)(item->value) = ReplaceOptions[i].token;
1601          i = 0;
1602          break;
1603       }
1604    }
1605    if (i != 0) {
1606       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1607    }
1608    scan_to_eol(lc);
1609    set_bit(index, res_all.hdr.item_present);
1610 }
1611
1612 /*
1613  * Store ACL (access control list)
1614  *
1615  */
1616 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1617 {
1618    int token;
1619
1620    for (;;) {
1621       token = lex_get_token(lc, T_STRING);
1622       if (pass == 1) {
1623          if (((alist **)item->value)[item->code] == NULL) {
1624             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1625             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1626          }
1627          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1628          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1629       }
1630       token = lex_get_token(lc, T_ALL);
1631       if (token == T_COMMA) {
1632          continue;                    /* get another ACL */
1633       }
1634       break;
1635    }
1636    set_bit(index, res_all.hdr.item_present);
1637 }
1638
1639 /* We build RunScripts items here */
1640 static RUNSCRIPT res_runscript;
1641
1642 /* Store a runscript->when in a bit field */
1643 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1644 {
1645    lex_get_token(lc, T_NAME);
1646
1647    if (strcasecmp(lc->str, "before") == 0) {
1648       *(int *)(item->value) = SCRIPT_Before ;
1649    } else if (strcasecmp(lc->str, "after") == 0) {
1650       *(int *)(item->value) = SCRIPT_After;
1651    } else if (strcasecmp(lc->str, "always") == 0) {
1652       *(int *)(item->value) = SCRIPT_Any;
1653    } else {
1654       scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1655    }
1656    scan_to_eol(lc);
1657 }
1658
1659 /* Store a runscript->target
1660  * 
1661  */
1662 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1663 {
1664    lex_get_token(lc, T_STRING);
1665
1666    if (pass == 2) {
1667       if (strcmp(lc->str, "%c") == 0) {
1668          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1669       } else if (strcasecmp(lc->str, "yes") == 0) {
1670          ((RUNSCRIPT*) item->value)->set_target("%c");
1671       } else if (strcasecmp(lc->str, "no") == 0) {
1672          ((RUNSCRIPT*) item->value)->set_target("");
1673       } else {
1674          RES *res = GetResWithName(R_CLIENT, lc->str);
1675          if (res == NULL) {
1676             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1677                       lc->str, lc->line_no, lc->line);
1678          }
1679
1680          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1681       }
1682    }
1683    scan_to_eol(lc);
1684 }
1685
1686 /* Store a runscript->command in a bit field
1687  * 
1688  */
1689 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1690 {
1691    lex_get_token(lc, T_STRING);
1692
1693    if (pass == 2) {
1694       ((RUNSCRIPT*) item->value)->set_command(lc->str);
1695    }
1696    scan_to_eol(lc);
1697 }
1698
1699 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1700 {
1701    lex_get_token(lc, T_STRING);
1702    alist **runscripts = (alist **)(item->value) ;
1703
1704    if (pass == 2) {
1705       RUNSCRIPT *script = new_runscript();
1706
1707       script->set_command(lc->str);
1708
1709       /* TODO: remove all script->old_proto with bacula 1.42 */
1710
1711       if (strcmp(item->name, "runbeforejob") == 0) {
1712          script->when = SCRIPT_Before;
1713          script->abort_on_error = true;
1714          script->set_target("");
1715
1716       } else if (strcmp(item->name, "runafterjob") == 0) {
1717          script->when = SCRIPT_After;
1718          script->on_success = true;
1719          script->on_failure = false;
1720          script->set_target("");
1721          
1722       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1723          script->old_proto = true;
1724          script->when = SCRIPT_After;
1725          script->set_target("%c");
1726          script->on_success = true;
1727          script->on_failure = false;
1728
1729       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1730          script->old_proto = true;
1731          script->when = SCRIPT_Before;
1732          script->set_target("%c");
1733          script->abort_on_error = true;
1734
1735       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1736          script->when = SCRIPT_After;
1737          script->on_failure = true;
1738          script->on_success = false;
1739          script->set_target("");
1740       }
1741
1742       if (*runscripts == NULL) {
1743         *runscripts = New(alist(10, not_owned_by_alist));
1744       }
1745       
1746       (*runscripts)->append(script);
1747       script->debug();
1748    }
1749
1750    scan_to_eol(lc);
1751 }
1752
1753 /* Store a bool in a bit field without modifing res_all.hdr 
1754  * We can also add an option to store_bool to skip res_all.hdr
1755  */
1756 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1757 {
1758    lex_get_token(lc, T_NAME);
1759    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1760       *(bool *)(item->value) = true;
1761    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1762       *(bool *)(item->value) = false;
1763    } else {
1764       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1765    }
1766    scan_to_eol(lc);
1767 }
1768
1769 /*
1770  * new RunScript items
1771  *   name             handler              value                             code flags default_value
1772  */
1773 static RES_ITEM runscript_items[] = {
1774  {"command",        store_runscript_cmd,  {(char **)&res_runscript},           0,  ITEM_REQUIRED, 0}, 
1775  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, 
1776  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
1777  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
1778  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.abort_on_error},0, 0, 0},
1779  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
1780  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
1781  {NULL, NULL, {0}, 0, 0, 0}
1782 };
1783
1784 /*
1785  * Store RunScript info
1786  *
1787  *  Note, when this routine is called, we are inside a Job
1788  *  resource.  We treat the RunScript like a sort of
1789  *  mini-resource within the Job resource.
1790  */
1791 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1792 {
1793    int token, i;
1794    alist **runscripts = (alist **)(item->value) ;
1795
1796    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1797
1798    res_runscript.reset_default();      /* setting on_success, on_failure, abort_on_error */
1799    
1800    token = lex_get_token(lc, T_SKIP_EOL);
1801    
1802    if (token != T_BOB) {
1803       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1804    }
1805    
1806    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1807       if (token == T_EOB) {
1808         break;
1809       }
1810       if (token != T_IDENTIFIER) {
1811         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1812       }
1813       for (i=0; runscript_items[i].name; i++) {
1814         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1815            token = lex_get_token(lc, T_SKIP_EOL);
1816            if (token != T_EQUALS) {
1817               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1818            }
1819            
1820            /* Call item handler */
1821            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1822            i = -1;
1823            break;
1824         }
1825       }
1826       
1827       if (i >=0) {
1828         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1829       }
1830    }
1831
1832    if (pass == 2) {
1833       if (res_runscript.command == NULL) {
1834          scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1835                    "command", "runscript");
1836       }
1837
1838       /* run on client by default */
1839       if (res_runscript.target == NULL) {
1840          res_runscript.set_target("%c");
1841       }
1842
1843       RUNSCRIPT *script = new_runscript();
1844       memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1845       
1846       if (*runscripts == NULL) {
1847         *runscripts = New(alist(10, not_owned_by_alist));
1848       }
1849       
1850       (*runscripts)->append(script);
1851       script->debug();
1852    }
1853
1854    scan_to_eol(lc);
1855    set_bit(index, res_all.hdr.item_present);
1856 }