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