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