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