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