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