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