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