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