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