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