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