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