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