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