]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Add support for arbitrary client-to-server certificate CN matching (TLS Allowed CN...
[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 MaxVolJobs=%d MaxVolFiles=%d\n"),
851               res->res_pool.recycle_oldest_volume,
852               res->res_pool.purge_oldest_volume,
853               res->res_pool.MaxVolJobs, res->res_pool.MaxVolFiles);
854       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
855               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
856               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
857               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
858       if (res->res_pool.NextPool) {
859          sendit(sock, _("      NextPool=%s\n"), res->res_pool.NextPool->name());
860       }
861       if (res->res_pool.RecyclePool) {
862          sendit(sock, _("      RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
863       }
864       if (res->res_pool.storage) {
865          STORE *store;
866          foreach_alist(store, res->res_pool.storage) {
867             sendit(sock, _("  --> "));
868             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
869          }
870       }
871       break;
872    case R_MSGS:
873       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
874       if (res->res_msgs.mail_cmd)
875          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
876       if (res->res_msgs.operator_cmd)
877          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
878       break;
879    default:
880       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
881       break;
882    }
883    if (recurse && res->res_dir.hdr.next) {
884       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
885    }
886 }
887
888 /*
889  * Free all the members of an INCEXE structure
890  */
891 static void free_incexe(INCEXE *incexe)
892 {
893    incexe->name_list.destroy();
894    for (int i=0; i<incexe->num_opts; i++) {
895       FOPTS *fopt = incexe->opts_list[i];
896       fopt->regex.destroy();
897       fopt->regexdir.destroy();
898       fopt->regexfile.destroy();
899       fopt->wild.destroy();
900       fopt->wilddir.destroy();
901       fopt->wildfile.destroy();
902       fopt->wildbase.destroy();
903       fopt->base.destroy();
904       fopt->fstype.destroy();
905       fopt->drivetype.destroy();
906       if (fopt->reader) {
907          free(fopt->reader);
908       }
909       if (fopt->writer) {
910          free(fopt->writer);
911       }
912       free(fopt);
913    }
914    if (incexe->opts_list) {
915       free(incexe->opts_list);
916    }
917    free(incexe);
918 }
919
920 /*
921  * Free memory of resource -- called when daemon terminates.
922  * NB, we don't need to worry about freeing any references
923  * to other resources as they will be freed when that
924  * resource chain is traversed.  Mainly we worry about freeing
925  * allocated strings (names).
926  */
927 void free_resource(RES *sres, int type)
928 {
929    int num;
930    RES *nres;                         /* next resource if linked */
931    URES *res = (URES *)sres;
932
933    if (res == NULL)
934       return;
935
936    /* common stuff -- free the resource name and description */
937    nres = (RES *)res->res_dir.hdr.next;
938    if (res->res_dir.hdr.name) {
939       free(res->res_dir.hdr.name);
940    }
941    if (res->res_dir.hdr.desc) {
942       free(res->res_dir.hdr.desc);
943    }
944
945    switch (type) {
946    case R_DIRECTOR:
947       if (res->res_dir.working_directory) {
948          free(res->res_dir.working_directory);
949       }
950       if (res->res_dir.scripts_directory) {
951          free((char *)res->res_dir.scripts_directory);
952       }
953       if (res->res_dir.pid_directory) {
954          free(res->res_dir.pid_directory);
955       }
956       if (res->res_dir.subsys_directory) {
957          free(res->res_dir.subsys_directory);
958       }
959       if (res->res_dir.password) {
960          free(res->res_dir.password);
961       }
962       if (res->res_dir.query_file) {
963          free(res->res_dir.query_file);
964       }
965       if (res->res_dir.DIRaddrs) {
966          free_addresses(res->res_dir.DIRaddrs);
967       }
968       if (res->res_dir.tls_ctx) { 
969          free_tls_context(res->res_dir.tls_ctx);
970       }
971       if (res->res_dir.tls_ca_certfile) {
972          free(res->res_dir.tls_ca_certfile);
973       }
974       if (res->res_dir.tls_ca_certdir) {
975          free(res->res_dir.tls_ca_certdir);
976       }
977       if (res->res_dir.tls_certfile) {
978          free(res->res_dir.tls_certfile);
979       }
980       if (res->res_dir.tls_keyfile) {
981          free(res->res_dir.tls_keyfile);
982       }
983       if (res->res_dir.tls_dhfile) {
984          free(res->res_dir.tls_dhfile);
985       }
986       if (res->res_dir.tls_allowed_cns) {
987          delete res->res_dir.tls_allowed_cns;
988       }
989       break;
990    case R_DEVICE:
991    case R_COUNTER:
992        break;
993    case R_CONSOLE:
994       if (res->res_con.password) {
995          free(res->res_con.password);
996       }
997       if (res->res_con.tls_ctx) { 
998          free_tls_context(res->res_con.tls_ctx);
999       }
1000       if (res->res_con.tls_ca_certfile) {
1001          free(res->res_con.tls_ca_certfile);
1002       }
1003       if (res->res_con.tls_ca_certdir) {
1004          free(res->res_con.tls_ca_certdir);
1005       }
1006       if (res->res_con.tls_certfile) {
1007          free(res->res_con.tls_certfile);
1008       }
1009       if (res->res_con.tls_keyfile) {
1010          free(res->res_con.tls_keyfile);
1011       }
1012       if (res->res_con.tls_dhfile) {
1013          free(res->res_con.tls_dhfile);
1014       }
1015       if (res->res_con.tls_allowed_cns) {
1016          delete res->res_con.tls_allowed_cns;
1017       }
1018       for (int i=0; i<Num_ACL; i++) {
1019          if (res->res_con.ACL_lists[i]) {
1020             delete res->res_con.ACL_lists[i];
1021             res->res_con.ACL_lists[i] = NULL;
1022          }
1023       }
1024       break;
1025    case R_CLIENT:
1026       if (res->res_client.address) {
1027          free(res->res_client.address);
1028       }
1029       if (res->res_client.password) {
1030          free(res->res_client.password);
1031       }
1032       if (res->res_client.tls_ctx) { 
1033          free_tls_context(res->res_client.tls_ctx);
1034       }
1035       if (res->res_client.tls_ca_certfile) {
1036          free(res->res_client.tls_ca_certfile);
1037       }
1038       if (res->res_client.tls_ca_certdir) {
1039          free(res->res_client.tls_ca_certdir);
1040       }
1041       if (res->res_client.tls_certfile) {
1042          free(res->res_client.tls_certfile);
1043       }
1044       if (res->res_client.tls_keyfile) {
1045          free(res->res_client.tls_keyfile);
1046       }
1047       if (res->res_client.tls_allowed_cns) {
1048          delete res->res_client.tls_allowed_cns;
1049       }
1050       break;
1051    case R_STORAGE:
1052       if (res->res_store.address) {
1053          free(res->res_store.address);
1054       }
1055       if (res->res_store.password) {
1056          free(res->res_store.password);
1057       }
1058       if (res->res_store.media_type) {
1059          free(res->res_store.media_type);
1060       }
1061       if (res->res_store.device) {
1062          delete res->res_store.device;
1063       }
1064       if (res->res_store.tls_ctx) { 
1065          free_tls_context(res->res_store.tls_ctx);
1066       }
1067       if (res->res_store.tls_ca_certfile) {
1068          free(res->res_store.tls_ca_certfile);
1069       }
1070       if (res->res_store.tls_ca_certdir) {
1071          free(res->res_store.tls_ca_certdir);
1072       }
1073       if (res->res_store.tls_certfile) {
1074          free(res->res_store.tls_certfile);
1075       }
1076       if (res->res_store.tls_keyfile) {
1077          free(res->res_store.tls_keyfile);
1078       }
1079       break;
1080    case R_CATALOG:
1081       if (res->res_cat.db_address) {
1082          free(res->res_cat.db_address);
1083       }
1084       if (res->res_cat.db_socket) {
1085          free(res->res_cat.db_socket);
1086       }
1087       if (res->res_cat.db_user) {
1088          free(res->res_cat.db_user);
1089       }
1090       if (res->res_cat.db_name) {
1091          free(res->res_cat.db_name);
1092       }
1093       if (res->res_cat.db_password) {
1094          free(res->res_cat.db_password);
1095       }
1096       break;
1097    case R_FILESET:
1098       if ((num=res->res_fs.num_includes)) {
1099          while (--num >= 0) {
1100             free_incexe(res->res_fs.include_items[num]);
1101          }
1102          free(res->res_fs.include_items);
1103       }
1104       res->res_fs.num_includes = 0;
1105       if ((num=res->res_fs.num_excludes)) {
1106          while (--num >= 0) {
1107             free_incexe(res->res_fs.exclude_items[num]);
1108          }
1109          free(res->res_fs.exclude_items);
1110       }
1111       res->res_fs.num_excludes = 0;
1112       break;
1113    case R_POOL:
1114       if (res->res_pool.pool_type) {
1115          free(res->res_pool.pool_type);
1116       }
1117       if (res->res_pool.label_format) {
1118          free(res->res_pool.label_format);
1119       }
1120       if (res->res_pool.cleaning_prefix) {
1121          free(res->res_pool.cleaning_prefix);
1122       }
1123       if (res->res_pool.storage) {
1124          delete res->res_pool.storage;
1125       }
1126       break;
1127    case R_SCHEDULE:
1128       if (res->res_sch.run) {
1129          RUN *nrun, *next;
1130          nrun = res->res_sch.run;
1131          while (nrun) {
1132             next = nrun->next;
1133             free(nrun);
1134             nrun = next;
1135          }
1136       }
1137       break;
1138    case R_JOB:
1139    case R_JOBDEFS:
1140       if (res->res_job.RestoreWhere) {
1141          free(res->res_job.RestoreWhere);
1142       }
1143       if (res->res_job.RestoreBootstrap) {
1144          free(res->res_job.RestoreBootstrap);
1145       }
1146       if (res->res_job.WriteBootstrap) {
1147          free(res->res_job.WriteBootstrap);
1148       }
1149       if (res->res_job.selection_pattern) {
1150          free(res->res_job.selection_pattern);
1151       }
1152       if (res->res_job.run_cmds) {
1153          delete res->res_job.run_cmds;
1154       }
1155       if (res->res_job.storage) {
1156          delete res->res_job.storage;
1157       }
1158       if (res->res_job.RunScripts) {
1159          free_runscripts(res->res_job.RunScripts);
1160          delete res->res_job.RunScripts;
1161       }
1162       break;
1163    case R_MSGS:
1164       if (res->res_msgs.mail_cmd) {
1165          free(res->res_msgs.mail_cmd);
1166       }
1167       if (res->res_msgs.operator_cmd) {
1168          free(res->res_msgs.operator_cmd);
1169       }
1170       free_msgs_res((MSGS *)res);  /* free message resource */
1171       res = NULL;
1172       break;
1173    default:
1174       printf(_("Unknown resource type %d in free_resource.\n"), type);
1175    }
1176    /* Common stuff again -- free the resource, recurse to next one */
1177    if (res) {
1178       free(res);
1179    }
1180    if (nres) {
1181       free_resource(nres, type);
1182    }
1183 }
1184
1185 /*
1186  * Save the new resource by chaining it into the head list for
1187  * the resource. If this is pass 2, we update any resource
1188  * pointers because they may not have been defined until
1189  * later in pass 1.
1190  */
1191 void save_resource(int type, RES_ITEM *items, int pass)
1192 {
1193    URES *res;
1194    int rindex = type - r_first;
1195    int i, size = 0;
1196    bool error = false;
1197
1198    /* Check Job requirements after applying JobDefs */
1199    if (type != R_JOB && type != R_JOBDEFS) {
1200       /*
1201        * Ensure that all required items are present
1202        */
1203       for (i=0; items[i].name; i++) {
1204          if (items[i].flags & ITEM_REQUIRED) {
1205             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1206                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1207                     items[i].name, resources[rindex]);
1208             }
1209          }
1210          /* If this triggers, take a look at lib/parse_conf.h */
1211          if (i >= MAX_RES_ITEMS) {
1212             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1213          }
1214       }
1215    } else if (type == R_JOB) {
1216       /*
1217        * Ensure that the name item is present
1218        */
1219       if (items[0].flags & ITEM_REQUIRED) {
1220          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1221              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1222                    items[0].name, resources[rindex]);
1223          }
1224       }
1225    }
1226
1227    /*
1228     * During pass 2 in each "store" routine, we looked up pointers
1229     * to all the resources referrenced in the current resource, now we
1230     * must copy their addresses from the static record to the allocated
1231     * record.
1232     */
1233    if (pass == 2) {
1234       switch (type) {
1235       /* Resources not containing a resource */
1236       case R_CATALOG:
1237       case R_MSGS:
1238       case R_FILESET:
1239       case R_DEVICE:
1240          break;
1241
1242       /*
1243        * Resources containing another resource or alist. First
1244        *  look up the resource which contains another resource. It
1245        *  was written during pass 1.  Then stuff in the pointers to
1246        *  the resources it contains, which were inserted this pass.
1247        *  Finally, it will all be stored back.
1248        */
1249       case R_POOL:
1250          /* Find resource saved in pass 1 */
1251          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1252             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1253          }
1254          /* Explicitly copy resource pointers from this pass (res_all) */
1255          res->res_pool.NextPool = res_all.res_pool.NextPool;
1256          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1257          res->res_pool.storage    = res_all.res_pool.storage;
1258          break;
1259       case R_CONSOLE:
1260          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1261             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1262          }
1263          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1264          break;
1265       case R_DIRECTOR:
1266          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1267             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1268          }
1269          res->res_dir.messages = res_all.res_dir.messages;
1270          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1271          break;
1272       case R_STORAGE:
1273          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1274             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1275                   res_all.res_dir.hdr.name);
1276          }
1277          /* we must explicitly copy the device alist pointer */
1278          res->res_store.device   = res_all.res_store.device;
1279          break;
1280       case R_JOB:
1281       case R_JOBDEFS:
1282          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1283             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1284                   res_all.res_dir.hdr.name);
1285          }
1286          res->res_job.messages   = res_all.res_job.messages;
1287          res->res_job.schedule   = res_all.res_job.schedule;
1288          res->res_job.client     = res_all.res_job.client;
1289          res->res_job.fileset    = res_all.res_job.fileset;
1290          res->res_job.storage    = res_all.res_job.storage;
1291          res->res_job.pool       = res_all.res_job.pool;
1292          res->res_job.full_pool  = res_all.res_job.full_pool;
1293          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1294          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1295          res->res_job.verify_job = res_all.res_job.verify_job;
1296          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1297          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1298          res->res_job.RunScripts = res_all.res_job.RunScripts;
1299          break;
1300       case R_COUNTER:
1301          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1302             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1303          }
1304          res->res_counter.Catalog = res_all.res_counter.Catalog;
1305          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1306          break;
1307
1308       case R_CLIENT:
1309          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1310             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1311          }
1312          res->res_client.catalog = res_all.res_client.catalog;
1313          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1314          break;
1315       case R_SCHEDULE:
1316          /*
1317           * Schedule is a bit different in that it contains a RUN record
1318           * chain which isn't a "named" resource. This chain was linked
1319           * in by run_conf.c during pass 2, so here we jam the pointer
1320           * into the Schedule resource.
1321           */
1322          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1323             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1324          }
1325          res->res_sch.run = res_all.res_sch.run;
1326          break;
1327       default:
1328          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1329          error = true;
1330          break;
1331       }
1332       /* Note, the resource name was already saved during pass 1,
1333        * so here, we can just release it.
1334        */
1335       if (res_all.res_dir.hdr.name) {
1336          free(res_all.res_dir.hdr.name);
1337          res_all.res_dir.hdr.name = NULL;
1338       }
1339       if (res_all.res_dir.hdr.desc) {
1340          free(res_all.res_dir.hdr.desc);
1341          res_all.res_dir.hdr.desc = NULL;
1342       }
1343       return;
1344    }
1345
1346    /*
1347     * The following code is only executed during pass 1
1348     */
1349    switch (type) {
1350    case R_DIRECTOR:
1351       size = sizeof(DIRRES);
1352       break;
1353    case R_CONSOLE:
1354       size = sizeof(CONRES);
1355       break;
1356    case R_CLIENT:
1357       size =sizeof(CLIENT);
1358       break;
1359    case R_STORAGE:
1360       size = sizeof(STORE);
1361       break;
1362    case R_CATALOG:
1363       size = sizeof(CAT);
1364       break;
1365    case R_JOB:
1366    case R_JOBDEFS:
1367       size = sizeof(JOB);
1368       break;
1369    case R_FILESET:
1370       size = sizeof(FILESET);
1371       break;
1372    case R_SCHEDULE:
1373       size = sizeof(SCHED);
1374       break;
1375    case R_POOL:
1376       size = sizeof(POOL);
1377       break;
1378    case R_MSGS:
1379       size = sizeof(MSGS);
1380       break;
1381    case R_COUNTER:
1382       size = sizeof(COUNTER);
1383       break;
1384    case R_DEVICE:
1385       error = true;
1386       break;
1387    default:
1388       printf(_("Unknown resource type %d in save_resource.\n"), type);
1389       error = true; 
1390       break;
1391    }
1392    /* Common */
1393    if (!error) {
1394       res = (URES *)malloc(size);
1395       memcpy(res, &res_all, size);
1396       if (!res_head[rindex]) {
1397          res_head[rindex] = (RES *)res; /* store first entry */
1398          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1399                res->res_dir.hdr.name, rindex);
1400       } else {
1401          RES *next, *last;
1402          if (res->res_dir.hdr.name == NULL) {
1403             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1404                   resources[rindex]);
1405          }   
1406          /* Add new res to end of chain */
1407          for (last=next=res_head[rindex]; next; next=next->next) {
1408             last = next;
1409             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1410                Emsg2(M_ERROR_TERM, 0,
1411                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1412                   resources[rindex].name, res->res_dir.hdr.name);
1413             }
1414          }
1415          last->next = (RES *)res;
1416          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1417                res->res_dir.hdr.name, rindex, pass);
1418       }
1419    }
1420 }
1421
1422 /*
1423  * Store Device. Note, the resource is created upon the
1424  *  first reference. The details of the resource are obtained
1425  *  later from the SD.
1426  */
1427 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1428 {
1429    int token;
1430    URES *res;
1431    int rindex = R_DEVICE - r_first;
1432    int size = sizeof(DEVICE);
1433    bool found = false;
1434
1435    if (pass == 1) {
1436       token = lex_get_token(lc, T_NAME);
1437       if (!res_head[rindex]) {
1438          res = (URES *)malloc(size);
1439          memset(res, 0, size);
1440          res->res_dev.hdr.name = bstrdup(lc->str);
1441          res_head[rindex] = (RES *)res; /* store first entry */
1442          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1443                res->res_dir.hdr.name, rindex);
1444       } else {
1445          RES *next;
1446          /* See if it is already defined */
1447          for (next=res_head[rindex]; next->next; next=next->next) {
1448             if (strcmp(next->name, lc->str) == 0) {
1449                found = true;
1450                break;
1451             }
1452          }
1453          if (!found) {
1454             res = (URES *)malloc(size);
1455             memset(res, 0, size);
1456             res->res_dev.hdr.name = bstrdup(lc->str);
1457             next->next = (RES *)res;
1458             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1459                res->res_dir.hdr.name, rindex, pass);
1460          }
1461       }
1462
1463       scan_to_eol(lc);
1464       set_bit(index, res_all.hdr.item_present);
1465    } else {
1466       store_alist_res(lc, item, index, pass);
1467    }
1468 }
1469
1470 /*
1471  * Store JobType (backup, verify, restore)
1472  *
1473  */
1474 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1475 {
1476    int token, i;
1477
1478    token = lex_get_token(lc, T_NAME);
1479    /* Store the type both pass 1 and pass 2 */
1480    for (i=0; migtypes[i].type_name; i++) {
1481       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1482          *(int *)(item->value) = migtypes[i].job_type;
1483          i = 0;
1484          break;
1485       }
1486    }
1487    if (i != 0) {
1488       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1489    }
1490    scan_to_eol(lc);
1491    set_bit(index, res_all.hdr.item_present);
1492 }
1493
1494
1495
1496 /*
1497  * Store JobType (backup, verify, restore)
1498  *
1499  */
1500 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1501 {
1502    int token, i;
1503
1504    token = lex_get_token(lc, T_NAME);
1505    /* Store the type both pass 1 and pass 2 */
1506    for (i=0; jobtypes[i].type_name; i++) {
1507       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1508          *(int *)(item->value) = jobtypes[i].job_type;
1509          i = 0;
1510          break;
1511       }
1512    }
1513    if (i != 0) {
1514       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1515    }
1516    scan_to_eol(lc);
1517    set_bit(index, res_all.hdr.item_present);
1518 }
1519
1520 /*
1521  * Store Job Level (Full, Incremental, ...)
1522  *
1523  */
1524 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1525 {
1526    int token, i;
1527
1528    token = lex_get_token(lc, T_NAME);
1529    /* Store the level pass 2 so that type is defined */
1530    for (i=0; joblevels[i].level_name; i++) {
1531       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1532          *(int *)(item->value) = joblevels[i].level;
1533          i = 0;
1534          break;
1535       }
1536    }
1537    if (i != 0) {
1538       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1539    }
1540    scan_to_eol(lc);
1541    set_bit(index, res_all.hdr.item_present);
1542 }
1543
1544
1545 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1546 {
1547    int token, i;
1548    token = lex_get_token(lc, T_NAME);
1549    /* Scan Replacement options */
1550    for (i=0; ReplaceOptions[i].name; i++) {
1551       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1552          *(int *)(item->value) = ReplaceOptions[i].token;
1553          i = 0;
1554          break;
1555       }
1556    }
1557    if (i != 0) {
1558       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1559    }
1560    scan_to_eol(lc);
1561    set_bit(index, res_all.hdr.item_present);
1562 }
1563
1564 /*
1565  * Store ACL (access control list)
1566  *
1567  */
1568 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1569 {
1570    int token;
1571
1572    for (;;) {
1573       token = lex_get_token(lc, T_STRING);
1574       if (pass == 1) {
1575          if (((alist **)item->value)[item->code] == NULL) {
1576             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1577             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1578          }
1579          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1580          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1581       }
1582       token = lex_get_token(lc, T_ALL);
1583       if (token == T_COMMA) {
1584          continue;                    /* get another ACL */
1585       }
1586       break;
1587    }
1588    set_bit(index, res_all.hdr.item_present);
1589 }
1590
1591 /* We build RunScripts items here */
1592 static RUNSCRIPT res_runscript;
1593
1594 /* Store a runscript->when in a bit field */
1595 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1596 {
1597    lex_get_token(lc, T_NAME);
1598
1599    if (strcasecmp(lc->str, "before") == 0) {
1600       *(int *)(item->value) = SCRIPT_Before ;
1601    } else if (strcasecmp(lc->str, "after") == 0) {
1602       *(int *)(item->value) = SCRIPT_After;
1603    } else if (strcasecmp(lc->str, "always") == 0) {
1604       *(int *)(item->value) = SCRIPT_Any;
1605    } else {
1606       scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1607    }
1608    scan_to_eol(lc);
1609 }
1610
1611 /* Store a runscript->target
1612  * 
1613  */
1614 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1615 {
1616    lex_get_token(lc, T_STRING);
1617
1618    if (pass == 2) {
1619       if (strcmp(lc->str, "%c") == 0) {
1620          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1621       } else if (strcasecmp(lc->str, "yes") == 0) {
1622          ((RUNSCRIPT*) item->value)->set_target("%c");
1623       } else if (strcasecmp(lc->str, "no") == 0) {
1624          ((RUNSCRIPT*) item->value)->set_target("");
1625       } else {
1626          RES *res = GetResWithName(R_CLIENT, lc->str);
1627          if (res == NULL) {
1628             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1629                       lc->str, lc->line_no, lc->line);
1630          }
1631
1632          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1633       }
1634    }
1635    scan_to_eol(lc);
1636 }
1637
1638 /* Store a runscript->command in a bit field
1639  * 
1640  */
1641 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1642 {
1643    lex_get_token(lc, T_STRING);
1644
1645    if (pass == 2) {
1646       ((RUNSCRIPT*) item->value)->set_command(lc->str);
1647    }
1648    scan_to_eol(lc);
1649 }
1650
1651 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1652 {
1653    lex_get_token(lc, T_STRING);
1654    alist **runscripts = (alist **)(item->value) ;
1655
1656    if (pass == 2) {
1657       RUNSCRIPT *script = new_runscript();
1658
1659       script->set_command(lc->str);
1660
1661       /* TODO: remove all script->old_proto with bacula 1.42 */
1662
1663       if (strcmp(item->name, "runbeforejob") == 0) {
1664          script->when = SCRIPT_Before;
1665          script->abort_on_error = true;
1666          script->set_target("");
1667
1668       } else if (strcmp(item->name, "runafterjob") == 0) {
1669          script->when = SCRIPT_After;
1670          script->on_success = true;
1671          script->on_failure = false;
1672          script->set_target("");
1673          
1674       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1675          script->old_proto = true;
1676          script->when = SCRIPT_After;
1677          script->set_target("%c");
1678          script->on_success = true;
1679          script->on_failure = false;
1680
1681       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1682          script->old_proto = true;
1683          script->when = SCRIPT_Before;
1684          script->set_target("%c");
1685          script->abort_on_error = true;
1686
1687       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1688          script->when = SCRIPT_After;
1689          script->on_failure = true;
1690          script->on_success = false;
1691          script->set_target("");
1692       }
1693
1694       if (*runscripts == NULL) {
1695         *runscripts = New(alist(10, not_owned_by_alist));
1696       }
1697       
1698       (*runscripts)->append(script);
1699       script->debug();
1700    }
1701
1702    scan_to_eol(lc);
1703 }
1704
1705 /* Store a bool in a bit field without modifing res_all.hdr 
1706  * We can also add an option to store_bool to skip res_all.hdr
1707  */
1708 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1709 {
1710    lex_get_token(lc, T_NAME);
1711    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1712       *(bool *)(item->value) = true;
1713    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1714       *(bool *)(item->value) = false;
1715    } else {
1716       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1717    }
1718    scan_to_eol(lc);
1719 }
1720
1721 /*
1722  * new RunScript items
1723  *   name             handler              value                             code flags default_value
1724  */
1725 static RES_ITEM runscript_items[] = {
1726  {"command",        store_runscript_cmd,  {(char **)&res_runscript},           0,  ITEM_REQUIRED, 0}, 
1727  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, 
1728  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
1729  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
1730  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.abort_on_error},0, 0, 0},
1731  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
1732  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
1733  {NULL, NULL, {0}, 0, 0, 0}
1734 };
1735
1736 /*
1737  * Store RunScript info
1738  *
1739  *  Note, when this routine is called, we are inside a Job
1740  *  resource.  We treat the RunScript like a sort of
1741  *  mini-resource within the Job resource.
1742  */
1743 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1744 {
1745    int token, i;
1746    alist **runscripts = (alist **)(item->value) ;
1747
1748    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1749
1750    res_runscript.reset_default();      /* setting on_success, on_failure, abort_on_error */
1751    
1752    token = lex_get_token(lc, T_SKIP_EOL);
1753    
1754    if (token != T_BOB) {
1755       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1756    }
1757    
1758    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1759       if (token == T_EOB) {
1760         break;
1761       }
1762       if (token != T_IDENTIFIER) {
1763         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1764       }
1765       for (i=0; runscript_items[i].name; i++) {
1766         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1767            token = lex_get_token(lc, T_SKIP_EOL);
1768            if (token != T_EQUALS) {
1769               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1770            }
1771            
1772            /* Call item handler */
1773            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1774            i = -1;
1775            break;
1776         }
1777       }
1778       
1779       if (i >=0) {
1780         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1781       }
1782    }
1783
1784    if (pass == 2) {
1785       if (res_runscript.command == NULL) {
1786          scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1787                    "command", "runscript");
1788       }
1789
1790       /* run on client by default */
1791       if (res_runscript.target == NULL) {
1792          res_runscript.set_target("%c");
1793       }
1794
1795       RUNSCRIPT *script = new_runscript();
1796       memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1797       
1798       if (*runscripts == NULL) {
1799         *runscripts = New(alist(10, not_owned_by_alist));
1800       }
1801       
1802       (*runscripts)->append(script);
1803       script->debug();
1804    }
1805
1806    scan_to_eol(lc);
1807    set_bit(index, res_all.hdr.item_present);
1808 }