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