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