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