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