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