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