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