]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Implement socket level heartbeat (tcp_keepidle)
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation plus additions
11    that are listed in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of John Walker.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Main configuration file parser for Bacula Directors,
30  *    some parts may be split into separate files such as
31  *    the schedule configuration (run_config.c).
32  *
33  *   Note, the configuration file parser consists of three parts
34  *
35  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
36  *
37  *   2. The generic config  scanner in lib/parse_config.c and
38  *      lib/parse_config.h.
39  *      These files contain the parser code, some utility
40  *      routines, and the common store routines (name, int,
41  *      string).
42  *
43  *   3. The daemon specific file, which contains the Resource
44  *      definitions as well as any specific store routines
45  *      for the resource records.
46  *
47  *     Kern Sibbald, January MM
48  *
49  *     Version $Id$
50  */
51
52
53 #include "bacula.h"
54 #include "dird.h"
55
56 /* Define the first and last resource ID record
57  * types. Note, these should be unique for each
58  * daemon though not a requirement.
59  */
60 int r_first = R_FIRST;
61 int r_last  = R_LAST;
62 static RES *sres_head[R_LAST - R_FIRST + 1];
63 RES **res_head = sres_head;
64
65 /* Imported subroutines */
66 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
67 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
68 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
69
70
71 /* Forward referenced subroutines */
72
73 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
74 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
75 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
76 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
77 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
79 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
80 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
81 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
82 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
83
84 /* We build the current resource here as we are
85  * scanning the resource configuration definition,
86  * then move it to allocated memory when the resource
87  * scan is complete.
88  */
89 #if defined(_MSC_VER)
90 extern "C" { // work around visual compiler mangling variables
91     URES res_all;
92 }
93 #else
94 URES res_all;
95 #endif
96 int  res_all_size = sizeof(res_all);
97
98
99 /* Definition of records permitted within each
100  * resource with the routine to process the record
101  * information.  NOTE! quoted names must be in lower case.
102  */
103 /*
104  *    Director Resource
105  *
106  *   name          handler     value                 code flags    default_value
107  */
108 static RES_ITEM dir_items[] = {
109    {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
110    {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
111    {"messages",    store_res,      ITEM(res_dir.messages), R_MSGS, 0, 0},
112    {"dirport",     store_addresses_port,    ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
113    {"diraddress",  store_addresses_address, ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
114    {"diraddresses",store_addresses,         ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
115    {"queryfile",   store_dir,      ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
116    {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
117    {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
118    {"piddirectory",store_dir,     ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
119    {"subsysdirectory", store_dir,  ITEM(res_dir.subsys_directory), 0, 0, 0},
120    {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
121    {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
122    {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
123    {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
124    {"heartbeatinterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 0},
125    {"tlsenable",            store_bool,      ITEM(res_dir.tls_enable), 0, 0, 0},
126    {"tlsrequire",           store_bool,      ITEM(res_dir.tls_require), 0, 0, 0},
127    {"tlsverifypeer",        store_bool,      ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
128    {"tlscacertificatefile", store_dir,       ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
129    {"tlscacertificatedir",  store_dir,       ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
130    {"tlscertificate",       store_dir,       ITEM(res_dir.tls_certfile), 0, 0, 0},
131    {"tlskey",               store_dir,       ITEM(res_dir.tls_keyfile), 0, 0, 0},
132    {"tlsdhfile",            store_dir,       ITEM(res_dir.tls_dhfile), 0, 0, 0},
133    {"tlsallowedcn",         store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
134    {NULL, NULL, {0}, 0, 0, 0}
135 };
136
137 /*
138  *    Console Resource
139  *
140  *   name          handler     value                 code flags    default_value
141  */
142 static RES_ITEM con_items[] = {
143    {"name",        store_name,     ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
144    {"description", store_str,      ITEM(res_con.hdr.desc), 0, 0, 0},
145    {"password",    store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
146    {"jobacl",      store_acl,      ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
147    {"clientacl",   store_acl,      ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
148    {"storageacl",  store_acl,      ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
149    {"scheduleacl", store_acl,      ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
150    {"runacl",      store_acl,      ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
151    {"poolacl",     store_acl,      ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
152    {"commandacl",  store_acl,      ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
153    {"filesetacl",  store_acl,      ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
154    {"catalogacl",  store_acl,      ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
155    {"whereacl",    store_acl,      ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
156    {"tlsenable",            store_bool,      ITEM(res_con.tls_enable), 0, 0, 0},
157    {"tlsrequire",           store_bool,      ITEM(res_con.tls_require), 0, 0, 0},
158    {"tlsverifypeer",        store_bool,      ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
159    {"tlscacertificatefile", store_dir,       ITEM(res_con.tls_ca_certfile), 0, 0, 0},
160    {"tlscacertificatedir",  store_dir,       ITEM(res_con.tls_ca_certdir), 0, 0, 0},
161    {"tlscertificate",       store_dir,       ITEM(res_con.tls_certfile), 0, 0, 0},
162    {"tlskey",               store_dir,       ITEM(res_con.tls_keyfile), 0, 0, 0},
163    {"tlsdhfile",            store_dir,       ITEM(res_con.tls_dhfile), 0, 0, 0},
164    {"tlsallowedcn",         store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
165    {NULL, NULL, {0}, 0, 0, 0}
166 };
167
168
169 /*
170  *    Client or File daemon resource
171  *
172  *   name          handler     value                 code flags    default_value
173  */
174
175 static RES_ITEM cli_items[] = {
176    {"name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
177    {"description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
178    {"address",  store_str,        ITEM(res_client.address),  0, ITEM_REQUIRED, 0},
179    {"fdaddress",  store_str,      ITEM(res_client.address),  0, 0, 0},
180    {"fdport",   store_pint,       ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
181    {"password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
182    {"fdpassword", store_password,   ITEM(res_client.password), 0, 0, 0},
183    {"catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, ITEM_REQUIRED, 0},
184    {"fileretention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
185    {"jobretention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*180},
186    {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0},
187    {"autoprune", store_bool,      ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
188    {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
189    {"tlsenable",            store_bool,      ITEM(res_client.tls_enable), 0, 0, 0},
190    {"tlsrequire",           store_bool,      ITEM(res_client.tls_require), 0, 0, 0},
191    {"tlscacertificatefile", store_dir,       ITEM(res_client.tls_ca_certfile), 0, 0, 0},
192    {"tlscacertificatedir",  store_dir,       ITEM(res_client.tls_ca_certdir), 0, 0, 0},
193    {"tlscertificate",       store_dir,       ITEM(res_client.tls_certfile), 0, 0, 0},
194    {"tlskey",               store_dir,       ITEM(res_client.tls_keyfile), 0, 0, 0},
195    {"tlsallowedcn",         store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
196    {NULL, NULL, {0}, 0, 0, 0}
197 };
198
199 /* Storage daemon resource
200  *
201  *   name          handler     value                 code flags    default_value
202  */
203 static RES_ITEM store_items[] = {
204    {"name",        store_name,     ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
205    {"description", store_str,      ITEM(res_store.hdr.desc),   0, 0, 0},
206    {"sdport",      store_pint,     ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
207    {"address",     store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
208    {"sdaddress",   store_str,      ITEM(res_store.address),    0, 0, 0},
209    {"password",    store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 0},
210    {"sdpassword",  store_password, ITEM(res_store.password),   0, 0, 0},
211    {"device",      store_device,   ITEM(res_store.device),     R_DEVICE, ITEM_REQUIRED, 0},
212    {"mediatype",   store_strname,  ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
213    {"autochanger", store_bool,     ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
214    {"enabled",     store_bool,     ITEM(res_store.enabled),     0, ITEM_DEFAULT, true},
215    {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
216    {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
217    {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
218    {"tlsenable",            store_bool,      ITEM(res_store.tls_enable), 0, 0, 0},
219    {"tlsrequire",           store_bool,      ITEM(res_store.tls_require), 0, 0, 0},
220    {"tlscacertificatefile", store_dir,       ITEM(res_store.tls_ca_certfile), 0, 0, 0},
221    {"tlscacertificatedir",  store_dir,       ITEM(res_store.tls_ca_certdir), 0, 0, 0},
222    {"tlscertificate",       store_dir,       ITEM(res_store.tls_certfile), 0, 0, 0},
223    {"tlskey",               store_dir,       ITEM(res_store.tls_keyfile), 0, 0, 0},
224    {NULL, NULL, {0}, 0, 0, 0}
225 };
226
227 /*
228  *    Catalog Resource Directives
229  *
230  *   name          handler     value                 code flags    default_value
231  */
232 static RES_ITEM cat_items[] = {
233    {"name",     store_name,     ITEM(res_cat.hdr.name),    0, ITEM_REQUIRED, 0},
234    {"description", store_str,   ITEM(res_cat.hdr.desc),    0, 0, 0},
235    {"address",  store_str,      ITEM(res_cat.db_address),  0, 0, 0},
236    {"dbaddress", store_str,     ITEM(res_cat.db_address),  0, 0, 0},
237    {"dbport",   store_pint,     ITEM(res_cat.db_port),      0, 0, 0},
238    /* keep this password as store_str for the moment */
239    {"password", store_str,      ITEM(res_cat.db_password), 0, 0, 0},
240    {"dbpassword", store_str,    ITEM(res_cat.db_password), 0, 0, 0},
241    {"user",     store_str,      ITEM(res_cat.db_user),     0, 0, 0},
242    {"dbname",   store_str,      ITEM(res_cat.db_name),     0, ITEM_REQUIRED, 0},
243    {"dbsocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0},
244    /* Turned off for the moment */
245    {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
246    {NULL, NULL, {0}, 0, 0, 0}
247 };
248
249 /*
250  *    Job Resource Directives
251  *
252  *   name          handler     value                 code flags    default_value
253  */
254 RES_ITEM job_items[] = {
255    {"name",      store_name,    ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
256    {"description", store_str,   ITEM(res_job.hdr.desc), 0, 0, 0},
257    {"type",      store_jobtype, ITEM(res_job.JobType),  0, ITEM_REQUIRED, 0},
258    {"level",     store_level,   ITEM(res_job.JobLevel),    0, 0, 0},
259    {"messages",  store_res,     ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
260    {"storage",   store_alist_res, ITEM(res_job.storage),  R_STORAGE, 0, 0},
261    {"pool",      store_res,     ITEM(res_job.pool),     R_POOL, ITEM_REQUIRED, 0},
262    {"fullbackuppool",  store_res, ITEM(res_job.full_pool),   R_POOL, 0, 0},
263    {"incrementalbackuppool",  store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
264    {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
265    {"client",    store_res,     ITEM(res_job.client),   R_CLIENT, ITEM_REQUIRED, 0},
266    {"fileset",   store_res,     ITEM(res_job.fileset),  R_FILESET, ITEM_REQUIRED, 0},
267    {"schedule",  store_res,     ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
268    {"verifyjob", store_res,     ITEM(res_job.verify_job), R_JOB, 0, 0},
269    {"jobtoverify", store_res,   ITEM(res_job.verify_job), R_JOB, 0, 0},
270    {"jobdefs",   store_res,     ITEM(res_job.jobdefs),    R_JOBDEFS, 0, 0},
271    {"run",       store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
272    /* Root of where to restore files */
273    {"where",    store_dir,      ITEM(res_job.RestoreWhere), 0, 0, 0},
274    {"whereuseregexp", store_bool, ITEM(res_job.where_use_regexp), 0, 0, 0},
275    {"stripprefix",    store_str,  ITEM(res_job.strip_prefix), 0, 0, 0},
276    {"addprefix",    store_str,  ITEM(res_job.add_prefix), 0, 0, 0},
277    {"addsuffix",    store_str,  ITEM(res_job.add_suffix), 0, 0, 0},
278    /* Where to find bootstrap during restore */
279    {"bootstrap",store_dir,      ITEM(res_job.RestoreBootstrap), 0, 0, 0},
280    /* Where to write bootstrap file during backup */
281    {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
282    {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
283    {"replace",  store_replace,  ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
284    {"maxruntime",   store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
285    {"fullmaxwaittime",  store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
286    {"incrementalmaxwaittime",  store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
287    {"differentialmaxwaittime",  store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
288    {"maxwaittime",  store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
289    {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
290    {"jobretention", store_time, ITEM(res_job.JobRetention),  0, 0, 0},
291    {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
292    {"prunejobs",   store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
293    {"prunefiles",  store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
294    {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
295    {"enabled",     store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
296    {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
297    {"spooldata",   store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
298    {"rerunfailedlevels",   store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
299    {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
300    {"runbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
301    {"runafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
302    {"runafterfailedjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
303    {"clientrunbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
304    {"clientrunafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
305    {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
306    {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
307    {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
308    {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
309    {"priority",   store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
310    {"writepartafterjob",   store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
311    {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
312    {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
313    {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
314    {NULL, NULL, {0}, 0, 0, 0}
315 };
316
317 /* FileSet resource
318  *
319  *   name          handler     value                 code flags    default_value
320  */
321 static RES_ITEM fs_items[] = {
322    {"name",        store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
323    {"description", store_str,  ITEM(res_fs.hdr.desc), 0, 0, 0},
324    {"include",     store_inc,  {0},                   0, ITEM_NO_EQUALS, 0},
325    {"exclude",     store_inc,  {0},                   1, ITEM_NO_EQUALS, 0},
326    {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
327    {"enablevss",   store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
328    {NULL,          NULL,       {0},                  0, 0, 0}
329 };
330
331 /* Schedule -- see run_conf.c */
332 /* Schedule
333  *
334  *   name          handler     value                 code flags    default_value
335  */
336 static RES_ITEM sch_items[] = {
337    {"name",     store_name,  ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
338    {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
339    {"run",      store_run,   ITEM(res_sch.run),      0, 0, 0},
340    {NULL, NULL, {0}, 0, 0, 0}
341 };
342
343 /* Pool resource
344  *
345  *   name             handler     value                        code flags default_value
346  */
347 static RES_ITEM pool_items[] = {
348    {"name",            store_name,    ITEM(res_pool.hdr.name),      0, ITEM_REQUIRED, 0},
349    {"description",     store_str,     ITEM(res_pool.hdr.desc),      0, 0,     0},
350    {"pooltype",        store_strname, ITEM(res_pool.pool_type),     0, ITEM_REQUIRED, 0},
351    {"labelformat",     store_strname, ITEM(res_pool.label_format),  0, 0,     0},
352    {"labeltype",       store_label,   ITEM(res_pool.LabelType),     0, 0,     0},     
353    {"cleaningprefix",  store_strname, ITEM(res_pool.cleaning_prefix), 0, 0,   0},
354    {"usecatalog",      store_bool,    ITEM(res_pool.use_catalog),    0, ITEM_DEFAULT, true},
355    {"usevolumeonce",   store_bool,    ITEM(res_pool.use_volume_once), 0, 0,   0},
356    {"purgeoldestvolume", store_bool,  ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
357    {"recycleoldestvolume", store_bool,  ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
358    {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
359    {"maximumvolumes",  store_pint,    ITEM(res_pool.max_volumes),   0, 0,        0},
360    {"maximumvolumejobs", store_pint,  ITEM(res_pool.MaxVolJobs),    0, 0,       0},
361    {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles),   0, 0,       0},
362    {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes),   0, 0,       0},
363    {"catalogfiles",    store_bool,    ITEM(res_pool.catalog_files),  0, ITEM_DEFAULT, true},
364    {"volumeretention", store_time,    ITEM(res_pool.VolRetention),   0, ITEM_DEFAULT, 60*60*24*365},
365    {"volumeuseduration", store_time,  ITEM(res_pool.VolUseDuration), 0, 0, 0},
366    {"migrationtime",  store_time,     ITEM(res_pool.MigrationTime), 0, 0, 0},
367    {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
368    {"migrationlowbytes", store_size,  ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
369    {"nextpool",      store_res,       ITEM(res_pool.NextPool), R_POOL, 0, 0},
370    {"storage",       store_alist_res, ITEM(res_pool.storage),  R_STORAGE, 0, 0},
371    {"autoprune",       store_bool,    ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
372    {"recycle",         store_bool,    ITEM(res_pool.Recycle),   0, ITEM_DEFAULT, true},
373    {"recyclepool",     store_res,     ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
374    {NULL, NULL, {0}, 0, 0, 0}
375 };
376
377 /*
378  * Counter Resource
379  *   name             handler     value                        code flags default_value
380  */
381 static RES_ITEM counter_items[] = {
382    {"name",            store_name,    ITEM(res_counter.hdr.name),        0, ITEM_REQUIRED, 0},
383    {"description",     store_str,     ITEM(res_counter.hdr.desc),        0, 0,     0},
384    {"minimum",         store_int,     ITEM(res_counter.MinValue),        0, ITEM_DEFAULT, 0},
385    {"maximum",         store_pint,    ITEM(res_counter.MaxValue),        0, ITEM_DEFAULT, INT32_MAX},
386    {"wrapcounter",     store_res,     ITEM(res_counter.WrapCounter),     R_COUNTER, 0, 0},
387    {"catalog",         store_res,     ITEM(res_counter.Catalog),         R_CATALOG, 0, 0},
388    {NULL, NULL, {0}, 0, 0, 0}
389 };
390
391
392 /* Message resource */
393 extern RES_ITEM msgs_items[];
394
395 /*
396  * This is the master resource definition.
397  * It must have one item for each of the resources.
398  *
399  *  NOTE!!! keep it in the same order as the R_codes
400  *    or eliminate all resources[rindex].name
401  *
402  *  name             items        rcode        res_head
403  */
404 RES_TABLE resources[] = {
405    {"director",      dir_items,   R_DIRECTOR},
406    {"client",        cli_items,   R_CLIENT},
407    {"job",           job_items,   R_JOB},
408    {"storage",       store_items, R_STORAGE},
409    {"catalog",       cat_items,   R_CATALOG},
410    {"schedule",      sch_items,   R_SCHEDULE},
411    {"fileset",       fs_items,    R_FILESET},
412    {"pool",          pool_items,  R_POOL},
413    {"messages",      msgs_items,  R_MSGS},
414    {"counter",       counter_items, R_COUNTER},
415    {"console",       con_items,   R_CONSOLE},
416    {"jobdefs",       job_items,   R_JOBDEFS},
417    {"device",        NULL,        R_DEVICE},  /* info obtained from SD */
418    {NULL,            NULL,        0}
419 };
420
421
422 /* Keywords (RHS) permitted in Job Level records
423  *
424  *   level_name      level              job_type
425  */
426 struct s_jl joblevels[] = {
427    {"Full",          L_FULL,            JT_BACKUP},
428    {"Base",          L_BASE,            JT_BACKUP},
429    {"Incremental",   L_INCREMENTAL,     JT_BACKUP},
430    {"Differential",  L_DIFFERENTIAL,    JT_BACKUP},
431    {"Since",         L_SINCE,           JT_BACKUP},
432    {"Catalog",       L_VERIFY_CATALOG,  JT_VERIFY},
433    {"InitCatalog",   L_VERIFY_INIT,     JT_VERIFY},
434    {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG,   JT_VERIFY},
435    {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG,   JT_VERIFY},
436    {"Data",          L_VERIFY_DATA,     JT_VERIFY},
437    {" ",             L_NONE,            JT_ADMIN},
438    {" ",             L_NONE,            JT_RESTORE},
439    {NULL,            0,                          0}
440 };
441
442 /* Keywords (RHS) permitted in Job type records
443  *
444  *   type_name       job_type
445  */
446 struct s_jt jobtypes[] = {
447    {"backup",        JT_BACKUP},
448    {"admin",         JT_ADMIN},
449    {"verify",        JT_VERIFY},
450    {"restore",       JT_RESTORE},
451    {"migrate",       JT_MIGRATE},
452    {NULL,            0}
453 };
454
455
456 /* Keywords (RHS) permitted in Selection type records
457  *
458  *   type_name       job_type
459  */
460 struct s_jt migtypes[] = {
461    {"smallestvolume",   MT_SMALLEST_VOL},
462    {"oldestvolume",     MT_OLDEST_VOL},
463    {"pooloccupancy",    MT_POOL_OCCUPANCY},
464    {"pooltime",         MT_POOL_TIME},
465    {"client",           MT_CLIENT},
466    {"volume",           MT_VOLUME},
467    {"job",              MT_JOB},
468    {"sqlquery",         MT_SQLQUERY},
469    {NULL,            0}
470 };
471
472
473
474 /* Options permitted in Restore replace= */
475 struct s_kw ReplaceOptions[] = {
476    {"always",         REPLACE_ALWAYS},
477    {"ifnewer",        REPLACE_IFNEWER},
478    {"ifolder",        REPLACE_IFOLDER},
479    {"never",          REPLACE_NEVER},
480    {NULL,               0}
481 };
482
483 const char *level_to_str(int level)
484 {
485    int i;
486    static char level_no[30];
487    const char *str = level_no;
488
489    bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level);    /* default if not found */
490    for (i=0; joblevels[i].level_name; i++) {
491       if (level == joblevels[i].level) {
492          str = joblevels[i].level_name;
493          break;
494       }
495    }
496    return str;
497 }
498
499 /* Dump contents of resource */
500 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
501 {
502    URES *res = (URES *)reshdr;
503    bool recurse = true;
504    char ed1[100], ed2[100], ed3[100];
505    DEVICE *dev;
506
507    if (res == NULL) {
508       sendit(sock, _("No %s resource defined\n"), res_to_str(type));
509       return;
510    }
511    if (type < 0) {                    /* no recursion */
512       type = - type;
513       recurse = false;
514    }
515    switch (type) {
516    case R_DIRECTOR:
517       sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
518          reshdr->name, res->res_dir.MaxConcurrentJobs,
519          edit_uint64(res->res_dir.FDConnectTimeout, ed1),
520          edit_uint64(res->res_dir.SDConnectTimeout, ed2));
521       if (res->res_dir.query_file) {
522          sendit(sock, _("   query_file=%s\n"), res->res_dir.query_file);
523       }
524       if (res->res_dir.messages) {
525          sendit(sock, _("  --> "));
526          dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
527       }
528       break;
529    case R_CONSOLE:
530       sendit(sock, _("Console: name=%s SSL=%d\n"),
531          res->res_con.hdr.name, res->res_con.tls_enable);
532       break;
533    case R_COUNTER:
534       if (res->res_counter.WrapCounter) {
535          sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
536             res->res_counter.hdr.name, res->res_counter.MinValue,
537             res->res_counter.MaxValue, res->res_counter.CurrentValue,
538             res->res_counter.WrapCounter->hdr.name);
539       } else {
540          sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
541             res->res_counter.hdr.name, res->res_counter.MinValue,
542             res->res_counter.MaxValue);
543       }
544       if (res->res_counter.Catalog) {
545          sendit(sock, _("  --> "));
546          dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
547       }
548       break;
549
550    case R_CLIENT:
551       sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
552          res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
553          res->res_client.MaxConcurrentJobs);
554       sendit(sock, _("      JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
555          edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
556          edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
557          res->res_client.AutoPrune);
558       if (res->res_client.catalog) {
559          sendit(sock, _("  --> "));
560          dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
561       }
562       break;
563    case R_DEVICE:
564       dev = &res->res_dev;
565       char ed1[50];
566       sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
567 "      reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
568 "      poolid=%s volname=%s MediaType=%s\n"),
569          dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
570          dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
571          dev->offline, dev->autochanger,
572          edit_uint64(dev->PoolId, ed1),
573          dev->VolumeName, dev->MediaType);
574       break;
575    case R_STORAGE:
576       sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
577 "      DeviceName=%s MediaType=%s StorageId=%s\n"),
578          res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
579          res->res_store.MaxConcurrentJobs,
580          res->res_store.dev_name(),
581          res->res_store.media_type,
582          edit_int64(res->res_store.StorageId, ed1));
583       break;
584    case R_CATALOG:
585       sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
586 "      db_user=%s MutliDBConn=%d\n"),
587          res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
588          res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
589          res->res_cat.mult_db_connections);
590       break;
591    case R_JOB:
592    case R_JOBDEFS:
593       sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
594          type == R_JOB ? _("Job") : _("JobDefs"),
595          res->res_job.hdr.name, res->res_job.JobType,
596          level_to_str(res->res_job.JobLevel), res->res_job.Priority,
597          res->res_job.enabled);
598       sendit(sock, _("     MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
599          res->res_job.MaxConcurrentJobs, 
600          res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
601          edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
602          res->res_job.spool_data, res->res_job.write_part_after_job);
603       if (res->res_job.JobType == JT_MIGRATE) {
604          sendit(sock, _("     SelectionType=%d\n"), res->res_job.selection_type);
605       }
606       if (res->res_job.client) {
607          sendit(sock, _("  --> "));
608          dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
609       }
610       if (res->res_job.fileset) {
611          sendit(sock, _("  --> "));
612          dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
613       }
614       if (res->res_job.schedule) {
615          sendit(sock, _("  --> "));
616          dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
617       }
618       if (res->res_job.RestoreWhere) {
619          sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
620       }
621       if (res->res_job.where_use_regexp) {
622          sendit(sock, _("  --> RWhere=%u\n"), res->res_job.where_use_regexp);
623       }
624       if (res->res_job.RestoreBootstrap) {
625          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
626       }
627       if (res->res_job.WriteBootstrap) {
628          sendit(sock, _("  --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
629       }
630       if (res->res_job.storage) {
631          STORE *store;
632          foreach_alist(store, res->res_job.storage) {
633             sendit(sock, _("  --> "));
634             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
635          }
636       }
637       if (res->res_job.RunScripts) {
638         RUNSCRIPT *script;
639         foreach_alist(script, res->res_job.RunScripts) {
640            sendit(sock, _(" --> RunScript\n"));
641            sendit(sock, _("  --> Command=%s\n"), NPRT(script->command));
642            sendit(sock, _("  --> Target=%s\n"),  NPRT(script->target));
643            sendit(sock, _("  --> RunOnSuccess=%u\n"),  script->on_success);
644            sendit(sock, _("  --> RunOnFailure=%u\n"),  script->on_failure);
645            sendit(sock, _("  --> AbortJobOnError=%u\n"),  script->abort_on_error);
646            sendit(sock, _("  --> RunWhen=%u\n"),  script->when);
647         }
648       }
649       if (res->res_job.pool) {
650          sendit(sock, _("  --> "));
651          dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
652       }
653       if (res->res_job.full_pool) {
654          sendit(sock, _("  --> "));
655          dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
656       }
657       if (res->res_job.inc_pool) {
658          sendit(sock, _("  --> "));
659          dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
660       }
661       if (res->res_job.diff_pool) {
662          sendit(sock, _("  --> "));
663          dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
664       }
665       if (res->res_job.verify_job) {
666          sendit(sock, _("  --> "));
667          dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
668       }
669       if (res->res_job.run_cmds) {
670          char *runcmd;
671          foreach_alist(runcmd, res->res_job.run_cmds) {
672             sendit(sock, _("  --> Run=%s\n"), runcmd);
673          }
674       }
675       if (res->res_job.selection_pattern) {
676          sendit(sock, _("  --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
677       }
678       if (res->res_job.messages) {
679          sendit(sock, _("  --> "));
680          dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
681       }
682       break;
683    case R_FILESET:
684    {
685       int i, j, k;
686       sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
687       for (i=0; i<res->res_fs.num_includes; i++) {
688          INCEXE *incexe = res->res_fs.include_items[i];
689          for (j=0; j<incexe->num_opts; j++) {
690             FOPTS *fo = incexe->opts_list[j];
691             sendit(sock, "      O %s\n", fo->opts);
692
693             bool enhanced_wild = false;
694             for (k=0; fo->opts[k]!='\0'; k++) {
695                if (fo->opts[k]=='W') {
696                   enhanced_wild = true;
697                   break;
698                }
699             }
700
701             for (k=0; k<fo->regex.size(); k++) {
702                sendit(sock, "      R %s\n", fo->regex.get(k));
703             }
704             for (k=0; k<fo->regexdir.size(); k++) {
705                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
706             }
707             for (k=0; k<fo->regexfile.size(); k++) {
708                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
709             }
710             for (k=0; k<fo->wild.size(); k++) {
711                sendit(sock, "      W %s\n", fo->wild.get(k));
712             }
713             for (k=0; k<fo->wilddir.size(); k++) {
714                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
715             }
716             for (k=0; k<fo->wildfile.size(); k++) {
717                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
718             }
719             for (k=0; k<fo->wildbase.size(); k++) {
720                sendit(sock, "      W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
721             }
722             for (k=0; k<fo->base.size(); k++) {
723                sendit(sock, "      B %s\n", fo->base.get(k));
724             }
725             for (k=0; k<fo->fstype.size(); k++) {
726                sendit(sock, "      X %s\n", fo->fstype.get(k));
727             }
728             for (k=0; k<fo->drivetype.size(); k++) {
729                sendit(sock, "      XD %s\n", fo->drivetype.get(k));
730             }
731             if (fo->reader) {
732                sendit(sock, "      D %s\n", fo->reader);
733             }
734             if (fo->writer) {
735                sendit(sock, "      T %s\n", fo->writer);
736             }
737             sendit(sock, "      N\n");
738          }
739          for (j=0; j<incexe->name_list.size(); j++) {
740             sendit(sock, "      I %s\n", incexe->name_list.get(j));
741          }
742          if (incexe->name_list.size()) {
743             sendit(sock, "      N\n");
744          }
745       }
746
747       for (i=0; i<res->res_fs.num_excludes; i++) {
748          INCEXE *incexe = res->res_fs.exclude_items[i];
749          for (j=0; j<incexe->name_list.size(); j++) {
750             sendit(sock, "      E %s\n", incexe->name_list.get(j));
751          }
752          if (incexe->name_list.size()) {
753             sendit(sock, "      N\n");
754          }
755       }
756       break;
757    }
758    case R_SCHEDULE:
759       if (res->res_sch.run) {
760          int i;
761          RUN *run = res->res_sch.run;
762          char buf[1000], num[30];
763          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
764          if (!run) {
765             break;
766          }
767 next_run:
768          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
769          bstrncpy(buf, _("      hour="), sizeof(buf));
770          for (i=0; i<24; i++) {
771             if (bit_is_set(i, run->hour)) {
772                bsnprintf(num, sizeof(num), "%d ", i);
773                bstrncat(buf, num, sizeof(buf));
774             }
775          }
776          bstrncat(buf, "\n", sizeof(buf));
777          sendit(sock, buf);
778          bstrncpy(buf, _("      mday="), sizeof(buf));
779          for (i=0; i<31; i++) {
780             if (bit_is_set(i, run->mday)) {
781                bsnprintf(num, sizeof(num), "%d ", i);
782                bstrncat(buf, num, sizeof(buf));
783             }
784          }
785          bstrncat(buf, "\n", sizeof(buf));
786          sendit(sock, buf);
787          bstrncpy(buf, _("      month="), sizeof(buf));
788          for (i=0; i<12; i++) {
789             if (bit_is_set(i, run->month)) {
790                bsnprintf(num, sizeof(num), "%d ", i);
791                bstrncat(buf, num, sizeof(buf));
792             }
793          }
794          bstrncat(buf, "\n", sizeof(buf));
795          sendit(sock, buf);
796          bstrncpy(buf, _("      wday="), sizeof(buf));
797          for (i=0; i<7; i++) {
798             if (bit_is_set(i, run->wday)) {
799                bsnprintf(num, sizeof(num), "%d ", i);
800                bstrncat(buf, num, sizeof(buf));
801             }
802          }
803          bstrncat(buf, "\n", sizeof(buf));
804          sendit(sock, buf);
805          bstrncpy(buf, _("      wom="), sizeof(buf));
806          for (i=0; i<5; i++) {
807             if (bit_is_set(i, run->wom)) {
808                bsnprintf(num, sizeof(num), "%d ", i);
809                bstrncat(buf, num, sizeof(buf));
810             }
811          }
812          bstrncat(buf, "\n", sizeof(buf));
813          sendit(sock, buf);
814          bstrncpy(buf, _("      woy="), sizeof(buf));
815          for (i=0; i<54; i++) {
816             if (bit_is_set(i, run->woy)) {
817                bsnprintf(num, sizeof(num), "%d ", i);
818                bstrncat(buf, num, sizeof(buf));
819             }
820          }
821          bstrncat(buf, "\n", sizeof(buf));
822          sendit(sock, buf);
823          sendit(sock, _("      mins=%d\n"), run->minute);
824          if (run->pool) {
825             sendit(sock, _("     --> "));
826             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
827          }
828          if (run->storage) {
829             sendit(sock, _("     --> "));
830             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
831          }
832          if (run->msgs) {
833             sendit(sock, _("     --> "));
834             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
835          }
836          /* If another Run record is chained in, go print it */
837          if (run->next) {
838             run = run->next;
839             goto next_run;
840          }
841       } else {
842          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
843       }
844       break;
845    case R_POOL:
846       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
847               res->res_pool.pool_type);
848       sendit(sock, _("      use_cat=%d use_once=%d cat_files=%d\n"),
849               res->res_pool.use_catalog, res->res_pool.use_volume_once,
850               res->res_pool.catalog_files);
851       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
852               res->res_pool.max_volumes, res->res_pool.AutoPrune,
853               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
854       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
855               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
856               res->res_pool.Recycle,
857               NPRT(res->res_pool.label_format));
858       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
859               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
860       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d\n"), 
861               res->res_pool.recycle_oldest_volume,
862               res->res_pool.purge_oldest_volume);
863       sendit(sock, _("      MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
864               res->res_pool.MaxVolJobs, 
865               res->res_pool.MaxVolFiles,
866               edit_uint64(res->res_pool.MaxVolFiles, ed1));
867       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
868               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
869               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
870               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
871       if (res->res_pool.NextPool) {
872          sendit(sock, _("      NextPool=%s\n"), res->res_pool.NextPool->name());
873       }
874       if (res->res_pool.RecyclePool) {
875          sendit(sock, _("      RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
876       }
877       if (res->res_pool.storage) {
878          STORE *store;
879          foreach_alist(store, res->res_pool.storage) {
880             sendit(sock, _("  --> "));
881             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
882          }
883       }
884       break;
885    case R_MSGS:
886       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
887       if (res->res_msgs.mail_cmd)
888          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
889       if (res->res_msgs.operator_cmd)
890          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
891       break;
892    default:
893       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
894       break;
895    }
896    if (recurse && res->res_dir.hdr.next) {
897       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
898    }
899 }
900
901 /*
902  * Free all the members of an INCEXE structure
903  */
904 static void free_incexe(INCEXE *incexe)
905 {
906    incexe->name_list.destroy();
907    for (int i=0; i<incexe->num_opts; i++) {
908       FOPTS *fopt = incexe->opts_list[i];
909       fopt->regex.destroy();
910       fopt->regexdir.destroy();
911       fopt->regexfile.destroy();
912       fopt->wild.destroy();
913       fopt->wilddir.destroy();
914       fopt->wildfile.destroy();
915       fopt->wildbase.destroy();
916       fopt->base.destroy();
917       fopt->fstype.destroy();
918       fopt->drivetype.destroy();
919       if (fopt->reader) {
920          free(fopt->reader);
921       }
922       if (fopt->writer) {
923          free(fopt->writer);
924       }
925       free(fopt);
926    }
927    if (incexe->opts_list) {
928       free(incexe->opts_list);
929    }
930    free(incexe);
931 }
932
933 /*
934  * Free memory of resource -- called when daemon terminates.
935  * NB, we don't need to worry about freeing any references
936  * to other resources as they will be freed when that
937  * resource chain is traversed.  Mainly we worry about freeing
938  * allocated strings (names).
939  */
940 void free_resource(RES *sres, int type)
941 {
942    int num;
943    RES *nres;                         /* next resource if linked */
944    URES *res = (URES *)sres;
945
946    if (res == NULL)
947       return;
948
949    /* common stuff -- free the resource name and description */
950    nres = (RES *)res->res_dir.hdr.next;
951    if (res->res_dir.hdr.name) {
952       free(res->res_dir.hdr.name);
953    }
954    if (res->res_dir.hdr.desc) {
955       free(res->res_dir.hdr.desc);
956    }
957
958    switch (type) {
959    case R_DIRECTOR:
960       if (res->res_dir.working_directory) {
961          free(res->res_dir.working_directory);
962       }
963       if (res->res_dir.scripts_directory) {
964          free((char *)res->res_dir.scripts_directory);
965       }
966       if (res->res_dir.pid_directory) {
967          free(res->res_dir.pid_directory);
968       }
969       if (res->res_dir.subsys_directory) {
970          free(res->res_dir.subsys_directory);
971       }
972       if (res->res_dir.password) {
973          free(res->res_dir.password);
974       }
975       if (res->res_dir.query_file) {
976          free(res->res_dir.query_file);
977       }
978       if (res->res_dir.DIRaddrs) {
979          free_addresses(res->res_dir.DIRaddrs);
980       }
981       if (res->res_dir.tls_ctx) { 
982          free_tls_context(res->res_dir.tls_ctx);
983       }
984       if (res->res_dir.tls_ca_certfile) {
985          free(res->res_dir.tls_ca_certfile);
986       }
987       if (res->res_dir.tls_ca_certdir) {
988          free(res->res_dir.tls_ca_certdir);
989       }
990       if (res->res_dir.tls_certfile) {
991          free(res->res_dir.tls_certfile);
992       }
993       if (res->res_dir.tls_keyfile) {
994          free(res->res_dir.tls_keyfile);
995       }
996       if (res->res_dir.tls_dhfile) {
997          free(res->res_dir.tls_dhfile);
998       }
999       if (res->res_dir.tls_allowed_cns) {
1000          delete res->res_dir.tls_allowed_cns;
1001       }
1002       break;
1003    case R_DEVICE:
1004    case R_COUNTER:
1005        break;
1006    case R_CONSOLE:
1007       if (res->res_con.password) {
1008          free(res->res_con.password);
1009       }
1010       if (res->res_con.tls_ctx) { 
1011          free_tls_context(res->res_con.tls_ctx);
1012       }
1013       if (res->res_con.tls_ca_certfile) {
1014          free(res->res_con.tls_ca_certfile);
1015       }
1016       if (res->res_con.tls_ca_certdir) {
1017          free(res->res_con.tls_ca_certdir);
1018       }
1019       if (res->res_con.tls_certfile) {
1020          free(res->res_con.tls_certfile);
1021       }
1022       if (res->res_con.tls_keyfile) {
1023          free(res->res_con.tls_keyfile);
1024       }
1025       if (res->res_con.tls_dhfile) {
1026          free(res->res_con.tls_dhfile);
1027       }
1028       if (res->res_con.tls_allowed_cns) {
1029          delete res->res_con.tls_allowed_cns;
1030       }
1031       for (int i=0; i<Num_ACL; i++) {
1032          if (res->res_con.ACL_lists[i]) {
1033             delete res->res_con.ACL_lists[i];
1034             res->res_con.ACL_lists[i] = NULL;
1035          }
1036       }
1037       break;
1038    case R_CLIENT:
1039       if (res->res_client.address) {
1040          free(res->res_client.address);
1041       }
1042       if (res->res_client.password) {
1043          free(res->res_client.password);
1044       }
1045       if (res->res_client.tls_ctx) { 
1046          free_tls_context(res->res_client.tls_ctx);
1047       }
1048       if (res->res_client.tls_ca_certfile) {
1049          free(res->res_client.tls_ca_certfile);
1050       }
1051       if (res->res_client.tls_ca_certdir) {
1052          free(res->res_client.tls_ca_certdir);
1053       }
1054       if (res->res_client.tls_certfile) {
1055          free(res->res_client.tls_certfile);
1056       }
1057       if (res->res_client.tls_keyfile) {
1058          free(res->res_client.tls_keyfile);
1059       }
1060       if (res->res_client.tls_allowed_cns) {
1061          delete res->res_client.tls_allowed_cns;
1062       }
1063       break;
1064    case R_STORAGE:
1065       if (res->res_store.address) {
1066          free(res->res_store.address);
1067       }
1068       if (res->res_store.password) {
1069          free(res->res_store.password);
1070       }
1071       if (res->res_store.media_type) {
1072          free(res->res_store.media_type);
1073       }
1074       if (res->res_store.device) {
1075          delete res->res_store.device;
1076       }
1077       if (res->res_store.tls_ctx) { 
1078          free_tls_context(res->res_store.tls_ctx);
1079       }
1080       if (res->res_store.tls_ca_certfile) {
1081          free(res->res_store.tls_ca_certfile);
1082       }
1083       if (res->res_store.tls_ca_certdir) {
1084          free(res->res_store.tls_ca_certdir);
1085       }
1086       if (res->res_store.tls_certfile) {
1087          free(res->res_store.tls_certfile);
1088       }
1089       if (res->res_store.tls_keyfile) {
1090          free(res->res_store.tls_keyfile);
1091       }
1092       break;
1093    case R_CATALOG:
1094       if (res->res_cat.db_address) {
1095          free(res->res_cat.db_address);
1096       }
1097       if (res->res_cat.db_socket) {
1098          free(res->res_cat.db_socket);
1099       }
1100       if (res->res_cat.db_user) {
1101          free(res->res_cat.db_user);
1102       }
1103       if (res->res_cat.db_name) {
1104          free(res->res_cat.db_name);
1105       }
1106       if (res->res_cat.db_password) {
1107          free(res->res_cat.db_password);
1108       }
1109       break;
1110    case R_FILESET:
1111       if ((num=res->res_fs.num_includes)) {
1112          while (--num >= 0) {
1113             free_incexe(res->res_fs.include_items[num]);
1114          }
1115          free(res->res_fs.include_items);
1116       }
1117       res->res_fs.num_includes = 0;
1118       if ((num=res->res_fs.num_excludes)) {
1119          while (--num >= 0) {
1120             free_incexe(res->res_fs.exclude_items[num]);
1121          }
1122          free(res->res_fs.exclude_items);
1123       }
1124       res->res_fs.num_excludes = 0;
1125       break;
1126    case R_POOL:
1127       if (res->res_pool.pool_type) {
1128          free(res->res_pool.pool_type);
1129       }
1130       if (res->res_pool.label_format) {
1131          free(res->res_pool.label_format);
1132       }
1133       if (res->res_pool.cleaning_prefix) {
1134          free(res->res_pool.cleaning_prefix);
1135       }
1136       if (res->res_pool.storage) {
1137          delete res->res_pool.storage;
1138       }
1139       break;
1140    case R_SCHEDULE:
1141       if (res->res_sch.run) {
1142          RUN *nrun, *next;
1143          nrun = res->res_sch.run;
1144          while (nrun) {
1145             next = nrun->next;
1146             free(nrun);
1147             nrun = next;
1148          }
1149       }
1150       break;
1151    case R_JOB:
1152    case R_JOBDEFS:
1153       if (res->res_job.RestoreWhere) {
1154          free(res->res_job.RestoreWhere);
1155       }
1156       if (res->res_job.strip_prefix) {
1157          free(res->res_job.strip_prefix);
1158       }
1159       if (res->res_job.add_prefix) {
1160          free(res->res_job.add_prefix);
1161       }
1162       if (res->res_job.add_suffix) {
1163          free(res->res_job.add_suffix);
1164       }
1165       if (res->res_job.RestoreBootstrap) {
1166          free(res->res_job.RestoreBootstrap);
1167       }
1168       if (res->res_job.WriteBootstrap) {
1169          free(res->res_job.WriteBootstrap);
1170       }
1171       if (res->res_job.selection_pattern) {
1172          free(res->res_job.selection_pattern);
1173       }
1174       if (res->res_job.run_cmds) {
1175          delete res->res_job.run_cmds;
1176       }
1177       if (res->res_job.storage) {
1178          delete res->res_job.storage;
1179       }
1180       if (res->res_job.RunScripts) {
1181          free_runscripts(res->res_job.RunScripts);
1182          delete res->res_job.RunScripts;
1183       }
1184       break;
1185    case R_MSGS:
1186       if (res->res_msgs.mail_cmd) {
1187          free(res->res_msgs.mail_cmd);
1188       }
1189       if (res->res_msgs.operator_cmd) {
1190          free(res->res_msgs.operator_cmd);
1191       }
1192       free_msgs_res((MSGS *)res);  /* free message resource */
1193       res = NULL;
1194       break;
1195    default:
1196       printf(_("Unknown resource type %d in free_resource.\n"), type);
1197    }
1198    /* Common stuff again -- free the resource, recurse to next one */
1199    if (res) {
1200       free(res);
1201    }
1202    if (nres) {
1203       free_resource(nres, type);
1204    }
1205 }
1206
1207 /*
1208  * Save the new resource by chaining it into the head list for
1209  * the resource. If this is pass 2, we update any resource
1210  * pointers because they may not have been defined until
1211  * later in pass 1.
1212  */
1213 void save_resource(int type, RES_ITEM *items, int pass)
1214 {
1215    URES *res;
1216    int rindex = type - r_first;
1217    int i, size = 0;
1218    bool error = false;
1219
1220    /* Check Job requirements after applying JobDefs */
1221    if (type != R_JOB && type != R_JOBDEFS) {
1222       /*
1223        * Ensure that all required items are present
1224        */
1225       for (i=0; items[i].name; i++) {
1226          if (items[i].flags & ITEM_REQUIRED) {
1227             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1228                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1229                     items[i].name, resources[rindex]);
1230             }
1231          }
1232          /* If this triggers, take a look at lib/parse_conf.h */
1233          if (i >= MAX_RES_ITEMS) {
1234             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1235          }
1236       }
1237    } else if (type == R_JOB) {
1238       /*
1239        * Ensure that the name item is present
1240        */
1241       if (items[0].flags & ITEM_REQUIRED) {
1242          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1243              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1244                    items[0].name, resources[rindex]);
1245          }
1246       }
1247    }
1248
1249    /*
1250     * During pass 2 in each "store" routine, we looked up pointers
1251     * to all the resources referrenced in the current resource, now we
1252     * must copy their addresses from the static record to the allocated
1253     * record.
1254     */
1255    if (pass == 2) {
1256       switch (type) {
1257       /* Resources not containing a resource */
1258       case R_CATALOG:
1259       case R_MSGS:
1260       case R_FILESET:
1261       case R_DEVICE:
1262          break;
1263
1264       /*
1265        * Resources containing another resource or alist. First
1266        *  look up the resource which contains another resource. It
1267        *  was written during pass 1.  Then stuff in the pointers to
1268        *  the resources it contains, which were inserted this pass.
1269        *  Finally, it will all be stored back.
1270        */
1271       case R_POOL:
1272          /* Find resource saved in pass 1 */
1273          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1274             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1275          }
1276          /* Explicitly copy resource pointers from this pass (res_all) */
1277          res->res_pool.NextPool = res_all.res_pool.NextPool;
1278          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1279          res->res_pool.storage    = res_all.res_pool.storage;
1280          break;
1281       case R_CONSOLE:
1282          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1283             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1284          }
1285          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1286          break;
1287       case R_DIRECTOR:
1288          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1289             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1290          }
1291          res->res_dir.messages = res_all.res_dir.messages;
1292          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1293          break;
1294       case R_STORAGE:
1295          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1296             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1297                   res_all.res_dir.hdr.name);
1298          }
1299          /* we must explicitly copy the device alist pointer */
1300          res->res_store.device   = res_all.res_store.device;
1301          break;
1302       case R_JOB:
1303       case R_JOBDEFS:
1304          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1305             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1306                   res_all.res_dir.hdr.name);
1307          }
1308          res->res_job.messages   = res_all.res_job.messages;
1309          res->res_job.schedule   = res_all.res_job.schedule;
1310          res->res_job.client     = res_all.res_job.client;
1311          res->res_job.fileset    = res_all.res_job.fileset;
1312          res->res_job.storage    = res_all.res_job.storage;
1313          res->res_job.pool       = res_all.res_job.pool;
1314          res->res_job.full_pool  = res_all.res_job.full_pool;
1315          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1316          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1317          res->res_job.verify_job = res_all.res_job.verify_job;
1318          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1319          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1320          res->res_job.RunScripts = res_all.res_job.RunScripts;
1321          if (res->res_job.strip_prefix ||
1322              res->res_job.add_suffix   ||
1323              res->res_job.add_prefix)
1324          {
1325             if (res->res_job.RestoreWhere) {
1326                free(res->res_job.RestoreWhere);
1327             }
1328             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1329                                                    res->res_job.add_prefix,
1330                                                    res->res_job.add_suffix);
1331             res->res_job.RestoreWhere = (char *) bmalloc (len * sizeof(char));
1332             bregexp_build_where(res->res_job.RestoreWhere, len,
1333                                 res->res_job.strip_prefix,
1334                                 res->res_job.add_prefix,
1335                                 res->res_job.add_suffix);
1336             res->res_job.where_use_regexp = true;
1337
1338             /* TODO: test bregexp */
1339          }
1340          break;
1341       case R_COUNTER:
1342          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1343             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1344          }
1345          res->res_counter.Catalog = res_all.res_counter.Catalog;
1346          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1347          break;
1348
1349       case R_CLIENT:
1350          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1351             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1352          }
1353          res->res_client.catalog = res_all.res_client.catalog;
1354          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1355          break;
1356       case R_SCHEDULE:
1357          /*
1358           * Schedule is a bit different in that it contains a RUN record
1359           * chain which isn't a "named" resource. This chain was linked
1360           * in by run_conf.c during pass 2, so here we jam the pointer
1361           * into the Schedule resource.
1362           */
1363          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1364             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1365          }
1366          res->res_sch.run = res_all.res_sch.run;
1367          break;
1368       default:
1369          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1370          error = true;
1371          break;
1372       }
1373       /* Note, the resource name was already saved during pass 1,
1374        * so here, we can just release it.
1375        */
1376       if (res_all.res_dir.hdr.name) {
1377          free(res_all.res_dir.hdr.name);
1378          res_all.res_dir.hdr.name = NULL;
1379       }
1380       if (res_all.res_dir.hdr.desc) {
1381          free(res_all.res_dir.hdr.desc);
1382          res_all.res_dir.hdr.desc = NULL;
1383       }
1384       return;
1385    }
1386
1387    /*
1388     * The following code is only executed during pass 1
1389     */
1390    switch (type) {
1391    case R_DIRECTOR:
1392       size = sizeof(DIRRES);
1393       break;
1394    case R_CONSOLE:
1395       size = sizeof(CONRES);
1396       break;
1397    case R_CLIENT:
1398       size =sizeof(CLIENT);
1399       break;
1400    case R_STORAGE:
1401       size = sizeof(STORE);
1402       break;
1403    case R_CATALOG:
1404       size = sizeof(CAT);
1405       break;
1406    case R_JOB:
1407    case R_JOBDEFS:
1408       size = sizeof(JOB);
1409       break;
1410    case R_FILESET:
1411       size = sizeof(FILESET);
1412       break;
1413    case R_SCHEDULE:
1414       size = sizeof(SCHED);
1415       break;
1416    case R_POOL:
1417       size = sizeof(POOL);
1418       break;
1419    case R_MSGS:
1420       size = sizeof(MSGS);
1421       break;
1422    case R_COUNTER:
1423       size = sizeof(COUNTER);
1424       break;
1425    case R_DEVICE:
1426       error = true;
1427       break;
1428    default:
1429       printf(_("Unknown resource type %d in save_resource.\n"), type);
1430       error = true; 
1431       break;
1432    }
1433    /* Common */
1434    if (!error) {
1435       res = (URES *)malloc(size);
1436       memcpy(res, &res_all, size);
1437       if (!res_head[rindex]) {
1438          res_head[rindex] = (RES *)res; /* store first entry */
1439          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1440                res->res_dir.hdr.name, rindex);
1441       } else {
1442          RES *next, *last;
1443          if (res->res_dir.hdr.name == NULL) {
1444             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1445                   resources[rindex]);
1446          }   
1447          /* Add new res to end of chain */
1448          for (last=next=res_head[rindex]; next; next=next->next) {
1449             last = next;
1450             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1451                Emsg2(M_ERROR_TERM, 0,
1452                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1453                   resources[rindex].name, res->res_dir.hdr.name);
1454             }
1455          }
1456          last->next = (RES *)res;
1457          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1458                res->res_dir.hdr.name, rindex, pass);
1459       }
1460    }
1461 }
1462
1463 /*
1464  * Store Device. Note, the resource is created upon the
1465  *  first reference. The details of the resource are obtained
1466  *  later from the SD.
1467  */
1468 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1469 {
1470    int token;
1471    URES *res;
1472    int rindex = R_DEVICE - r_first;
1473    int size = sizeof(DEVICE);
1474    bool found = false;
1475
1476    if (pass == 1) {
1477       token = lex_get_token(lc, T_NAME);
1478       if (!res_head[rindex]) {
1479          res = (URES *)malloc(size);
1480          memset(res, 0, size);
1481          res->res_dev.hdr.name = bstrdup(lc->str);
1482          res_head[rindex] = (RES *)res; /* store first entry */
1483          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1484                res->res_dir.hdr.name, rindex);
1485       } else {
1486          RES *next;
1487          /* See if it is already defined */
1488          for (next=res_head[rindex]; next->next; next=next->next) {
1489             if (strcmp(next->name, lc->str) == 0) {
1490                found = true;
1491                break;
1492             }
1493          }
1494          if (!found) {
1495             res = (URES *)malloc(size);
1496             memset(res, 0, size);
1497             res->res_dev.hdr.name = bstrdup(lc->str);
1498             next->next = (RES *)res;
1499             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1500                res->res_dir.hdr.name, rindex, pass);
1501          }
1502       }
1503
1504       scan_to_eol(lc);
1505       set_bit(index, res_all.hdr.item_present);
1506    } else {
1507       store_alist_res(lc, item, index, pass);
1508    }
1509 }
1510
1511 /*
1512  * Store JobType (backup, verify, restore)
1513  *
1514  */
1515 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1516 {
1517    int token, i;
1518
1519    token = lex_get_token(lc, T_NAME);
1520    /* Store the type both pass 1 and pass 2 */
1521    for (i=0; migtypes[i].type_name; i++) {
1522       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1523          *(int *)(item->value) = migtypes[i].job_type;
1524          i = 0;
1525          break;
1526       }
1527    }
1528    if (i != 0) {
1529       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1530    }
1531    scan_to_eol(lc);
1532    set_bit(index, res_all.hdr.item_present);
1533 }
1534
1535
1536
1537 /*
1538  * Store JobType (backup, verify, restore)
1539  *
1540  */
1541 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1542 {
1543    int token, i;
1544
1545    token = lex_get_token(lc, T_NAME);
1546    /* Store the type both pass 1 and pass 2 */
1547    for (i=0; jobtypes[i].type_name; i++) {
1548       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1549          *(int *)(item->value) = jobtypes[i].job_type;
1550          i = 0;
1551          break;
1552       }
1553    }
1554    if (i != 0) {
1555       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1556    }
1557    scan_to_eol(lc);
1558    set_bit(index, res_all.hdr.item_present);
1559 }
1560
1561 /*
1562  * Store Job Level (Full, Incremental, ...)
1563  *
1564  */
1565 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1566 {
1567    int token, i;
1568
1569    token = lex_get_token(lc, T_NAME);
1570    /* Store the level pass 2 so that type is defined */
1571    for (i=0; joblevels[i].level_name; i++) {
1572       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1573          *(int *)(item->value) = joblevels[i].level;
1574          i = 0;
1575          break;
1576       }
1577    }
1578    if (i != 0) {
1579       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1580    }
1581    scan_to_eol(lc);
1582    set_bit(index, res_all.hdr.item_present);
1583 }
1584
1585
1586 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1587 {
1588    int token, i;
1589    token = lex_get_token(lc, T_NAME);
1590    /* Scan Replacement options */
1591    for (i=0; ReplaceOptions[i].name; i++) {
1592       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1593          *(int *)(item->value) = ReplaceOptions[i].token;
1594          i = 0;
1595          break;
1596       }
1597    }
1598    if (i != 0) {
1599       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1600    }
1601    scan_to_eol(lc);
1602    set_bit(index, res_all.hdr.item_present);
1603 }
1604
1605 /*
1606  * Store ACL (access control list)
1607  *
1608  */
1609 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1610 {
1611    int token;
1612
1613    for (;;) {
1614       token = lex_get_token(lc, T_STRING);
1615       if (pass == 1) {
1616          if (((alist **)item->value)[item->code] == NULL) {
1617             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1618             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1619          }
1620          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1621          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1622       }
1623       token = lex_get_token(lc, T_ALL);
1624       if (token == T_COMMA) {
1625          continue;                    /* get another ACL */
1626       }
1627       break;
1628    }
1629    set_bit(index, res_all.hdr.item_present);
1630 }
1631
1632 /* We build RunScripts items here */
1633 static RUNSCRIPT res_runscript;
1634
1635 /* Store a runscript->when in a bit field */
1636 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1637 {
1638    lex_get_token(lc, T_NAME);
1639
1640    if (strcasecmp(lc->str, "before") == 0) {
1641       *(int *)(item->value) = SCRIPT_Before ;
1642    } else if (strcasecmp(lc->str, "after") == 0) {
1643       *(int *)(item->value) = SCRIPT_After;
1644    } else if (strcasecmp(lc->str, "always") == 0) {
1645       *(int *)(item->value) = SCRIPT_Any;
1646    } else {
1647       scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1648    }
1649    scan_to_eol(lc);
1650 }
1651
1652 /* Store a runscript->target
1653  * 
1654  */
1655 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1656 {
1657    lex_get_token(lc, T_STRING);
1658
1659    if (pass == 2) {
1660       if (strcmp(lc->str, "%c") == 0) {
1661          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1662       } else if (strcasecmp(lc->str, "yes") == 0) {
1663          ((RUNSCRIPT*) item->value)->set_target("%c");
1664       } else if (strcasecmp(lc->str, "no") == 0) {
1665          ((RUNSCRIPT*) item->value)->set_target("");
1666       } else {
1667          RES *res = GetResWithName(R_CLIENT, lc->str);
1668          if (res == NULL) {
1669             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1670                       lc->str, lc->line_no, lc->line);
1671          }
1672
1673          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1674       }
1675    }
1676    scan_to_eol(lc);
1677 }
1678
1679 /* Store a runscript->command in a bit field
1680  * 
1681  */
1682 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1683 {
1684    lex_get_token(lc, T_STRING);
1685
1686    if (pass == 2) {
1687       ((RUNSCRIPT*) item->value)->set_command(lc->str);
1688    }
1689    scan_to_eol(lc);
1690 }
1691
1692 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1693 {
1694    lex_get_token(lc, T_STRING);
1695    alist **runscripts = (alist **)(item->value) ;
1696
1697    if (pass == 2) {
1698       RUNSCRIPT *script = new_runscript();
1699
1700       script->set_command(lc->str);
1701
1702       /* TODO: remove all script->old_proto with bacula 1.42 */
1703
1704       if (strcmp(item->name, "runbeforejob") == 0) {
1705          script->when = SCRIPT_Before;
1706          script->abort_on_error = true;
1707          script->set_target("");
1708
1709       } else if (strcmp(item->name, "runafterjob") == 0) {
1710          script->when = SCRIPT_After;
1711          script->on_success = true;
1712          script->on_failure = false;
1713          script->set_target("");
1714          
1715       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1716          script->old_proto = true;
1717          script->when = SCRIPT_After;
1718          script->set_target("%c");
1719          script->on_success = true;
1720          script->on_failure = false;
1721
1722       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1723          script->old_proto = true;
1724          script->when = SCRIPT_Before;
1725          script->set_target("%c");
1726          script->abort_on_error = true;
1727
1728       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1729          script->when = SCRIPT_After;
1730          script->on_failure = true;
1731          script->on_success = false;
1732          script->set_target("");
1733       }
1734
1735       if (*runscripts == NULL) {
1736         *runscripts = New(alist(10, not_owned_by_alist));
1737       }
1738       
1739       (*runscripts)->append(script);
1740       script->debug();
1741    }
1742
1743    scan_to_eol(lc);
1744 }
1745
1746 /* Store a bool in a bit field without modifing res_all.hdr 
1747  * We can also add an option to store_bool to skip res_all.hdr
1748  */
1749 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1750 {
1751    lex_get_token(lc, T_NAME);
1752    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1753       *(bool *)(item->value) = true;
1754    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1755       *(bool *)(item->value) = false;
1756    } else {
1757       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1758    }
1759    scan_to_eol(lc);
1760 }
1761
1762 /*
1763  * new RunScript items
1764  *   name             handler              value                             code flags default_value
1765  */
1766 static RES_ITEM runscript_items[] = {
1767  {"command",        store_runscript_cmd,  {(char **)&res_runscript},           0,  ITEM_REQUIRED, 0}, 
1768  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, 
1769  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
1770  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
1771  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.abort_on_error},0, 0, 0},
1772  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
1773  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
1774  {NULL, NULL, {0}, 0, 0, 0}
1775 };
1776
1777 /*
1778  * Store RunScript info
1779  *
1780  *  Note, when this routine is called, we are inside a Job
1781  *  resource.  We treat the RunScript like a sort of
1782  *  mini-resource within the Job resource.
1783  */
1784 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1785 {
1786    int token, i;
1787    alist **runscripts = (alist **)(item->value) ;
1788
1789    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1790
1791    res_runscript.reset_default();      /* setting on_success, on_failure, abort_on_error */
1792    
1793    token = lex_get_token(lc, T_SKIP_EOL);
1794    
1795    if (token != T_BOB) {
1796       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1797    }
1798    
1799    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1800       if (token == T_EOB) {
1801         break;
1802       }
1803       if (token != T_IDENTIFIER) {
1804         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1805       }
1806       for (i=0; runscript_items[i].name; i++) {
1807         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1808            token = lex_get_token(lc, T_SKIP_EOL);
1809            if (token != T_EQUALS) {
1810               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1811            }
1812            
1813            /* Call item handler */
1814            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1815            i = -1;
1816            break;
1817         }
1818       }
1819       
1820       if (i >=0) {
1821         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1822       }
1823    }
1824
1825    if (pass == 2) {
1826       if (res_runscript.command == NULL) {
1827          scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1828                    "command", "runscript");
1829       }
1830
1831       /* run on client by default */
1832       if (res_runscript.target == NULL) {
1833          res_runscript.set_target("%c");
1834       }
1835
1836       RUNSCRIPT *script = new_runscript();
1837       memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1838       
1839       if (*runscripts == NULL) {
1840         *runscripts = New(alist(10, not_owned_by_alist));
1841       }
1842       
1843       (*runscripts)->append(script);
1844       script->debug();
1845    }
1846
1847    scan_to_eol(lc);
1848    set_bit(index, res_all.hdr.item_present);
1849 }