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