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