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