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