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