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