]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Plugin debug code + tweak a couple bat dialog layouts
[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.plugin_directory) {
1019          free((char *)res->res_dir.plugin_directory);
1020       }
1021       if (res->res_dir.pid_directory) {
1022          free(res->res_dir.pid_directory);
1023       }
1024       if (res->res_dir.subsys_directory) {
1025          free(res->res_dir.subsys_directory);
1026       }
1027       if (res->res_dir.password) {
1028          free(res->res_dir.password);
1029       }
1030       if (res->res_dir.query_file) {
1031          free(res->res_dir.query_file);
1032       }
1033       if (res->res_dir.DIRaddrs) {
1034          free_addresses(res->res_dir.DIRaddrs);
1035       }
1036       if (res->res_dir.tls_ctx) { 
1037          free_tls_context(res->res_dir.tls_ctx);
1038       }
1039       if (res->res_dir.tls_ca_certfile) {
1040          free(res->res_dir.tls_ca_certfile);
1041       }
1042       if (res->res_dir.tls_ca_certdir) {
1043          free(res->res_dir.tls_ca_certdir);
1044       }
1045       if (res->res_dir.tls_certfile) {
1046          free(res->res_dir.tls_certfile);
1047       }
1048       if (res->res_dir.tls_keyfile) {
1049          free(res->res_dir.tls_keyfile);
1050       }
1051       if (res->res_dir.tls_dhfile) {
1052          free(res->res_dir.tls_dhfile);
1053       }
1054       if (res->res_dir.tls_allowed_cns) {
1055          delete res->res_dir.tls_allowed_cns;
1056       }
1057       break;
1058    case R_DEVICE:
1059    case R_COUNTER:
1060        break;
1061    case R_CONSOLE:
1062       if (res->res_con.password) {
1063          free(res->res_con.password);
1064       }
1065       if (res->res_con.tls_ctx) { 
1066          free_tls_context(res->res_con.tls_ctx);
1067       }
1068       if (res->res_con.tls_ca_certfile) {
1069          free(res->res_con.tls_ca_certfile);
1070       }
1071       if (res->res_con.tls_ca_certdir) {
1072          free(res->res_con.tls_ca_certdir);
1073       }
1074       if (res->res_con.tls_certfile) {
1075          free(res->res_con.tls_certfile);
1076       }
1077       if (res->res_con.tls_keyfile) {
1078          free(res->res_con.tls_keyfile);
1079       }
1080       if (res->res_con.tls_dhfile) {
1081          free(res->res_con.tls_dhfile);
1082       }
1083       if (res->res_con.tls_allowed_cns) {
1084          delete res->res_con.tls_allowed_cns;
1085       }
1086       for (int i=0; i<Num_ACL; i++) {
1087          if (res->res_con.ACL_lists[i]) {
1088             delete res->res_con.ACL_lists[i];
1089             res->res_con.ACL_lists[i] = NULL;
1090          }
1091       }
1092       break;
1093    case R_CLIENT:
1094       if (res->res_client.address) {
1095          free(res->res_client.address);
1096       }
1097       if (res->res_client.password) {
1098          free(res->res_client.password);
1099       }
1100       if (res->res_client.tls_ctx) { 
1101          free_tls_context(res->res_client.tls_ctx);
1102       }
1103       if (res->res_client.tls_ca_certfile) {
1104          free(res->res_client.tls_ca_certfile);
1105       }
1106       if (res->res_client.tls_ca_certdir) {
1107          free(res->res_client.tls_ca_certdir);
1108       }
1109       if (res->res_client.tls_certfile) {
1110          free(res->res_client.tls_certfile);
1111       }
1112       if (res->res_client.tls_keyfile) {
1113          free(res->res_client.tls_keyfile);
1114       }
1115       if (res->res_client.tls_allowed_cns) {
1116          delete res->res_client.tls_allowed_cns;
1117       }
1118       break;
1119    case R_STORAGE:
1120       if (res->res_store.address) {
1121          free(res->res_store.address);
1122       }
1123       if (res->res_store.password) {
1124          free(res->res_store.password);
1125       }
1126       if (res->res_store.media_type) {
1127          free(res->res_store.media_type);
1128       }
1129       if (res->res_store.device) {
1130          delete res->res_store.device;
1131       }
1132       if (res->res_store.tls_ctx) { 
1133          free_tls_context(res->res_store.tls_ctx);
1134       }
1135       if (res->res_store.tls_ca_certfile) {
1136          free(res->res_store.tls_ca_certfile);
1137       }
1138       if (res->res_store.tls_ca_certdir) {
1139          free(res->res_store.tls_ca_certdir);
1140       }
1141       if (res->res_store.tls_certfile) {
1142          free(res->res_store.tls_certfile);
1143       }
1144       if (res->res_store.tls_keyfile) {
1145          free(res->res_store.tls_keyfile);
1146       }
1147       break;
1148    case R_CATALOG:
1149       if (res->res_cat.db_address) {
1150          free(res->res_cat.db_address);
1151       }
1152       if (res->res_cat.db_socket) {
1153          free(res->res_cat.db_socket);
1154       }
1155       if (res->res_cat.db_user) {
1156          free(res->res_cat.db_user);
1157       }
1158       if (res->res_cat.db_name) {
1159          free(res->res_cat.db_name);
1160       }
1161       if (res->res_cat.db_driver) {
1162          free(res->res_cat.db_driver);
1163       }
1164       if (res->res_cat.db_password) {
1165          free(res->res_cat.db_password);
1166       }
1167       break;
1168    case R_FILESET:
1169       if ((num=res->res_fs.num_includes)) {
1170          while (--num >= 0) {
1171             free_incexe(res->res_fs.include_items[num]);
1172          }
1173          free(res->res_fs.include_items);
1174       }
1175       res->res_fs.num_includes = 0;
1176       if ((num=res->res_fs.num_excludes)) {
1177          while (--num >= 0) {
1178             free_incexe(res->res_fs.exclude_items[num]);
1179          }
1180          free(res->res_fs.exclude_items);
1181       }
1182       res->res_fs.num_excludes = 0;
1183       break;
1184    case R_POOL:
1185       if (res->res_pool.pool_type) {
1186          free(res->res_pool.pool_type);
1187       }
1188       if (res->res_pool.label_format) {
1189          free(res->res_pool.label_format);
1190       }
1191       if (res->res_pool.cleaning_prefix) {
1192          free(res->res_pool.cleaning_prefix);
1193       }
1194       if (res->res_pool.storage) {
1195          delete res->res_pool.storage;
1196       }
1197       break;
1198    case R_SCHEDULE:
1199       if (res->res_sch.run) {
1200          RUN *nrun, *next;
1201          nrun = res->res_sch.run;
1202          while (nrun) {
1203             next = nrun->next;
1204             free(nrun);
1205             nrun = next;
1206          }
1207       }
1208       break;
1209    case R_JOB:
1210    case R_JOBDEFS:
1211       if (res->res_job.RestoreWhere) {
1212          free(res->res_job.RestoreWhere);
1213       }
1214       if (res->res_job.RegexWhere) {
1215          free(res->res_job.RegexWhere);
1216       }
1217       if (res->res_job.strip_prefix) {
1218          free(res->res_job.strip_prefix);
1219       }
1220       if (res->res_job.add_prefix) {
1221          free(res->res_job.add_prefix);
1222       }
1223       if (res->res_job.add_suffix) {
1224          free(res->res_job.add_suffix);
1225       }
1226       if (res->res_job.RestoreBootstrap) {
1227          free(res->res_job.RestoreBootstrap);
1228       }
1229       if (res->res_job.WriteBootstrap) {
1230          free(res->res_job.WriteBootstrap);
1231       }
1232       if (res->res_job.selection_pattern) {
1233          free(res->res_job.selection_pattern);
1234       }
1235       if (res->res_job.run_cmds) {
1236          delete res->res_job.run_cmds;
1237       }
1238       if (res->res_job.storage) {
1239          delete res->res_job.storage;
1240       }
1241       if (res->res_job.RunScripts) {
1242          free_runscripts(res->res_job.RunScripts);
1243          delete res->res_job.RunScripts;
1244       }
1245       break;
1246    case R_MSGS:
1247       if (res->res_msgs.mail_cmd) {
1248          free(res->res_msgs.mail_cmd);
1249       }
1250       if (res->res_msgs.operator_cmd) {
1251          free(res->res_msgs.operator_cmd);
1252       }
1253       free_msgs_res((MSGS *)res);  /* free message resource */
1254       res = NULL;
1255       break;
1256    default:
1257       printf(_("Unknown resource type %d in free_resource.\n"), type);
1258    }
1259    /* Common stuff again -- free the resource, recurse to next one */
1260    if (res) {
1261       free(res);
1262    }
1263    if (nres) {
1264       free_resource(nres, type);
1265    }
1266 }
1267
1268 /*
1269  * Save the new resource by chaining it into the head list for
1270  * the resource. If this is pass 2, we update any resource
1271  * pointers because they may not have been defined until
1272  * later in pass 1.
1273  */
1274 void save_resource(int type, RES_ITEM *items, int pass)
1275 {
1276    URES *res;
1277    int rindex = type - r_first;
1278    int i, size = 0;
1279    bool error = false;
1280
1281    /* Check Job requirements after applying JobDefs */
1282    if (type != R_JOB && type != R_JOBDEFS) {
1283       /*
1284        * Ensure that all required items are present
1285        */
1286       for (i=0; items[i].name; i++) {
1287          if (items[i].flags & ITEM_REQUIRED) {
1288             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1289                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1290                     items[i].name, resources[rindex]);
1291             }
1292          }
1293          /* If this triggers, take a look at lib/parse_conf.h */
1294          if (i >= MAX_RES_ITEMS) {
1295             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1296          }
1297       }
1298    } else if (type == R_JOB) {
1299       /*
1300        * Ensure that the name item is present
1301        */
1302       if (items[0].flags & ITEM_REQUIRED) {
1303          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1304              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1305                    items[0].name, resources[rindex]);
1306          }
1307       }
1308    }
1309
1310    /*
1311     * During pass 2 in each "store" routine, we looked up pointers
1312     * to all the resources referrenced in the current resource, now we
1313     * must copy their addresses from the static record to the allocated
1314     * record.
1315     */
1316    if (pass == 2) {
1317       switch (type) {
1318       /* Resources not containing a resource */
1319       case R_CATALOG:
1320       case R_MSGS:
1321       case R_FILESET:
1322       case R_DEVICE:
1323          break;
1324
1325       /*
1326        * Resources containing another resource or alist. First
1327        *  look up the resource which contains another resource. It
1328        *  was written during pass 1.  Then stuff in the pointers to
1329        *  the resources it contains, which were inserted this pass.
1330        *  Finally, it will all be stored back.
1331        */
1332       case R_POOL:
1333          /* Find resource saved in pass 1 */
1334          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1335             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1336          }
1337          /* Explicitly copy resource pointers from this pass (res_all) */
1338          res->res_pool.NextPool = res_all.res_pool.NextPool;
1339          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1340          res->res_pool.storage    = res_all.res_pool.storage;
1341          res->res_pool.Catalog    = res_all.res_pool.Catalog;
1342          break;
1343       case R_CONSOLE:
1344          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1345             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1346          }
1347          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1348          break;
1349       case R_DIRECTOR:
1350          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1351             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1352          }
1353          res->res_dir.messages = res_all.res_dir.messages;
1354          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1355          break;
1356       case R_STORAGE:
1357          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1358             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1359                   res_all.res_dir.hdr.name);
1360          }
1361          /* we must explicitly copy the device alist pointer */
1362          res->res_store.device   = res_all.res_store.device;
1363          break;
1364       case R_JOB:
1365       case R_JOBDEFS:
1366          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1367             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1368                   res_all.res_dir.hdr.name);
1369          }
1370          res->res_job.messages   = res_all.res_job.messages;
1371          res->res_job.schedule   = res_all.res_job.schedule;
1372          res->res_job.client     = res_all.res_job.client;
1373          res->res_job.fileset    = res_all.res_job.fileset;
1374          res->res_job.storage    = res_all.res_job.storage;
1375          res->res_job.pool       = res_all.res_job.pool;
1376          res->res_job.full_pool  = res_all.res_job.full_pool;
1377          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1378          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1379          res->res_job.verify_job = res_all.res_job.verify_job;
1380          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1381          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1382          res->res_job.RunScripts = res_all.res_job.RunScripts;
1383
1384          /* TODO: JobDefs where/regexwhere doesn't work well (but this
1385           * is not very useful) 
1386           * We have to set_bit(index, res_all.hdr.item_present);
1387           * or something like that
1388           */
1389
1390          /* we take RegexWhere before all other options */
1391          if (!res->res_job.RegexWhere 
1392              &&
1393              (res->res_job.strip_prefix ||
1394               res->res_job.add_suffix   ||
1395               res->res_job.add_prefix))
1396          {
1397             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1398                                                    res->res_job.add_prefix,
1399                                                    res->res_job.add_suffix);
1400             res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1401             bregexp_build_where(res->res_job.RegexWhere, len,
1402                                 res->res_job.strip_prefix,
1403                                 res->res_job.add_prefix,
1404                                 res->res_job.add_suffix);
1405             /* TODO: test bregexp */
1406          }
1407
1408          if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1409             free(res->res_job.RestoreWhere);
1410             res->res_job.RestoreWhere = NULL;
1411          }
1412
1413          break;
1414       case R_COUNTER:
1415          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1416             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1417          }
1418          res->res_counter.Catalog = res_all.res_counter.Catalog;
1419          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1420          break;
1421
1422       case R_CLIENT:
1423          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1424             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1425          }
1426          res->res_client.catalog = res_all.res_client.catalog;
1427          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1428          break;
1429       case R_SCHEDULE:
1430          /*
1431           * Schedule is a bit different in that it contains a RUN record
1432           * chain which isn't a "named" resource. This chain was linked
1433           * in by run_conf.c during pass 2, so here we jam the pointer
1434           * into the Schedule resource.
1435           */
1436          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1437             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1438          }
1439          res->res_sch.run = res_all.res_sch.run;
1440          break;
1441       default:
1442          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1443          error = true;
1444          break;
1445       }
1446       /* Note, the resource name was already saved during pass 1,
1447        * so here, we can just release it.
1448        */
1449       if (res_all.res_dir.hdr.name) {
1450          free(res_all.res_dir.hdr.name);
1451          res_all.res_dir.hdr.name = NULL;
1452       }
1453       if (res_all.res_dir.hdr.desc) {
1454          free(res_all.res_dir.hdr.desc);
1455          res_all.res_dir.hdr.desc = NULL;
1456       }
1457       return;
1458    }
1459
1460    /*
1461     * The following code is only executed during pass 1
1462     */
1463    switch (type) {
1464    case R_DIRECTOR:
1465       size = sizeof(DIRRES);
1466       break;
1467    case R_CONSOLE:
1468       size = sizeof(CONRES);
1469       break;
1470    case R_CLIENT:
1471       size =sizeof(CLIENT);
1472       break;
1473    case R_STORAGE:
1474       size = sizeof(STORE);
1475       break;
1476    case R_CATALOG:
1477       size = sizeof(CAT);
1478       break;
1479    case R_JOB:
1480    case R_JOBDEFS:
1481       size = sizeof(JOB);
1482       break;
1483    case R_FILESET:
1484       size = sizeof(FILESET);
1485       break;
1486    case R_SCHEDULE:
1487       size = sizeof(SCHED);
1488       break;
1489    case R_POOL:
1490       size = sizeof(POOL);
1491       break;
1492    case R_MSGS:
1493       size = sizeof(MSGS);
1494       break;
1495    case R_COUNTER:
1496       size = sizeof(COUNTER);
1497       break;
1498    case R_DEVICE:
1499       error = true;
1500       break;
1501    default:
1502       printf(_("Unknown resource type %d in save_resource.\n"), type);
1503       error = true; 
1504       break;
1505    }
1506    /* Common */
1507    if (!error) {
1508       res = (URES *)malloc(size);
1509       memcpy(res, &res_all, size);
1510       if (!res_head[rindex]) {
1511          res_head[rindex] = (RES *)res; /* store first entry */
1512          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1513                res->res_dir.hdr.name, rindex);
1514       } else {
1515          RES *next, *last;
1516          if (res->res_dir.hdr.name == NULL) {
1517             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1518                   resources[rindex]);
1519          }   
1520          /* Add new res to end of chain */
1521          for (last=next=res_head[rindex]; next; next=next->next) {
1522             last = next;
1523             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1524                Emsg2(M_ERROR_TERM, 0,
1525                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1526                   resources[rindex].name, res->res_dir.hdr.name);
1527             }
1528          }
1529          last->next = (RES *)res;
1530          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1531                res->res_dir.hdr.name, rindex, pass);
1532       }
1533    }
1534 }
1535
1536 /*
1537  * Store Device. Note, the resource is created upon the
1538  *  first reference. The details of the resource are obtained
1539  *  later from the SD.
1540  */
1541 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1542 {
1543    int token;
1544    URES *res;
1545    int rindex = R_DEVICE - r_first;
1546    int size = sizeof(DEVICE);
1547    bool found = false;
1548
1549    if (pass == 1) {
1550       token = lex_get_token(lc, T_NAME);
1551       if (!res_head[rindex]) {
1552          res = (URES *)malloc(size);
1553          memset(res, 0, size);
1554          res->res_dev.hdr.name = bstrdup(lc->str);
1555          res_head[rindex] = (RES *)res; /* store first entry */
1556          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1557                res->res_dir.hdr.name, rindex);
1558       } else {
1559          RES *next;
1560          /* See if it is already defined */
1561          for (next=res_head[rindex]; next->next; next=next->next) {
1562             if (strcmp(next->name, lc->str) == 0) {
1563                found = true;
1564                break;
1565             }
1566          }
1567          if (!found) {
1568             res = (URES *)malloc(size);
1569             memset(res, 0, size);
1570             res->res_dev.hdr.name = bstrdup(lc->str);
1571             next->next = (RES *)res;
1572             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1573                res->res_dir.hdr.name, rindex, pass);
1574          }
1575       }
1576
1577       scan_to_eol(lc);
1578       set_bit(index, res_all.hdr.item_present);
1579    } else {
1580       store_alist_res(lc, item, index, pass);
1581    }
1582 }
1583
1584 /*
1585  * Store JobType (backup, verify, restore)
1586  *
1587  */
1588 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1589 {
1590    int token, i;
1591
1592    token = lex_get_token(lc, T_NAME);
1593    /* Store the type both pass 1 and pass 2 */
1594    for (i=0; migtypes[i].type_name; i++) {
1595       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1596          *(int *)(item->value) = migtypes[i].job_type;
1597          i = 0;
1598          break;
1599       }
1600    }
1601    if (i != 0) {
1602       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1603    }
1604    scan_to_eol(lc);
1605    set_bit(index, res_all.hdr.item_present);
1606 }
1607
1608
1609
1610 /*
1611  * Store JobType (backup, verify, restore)
1612  *
1613  */
1614 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1615 {
1616    int token, i;
1617
1618    token = lex_get_token(lc, T_NAME);
1619    /* Store the type both pass 1 and pass 2 */
1620    for (i=0; jobtypes[i].type_name; i++) {
1621       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1622          *(int *)(item->value) = jobtypes[i].job_type;
1623          i = 0;
1624          break;
1625       }
1626    }
1627    if (i != 0) {
1628       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1629    }
1630    scan_to_eol(lc);
1631    set_bit(index, res_all.hdr.item_present);
1632 }
1633
1634 /*
1635  * Store Job Level (Full, Incremental, ...)
1636  *
1637  */
1638 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1639 {
1640    int token, i;
1641
1642    token = lex_get_token(lc, T_NAME);
1643    /* Store the level pass 2 so that type is defined */
1644    for (i=0; joblevels[i].level_name; i++) {
1645       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1646          *(int *)(item->value) = joblevels[i].level;
1647          i = 0;
1648          break;
1649       }
1650    }
1651    if (i != 0) {
1652       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1653    }
1654    scan_to_eol(lc);
1655    set_bit(index, res_all.hdr.item_present);
1656 }
1657
1658
1659 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1660 {
1661    int token, i;
1662    token = lex_get_token(lc, T_NAME);
1663    /* Scan Replacement options */
1664    for (i=0; ReplaceOptions[i].name; i++) {
1665       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1666          *(int *)(item->value) = ReplaceOptions[i].token;
1667          i = 0;
1668          break;
1669       }
1670    }
1671    if (i != 0) {
1672       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1673    }
1674    scan_to_eol(lc);
1675    set_bit(index, res_all.hdr.item_present);
1676 }
1677
1678 /*
1679  * Store ACL (access control list)
1680  *
1681  */
1682 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1683 {
1684    int token;
1685
1686    for (;;) {
1687       token = lex_get_token(lc, T_STRING);
1688       if (pass == 1) {
1689          if (((alist **)item->value)[item->code] == NULL) {
1690             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1691             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1692          }
1693          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1694          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1695       }
1696       token = lex_get_token(lc, T_ALL);
1697       if (token == T_COMMA) {
1698          continue;                    /* get another ACL */
1699       }
1700       break;
1701    }
1702    set_bit(index, res_all.hdr.item_present);
1703 }
1704
1705 /* We build RunScripts items here */
1706 static RUNSCRIPT res_runscript;
1707
1708 /* Store a runscript->when in a bit field */
1709 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1710 {
1711    lex_get_token(lc, T_NAME);
1712
1713    if (strcasecmp(lc->str, "before") == 0) {
1714       *(int *)(item->value) = SCRIPT_Before ;
1715    } else if (strcasecmp(lc->str, "after") == 0) {
1716       *(int *)(item->value) = SCRIPT_After;
1717    } else if (strcasecmp(lc->str, "always") == 0) {
1718       *(int *)(item->value) = SCRIPT_Any;
1719    } else {
1720       scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1721    }
1722    scan_to_eol(lc);
1723 }
1724
1725 /* Store a runscript->target
1726  * 
1727  */
1728 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1729 {
1730    lex_get_token(lc, T_STRING);
1731
1732    if (pass == 2) {
1733       if (strcmp(lc->str, "%c") == 0) {
1734          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1735       } else if (strcasecmp(lc->str, "yes") == 0) {
1736          ((RUNSCRIPT*) item->value)->set_target("%c");
1737       } else if (strcasecmp(lc->str, "no") == 0) {
1738          ((RUNSCRIPT*) item->value)->set_target("");
1739       } else {
1740          RES *res = GetResWithName(R_CLIENT, lc->str);
1741          if (res == NULL) {
1742             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1743                       lc->str, lc->line_no, lc->line);
1744          }
1745
1746          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1747       }
1748    }
1749    scan_to_eol(lc);
1750 }
1751
1752 /*
1753  * Store a runscript->command as a string
1754  */
1755 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1756 {
1757    lex_get_token(lc, T_STRING);
1758
1759    if (pass == 2) {
1760       ((RUNSCRIPT*)item->value)->set_command(lc->str, item->code);
1761    }
1762    scan_to_eol(lc);
1763 }
1764
1765 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1766 {
1767    lex_get_token(lc, T_STRING);
1768    alist **runscripts = (alist **)(item->value) ;
1769
1770    if (pass == 2) {
1771       RUNSCRIPT *script = new_runscript();
1772       script->set_job_code_callback(job_code_callback_filesetname);
1773
1774       script->set_command(lc->str);
1775
1776       /* TODO: remove all script->old_proto with bacula 1.42 */
1777
1778       if (strcmp(item->name, "runbeforejob") == 0) {
1779          script->when = SCRIPT_Before;
1780          script->fail_on_error = true;
1781          script->set_target("");
1782
1783       } else if (strcmp(item->name, "runafterjob") == 0) {
1784          script->when = SCRIPT_After;
1785          script->on_success = true;
1786          script->on_failure = false;
1787          script->set_target("");
1788          
1789       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1790          script->old_proto = true;
1791          script->when = SCRIPT_After;
1792          script->set_target("%c");
1793          script->on_success = true;
1794          script->on_failure = false;
1795
1796       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1797          script->old_proto = true;
1798          script->when = SCRIPT_Before;
1799          script->set_target("%c");
1800          script->fail_on_error = true;
1801
1802       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1803          script->when = SCRIPT_After;
1804          script->on_failure = true;
1805          script->on_success = false;
1806          script->set_target("");
1807       }
1808
1809       if (*runscripts == NULL) {
1810         *runscripts = New(alist(10, not_owned_by_alist));
1811       }
1812       
1813       (*runscripts)->append(script);
1814       script->debug();
1815    }
1816
1817    scan_to_eol(lc);
1818 }
1819
1820 /* Store a bool in a bit field without modifing res_all.hdr 
1821  * We can also add an option to store_bool to skip res_all.hdr
1822  */
1823 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1824 {
1825    lex_get_token(lc, T_NAME);
1826    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1827       *(bool *)(item->value) = true;
1828    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1829       *(bool *)(item->value) = false;
1830    } else {
1831       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1832    }
1833    scan_to_eol(lc);
1834 }
1835
1836 /*
1837  * new RunScript items
1838  *   name     handler     value               code flags default_value
1839  */
1840 static RES_ITEM runscript_items[] = {
1841  {"command",        store_runscript_cmd,  {(char **)&res_runscript},     SHELL_CMD, 0, 0}, 
1842  {"console",        store_runscript_cmd,  {(char **)&res_runscript},     CONSOLE_CMD, 0, 0}, 
1843  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, 
1844  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
1845  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
1846  {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1847  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1848  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
1849  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
1850  {NULL, NULL, {0}, 0, 0, 0}
1851 };
1852
1853 /*
1854  * Store RunScript info
1855  *
1856  *  Note, when this routine is called, we are inside a Job
1857  *  resource.  We treat the RunScript like a sort of
1858  *  mini-resource within the Job resource.
1859  */
1860 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1861 {
1862    int token, i;
1863    alist **runscripts = (alist **)(item->value) ;
1864
1865    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1866
1867    res_runscript.reset_default();      /* setting on_success, on_failure, fail_on_error */
1868    
1869    token = lex_get_token(lc, T_SKIP_EOL);
1870    
1871    if (token != T_BOB) {
1872       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1873    }
1874    
1875    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1876       if (token == T_EOB) {
1877         break;
1878       }
1879       if (token != T_IDENTIFIER) {
1880         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1881       }
1882       for (i=0; runscript_items[i].name; i++) {
1883         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1884            token = lex_get_token(lc, T_SKIP_EOL);
1885            if (token != T_EQUALS) {
1886               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1887            }
1888            
1889            /* Call item handler */
1890            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1891            i = -1;
1892            break;
1893         }
1894       }
1895       
1896       if (i >=0) {
1897         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1898       }
1899    }
1900
1901    if (pass == 2) {
1902       if (res_runscript.command == NULL) {
1903          scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1904                    "command", "runscript");
1905       }
1906
1907       /* run on client by default */
1908       if (res_runscript.target == NULL) {
1909          res_runscript.set_target("%c");
1910       }
1911
1912       RUNSCRIPT *script = new_runscript();
1913       memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1914       script->set_job_code_callback(job_code_callback_filesetname);
1915       
1916       if (*runscripts == NULL) {
1917         *runscripts = New(alist(10, not_owned_by_alist));
1918       }
1919       
1920       (*runscripts)->append(script);
1921       script->debug();
1922    }
1923
1924    scan_to_eol(lc);
1925    set_bit(index, res_all.hdr.item_present);
1926 }
1927
1928 /* callback function for edit_job_codes */
1929 extern "C" char *job_code_callback_filesetname(JCR *jcr, const char* param)
1930 {
1931    if (param[0] == 'f') {
1932       return jcr->fileset->name();
1933    } else {
1934       return NULL;
1935    }
1936 }