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