]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Merge branch 'master' of ssh://bacula.git.sourceforge.net/gitroot/bacula/bacula
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2009 Free Software Foundation Europe e.V.
5
6    The main author of Bacula is Kern Sibbald, with contributions from
7    many others, a complete list can be found in the file AUTHORS.
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version two of the GNU General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22
23    Bacula® is a registered trademark of Kern Sibbald.
24    The licensor of Bacula is the Free Software Foundation Europe
25    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26    Switzerland, email:ftf@fsfeurope.org.
27 */
28 /*
29  *   Main configuration file parser for Bacula Directors,
30  *    some parts may be split into separate files such as
31  *    the schedule configuration (run_config.c).
32  *
33  *   Note, the configuration file parser consists of three parts
34  *
35  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
36  *
37  *   2. The generic config  scanner in lib/parse_config.c and
38  *      lib/parse_config.h.
39  *      These files contain the parser code, some utility
40  *      routines, and the common store routines (name, int,
41  *      string).
42  *
43  *   3. The daemon specific file, which contains the Resource
44  *      definitions as well as any specific store routines
45  *      for the resource records.
46  *
47  *     Kern Sibbald, January MM
48  *
49  *     Version $Id$
50  */
51
52
53 #include "bacula.h"
54 #include "dird.h"
55
56 /* Define the first and last resource ID record
57  * types. Note, these should be unique for each
58  * daemon though not a requirement.
59  */
60 int32_t r_first = R_FIRST;
61 int32_t r_last  = R_LAST;
62 static RES *sres_head[R_LAST - R_FIRST + 1];
63 RES **res_head = sres_head;
64
65 /* Imported subroutines */
66 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
67 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
68 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
69
70
71 /* Forward referenced subroutines */
72
73 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
74 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
75 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
76 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
77 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass);
79 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
80 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
81 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
82 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
83 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
84
85 /* We build the current resource here as we are
86  * scanning the resource configuration definition,
87  * then move it to allocated memory when the resource
88  * scan is complete.
89  */
90 #if defined(_MSC_VER)
91 extern "C" { // work around visual compiler mangling variables
92    URES res_all;
93 }
94 #else
95 URES res_all;
96 #endif
97 int32_t res_all_size = sizeof(res_all);
98
99
100 /* Definition of records permitted within each
101  * resource with the routine to process the record
102  * information.  NOTE! quoted names must be in lower case.
103  */
104 /*
105  *    Director Resource
106  *
107  *   name          handler     value                 code flags    default_value
108  */
109 static RES_ITEM dir_items[] = {
110    {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
111    {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
112    {"messages",    store_res,      ITEM(res_dir.messages), R_MSGS, 0, 0},
113    {"dirport",     store_addresses_port,    ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
114    {"diraddress",  store_addresses_address, ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
115    {"diraddresses",store_addresses,         ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
116    {"dirsourceaddress",store_addresses_address, ITEM(res_dir.DIRsrc_addr),  0, ITEM_DEFAULT, 0},
117    {"queryfile",   store_dir,      ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
118    {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
119    {"plugindirectory",  store_dir, ITEM(res_dir.plugin_directory),  0, 0, 0},
120    {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
121    {"piddirectory",     store_dir, ITEM(res_dir.pid_directory),     0, ITEM_REQUIRED, 0},
122    {"subsysdirectory",  store_dir, ITEM(res_dir.subsys_directory),  0, 0, 0},
123    {"maximumconcurrentjobs", store_pint32, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
124    {"maximumconsoleconnections", store_pint32, ITEM(res_dir.MaxConsoleConnect), 0, ITEM_DEFAULT, 20},
125    {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
126    {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 3 * 60},
127    {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 30 * 60},
128    {"heartbeatinterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 0},
129    {"tlsauthenticate",      store_bool,      ITEM(res_dir.tls_authenticate), 0, 0, 0},
130    {"tlsenable",            store_bool,      ITEM(res_dir.tls_enable), 0, 0, 0},
131    {"tlsrequire",           store_bool,      ITEM(res_dir.tls_require), 0, 0, 0},
132    {"tlsverifypeer",        store_bool,      ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
133    {"tlscacertificatefile", store_dir,       ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
134    {"tlscacertificatedir",  store_dir,       ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
135    {"tlscertificate",       store_dir,       ITEM(res_dir.tls_certfile), 0, 0, 0},
136    {"tlskey",               store_dir,       ITEM(res_dir.tls_keyfile), 0, 0, 0},
137    {"tlsdhfile",            store_dir,       ITEM(res_dir.tls_dhfile), 0, 0, 0},
138    {"tlsallowedcn",         store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
139    {"statisticsretention",  store_time,      ITEM(res_dir.stats_retention),  0, ITEM_DEFAULT, 60*60*24*31*12*5},
140    {"verid",                store_str,       ITEM(res_dir.verid), 0, 0, 0},
141    {NULL, NULL, {0}, 0, 0, 0}
142 };
143
144 /*
145  *    Console Resource
146  *
147  *   name          handler     value                 code flags    default_value
148  */
149 static RES_ITEM con_items[] = {
150    {"name",        store_name,     ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
151    {"description", store_str,      ITEM(res_con.hdr.desc), 0, 0, 0},
152    {"password",    store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
153    {"jobacl",      store_acl,      ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
154    {"clientacl",   store_acl,      ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
155    {"storageacl",  store_acl,      ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
156    {"scheduleacl", store_acl,      ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
157    {"runacl",      store_acl,      ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
158    {"poolacl",     store_acl,      ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
159    {"commandacl",  store_acl,      ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
160    {"filesetacl",  store_acl,      ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
161    {"catalogacl",  store_acl,      ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
162    {"whereacl",    store_acl,      ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
163    {"pluginoptionsacl",     store_acl,       ITEM(res_con.ACL_lists), PluginOptions_ACL, 0, 0},
164    {"tlsauthenticate",      store_bool,      ITEM(res_con.tls_authenticate), 0, 0, 0},
165    {"tlsenable",            store_bool,      ITEM(res_con.tls_enable), 0, 0, 0},
166    {"tlsrequire",           store_bool,      ITEM(res_con.tls_require), 0, 0, 0},
167    {"tlsverifypeer",        store_bool,      ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
168    {"tlscacertificatefile", store_dir,       ITEM(res_con.tls_ca_certfile), 0, 0, 0},
169    {"tlscacertificatedir",  store_dir,       ITEM(res_con.tls_ca_certdir), 0, 0, 0},
170    {"tlscertificate",       store_dir,       ITEM(res_con.tls_certfile), 0, 0, 0},
171    {"tlskey",               store_dir,       ITEM(res_con.tls_keyfile), 0, 0, 0},
172    {"tlsdhfile",            store_dir,       ITEM(res_con.tls_dhfile), 0, 0, 0},
173    {"tlsallowedcn",         store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
174    {NULL, NULL, {0}, 0, 0, 0}
175 };
176
177
178 /*
179  *    Client or File daemon resource
180  *
181  *   name          handler     value                 code flags    default_value
182  */
183
184 static RES_ITEM cli_items[] = {
185    {"name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
186    {"description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
187    {"address",  store_str,        ITEM(res_client.address),  0, ITEM_REQUIRED, 0},
188    {"fdaddress",  store_str,      ITEM(res_client.address),  0, 0, 0},
189    {"fdport",   store_pint32,     ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
190    {"password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
191    {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
192    {"catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, ITEM_REQUIRED, 0},
193    {"fileretention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
194    {"jobretention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*180},
195    {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0},
196    {"autoprune", store_bool,      ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
197    {"maximumconcurrentjobs", store_pint32,   ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
198    {"tlsauthenticate",      store_bool,      ITEM(res_client.tls_authenticate), 0, 0, 0},
199    {"tlsenable",            store_bool,      ITEM(res_client.tls_enable), 0, 0, 0},
200    {"tlsrequire",           store_bool,      ITEM(res_client.tls_require), 0, 0, 0},
201    {"tlscacertificatefile", store_dir,       ITEM(res_client.tls_ca_certfile), 0, 0, 0},
202    {"tlscacertificatedir",  store_dir,       ITEM(res_client.tls_ca_certdir), 0, 0, 0},
203    {"tlscertificate",       store_dir,       ITEM(res_client.tls_certfile), 0, 0, 0},
204    {"tlskey",               store_dir,       ITEM(res_client.tls_keyfile), 0, 0, 0},
205    {"tlsallowedcn",         store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
206    {NULL, NULL, {0}, 0, 0, 0}
207 };
208
209 /* Storage daemon resource
210  *
211  *   name          handler     value                 code flags    default_value
212  */
213 static RES_ITEM store_items[] = {
214    {"name",        store_name,     ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
215    {"description", store_str,      ITEM(res_store.hdr.desc),   0, 0, 0},
216    {"sdport",      store_pint32,   ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
217    {"address",     store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
218    {"sdaddress",   store_str,      ITEM(res_store.address),    0, 0, 0},
219    {"password",    store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 0},
220    {"sdpassword",  store_password, ITEM(res_store.password),   0, 0, 0},
221    {"device",      store_device,   ITEM(res_store.device),     R_DEVICE, ITEM_REQUIRED, 0},
222    {"mediatype",   store_strname,  ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
223    {"autochanger", store_bool,     ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
224    {"enabled",     store_bool,     ITEM(res_store.enabled),     0, ITEM_DEFAULT, true},
225    {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
226    {"maximumconcurrentjobs", store_pint32, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
227    {"sddport", store_pint32, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
228    {"tlsauthenticate",      store_bool,      ITEM(res_store.tls_authenticate), 0, 0, 0},
229    {"tlsenable",            store_bool,      ITEM(res_store.tls_enable), 0, 0, 0},
230    {"tlsrequire",           store_bool,      ITEM(res_store.tls_require), 0, 0, 0},
231    {"tlscacertificatefile", store_dir,       ITEM(res_store.tls_ca_certfile), 0, 0, 0},
232    {"tlscacertificatedir",  store_dir,       ITEM(res_store.tls_ca_certdir), 0, 0, 0},
233    {"tlscertificate",       store_dir,       ITEM(res_store.tls_certfile), 0, 0, 0},
234    {"tlskey",               store_dir,       ITEM(res_store.tls_keyfile), 0, 0, 0},
235    {NULL, NULL, {0}, 0, 0, 0}
236 };
237
238 /*
239  *    Catalog Resource Directives
240  *
241  *   name          handler     value                 code flags    default_value
242  */
243 static RES_ITEM cat_items[] = {
244    {"name",     store_name,     ITEM(res_cat.hdr.name),    0, ITEM_REQUIRED, 0},
245    {"description", store_str,   ITEM(res_cat.hdr.desc),    0, 0, 0},
246    {"address",  store_str,      ITEM(res_cat.db_address),  0, 0, 0},
247    {"dbaddress", store_str,     ITEM(res_cat.db_address),  0, 0, 0},
248    {"dbport",   store_pint32,   ITEM(res_cat.db_port),      0, 0, 0},
249    /* keep this password as store_str for the moment */
250    {"password", store_str,      ITEM(res_cat.db_password), 0, 0, 0},
251    {"dbpassword", store_str,    ITEM(res_cat.db_password), 0, 0, 0},
252    {"dbuser",   store_str,      ITEM(res_cat.db_user),     0, 0, 0},
253    {"user",     store_str,      ITEM(res_cat.db_user),     0, 0, 0},
254    {"dbname",   store_str,      ITEM(res_cat.db_name),     0, ITEM_REQUIRED, 0},
255    {"dbdriver", store_str,      ITEM(res_cat.db_driver),   0, 0, 0},
256    {"dbsocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0},
257    /* Turned off for the moment */
258    {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
259    {NULL, NULL, {0}, 0, 0, 0}
260 };
261
262 /*
263  *    Job Resource Directives
264  *
265  *   name          handler     value                 code flags    default_value
266  */
267 RES_ITEM job_items[] = {
268    {"name",      store_name,    ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
269    {"description", store_str,   ITEM(res_job.hdr.desc), 0, 0, 0},
270    {"type",      store_jobtype, ITEM(res_job.JobType),  0, ITEM_REQUIRED, 0},
271    {"level",     store_level,   ITEM(res_job.JobLevel),    0, 0, 0},
272    {"messages",  store_res,     ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
273    {"storage",   store_alist_res, ITEM(res_job.storage),  R_STORAGE, 0, 0},
274    {"pool",      store_res,     ITEM(res_job.pool),     R_POOL, ITEM_REQUIRED, 0},
275    {"fullbackuppool",  store_res, ITEM(res_job.full_pool),   R_POOL, 0, 0},
276    {"incrementalbackuppool",  store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
277    {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
278    {"client",    store_res,     ITEM(res_job.client),   R_CLIENT, ITEM_REQUIRED, 0},
279    {"fileset",   store_res,     ITEM(res_job.fileset),  R_FILESET, ITEM_REQUIRED, 0},
280    {"schedule",  store_res,     ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
281    {"verifyjob", store_res,     ITEM(res_job.verify_job), R_JOB, 0, 0},
282    {"jobtoverify", store_res,   ITEM(res_job.verify_job), R_JOB, 0, 0},
283    {"jobdefs",   store_res,     ITEM(res_job.jobdefs),    R_JOBDEFS, 0, 0},
284    {"run",       store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
285    /* Root of where to restore files */
286    {"where",    store_dir,      ITEM(res_job.RestoreWhere), 0, 0, 0},
287    {"regexwhere",    store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
288    {"stripprefix",   store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
289    {"addprefix",    store_str,  ITEM(res_job.add_prefix), 0, 0, 0},
290    {"addsuffix",    store_str,  ITEM(res_job.add_suffix), 0, 0, 0},
291    /* Where to find bootstrap during restore */
292    {"bootstrap",store_dir,      ITEM(res_job.RestoreBootstrap), 0, 0, 0},
293    /* Where to write bootstrap file during backup */
294    {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
295    {"writeverifylist",store_dir,ITEM(res_job.WriteVerifyList), 0, 0, 0},
296    {"replace",  store_replace,  ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
297    {"maxrunschedtime", store_time, ITEM(res_job.MaxRunSchedTime), 0, 0, 0},
298    {"maxruntime",   store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
299    /* xxxMaxWaitTime are deprecated */
300    {"fullmaxwaittime",  store_time, ITEM(res_job.FullMaxRunTime), 0, 0, 0},
301    {"incrementalmaxwaittime",  store_time, ITEM(res_job.IncMaxRunTime), 0, 0, 0},
302    {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxRunTime), 0, 0, 0},
303    {"fullmaxruntime",  store_time, ITEM(res_job.FullMaxRunTime), 0, 0, 0},
304    {"incrementalmaxruntime",  store_time, ITEM(res_job.IncMaxRunTime), 0, 0, 0},
305    {"differentialmaxruntime", store_time, ITEM(res_job.DiffMaxRunTime), 0, 0, 0},
306    {"maxwaittime",  store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
307    {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
308    {"maxfullinterval",  store_time, ITEM(res_job.MaxFullInterval), 0, 0, 0},
309    {"maxdiffinterval",  store_time, ITEM(res_job.MaxDiffInterval), 0, 0, 0},
310    {"jobretention", store_time, ITEM(res_job.JobRetention),  0, 0, 0},
311    {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
312    {"prunejobs",   store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
313    {"prunefiles",  store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
314    {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
315    {"enabled",     store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
316    {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
317    {"spooldata",   store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
318    {"spoolsize",   store_size64, ITEM(res_job.spool_size), 0, 0, 0},
319    {"rerunfailedlevels",   store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
320    {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
321    {"runbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
322    {"runafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
323    {"runafterfailedjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
324    {"clientrunbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
325    {"clientrunafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
326    {"maximumconcurrentjobs", store_pint32, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
327    {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
328    {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
329    {"rescheduletimes",    store_pint32, ITEM(res_job.RescheduleTimes), 0, 0, 0},
330    {"priority",           store_pint32, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
331    {"allowmixedpriority", store_bool, ITEM(res_job.allow_mixed_priority), 0, ITEM_DEFAULT, false},
332    {"writepartafterjob",  store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
333    {"selectionpattern",   store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
334    {"runscript",          store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
335    {"selectiontype",      store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
336    {"accurate",           store_bool, ITEM(res_job.accurate), 0,0,0},
337    {"allowduplicatejobs", store_bool, ITEM(res_job.AllowDuplicateJobs), 0, ITEM_DEFAULT, false},
338    {"allowhigherduplicates",   store_bool, ITEM(res_job.AllowHigherDuplicates), 0, ITEM_DEFAULT, true},
339    {"cancelqueuedduplicates",  store_bool, ITEM(res_job.CancelQueuedDuplicates), 0, ITEM_DEFAULT, false},
340    {"cancelrunningduplicates", store_bool, ITEM(res_job.CancelRunningDuplicates), 0, ITEM_DEFAULT, false},
341    {"pluginoptions", store_str, ITEM(res_job.PluginOptions), 0, 0, 0},
342    {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    if (strcasecmp(lc->str, "truncate") == 0) {
1607       *destination = (*destination) | AOP_TRUNCATE;
1608    } else {
1609       scan_err2(lc, _("Expected one of: %s, got: %s"), "Truncate", lc->str);
1610       return;
1611    }
1612    scan_to_eol(lc);
1613    set_bit(index, res_all.hdr.item_present);
1614 }
1615
1616 /*
1617  * Store Device. Note, the resource is created upon the
1618  *  first reference. The details of the resource are obtained
1619  *  later from the SD.
1620  */
1621 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1622 {
1623    int token;
1624    URES *res;
1625    int rindex = R_DEVICE - r_first;
1626    int size = sizeof(DEVICE);
1627    bool found = false;
1628
1629    if (pass == 1) {
1630       token = lex_get_token(lc, T_NAME);
1631       if (!res_head[rindex]) {
1632          res = (URES *)malloc(size);
1633          memset(res, 0, size);
1634          res->res_dev.hdr.name = bstrdup(lc->str);
1635          res_head[rindex] = (RES *)res; /* store first entry */
1636          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1637                res->res_dir.hdr.name, rindex);
1638       } else {
1639          RES *next;
1640          /* See if it is already defined */
1641          for (next=res_head[rindex]; next->next; next=next->next) {
1642             if (strcmp(next->name, lc->str) == 0) {
1643                found = true;
1644                break;
1645             }
1646          }
1647          if (!found) {
1648             res = (URES *)malloc(size);
1649             memset(res, 0, size);
1650             res->res_dev.hdr.name = bstrdup(lc->str);
1651             next->next = (RES *)res;
1652             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1653                res->res_dir.hdr.name, rindex, pass);
1654          }
1655       }
1656
1657       scan_to_eol(lc);
1658       set_bit(index, res_all.hdr.item_present);
1659    } else {
1660       store_alist_res(lc, item, index, pass);
1661    }
1662 }
1663
1664 /*
1665  * Store Migration/Copy type
1666  *
1667  */
1668 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1669 {
1670    int token, i;
1671
1672    token = lex_get_token(lc, T_NAME);
1673    /* Store the type both pass 1 and pass 2 */
1674    for (i=0; migtypes[i].type_name; i++) {
1675       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1676          *(uint32_t *)(item->value) = migtypes[i].job_type;
1677          i = 0;
1678          break;
1679       }
1680    }
1681    if (i != 0) {
1682       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1683    }
1684    scan_to_eol(lc);
1685    set_bit(index, res_all.hdr.item_present);
1686 }
1687
1688
1689
1690 /*
1691  * Store JobType (backup, verify, restore)
1692  *
1693  */
1694 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1695 {
1696    int token, i;
1697
1698    token = lex_get_token(lc, T_NAME);
1699    /* Store the type both pass 1 and pass 2 */
1700    for (i=0; jobtypes[i].type_name; i++) {
1701       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1702          *(uint32_t *)(item->value) = jobtypes[i].job_type;
1703          i = 0;
1704          break;
1705       }
1706    }
1707    if (i != 0) {
1708       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1709    }
1710    scan_to_eol(lc);
1711    set_bit(index, res_all.hdr.item_present);
1712 }
1713
1714 /*
1715  * Store Job Level (Full, Incremental, ...)
1716  *
1717  */
1718 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1719 {
1720    int token, i;
1721
1722    token = lex_get_token(lc, T_NAME);
1723    /* Store the level pass 2 so that type is defined */
1724    for (i=0; joblevels[i].level_name; i++) {
1725       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1726          *(uint32_t *)(item->value) = joblevels[i].level;
1727          i = 0;
1728          break;
1729       }
1730    }
1731    if (i != 0) {
1732       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1733    }
1734    scan_to_eol(lc);
1735    set_bit(index, res_all.hdr.item_present);
1736 }
1737
1738
1739 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1740 {
1741    int token, i;
1742    token = lex_get_token(lc, T_NAME);
1743    /* Scan Replacement options */
1744    for (i=0; ReplaceOptions[i].name; i++) {
1745       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1746          *(uint32_t *)(item->value) = ReplaceOptions[i].token;
1747          i = 0;
1748          break;
1749       }
1750    }
1751    if (i != 0) {
1752       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1753    }
1754    scan_to_eol(lc);
1755    set_bit(index, res_all.hdr.item_present);
1756 }
1757
1758 /*
1759  * Store ACL (access control list)
1760  *
1761  */
1762 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1763 {
1764    int token;
1765
1766    for (;;) {
1767       token = lex_get_token(lc, T_STRING);
1768       if (pass == 1) {
1769          if (((alist **)item->value)[item->code] == NULL) {
1770             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1771             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1772          }
1773          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1774          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1775       }
1776       token = lex_get_token(lc, T_ALL);
1777       if (token == T_COMMA) {
1778          continue;                    /* get another ACL */
1779       }
1780       break;
1781    }
1782    set_bit(index, res_all.hdr.item_present);
1783 }
1784
1785 /* We build RunScripts items here */
1786 static RUNSCRIPT res_runscript;
1787
1788 /* Store a runscript->when in a bit field */
1789 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1790 {
1791    lex_get_token(lc, T_NAME);
1792
1793    if (strcasecmp(lc->str, "before") == 0) {
1794       *(uint32_t *)(item->value) = SCRIPT_Before ;
1795    } else if (strcasecmp(lc->str, "after") == 0) {
1796       *(uint32_t *)(item->value) = SCRIPT_After;
1797    } else if (strcasecmp(lc->str, "aftervss") == 0) {
1798       *(uint32_t *)(item->value) = SCRIPT_AfterVSS;
1799    } else if (strcasecmp(lc->str, "always") == 0) {
1800       *(uint32_t *)(item->value) = SCRIPT_Any;
1801    } else {
1802       scan_err2(lc, _("Expect %s, got: %s"), "Before, After, AfterVSS or Always", lc->str);
1803    }
1804    scan_to_eol(lc);
1805 }
1806
1807 /* Store a runscript->target
1808  * 
1809  */
1810 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1811 {
1812    lex_get_token(lc, T_STRING);
1813
1814    if (pass == 2) {
1815       if (strcmp(lc->str, "%c") == 0) {
1816          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1817       } else if (strcasecmp(lc->str, "yes") == 0) {
1818          ((RUNSCRIPT*) item->value)->set_target("%c");
1819       } else if (strcasecmp(lc->str, "no") == 0) {
1820          ((RUNSCRIPT*) item->value)->set_target("");
1821       } else {
1822          RES *res = GetResWithName(R_CLIENT, lc->str);
1823          if (res == NULL) {
1824             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1825                       lc->str, lc->line_no, lc->line);
1826          }
1827
1828          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1829       }
1830    }
1831    scan_to_eol(lc);
1832 }
1833
1834 /*
1835  * Store a runscript->command as a string and runscript->cmd_type as a pointer
1836  */
1837 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1838 {
1839    lex_get_token(lc, T_STRING);
1840
1841    if (pass == 2) {
1842       Dmsg2(1, "runscript cmd=%s type=%c\n", lc->str, item->code);
1843       POOLMEM *c = get_pool_memory(PM_FNAME);
1844       /* Each runscript command takes 2 entries in commands list */
1845       pm_strcpy(c, lc->str);
1846       ((RUNSCRIPT*) item->value)->commands->prepend(c); /* command line */
1847       ((RUNSCRIPT*) item->value)->commands->prepend((void *)item->code); /* command type */
1848    }
1849    scan_to_eol(lc);
1850 }
1851
1852 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1853 {
1854    lex_get_token(lc, T_STRING);
1855    alist **runscripts = (alist **)(item->value) ;
1856
1857    if (pass == 2) {
1858       RUNSCRIPT *script = new_runscript();
1859       script->set_job_code_callback(job_code_callback_filesetname);
1860
1861       script->set_command(lc->str);
1862
1863       /* TODO: remove all script->old_proto with bacula 1.42 */
1864
1865       if (strcmp(item->name, "runbeforejob") == 0) {
1866          script->when = SCRIPT_Before;
1867          script->fail_on_error = true;
1868          script->set_target("");
1869
1870       } else if (strcmp(item->name, "runafterjob") == 0) {
1871          script->when = SCRIPT_After;
1872          script->on_success = true;
1873          script->on_failure = false;
1874          script->set_target("");
1875          
1876       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1877          script->old_proto = true;
1878          script->when = SCRIPT_After;
1879          script->set_target("%c");
1880          script->on_success = true;
1881          script->on_failure = false;
1882
1883       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1884          script->old_proto = true;
1885          script->when = SCRIPT_Before;
1886          script->set_target("%c");
1887          script->fail_on_error = true;
1888
1889       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1890          script->when = SCRIPT_After;
1891          script->on_failure = true;
1892          script->on_success = false;
1893          script->set_target("");
1894       }
1895
1896       if (*runscripts == NULL) {
1897         *runscripts = New(alist(10, not_owned_by_alist));
1898       }
1899       
1900       (*runscripts)->append(script);
1901       script->debug();
1902    }
1903
1904    scan_to_eol(lc);
1905 }
1906
1907 /* Store a bool in a bit field without modifing res_all.hdr 
1908  * We can also add an option to store_bool to skip res_all.hdr
1909  */
1910 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1911 {
1912    lex_get_token(lc, T_NAME);
1913    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1914       *(bool *)(item->value) = true;
1915    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1916       *(bool *)(item->value) = false;
1917    } else {
1918       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1919    }
1920    scan_to_eol(lc);
1921 }
1922
1923 /*
1924  * new RunScript items
1925  *   name     handler     value               code flags default_value
1926  */
1927 static RES_ITEM runscript_items[] = {
1928  {"command",        store_runscript_cmd,  {(char **)&res_runscript},     SHELL_CMD, 0, 0}, 
1929  {"console",        store_runscript_cmd,  {(char **)&res_runscript},     CONSOLE_CMD, 0, 0}, 
1930  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, 
1931  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
1932  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
1933  {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1934  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1935  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
1936  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
1937  {NULL, NULL, {0}, 0, 0, 0}
1938 };
1939
1940 /*
1941  * Store RunScript info
1942  *
1943  *  Note, when this routine is called, we are inside a Job
1944  *  resource.  We treat the RunScript like a sort of
1945  *  mini-resource within the Job resource.
1946  */
1947 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1948 {
1949    char *c;
1950    int token, i, t;
1951    alist **runscripts = (alist **)(item->value) ;
1952
1953    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1954
1955    token = lex_get_token(lc, T_SKIP_EOL);
1956    
1957    if (token != T_BOB) {
1958       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1959    }
1960    /* setting on_success, on_failure, fail_on_error */
1961    res_runscript.reset_default();   
1962
1963    if (pass == 2) {
1964       res_runscript.commands = New(alist(10, not_owned_by_alist));
1965    }
1966
1967    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1968       if (token == T_EOB) {
1969         break;
1970       }
1971       if (token != T_IDENTIFIER) {
1972         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1973       }
1974       for (i=0; runscript_items[i].name; i++) {
1975         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1976            token = lex_get_token(lc, T_SKIP_EOL);
1977            if (token != T_EQUALS) {
1978               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1979            }
1980            
1981            /* Call item handler */
1982            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1983            i = -1;
1984            break;
1985         }
1986       }
1987       
1988       if (i >=0) {
1989         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1990       }
1991    }
1992
1993    if (pass == 2) {
1994       /* run on client by default */
1995       if (res_runscript.target == NULL) {
1996          res_runscript.set_target("%c");
1997       }
1998       if (*runscripts == NULL) {
1999          *runscripts = New(alist(10, not_owned_by_alist));
2000       }
2001       /*
2002        * commands list contains 2 values per command
2003        *  - POOLMEM command string (ex: /bin/true) 
2004        *  - int command type (ex: SHELL_CMD)
2005        */
2006       res_runscript.set_job_code_callback(job_code_callback_filesetname);
2007       while ((c=(char*)res_runscript.commands->pop()) != NULL) {
2008          t = (intptr_t)res_runscript.commands->pop();
2009          RUNSCRIPT *script = new_runscript();
2010          memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
2011          script->command = c;
2012          script->cmd_type = t;
2013          /* target is taken from res_runscript, each runscript object have
2014           * a copy 
2015           */
2016          script->target = NULL;
2017          script->set_target(res_runscript.target);
2018
2019          (*runscripts)->append(script);
2020          script->debug();
2021       }
2022       delete res_runscript.commands;
2023       /* setting on_success, on_failure... cleanup target field */
2024       res_runscript.reset_default(true); 
2025    }
2026
2027    scan_to_eol(lc);
2028    set_bit(index, res_all.hdr.item_present);
2029 }
2030
2031 /* callback function for edit_job_codes */
2032 extern "C" char *job_code_callback_filesetname(JCR *jcr, const char* param)
2033 {
2034    if (param[0] == 'f') {
2035       return jcr->fileset->name();
2036    } else {
2037       return NULL;
2038    }
2039 }
2040
2041 bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code)
2042 {
2043    config->init(configfile, NULL, exit_code, (void *)&res_all, res_all_size,
2044       r_first, r_last, resources, res_head);
2045    return config->parse_config();
2046 }