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