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