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