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