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