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