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