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