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