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