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