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