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