]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
5cad2b9d6b0e8eb7d51c6dba7531bbf2a0f17d68
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2016 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is 
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   Main configuration file parser for Bacula Directors,
21  *    some parts may be split into separate files such as
22  *    the schedule configuration (run_config.c).
23  *
24  *   Note, the configuration file parser consists of three parts
25  *
26  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
27  *
28  *   2. The generic config  scanner in lib/parse_config.c and
29  *      lib/parse_config.h.
30  *      These files contain the parser code, some utility
31  *      routines, and the common store routines (name, int,
32  *      string).
33  *
34  *   3. The daemon specific file, which contains the Resource
35  *      definitions as well as any specific store routines
36  *      for the resource records.
37  *
38  *     Kern Sibbald, January MM
39  *
40  */
41
42
43 #include "bacula.h"
44 #include "dird.h"
45
46 /* Define the first and last resource ID record
47  * types. Note, these should be unique for each
48  * daemon though not a requirement.
49  */
50 int32_t r_first = R_FIRST;
51 int32_t r_last  = R_LAST;
52 static RES *sres_head[R_LAST - R_FIRST + 1];
53 RES **res_head = sres_head;
54
55 /* Imported subroutines */
56 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
57 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
58 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
59
60
61 /* Forward referenced subroutines */
62
63 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
64 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
65 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
66 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
67 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
68 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
69 void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass);
70 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
71 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
72 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
73
74 /* We build the current resource here as we are
75  * scanning the resource configuration definition,
76  * then move it to allocated memory when the resource
77  * scan is complete.
78  */
79 #if defined(_MSC_VER)
80 extern "C" { // work around visual compiler mangling variables
81    URES res_all;
82 }
83 #else
84 URES res_all;
85 #endif
86 int32_t res_all_size = sizeof(res_all);
87
88 /*
89  * Definition of records permitted within each
90  * resource with the routine to process the record
91  * information.
92  */
93 /*
94  *    Director Resource
95  *
96  *   name          handler     value                 code flags    default_value
97  */
98 static RES_ITEM dir_items[] = {
99    {"Name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
100    {"Description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
101    {"Messages",    store_res,      ITEM(res_dir.messages), R_MSGS, 0, 0},
102    {"DirPort",     store_addresses_port,    ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
103    {"DirAddress",  store_addresses_address, ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
104    {"DirAddresses",store_addresses,         ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
105    {"DirSourceAddress",store_addresses_address, ITEM(res_dir.DIRsrc_addr),  0, ITEM_DEFAULT, 0},
106    {"QueryFile",   store_dir,      ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
107    {"WorkingDirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
108    {"PluginDirectory",  store_dir, ITEM(res_dir.plugin_directory),  0, 0, 0},
109    {"ScriptsDirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
110    {"PidDirectory",     store_dir, ITEM(res_dir.pid_directory),     0, ITEM_REQUIRED, 0},
111    {"SubsysDirectory",  store_dir, ITEM(res_dir.subsys_directory),  0, 0, 0},
112    {"MaximumConcurrentJobs", store_pint32, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
113    {"MaximumReloadRequests", store_pint32, ITEM(res_dir.MaxReload), 0, ITEM_DEFAULT, 32},
114    {"MaximumConsoleConnections", store_pint32, ITEM(res_dir.MaxConsoleConnect), 0, ITEM_DEFAULT, 20},
115    {"Password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
116    {"FdConnectTimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 3 * 60},
117    {"SdConnectTimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 30 * 60},
118    {"HeartbeatInterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 5 * 60},
119    {"TlsAuthenticate",      store_bool,      ITEM(res_dir.tls_authenticate), 0, 0, 0},
120    {"TlsEnable",            store_bool,      ITEM(res_dir.tls_enable), 0, 0, 0},
121    {"TlsRequire",           store_bool,      ITEM(res_dir.tls_require), 0, 0, 0},
122    {"TlsVerifyPeer",        store_bool,      ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
123    {"TlsCaCertificateFile", store_dir,       ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
124    {"TlsCaCertificateDir",  store_dir,       ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
125    {"TlsCertificate",       store_dir,       ITEM(res_dir.tls_certfile), 0, 0, 0},
126    {"TlsKey",               store_dir,       ITEM(res_dir.tls_keyfile), 0, 0, 0},
127    {"TlsDhFile",            store_dir,       ITEM(res_dir.tls_dhfile), 0, 0, 0},
128    {"TlsAllowedCn",         store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
129    {"StatisticsRetention",  store_time,      ITEM(res_dir.stats_retention),  0, ITEM_DEFAULT, 60*60*24*31*12*5},
130    {"VerId",                store_str,       ITEM(res_dir.verid), 0, 0, 0},
131    {NULL, NULL, {0}, 0, 0, 0}
132 };
133
134 /*
135  *    Console Resource
136  *
137  *   name          handler     value                 code flags    default_value
138  */
139 static RES_ITEM con_items[] = {
140    {"Name",        store_name,     ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
141    {"Description", store_str,      ITEM(res_con.hdr.desc), 0, 0, 0},
142    {"Password",    store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
143    {"JobAcl",      store_acl,      ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
144    {"ClientAcl",   store_acl,      ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
145    {"StorageAcl",  store_acl,      ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
146    {"ScheduleAcl", store_acl,      ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
147    {"RunAcl",      store_acl,      ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
148    {"PoolAcl",     store_acl,      ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
149    {"CommandAcl",  store_acl,      ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
150    {"FilesetAcl",  store_acl,      ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
151    {"CatalogAcl",  store_acl,      ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
152    {"WhereAcl",    store_acl,      ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
153    {"PluginOptionsAcl",     store_acl,       ITEM(res_con.ACL_lists), PluginOptions_ACL, 0, 0},
154    {"TlsAuthenticate",      store_bool,      ITEM(res_con.tls_authenticate), 0, 0, 0},
155    {"TlsEnable",            store_bool,      ITEM(res_con.tls_enable), 0, 0, 0},
156    {"TlsRequire",           store_bool,      ITEM(res_con.tls_require), 0, 0, 0},
157    {"TlsVerifyPeer",        store_bool,      ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
158    {"TlsCaCertificateFile", store_dir,       ITEM(res_con.tls_ca_certfile), 0, 0, 0},
159    {"TlsCaCertificateDir",  store_dir,       ITEM(res_con.tls_ca_certdir), 0, 0, 0},
160    {"TlsCertificate",       store_dir,       ITEM(res_con.tls_certfile), 0, 0, 0},
161    {"TlsKey",               store_dir,       ITEM(res_con.tls_keyfile), 0, 0, 0},
162    {"TlsDhFile",            store_dir,       ITEM(res_con.tls_dhfile), 0, 0, 0},
163    {"TlsAllowedCn",         store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
164    {NULL, NULL, {0}, 0, 0, 0}
165 };
166
167
168 /*
169  *    Client or File daemon resource
170  *
171  *   name          handler     value                 code flags    default_value
172  */
173
174 static RES_ITEM cli_items[] = {
175    {"Name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
176    {"Description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
177    {"fdaddress",  store_str,      ITEM(res_client.address),  0, 0, 0},
178    {"Address",  store_str,        ITEM(res_client.address),  0, ITEM_REQUIRED, 0},
179    {"FdPort",   store_pint32,     ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
180    {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
181    {"Password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
182    {"FdStorageAddress", store_str, ITEM(res_client.fd_storage_address), 0, 0, 0},
183    {"Catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, ITEM_REQUIRED, 0},
184    {"FileRetention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
185    {"JobRetention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*180},
186    {"HeartbeatInterval",    store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 5 * 60},
187    {"AutoPrune",            store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
188    {"SDCallsClient",        store_bool, ITEM(res_client.sd_calls_client), 0, ITEM_DEFAULT, false},
189    {"SnapshotRetention",  store_time,  ITEM(res_client.SnapRetention),  0, ITEM_DEFAULT, 0},
190    {"MaximumConcurrentJobs", store_pint32,   ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
191    {"TlsAuthenticate",      store_bool,      ITEM(res_client.tls_authenticate), 0, 0, 0},
192    {"TlsEnable",            store_bool,      ITEM(res_client.tls_enable), 0, 0, 0},
193    {"TlsRequire",           store_bool,      ITEM(res_client.tls_require), 0, 0, 0},
194    {"TlsCaCertificateFile", store_dir,       ITEM(res_client.tls_ca_certfile), 0, 0, 0},
195    {"TlsCaCertificateDir",  store_dir,       ITEM(res_client.tls_ca_certdir), 0, 0, 0},
196    {"TlsCertificate",       store_dir,       ITEM(res_client.tls_certfile), 0, 0, 0},
197    {"TlsKey",               store_dir,       ITEM(res_client.tls_keyfile), 0, 0, 0},
198    {"TlsAllowedCn",         store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
199    {"MaximumBandwidthPerJob", store_speed, ITEM(res_client.max_bandwidth), 0, 0, 0},
200    {"Enabled",     store_bool, ITEM(res_client.enabled), 0, ITEM_DEFAULT, true},
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_pint32,   ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
212    {"sdaddress",   store_str,      ITEM(res_store.address),    0, 0, 0},
213    {"Address",     store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
214    {"FdStorageAddress", store_str, ITEM(res_store.fd_storage_address), 0, 0, 0},
215    {"sdpassword",  store_password, ITEM(res_store.password),   0, 0, 0},
216    {"Password",    store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 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, false},
220    {"Enabled",     store_bool,     ITEM(res_store.enabled),     0, ITEM_DEFAULT, true},
221    {"AllowCompression",  store_bool, ITEM(res_store.AllowCompress), 0, ITEM_DEFAULT, true},
222    {"HeartbeatInterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 5 * 60},
223    {"MaximumConcurrentJobs", store_pint32, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
224    {"MaximumConcurrentReadjobs", store_pint32, ITEM(res_store.MaxConcurrentReadJobs), 0, ITEM_DEFAULT, 0},
225    {"sddport", store_pint32, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
226    {"TlsAuthenticate",      store_bool,      ITEM(res_store.tls_authenticate), 0, 0, 0},
227    {"TlsEnable",            store_bool,      ITEM(res_store.tls_enable), 0, 0, 0},
228    {"TlsRequire",           store_bool,      ITEM(res_store.tls_require), 0, 0, 0},
229    {"TlsCaCertificateFile", store_dir,       ITEM(res_store.tls_ca_certfile), 0, 0, 0},
230    {"TlsCaCertificateDir",  store_dir,       ITEM(res_store.tls_ca_certdir), 0, 0, 0},
231    {"TlsCertificate",       store_dir,       ITEM(res_store.tls_certfile), 0, 0, 0},
232    {"TlsKey",               store_dir,       ITEM(res_store.tls_keyfile), 0, 0, 0},
233    {NULL, NULL, {0}, 0, 0, 0}
234 };
235
236 /*
237  *    Catalog Resource Directives
238  *
239  *   name          handler     value                 code flags    default_value
240  */
241 static RES_ITEM cat_items[] = {
242    {"Name",     store_name,     ITEM(res_cat.hdr.name),    0, ITEM_REQUIRED, 0},
243    {"Description", store_str,   ITEM(res_cat.hdr.desc),    0, 0, 0},
244    {"dbaddress", store_str,     ITEM(res_cat.db_address),  0, 0, 0},
245    {"Address",  store_str,      ITEM(res_cat.db_address),  0, 0, 0},
246    {"DbPort",   store_pint32,   ITEM(res_cat.db_port),      0, 0, 0},
247    /* keep this password as store_str for the moment */
248    {"dbpassword", store_str,    ITEM(res_cat.db_password), 0, 0, 0},
249    {"Password", store_str,      ITEM(res_cat.db_password), 0, 0, 0},
250    {"dbuser",   store_str,      ITEM(res_cat.db_user),     0, 0, 0},
251    {"User",     store_str,      ITEM(res_cat.db_user),     0, 0, 0},
252    {"DbName",   store_str,      ITEM(res_cat.db_name),     0, ITEM_REQUIRED, 0},
253    {"dbdriver", store_str,      ITEM(res_cat.db_driver),   0, 0, 0},
254    {"DbSocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0},
255    {"dbsslkey", store_str,      ITEM(res_cat.db_ssl_key),  0, 0, 0},
256    {"dbsslcert", store_str,     ITEM(res_cat.db_ssl_cert),  0, 0, 0},
257    {"dbsslca", store_str,       ITEM(res_cat.db_ssl_ca),  0, 0, 0},
258    {"dbsslcapath", store_str,   ITEM(res_cat.db_ssl_capath),  0, 0, 0},
259    {"dbsslcipher", store_str,   ITEM(res_cat.db_ssl_cipher),  0, 0, 0},
260    /* Turned off for the moment */
261    {"MultipleConnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
262    {"DisableBatchInsert", store_bool, ITEM(res_cat.disable_batch_insert), 0, ITEM_DEFAULT, false},
263    {NULL, NULL, {0}, 0, 0, 0}
264 };
265
266 /*
267  *    Job Resource Directives
268  *
269  *   name          handler     value                 code flags    default_value
270  */
271 RES_ITEM job_items[] = {
272    {"Name",      store_name,    ITEM(res_job.hdr.name),  0, ITEM_REQUIRED, 0},
273    {"Description", store_str,   ITEM(res_job.hdr.desc),  0, 0, 0},
274    {"Type",      store_jobtype, ITEM(res_job.JobType),   0, ITEM_REQUIRED, 0},
275    {"Level",     store_level,   ITEM(res_job.JobLevel),    0, 0, 0},
276    {"Messages",  store_res,     ITEM(res_job.messages),  R_MSGS, ITEM_REQUIRED, 0},
277    {"Storage",   store_alist_res, ITEM(res_job.storage),  R_STORAGE, 0, 0},
278    {"Pool",      store_res,     ITEM(res_job.pool),      R_POOL, ITEM_REQUIRED, 0},
279    {"NextPool",  store_res,     ITEM(res_job.next_pool), R_POOL, 0, 0},
280    {"FullBackupPool",  store_res, ITEM(res_job.full_pool),   R_POOL, 0, 0},
281    {"IncrementalBackupPool",  store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
282    {"DifferentialBackupPool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
283    {"Client",    store_res,     ITEM(res_job.client),   R_CLIENT, ITEM_REQUIRED, 0},
284    {"Fileset",   store_res,     ITEM(res_job.fileset),  R_FILESET, ITEM_REQUIRED, 0},
285    {"Schedule",  store_res,     ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
286    {"VerifyJob", store_res,     ITEM(res_job.verify_job), R_JOB, 0, 0},
287    {"JobToVerify", store_res,   ITEM(res_job.verify_job), R_JOB, 0, 0},
288    {"JobDefs",   store_res,     ITEM(res_job.jobdefs),    R_JOBDEFS, 0, 0},
289    {"Run",       store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
290    /* Root of where to restore files */
291    {"Where",    store_dir,      ITEM(res_job.RestoreWhere), 0, 0, 0},
292    {"RegexWhere",    store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
293    {"StripPrefix",   store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
294    {"AddPrefix",    store_str,  ITEM(res_job.add_prefix), 0, 0, 0},
295    {"AddSuffix",    store_str,  ITEM(res_job.add_suffix), 0, 0, 0},
296    /* Where to find bootstrap during restore */
297    {"Bootstrap",store_dir,      ITEM(res_job.RestoreBootstrap), 0, 0, 0},
298    /* Where to write bootstrap file during backup */
299    {"WriteBootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
300    {"WriteVerifyList",store_dir,ITEM(res_job.WriteVerifyList), 0, 0, 0},
301    {"Replace",  store_replace,  ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
302    {"MaximumBandwidth", store_speed, ITEM(res_job.max_bandwidth), 0, 0, 0},
303    {"MaxRunSchedTime", store_time, ITEM(res_job.MaxRunSchedTime), 0, 0, 0},
304    {"MaxRunTime",   store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
305    /* xxxMaxWaitTime are deprecated */
306    {"fullmaxwaittime",  store_time, ITEM(res_job.FullMaxRunTime), 0, 0, 0},
307    {"incrementalmaxwaittime",  store_time, ITEM(res_job.IncMaxRunTime), 0, 0, 0},
308    {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxRunTime), 0, 0, 0},
309    {"FullMaxRunTime",  store_time, ITEM(res_job.FullMaxRunTime), 0, 0, 0},
310    {"IncrementalMaxRunTime",  store_time, ITEM(res_job.IncMaxRunTime), 0, 0, 0},
311    {"DifferentialMaxRunTime", store_time, ITEM(res_job.DiffMaxRunTime), 0, 0, 0},
312    {"MaxWaitTime",  store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
313    {"MaxStartDelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
314    {"MaxFullInterval",  store_time, ITEM(res_job.MaxFullInterval), 0, 0, 0},
315    {"MaxDiffInterval",  store_time, ITEM(res_job.MaxDiffInterval), 0, 0, 0},
316    {"PrefixLinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
317    {"PruneJobs",   store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
318    {"PruneFiles",  store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
319    {"PruneVolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
320    {"PurgeMigrationJob",  store_bool, ITEM(res_job.PurgeMigrateJob), 0, ITEM_DEFAULT, false},
321    {"Enabled",     store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
322    {"SnapshotRetention",  store_time,  ITEM(res_job.SnapRetention),  0, ITEM_DEFAULT, 0},
323    {"SpoolAttributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, true},
324    {"SpoolData",   store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
325    {"SpoolSize",   store_size64, ITEM(res_job.spool_size), 0, 0, 0},
326    {"ReRunFailedLevels",   store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
327    {"PreferMountedVolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
328    {"RunBeforeJob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
329    {"RunAfterJob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
330    {"RunAfterFailedJob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
331    {"ClientRunBeforeJob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
332    {"ClientRunAfterJob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
333    {"ConsoleRunBeforeJob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
334    {"ConsoleRunAfterJob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
335    {"Runscript",          store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
336    {"MaximumConcurrentJobs", store_pint32, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
337    {"MaximumSpawnedJobs", store_pint32, ITEM(res_job.MaxSpawnedJobs), 0, ITEM_DEFAULT, 600},
338    {"RescheduleOnError", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
339    {"RescheduleIncompleteJobs", store_bool, ITEM(res_job.RescheduleIncompleteJobs), 0, ITEM_DEFAULT, true},
340    {"RescheduleInterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
341    {"RescheduleTimes",    store_pint32, ITEM(res_job.RescheduleTimes), 0, 0, 0},
342    {"Priority",           store_pint32, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
343    {"AllowMixedPriority", store_bool, ITEM(res_job.allow_mixed_priority), 0, ITEM_DEFAULT, false},
344    {"WritePartAfterJob",  store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
345    {"SelectionPattern",   store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
346    {"SelectionType",      store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
347    {"Accurate",           store_bool, ITEM(res_job.accurate), 0,0,0},
348    {"AllowDuplicateJobs", store_bool, ITEM(res_job.AllowDuplicateJobs), 0, ITEM_DEFAULT, true},
349    {"allowhigherduplicates",   store_bool, ITEM(res_job.AllowHigherDuplicates), 0, ITEM_DEFAULT, true},
350    {"CancelLowerLevelDuplicates", store_bool, ITEM(res_job.CancelLowerLevelDuplicates), 0, ITEM_DEFAULT, false},
351    {"CancelQueuedDuplicates",  store_bool, ITEM(res_job.CancelQueuedDuplicates), 0, ITEM_DEFAULT, false},
352    {"CancelRunningDuplicates", store_bool, ITEM(res_job.CancelRunningDuplicates), 0, ITEM_DEFAULT, false},
353    {"PluginOptions", store_str, ITEM(res_job.PluginOptions), 0, 0, 0},
354    {"Base", store_alist_res, ITEM(res_job.base),  R_JOB, 0, 0},
355    {NULL, NULL, {0}, 0, 0, 0}
356 };
357
358 /* Fileset resource
359  *
360  *   Name          handler     value                 code flags    default_value
361  */
362 static RES_ITEM fs_items[] = {
363    {"Name",        store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
364    {"Description", store_str,  ITEM(res_fs.hdr.desc), 0, 0, 0},
365    {"IgnoreFilesetChanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
366    {"EnableVss",     store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
367    {"EnableSnapshot",store_bool, ITEM(res_fs.enable_snapshot), 0, ITEM_DEFAULT, false},
368    {"Include",     store_inc,  {0},                   0, ITEM_NO_EQUALS, 0},
369    {"Exclude",     store_inc,  {0},                   1, ITEM_NO_EQUALS, 0},
370    {NULL,          NULL,       {0},                  0, 0, 0}
371 };
372
373 /* Schedule -- see run_conf.c */
374 /* Schedule
375  *
376  *   name          handler     value                 code flags    default_value
377  */
378 static RES_ITEM sch_items[] = {
379    {"Name",        store_name,  ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
380    {"Description", store_str,   ITEM(res_sch.hdr.desc), 0, 0, 0},
381    {"Run",         store_run,   ITEM(res_sch.run),      0, 0, 0},
382    {"Enabled",     store_bool,  ITEM(res_sch.enabled),  0, ITEM_DEFAULT, true},
383    {NULL, NULL, {0}, 0, 0, 0}
384 };
385
386 /* Pool resource
387  *
388  *   name             handler     value                        code flags default_value
389  */
390 static RES_ITEM pool_items[] = {
391    {"Name",            store_name,    ITEM(res_pool.hdr.name),      0, ITEM_REQUIRED, 0},
392    {"Description",     store_str,     ITEM(res_pool.hdr.desc),      0, 0,     0},
393    {"PoolType",        store_strname, ITEM(res_pool.pool_type),     0, ITEM_REQUIRED, 0},
394    {"LabelFormat",     store_strname, ITEM(res_pool.label_format),  0, 0,     0},
395    {"LabelType",       store_label,   ITEM(res_pool.LabelType),     0, 0,     0},
396    {"CleaningPrefix",  store_strname, ITEM(res_pool.cleaning_prefix), 0, 0,   0},
397    {"UseCatalog",      store_bool,    ITEM(res_pool.use_catalog),    0, ITEM_DEFAULT, true},
398    {"UseVolumeOnce",   store_bool,    ITEM(res_pool.use_volume_once), 0, 0,   0},
399    {"PurgeOldestVolume", store_bool,  ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
400    {"ActionOnPurge",   store_actiononpurge, ITEM(res_pool.action_on_purge), 0, 0, 0},
401    {"RecycleOldestVolume", store_bool,  ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
402    {"RecycleCurrentVolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
403    {"MaximumVolumes",  store_pint32,    ITEM(res_pool.max_volumes),   0, 0,        0},
404    {"MaximumVolumeJobs", store_pint32,  ITEM(res_pool.MaxVolJobs),    0, 0,       0},
405    {"MaximumVolumeFiles", store_pint32, ITEM(res_pool.MaxVolFiles),   0, 0,       0},
406    {"MaximumVolumeBytes", store_size64, ITEM(res_pool.MaxVolBytes),   0, 0,       0},
407    {"CatalogFiles",    store_bool,    ITEM(res_pool.catalog_files),  0, ITEM_DEFAULT, true},
408    {"VolumeRetention", store_time,    ITEM(res_pool.VolRetention),   0, ITEM_DEFAULT, 60*60*24*365},
409    {"VolumeUseDuration", store_time,  ITEM(res_pool.VolUseDuration), 0, 0, 0},
410    {"MigrationTime",  store_time,     ITEM(res_pool.MigrationTime), 0, 0, 0},
411    {"MigrationHighBytes", store_size64, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
412    {"MigrationLowBytes", store_size64,  ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
413    {"NextPool",      store_res,       ITEM(res_pool.NextPool), R_POOL, 0, 0},
414    {"Storage",       store_alist_res, ITEM(res_pool.storage),  R_STORAGE, 0, 0},
415    {"AutoPrune",     store_bool,      ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
416    {"Recycle",       store_bool,      ITEM(res_pool.Recycle),   0, ITEM_DEFAULT, true},
417    {"RecyclePool",   store_res,       ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
418    {"ScratchPool",   store_res,       ITEM(res_pool.ScratchPool), R_POOL, 0, 0},
419    {"CopyPool",      store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
420    {"Catalog",       store_res,       ITEM(res_pool.catalog), R_CATALOG, 0, 0},
421    {"FileRetention", store_time,      ITEM(res_pool.FileRetention), 0, 0, 0},
422    {"JobRetention",  store_time,      ITEM(res_pool.JobRetention),  0, 0, 0},
423
424    {NULL, NULL, {0}, 0, 0, 0}
425 };
426
427 /*
428  * Counter Resource
429  *   name             handler     value                        code flags default_value
430  */
431 static RES_ITEM counter_items[] = {
432    {"Name",            store_name,    ITEM(res_counter.hdr.name),        0, ITEM_REQUIRED, 0},
433    {"Description",     store_str,     ITEM(res_counter.hdr.desc),        0, 0,     0},
434    {"Minimum",         store_int32,   ITEM(res_counter.MinValue),        0, ITEM_DEFAULT, 0},
435    {"Maximum",         store_pint32,  ITEM(res_counter.MaxValue),        0, ITEM_DEFAULT, INT32_MAX},
436    {"WrapCounter",     store_res,     ITEM(res_counter.WrapCounter),     R_COUNTER, 0, 0},
437    {"Catalog",         store_res,     ITEM(res_counter.Catalog),         R_CATALOG, 0, 0},
438    {NULL, NULL, {0}, 0, 0, 0}
439 };
440
441
442 /* Message resource */
443 extern RES_ITEM msgs_items[];
444
445 /*
446  * This is the master resource definition.
447  * It must have one item for each of the resources.
448  *
449  *  NOTE!!! keep it in the same order as the R_codes
450  *    or eliminate all resources[rindex].name
451  *
452  *  name             items        rcode
453  */
454 RES_TABLE resources[] = {
455    {"Director",      dir_items,   R_DIRECTOR},
456    {"Client",        cli_items,   R_CLIENT},
457    {"Job",           job_items,   R_JOB},
458    {"Storage",       store_items, R_STORAGE},
459    {"Catalog",       cat_items,   R_CATALOG},
460    {"Schedule",      sch_items,   R_SCHEDULE},
461    {"Fileset",       fs_items,    R_FILESET},
462    {"Pool",          pool_items,  R_POOL},
463    {"Messages",      msgs_items,  R_MSGS},
464    {"Counter",       counter_items, R_COUNTER},
465    {"Console",       con_items,   R_CONSOLE},
466    {"JobDefs",       job_items,   R_JOBDEFS},
467    {"Device",        NULL,        R_DEVICE},  /* info obtained from SD */
468    {NULL,            NULL,        0}
469 };
470
471
472 /* Keywords (RHS) permitted in Job Level records
473  *
474  *   level_name      level              job_type
475  */
476 struct s_jl joblevels[] = {
477    {"Full",          L_FULL,            JT_BACKUP},
478    {"Base",          L_BASE,            JT_BACKUP},
479    {"Incremental",   L_INCREMENTAL,     JT_BACKUP},
480    {"Differential",  L_DIFFERENTIAL,    JT_BACKUP},
481    {"Since",         L_SINCE,           JT_BACKUP},
482    {"VirtualFull",   L_VIRTUAL_FULL,    JT_BACKUP},
483    {"Catalog",       L_VERIFY_CATALOG,  JT_VERIFY},
484    {"InitCatalog",   L_VERIFY_INIT,     JT_VERIFY},
485    {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG,   JT_VERIFY},
486    {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG,   JT_VERIFY},
487    {"Data",          L_VERIFY_DATA,     JT_VERIFY},
488    {"Full",          L_FULL,            JT_COPY},
489    {"Incremental",   L_INCREMENTAL,     JT_COPY},
490    {"Differential",  L_DIFFERENTIAL,    JT_COPY},
491    {"Full",          L_FULL,            JT_MIGRATE},
492    {"Incremental",   L_INCREMENTAL,     JT_MIGRATE},
493    {"Differential",  L_DIFFERENTIAL,    JT_MIGRATE},
494    {" ",             L_NONE,            JT_ADMIN},
495    {" ",             L_NONE,            JT_RESTORE},
496    {NULL,            0,                          0}
497 };
498
499
500 /* Keywords (RHS) permitted in Job type records
501  *
502  *   type_name       job_type
503  */
504 s_jt jobtypes[] = {
505    {"Backup",        JT_BACKUP},
506    {"Admin",         JT_ADMIN},
507    {"Verify",        JT_VERIFY},
508    {"Restore",       JT_RESTORE},
509    {"Migrate",       JT_MIGRATE},
510    {"Copy",          JT_COPY},
511    {NULL,            0}
512 };
513
514
515 /* Keywords (RHS) permitted in Selection type records
516  *
517  *   type_name       job_type
518  */
519 s_jt migtypes[] = {
520    {"SmallestVolume",   MT_SMALLEST_VOL},
521    {"OldestVolume",     MT_OLDEST_VOL},
522    {"PoolOccupancy",    MT_POOL_OCCUPANCY},
523    {"PoolTime",         MT_POOL_TIME},
524    {"PoolUncopiedJobs", MT_POOL_UNCOPIED_JOBS},
525    {"Client",           MT_CLIENT},
526    {"Volume",           MT_VOLUME},
527    {"Job",              MT_JOB},
528    {"SqlQuery",         MT_SQLQUERY},
529    {NULL,            0}
530 };
531
532
533
534 /* Options permitted in Restore replace= */
535 struct s_kw ReplaceOptions[] = {
536    {"Always",         REPLACE_ALWAYS},
537    {"IfNewer",        REPLACE_IFNEWER},
538    {"IfOlder",        REPLACE_IFOLDER},
539    {"Never",          REPLACE_NEVER},
540    {NULL,               0}
541 };
542
543 char *CAT::display(POOLMEM *dst) {
544    Mmsg(dst,"catalog=%s\ndb_name=%s\ndb_driver=%s\ndb_user=%s\n"
545         "db_password=%s\ndb_address=%s\ndb_port=%i\n"
546         "db_socket=%s\n",
547         name(), NPRTB(db_name),
548         NPRTB(db_driver), NPRTB(db_user), NPRTB(db_password),
549         NPRTB(db_address), db_port, NPRTB(db_socket));
550    return dst;
551 }
552
553 const char *level_to_str(int level)
554 {
555    int i;
556    static char level_no[30];
557    const char *str = level_no;
558
559    bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level);    /* default if not found */
560    for (i=0; joblevels[i].level_name; i++) {
561       if (level == (int)joblevels[i].level) {
562          str = joblevels[i].level_name;
563          break;
564       }
565    }
566    return str;
567 }
568
569 /* Dump contents of resource */
570 void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, ...), void *sock)
571 {
572    URES *res = (URES *)ares;
573    bool recurse = true;
574    char ed1[100], ed2[100], ed3[100];
575    DEVICE *dev;
576    UAContext *ua = (UAContext *)sock;
577
578    if (res == NULL) {
579       sendit(sock, _("No %s resource defined\n"), res_to_str(type));
580       return;
581    }
582    if (type < 0) {                    /* no recursion */
583       type = -type;
584       recurse = false;
585    }
586    switch (type) {
587    case R_DIRECTOR:
588       sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
589          ares->name, res->res_dir.MaxConcurrentJobs,
590          edit_uint64(res->res_dir.FDConnectTimeout, ed1),
591          edit_uint64(res->res_dir.SDConnectTimeout, ed2));
592       if (res->res_dir.query_file) {
593          sendit(sock, _("   query_file=%s\n"), res->res_dir.query_file);
594       }
595       if (res->res_dir.messages) {
596          sendit(sock, _("  --> "));
597          dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
598       }
599       break;
600    case R_CONSOLE:
601       sendit(sock, _("Console: name=%s SSL=%d\n"),
602          res->res_con.hdr.name, res->res_con.tls_enable);
603       break;
604    case R_COUNTER:
605       if (res->res_counter.WrapCounter) {
606          sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
607             res->res_counter.hdr.name, res->res_counter.MinValue,
608             res->res_counter.MaxValue, res->res_counter.CurrentValue,
609             res->res_counter.WrapCounter->hdr.name);
610       } else {
611          sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
612             res->res_counter.hdr.name, res->res_counter.MinValue,
613             res->res_counter.MaxValue);
614       }
615       if (res->res_counter.Catalog) {
616          sendit(sock, _("  --> "));
617          dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
618       }
619       break;
620
621    case R_CLIENT:
622       if (!acl_access_ok(ua, Client_ACL, res->res_client.hdr.name)) {
623          break;
624       }
625       sendit(sock, _("Client: Name=%s Enabled=%d Address=%s FDport=%d MaxJobs=%u\n"),
626          res->res_client.hdr.name, res->res_client.enabled,
627          res->res_client.address, res->res_client.FDport,
628          res->res_client.MaxConcurrentJobs);
629       sendit(sock, _("      JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
630          edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
631          edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
632          res->res_client.AutoPrune);
633       if (res->res_client.fd_storage_address) {
634          sendit(sock, "      FDStorageAddress=%s\n", res->res_client.fd_storage_address);
635       }
636       if (res->res_client.max_bandwidth) {
637          sendit(sock, _("     MaximumBandwidth=%lld\n"),
638                 res->res_client.max_bandwidth);
639       }
640       if (res->res_client.catalog) {
641          sendit(sock, _("  --> "));
642          dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
643       }
644       break;
645
646    case R_DEVICE:
647       dev = &res->res_dev;
648       char ed1[50];
649       sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
650 "      reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
651 "      poolid=%s volname=%s MediaType=%s\n"),
652          dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
653          dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
654          dev->offline, dev->autochanger,
655          edit_uint64(dev->PoolId, ed1),
656          dev->VolumeName, dev->MediaType);
657       break;
658
659    case R_STORAGE:
660       if (!acl_access_ok(ua, Storage_ACL, res->res_store.hdr.name)) {
661          break;
662       }
663       sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
664 "      DeviceName=%s MediaType=%s StorageId=%s Autochanger=%d\n"),
665          res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
666          res->res_store.MaxConcurrentJobs,
667          res->res_store.dev_name(),
668          res->res_store.media_type,
669          edit_int64(res->res_store.StorageId, ed1),
670          res->res_store.autochanger);
671       if (res->res_store.fd_storage_address) {
672          sendit(sock, "      FDStorageAddress=%s\n", res->res_store.fd_storage_address);
673       }
674       break;
675
676    case R_CATALOG:
677       if (!acl_access_ok(ua, Catalog_ACL, res->res_cat.hdr.name)) {
678          break;
679       }
680       sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
681 "      db_driver=%s db_user=%s MutliDBConn=%d\n"),
682          res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
683          res->res_cat.db_port, res->res_cat.db_name,
684          NPRT(res->res_cat.db_driver), NPRT(res->res_cat.db_user),
685          res->res_cat.mult_db_connections);
686       break;
687
688    case R_JOB:
689    case R_JOBDEFS:
690       if (!acl_access_ok(ua, Job_ACL, res->res_job.hdr.name)) {
691          break;
692       }
693       sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
694          type == R_JOB ? _("Job") : _("JobDefs"),
695          res->res_job.hdr.name, res->res_job.JobType,
696          level_to_str(res->res_job.JobLevel), res->res_job.Priority,
697          res->res_job.enabled);
698       sendit(sock, _("     MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
699          res->res_job.MaxConcurrentJobs,
700          res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
701          edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
702          res->res_job.spool_data, res->res_job.write_part_after_job);
703       if (res->res_job.spool_size) {
704          sendit(sock, _("     SpoolSize=%s\n"),        edit_uint64(res->res_job.spool_size, ed1));
705       }
706       if (res->res_job.JobType == JT_BACKUP) {
707          sendit(sock, _("     Accurate=%d\n"), res->res_job.accurate);
708       }
709       if (res->res_job.max_bandwidth) {
710          sendit(sock, _("     MaximumBandwidth=%lld\n"),
711                 res->res_job.max_bandwidth);
712       }
713       if (res->res_job.JobType == JT_MIGRATE || res->res_job.JobType == JT_COPY) {
714          sendit(sock, _("     SelectionType=%d\n"), res->res_job.selection_type);
715       }
716       if (res->res_job.JobType == JT_RESTORE) {
717          sendit(sock, _("     PrefixLinks=%d\n"), res->res_job.PrefixLinks);
718       }
719       if (res->res_job.client) {
720          sendit(sock, _("  --> "));
721          dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
722       }
723       if (res->res_job.fileset) {
724          sendit(sock, _("  --> "));
725          dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
726       }
727       if (res->res_job.schedule) {
728          sendit(sock, _("  --> "));
729          dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
730       }
731       if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
732            sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
733       }
734       if (res->res_job.RegexWhere) {
735            sendit(sock, _("  --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
736       }
737       if (res->res_job.RestoreBootstrap) {
738          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
739       }
740       if (res->res_job.WriteBootstrap) {
741          sendit(sock, _("  --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
742       }
743       if (res->res_job.PluginOptions) {
744          sendit(sock, _("  --> PluginOptions=%s\n"), NPRT(res->res_job.PluginOptions));
745       }
746       if (res->res_job.MaxRunTime) {
747          sendit(sock, _("  --> MaxRunTime=%u\n"), res->res_job.MaxRunTime);
748       }
749       if (res->res_job.MaxWaitTime) {
750          sendit(sock, _("  --> MaxWaitTime=%u\n"), res->res_job.MaxWaitTime);
751       }
752       if (res->res_job.MaxStartDelay) {
753          sendit(sock, _("  --> MaxStartDelay=%u\n"), res->res_job.MaxStartDelay);
754       }
755       if (res->res_job.MaxRunSchedTime) {
756          sendit(sock, _("  --> MaxRunSchedTime=%u\n"), res->res_job.MaxRunSchedTime);
757       }
758       if (res->res_job.storage) {
759          STORE *store;
760          foreach_alist(store, res->res_job.storage) {
761             sendit(sock, _("  --> "));
762             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
763          }
764       }
765       if (res->res_job.base) {
766          JOB *job;
767          foreach_alist(job, res->res_job.base) {
768             sendit(sock, _("  --> Base %s\n"), job->name());
769          }
770       }
771       if (res->res_job.RunScripts) {
772         RUNSCRIPT *script;
773         foreach_alist(script, res->res_job.RunScripts) {
774            sendit(sock, _(" --> RunScript\n"));
775            sendit(sock, _("  --> Command=%s\n"), NPRT(script->command));
776            sendit(sock, _("  --> Target=%s\n"),  NPRT(script->target));
777            sendit(sock, _("  --> RunOnSuccess=%u\n"),  script->on_success);
778            sendit(sock, _("  --> RunOnFailure=%u\n"),  script->on_failure);
779            sendit(sock, _("  --> FailJobOnError=%u\n"),  script->fail_on_error);
780            sendit(sock, _("  --> RunWhen=%u\n"),  script->when);
781         }
782       }
783       if (res->res_job.pool) {
784          sendit(sock, _("  --> "));
785          dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
786       }
787       if (res->res_job.full_pool) {
788          sendit(sock, _("  --> FullBackup"));
789          dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
790       }
791       if (res->res_job.inc_pool) {
792          sendit(sock, _("  --> IncrementalBackup"));
793          dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
794       }
795       if (res->res_job.diff_pool) {
796          sendit(sock, _("  --> DifferentialBackup"));
797          dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
798       }
799       if (res->res_job.verify_job) {
800          sendit(sock, _("  --> "));
801          dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
802       }
803       if (res->res_job.run_cmds) {
804          char *runcmd;
805          foreach_alist(runcmd, res->res_job.run_cmds) {
806             sendit(sock, _("  --> Run=%s\n"), runcmd);
807          }
808       }
809       if (res->res_job.selection_pattern) {
810          sendit(sock, _("  --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
811       }
812       if (res->res_job.messages) {
813          sendit(sock, _("  --> "));
814          dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
815       }
816       break;
817
818    case R_FILESET:
819    {
820       int i, j, k;
821       if (!acl_access_ok(ua, FileSet_ACL, res->res_fs.hdr.name)) {
822          break;
823       }
824       sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
825       for (i=0; i<res->res_fs.num_includes; i++) {
826          INCEXE *incexe = res->res_fs.include_items[i];
827          for (j=0; j<incexe->num_opts; j++) {
828             FOPTS *fo = incexe->opts_list[j];
829             sendit(sock, "      O %s\n", fo->opts);
830
831             bool enhanced_wild = false;
832             for (k=0; fo->opts[k]!='\0'; k++) {
833                if (fo->opts[k]=='W') {
834                   enhanced_wild = true;
835                   break;
836                }
837             }
838
839             for (k=0; k<fo->regex.size(); k++) {
840                sendit(sock, "      R %s\n", fo->regex.get(k));
841             }
842             for (k=0; k<fo->regexdir.size(); k++) {
843                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
844             }
845             for (k=0; k<fo->regexfile.size(); k++) {
846                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
847             }
848             for (k=0; k<fo->wild.size(); k++) {
849                sendit(sock, "      W %s\n", fo->wild.get(k));
850             }
851             for (k=0; k<fo->wilddir.size(); k++) {
852                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
853             }
854             for (k=0; k<fo->wildfile.size(); k++) {
855                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
856             }
857             for (k=0; k<fo->wildbase.size(); k++) {
858                sendit(sock, "      W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
859             }
860             for (k=0; k<fo->base.size(); k++) {
861                sendit(sock, "      B %s\n", fo->base.get(k));
862             }
863             for (k=0; k<fo->fstype.size(); k++) {
864                sendit(sock, "      X %s\n", fo->fstype.get(k));
865             }
866             for (k=0; k<fo->drivetype.size(); k++) {
867                sendit(sock, "      XD %s\n", fo->drivetype.get(k));
868             }
869             if (fo->plugin) {
870                sendit(sock, "      G %s\n", fo->plugin);
871             }
872             if (fo->reader) {
873                sendit(sock, "      D %s\n", fo->reader);
874             }
875             if (fo->writer) {
876                sendit(sock, "      T %s\n", fo->writer);
877             }
878             sendit(sock, "      N\n");
879          }
880          if (incexe->ignoredir) {
881             sendit(sock, "      Z %s\n", incexe->ignoredir);
882          }
883          for (j=0; j<incexe->name_list.size(); j++) {
884             sendit(sock, "      I %s\n", incexe->name_list.get(j));
885          }
886          if (incexe->name_list.size()) {
887             sendit(sock, "      N\n");
888          }
889          for (j=0; j<incexe->plugin_list.size(); j++) {
890             sendit(sock, "      P %s\n", incexe->plugin_list.get(j));
891          }
892          if (incexe->plugin_list.size()) {
893             sendit(sock, "      N\n");
894          }
895       } /* end for over includes */
896
897       for (i=0; i<res->res_fs.num_excludes; i++) {
898          INCEXE *incexe = res->res_fs.exclude_items[i];
899          for (j=0; j<incexe->name_list.size(); j++) {
900             sendit(sock, "      E %s\n", incexe->name_list.get(j));
901          }
902          if (incexe->name_list.size()) {
903             sendit(sock, "      N\n");
904          }
905       }
906       break;
907    } /* end case R_FILESET */
908
909    case R_SCHEDULE:
910       if (!acl_access_ok(ua, Schedule_ACL, res->res_sch.hdr.name)) {
911          break;
912       }
913
914       if (res->res_sch.run) {
915          int i;
916          RUN *run = res->res_sch.run;
917          char buf[1000], num[30];
918          sendit(sock, _("Schedule: Name=%s Enabled=%d\n"), 
919             res->res_sch.hdr.name, res->res_sch.enabled);
920          if (!run) {
921             break;
922          }
923 next_run:
924          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
925          if (run->MaxRunSchedTime) {
926             sendit(sock, _("      MaxRunSchedTime=%u\n"), run->MaxRunSchedTime);
927          }
928          if (run->Priority) {
929             sendit(sock, _("      Priority=%u\n"), run->Priority);
930          }
931          bstrncpy(buf, _("      hour="), sizeof(buf));
932          for (i=0; i<24; i++) {
933             if (bit_is_set(i, run->hour)) {
934                bsnprintf(num, sizeof(num), "%d ", i);
935                bstrncat(buf, num, sizeof(buf));
936             }
937          }
938          bstrncat(buf, "\n", sizeof(buf));
939          sendit(sock, buf);
940          bstrncpy(buf, _("      mday="), sizeof(buf));
941          for (i=0; i<32; i++) {
942             if (bit_is_set(i, run->mday)) {
943                bsnprintf(num, sizeof(num), "%d ", i);
944                bstrncat(buf, num, sizeof(buf));
945             }
946          }
947          bstrncat(buf, "\n", sizeof(buf));
948          sendit(sock, buf);
949          bstrncpy(buf, _("      month="), sizeof(buf));
950          for (i=0; i<12; i++) {
951             if (bit_is_set(i, run->month)) {
952                bsnprintf(num, sizeof(num), "%d ", i);
953                bstrncat(buf, num, sizeof(buf));
954             }
955          }
956          bstrncat(buf, "\n", sizeof(buf));
957          sendit(sock, buf);
958          bstrncpy(buf, _("      wday="), sizeof(buf));
959          for (i=0; i<7; i++) {
960             if (bit_is_set(i, run->wday)) {
961                bsnprintf(num, sizeof(num), "%d ", i);
962                bstrncat(buf, num, sizeof(buf));
963             }
964          }
965          bstrncat(buf, "\n", sizeof(buf));
966          sendit(sock, buf);
967          bstrncpy(buf, _("      wom="), sizeof(buf));
968          for (i=0; i<6; i++) {
969             if (bit_is_set(i, run->wom)) {
970                bsnprintf(num, sizeof(num), "%d ", i);
971                bstrncat(buf, num, sizeof(buf));
972             }
973          }
974          bstrncat(buf, "\n", sizeof(buf));
975          sendit(sock, buf);
976          bstrncpy(buf, _("      woy="), sizeof(buf));
977          for (i=0; i<54; i++) {
978             if (bit_is_set(i, run->woy)) {
979                bsnprintf(num, sizeof(num), "%d ", i);
980                bstrncat(buf, num, sizeof(buf));
981             }
982          }
983          bstrncat(buf, "\n", sizeof(buf));
984          sendit(sock, buf);
985          sendit(sock, _("      mins=%d\n"), run->minute);
986          if (run->pool) {
987             sendit(sock, _("     --> "));
988             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
989          }
990          if (run->storage) {
991             sendit(sock, _("     --> "));
992             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
993          }
994          if (run->msgs) {
995             sendit(sock, _("     --> "));
996             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
997          }
998          /* If another Run record is chained in, go print it */
999          if (run->next) {
1000             run = run->next;
1001             goto next_run;
1002          }
1003       } else {
1004          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
1005       }
1006       break;
1007
1008    case R_POOL:
1009       if (!acl_access_ok(ua, Pool_ACL, res->res_pool.hdr.name)) {
1010          break;
1011       }
1012       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
1013               res->res_pool.pool_type);
1014       sendit(sock, _("      use_cat=%d use_once=%d cat_files=%d\n"),
1015               res->res_pool.use_catalog, res->res_pool.use_volume_once,
1016               res->res_pool.catalog_files);
1017       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
1018               res->res_pool.max_volumes, res->res_pool.AutoPrune,
1019               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
1020       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
1021               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
1022               res->res_pool.Recycle,
1023               NPRT(res->res_pool.label_format));
1024       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
1025               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
1026       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d ActionOnPurge=%d\n"),
1027               res->res_pool.recycle_oldest_volume,
1028               res->res_pool.purge_oldest_volume,
1029               res->res_pool.action_on_purge);
1030       sendit(sock, _("      MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
1031               res->res_pool.MaxVolJobs,
1032               res->res_pool.MaxVolFiles,
1033               edit_uint64(res->res_pool.MaxVolBytes, ed1));
1034       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
1035               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
1036               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
1037               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
1038       sendit(sock, _("      JobRetention=%s FileRetention=%s\n"),
1039          edit_utime(res->res_pool.JobRetention, ed1, sizeof(ed1)),
1040          edit_utime(res->res_pool.FileRetention, ed2, sizeof(ed2)));
1041       if (res->res_pool.NextPool) {
1042          sendit(sock, _("      NextPool=%s\n"), res->res_pool.NextPool->name());
1043       }
1044       if (res->res_pool.RecyclePool) {
1045          sendit(sock, _("      RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
1046       }
1047       if (res->res_pool.ScratchPool) {
1048          sendit(sock, _("      ScratchPool=%s\n"), res->res_pool.ScratchPool->name());
1049       }
1050       if (res->res_pool.catalog) {
1051          sendit(sock, _("      Catalog=%s\n"), res->res_pool.catalog->name());
1052       }
1053       if (res->res_pool.storage) {
1054          STORE *store;
1055          foreach_alist(store, res->res_pool.storage) {
1056             sendit(sock, _("  --> "));
1057             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
1058          }
1059       }
1060       if (res->res_pool.CopyPool) {
1061          POOL *copy;
1062          foreach_alist(copy, res->res_pool.CopyPool) {
1063             sendit(sock, _("  --> "));
1064             dump_resource(-R_POOL, (RES *)copy, sendit, sock);
1065          }
1066       }
1067
1068       break;
1069
1070    case R_MSGS:
1071       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
1072       if (res->res_msgs.mail_cmd)
1073          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
1074       if (res->res_msgs.operator_cmd)
1075          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
1076       break;
1077
1078    default:
1079       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
1080       break;
1081    }
1082    if (recurse && res->res_dir.hdr.next) {
1083       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
1084    }
1085
1086 }
1087
1088 /*
1089  * Free all the members of an INCEXE structure
1090  */
1091 static void free_incexe(INCEXE *incexe)
1092 {
1093    incexe->name_list.destroy();
1094    incexe->plugin_list.destroy();
1095    for (int i=0; i<incexe->num_opts; i++) {
1096       FOPTS *fopt = incexe->opts_list[i];
1097       fopt->regex.destroy();
1098       fopt->regexdir.destroy();
1099       fopt->regexfile.destroy();
1100       fopt->wild.destroy();
1101       fopt->wilddir.destroy();
1102       fopt->wildfile.destroy();
1103       fopt->wildbase.destroy();
1104       fopt->base.destroy();
1105       fopt->fstype.destroy();
1106       fopt->drivetype.destroy();
1107       if (fopt->plugin) {
1108          free(fopt->plugin);
1109       }
1110       if (fopt->reader) {
1111          free(fopt->reader);
1112       }
1113       if (fopt->writer) {
1114          free(fopt->writer);
1115       }
1116       free(fopt);
1117    }
1118    if (incexe->opts_list) {
1119       free(incexe->opts_list);
1120    }
1121    if (incexe->ignoredir) {
1122       free(incexe->ignoredir);
1123    }
1124    free(incexe);
1125 }
1126
1127
1128 /*
1129  * Free memory of resource -- called when daemon terminates.
1130  * NB, we don't need to worry about freeing any references
1131  * to other resources as they will be freed when that
1132  * resource chain is traversed.  Mainly we worry about freeing
1133  * allocated strings (names).
1134  */
1135 void free_resource(RES *rres, int type)
1136 {
1137    int num;
1138    RES *nres;
1139    URES *res = (URES *)rres;
1140
1141    if (res == NULL) {
1142       return;
1143    }
1144
1145    Dmsg3(200, "type=%d res=%p name=%s\n", type, res, res->res_dir.hdr.name);
1146    /* common stuff -- free the resource name and description */
1147    nres = (RES *)res->res_dir.hdr.next;
1148    if (res->res_dir.hdr.name) {
1149       free(res->res_dir.hdr.name);
1150    }
1151    if (res->res_dir.hdr.desc) {
1152       free(res->res_dir.hdr.desc);
1153    }
1154
1155    switch (type) {
1156    case R_DIRECTOR:
1157       if (res->res_dir.working_directory) {
1158          free(res->res_dir.working_directory);
1159       }
1160       if (res->res_dir.scripts_directory) {
1161          free((char *)res->res_dir.scripts_directory);
1162       }
1163       if (res->res_dir.plugin_directory) {
1164          free((char *)res->res_dir.plugin_directory);
1165       }
1166       if (res->res_dir.pid_directory) {
1167          free(res->res_dir.pid_directory);
1168       }
1169       if (res->res_dir.subsys_directory) {
1170          free(res->res_dir.subsys_directory);
1171       }
1172       if (res->res_dir.password) {
1173          free(res->res_dir.password);
1174       }
1175       if (res->res_dir.query_file) {
1176          free(res->res_dir.query_file);
1177       }
1178       if (res->res_dir.DIRaddrs) {
1179          free_addresses(res->res_dir.DIRaddrs);
1180       }
1181       if (res->res_dir.DIRsrc_addr) {
1182          free_addresses(res->res_dir.DIRsrc_addr);
1183       }
1184       if (res->res_dir.tls_ctx) {
1185          free_tls_context(res->res_dir.tls_ctx);
1186       }
1187       if (res->res_dir.tls_ca_certfile) {
1188          free(res->res_dir.tls_ca_certfile);
1189       }
1190       if (res->res_dir.tls_ca_certdir) {
1191          free(res->res_dir.tls_ca_certdir);
1192       }
1193       if (res->res_dir.tls_certfile) {
1194          free(res->res_dir.tls_certfile);
1195       }
1196       if (res->res_dir.tls_keyfile) {
1197          free(res->res_dir.tls_keyfile);
1198       }
1199       if (res->res_dir.tls_dhfile) {
1200          free(res->res_dir.tls_dhfile);
1201       }
1202       if (res->res_dir.tls_allowed_cns) {
1203          delete res->res_dir.tls_allowed_cns;
1204       }
1205       if (res->res_dir.verid) {
1206          free(res->res_dir.verid);
1207       }
1208       break;
1209    case R_DEVICE:
1210    case R_COUNTER:
1211        break;
1212    case R_CONSOLE:
1213       if (res->res_con.password) {
1214          free(res->res_con.password);
1215       }
1216       if (res->res_con.tls_ctx) {
1217          free_tls_context(res->res_con.tls_ctx);
1218       }
1219       if (res->res_con.tls_ca_certfile) {
1220          free(res->res_con.tls_ca_certfile);
1221       }
1222       if (res->res_con.tls_ca_certdir) {
1223          free(res->res_con.tls_ca_certdir);
1224       }
1225       if (res->res_con.tls_certfile) {
1226          free(res->res_con.tls_certfile);
1227       }
1228       if (res->res_con.tls_keyfile) {
1229          free(res->res_con.tls_keyfile);
1230       }
1231       if (res->res_con.tls_dhfile) {
1232          free(res->res_con.tls_dhfile);
1233       }
1234       if (res->res_con.tls_allowed_cns) {
1235          delete res->res_con.tls_allowed_cns;
1236       }
1237       for (int i=0; i<Num_ACL; i++) {
1238          if (res->res_con.ACL_lists[i]) {
1239             delete res->res_con.ACL_lists[i];
1240             res->res_con.ACL_lists[i] = NULL;
1241          }
1242       }
1243       break;
1244    case R_CLIENT:
1245       if (res->res_client.address) {
1246          free(res->res_client.address);
1247       }
1248       if (res->res_client.fd_storage_address) {
1249          free(res->res_client.fd_storage_address);
1250       }
1251       if (res->res_client.password) {
1252          free(res->res_client.password);
1253       }
1254       if (res->res_client.tls_ctx) {
1255          free_tls_context(res->res_client.tls_ctx);
1256       }
1257       if (res->res_client.tls_ca_certfile) {
1258          free(res->res_client.tls_ca_certfile);
1259       }
1260       if (res->res_client.tls_ca_certdir) {
1261          free(res->res_client.tls_ca_certdir);
1262       }
1263       if (res->res_client.tls_certfile) {
1264          free(res->res_client.tls_certfile);
1265       }
1266       if (res->res_client.tls_keyfile) {
1267          free(res->res_client.tls_keyfile);
1268       }
1269       if (res->res_client.tls_allowed_cns) {
1270          delete res->res_client.tls_allowed_cns;
1271       }
1272       break;
1273    case R_STORAGE:
1274       if (res->res_store.address) {
1275          free(res->res_store.address);
1276       }
1277       if (res->res_store.fd_storage_address) {
1278          free(res->res_store.fd_storage_address);
1279       }
1280       if (res->res_store.password) {
1281          free(res->res_store.password);
1282       }
1283       if (res->res_store.media_type) {
1284          free(res->res_store.media_type);
1285       }
1286       if (res->res_store.device) {
1287          delete res->res_store.device;
1288       }
1289       if (res->res_store.tls_ctx) {
1290          free_tls_context(res->res_store.tls_ctx);
1291       }
1292       if (res->res_store.tls_ca_certfile) {
1293          free(res->res_store.tls_ca_certfile);
1294       }
1295       if (res->res_store.tls_ca_certdir) {
1296          free(res->res_store.tls_ca_certdir);
1297       }
1298       if (res->res_store.tls_certfile) {
1299          free(res->res_store.tls_certfile);
1300       }
1301       if (res->res_store.tls_keyfile) {
1302          free(res->res_store.tls_keyfile);
1303       }
1304       break;
1305    case R_CATALOG:
1306       if (res->res_cat.db_address) {
1307          free(res->res_cat.db_address);
1308       }
1309       if (res->res_cat.db_socket) {
1310          free(res->res_cat.db_socket);
1311       }
1312       if (res->res_cat.db_user) {
1313          free(res->res_cat.db_user);
1314       }
1315       if (res->res_cat.db_name) {
1316          free(res->res_cat.db_name);
1317       }
1318       if (res->res_cat.db_driver) {
1319          free(res->res_cat.db_driver);
1320       }
1321       if (res->res_cat.db_password) {
1322          free(res->res_cat.db_password);
1323       }
1324       if (res->res_cat.db_ssl_key) {
1325          free(res->res_cat.db_ssl_key);
1326       }
1327       if (res->res_cat.db_ssl_cert) {
1328          free(res->res_cat.db_ssl_cert);
1329       }
1330       if (res->res_cat.db_ssl_ca) {
1331          free(res->res_cat.db_ssl_ca);
1332       }
1333       if (res->res_cat.db_ssl_capath) {
1334          free(res->res_cat.db_ssl_capath);
1335       }
1336       if (res->res_cat.db_ssl_cipher) {
1337          free(res->res_cat.db_ssl_cipher);
1338       }
1339       break;
1340    case R_FILESET:
1341       if ((num=res->res_fs.num_includes)) {
1342          while (--num >= 0) {
1343             free_incexe(res->res_fs.include_items[num]);
1344          }
1345          free(res->res_fs.include_items);
1346       }
1347       res->res_fs.num_includes = 0;
1348       if ((num=res->res_fs.num_excludes)) {
1349          while (--num >= 0) {
1350             free_incexe(res->res_fs.exclude_items[num]);
1351          }
1352          free(res->res_fs.exclude_items);
1353       }
1354       res->res_fs.num_excludes = 0;
1355       break;
1356    case R_POOL:
1357       if (res->res_pool.pool_type) {
1358          free(res->res_pool.pool_type);
1359       }
1360       if (res->res_pool.label_format) {
1361          free(res->res_pool.label_format);
1362       }
1363       if (res->res_pool.cleaning_prefix) {
1364          free(res->res_pool.cleaning_prefix);
1365       }
1366       if (res->res_pool.storage) {
1367          delete res->res_pool.storage;
1368       }
1369       break;
1370    case R_SCHEDULE:
1371       if (res->res_sch.run) {
1372          RUN *nrun, *next;
1373          nrun = res->res_sch.run;
1374          while (nrun) {
1375             next = nrun->next;
1376             free(nrun);
1377             nrun = next;
1378          }
1379       }
1380       break;
1381    case R_JOB:
1382    case R_JOBDEFS:
1383       if (res->res_job.RestoreWhere) {
1384          free(res->res_job.RestoreWhere);
1385       }
1386       if (res->res_job.RegexWhere) {
1387          free(res->res_job.RegexWhere);
1388       }
1389       if (res->res_job.strip_prefix) {
1390          free(res->res_job.strip_prefix);
1391       }
1392       if (res->res_job.add_prefix) {
1393          free(res->res_job.add_prefix);
1394       }
1395       if (res->res_job.add_suffix) {
1396          free(res->res_job.add_suffix);
1397       }
1398       if (res->res_job.RestoreBootstrap) {
1399          free(res->res_job.RestoreBootstrap);
1400       }
1401       if (res->res_job.WriteBootstrap) {
1402          free(res->res_job.WriteBootstrap);
1403       }
1404       if (res->res_job.PluginOptions) {
1405          free(res->res_job.PluginOptions);
1406       }
1407       if (res->res_job.selection_pattern) {
1408          free(res->res_job.selection_pattern);
1409       }
1410       if (res->res_job.run_cmds) {
1411          delete res->res_job.run_cmds;
1412       }
1413       if (res->res_job.storage) {
1414          delete res->res_job.storage;
1415       }
1416       if (res->res_job.base) {
1417          delete res->res_job.base;
1418       }
1419       if (res->res_job.RunScripts) {
1420          free_runscripts(res->res_job.RunScripts);
1421          delete res->res_job.RunScripts;
1422       }
1423       break;
1424    case R_MSGS:
1425       if (res->res_msgs.mail_cmd) {
1426          free(res->res_msgs.mail_cmd);
1427       }
1428       if (res->res_msgs.operator_cmd) {
1429          free(res->res_msgs.operator_cmd);
1430       }
1431       free_msgs_res((MSGS *)res);  /* free message resource */
1432       res = NULL;
1433       break;
1434    default:
1435       printf(_("Unknown resource type %d in free_resource.\n"), type);
1436    }
1437    /* Common stuff again -- free the resource, recurse to next one */
1438    if (res) {
1439       free(res);
1440    }
1441    if (nres) {
1442       free_resource(nres, type);
1443    }
1444 }
1445
1446 /*
1447  * Save the new resource by chaining it into the head list for
1448  * the resource. If this is pass 2, we update any resource
1449  * pointers because they may not have been defined until
1450  * later in pass 1.
1451  */
1452 void save_resource(int type, RES_ITEM *items, int pass)
1453 {
1454    URES *res;
1455    int rindex = type - r_first;
1456    int i, size = 0;
1457    bool error = false;
1458
1459    /* Check Job requirements after applying JobDefs */
1460    if (type != R_JOB && type != R_JOBDEFS) {
1461       /*
1462        * Ensure that all required items are present
1463        */
1464       for (i=0; items[i].name; i++) {
1465          if (items[i].flags & ITEM_REQUIRED) {
1466             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1467                 Emsg2(M_ERROR_TERM, 0, _("\"%s\" directive is required in \"%s\" resource, but not found.\n"),
1468                     items[i].name, resources[rindex].name);
1469             }
1470          }
1471          /* If this triggers, take a look at lib/parse_conf.h */
1472          if (i >= MAX_RES_ITEMS) {
1473             Emsg1(M_ERROR_TERM, 0, _("Too many directive in \"%s\" resource\n"), resources[rindex].name);
1474          }
1475       }
1476    } else if (type == R_JOB) {
1477       /*
1478        * Ensure that the name item is present
1479        */
1480       if (items[0].flags & ITEM_REQUIRED) {
1481          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1482              Emsg2(M_ERROR_TERM, 0, _("\"%s\" directive is required in \"%s\" resource, but not found.\n"),
1483                    items[0].name, resources[rindex].name);
1484          }
1485       }
1486    }
1487
1488    /*
1489     * During pass 2 in each "store" routine, we looked up pointers
1490     * to all the resources referrenced in the current resource, now we
1491     * must copy their addresses from the static record to the allocated
1492     * record.
1493     */
1494    if (pass == 2) {
1495       switch (type) {
1496       /* Resources not containing a resource */
1497       case R_CATALOG:
1498       case R_MSGS:
1499       case R_FILESET:
1500       case R_DEVICE:
1501          break;
1502
1503       /*
1504        * Resources containing another resource or alist. First
1505        *  look up the resource which contains another resource. It
1506        *  was written during pass 1.  Then stuff in the pointers to
1507        *  the resources it contains, which were inserted this pass.
1508        *  Finally, it will all be stored back.
1509        */
1510       case R_POOL:
1511          /* Find resource saved in pass 1 */
1512          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1513             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1514          }
1515          /* Explicitly copy resource pointers from this pass (res_all) */
1516          res->res_pool.NextPool = res_all.res_pool.NextPool;
1517          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1518          res->res_pool.ScratchPool = res_all.res_pool.ScratchPool;
1519          res->res_pool.storage    = res_all.res_pool.storage;
1520          res->res_pool.catalog    = res_all.res_pool.catalog;
1521          break;
1522       case R_CONSOLE:
1523          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1524             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1525          }
1526          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1527          break;
1528       case R_DIRECTOR:
1529          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1530             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1531          }
1532          res->res_dir.messages = res_all.res_dir.messages;
1533          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1534          break;
1535       case R_STORAGE:
1536          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1537             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1538                   res_all.res_dir.hdr.name);
1539          }
1540          /* we must explicitly copy the device alist pointer */
1541          res->res_store.device   = res_all.res_store.device;
1542          break;
1543       case R_JOB:
1544       case R_JOBDEFS:
1545          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1546             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1547                   res_all.res_dir.hdr.name);
1548          }
1549          res->res_job.messages   = res_all.res_job.messages;
1550          res->res_job.schedule   = res_all.res_job.schedule;
1551          res->res_job.client     = res_all.res_job.client;
1552          res->res_job.fileset    = res_all.res_job.fileset;
1553          res->res_job.storage    = res_all.res_job.storage;
1554          res->res_job.base       = res_all.res_job.base;
1555          res->res_job.pool       = res_all.res_job.pool;
1556          res->res_job.next_pool  = res_all.res_job.next_pool;
1557          res->res_job.full_pool  = res_all.res_job.full_pool;
1558          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1559          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1560          res->res_job.verify_job = res_all.res_job.verify_job;
1561          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1562          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1563          res->res_job.RunScripts = res_all.res_job.RunScripts;
1564
1565          /* TODO: JobDefs where/regexwhere doesn't work well (but this
1566           * is not very useful)
1567           * We have to set_bit(index, res_all.hdr.item_present);
1568           * or something like that
1569           */
1570
1571          /* we take RegexWhere before all other options */
1572          if (!res->res_job.RegexWhere
1573              &&
1574              (res->res_job.strip_prefix ||
1575               res->res_job.add_suffix   ||
1576               res->res_job.add_prefix))
1577          {
1578             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1579                                                    res->res_job.add_prefix,
1580                                                    res->res_job.add_suffix);
1581             res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1582             bregexp_build_where(res->res_job.RegexWhere, len,
1583                                 res->res_job.strip_prefix,
1584                                 res->res_job.add_prefix,
1585                                 res->res_job.add_suffix);
1586             /* TODO: test bregexp */
1587          }
1588
1589          if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1590             free(res->res_job.RestoreWhere);
1591             res->res_job.RestoreWhere = NULL;
1592          }
1593
1594          break;
1595       case R_COUNTER:
1596          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1597             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1598          }
1599          res->res_counter.Catalog = res_all.res_counter.Catalog;
1600          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1601          break;
1602
1603       case R_CLIENT:
1604          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1605             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1606          }
1607          res->res_client.catalog = res_all.res_client.catalog;
1608          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1609          break;
1610       case R_SCHEDULE:
1611          /*
1612           * Schedule is a bit different in that it contains a RUN record
1613           * chain which isn't a "named" resource. This chain was linked
1614           * in by run_conf.c during pass 2, so here we jam the pointer
1615           * into the Schedule resource.
1616           */
1617          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1618             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1619          }
1620          res->res_sch.run = res_all.res_sch.run;
1621          break;
1622       default:
1623          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1624          error = true;
1625          break;
1626       }
1627       /* Note, the resource name was already saved during pass 1,
1628        * so here, we can just release it.
1629        */
1630       if (res_all.res_dir.hdr.name) {
1631          free(res_all.res_dir.hdr.name);
1632          res_all.res_dir.hdr.name = NULL;
1633       }
1634       if (res_all.res_dir.hdr.desc) {
1635          free(res_all.res_dir.hdr.desc);
1636          res_all.res_dir.hdr.desc = NULL;
1637       }
1638       return;
1639    }
1640
1641    /*
1642     * The following code is only executed during pass 1
1643     */
1644    switch (type) {
1645    case R_DIRECTOR:
1646       size = sizeof(DIRRES);
1647       break;
1648    case R_CONSOLE:
1649       size = sizeof(CONRES);
1650       break;
1651    case R_CLIENT:
1652       size =sizeof(CLIENT);
1653       break;
1654    case R_STORAGE:
1655       size = sizeof(STORE);
1656       break;
1657    case R_CATALOG:
1658       size = sizeof(CAT);
1659       break;
1660    case R_JOB:
1661    case R_JOBDEFS:
1662       size = sizeof(JOB);
1663       break;
1664    case R_FILESET:
1665       size = sizeof(FILESET);
1666       break;
1667    case R_SCHEDULE:
1668       size = sizeof(SCHED);
1669       break;
1670    case R_POOL:
1671       size = sizeof(POOL);
1672       break;
1673    case R_MSGS:
1674       size = sizeof(MSGS);
1675       break;
1676    case R_COUNTER:
1677       size = sizeof(COUNTER);
1678       break;
1679    case R_DEVICE:
1680       error = true;
1681       break;
1682    default:
1683       printf(_("Unknown resource type %d in save_resource.\n"), type);
1684       error = true;
1685       break;
1686    }
1687    /* Common */
1688    if (!error) {
1689       res = (URES *)malloc(size);
1690       memcpy(res, &res_all, size);
1691       if (!res_head[rindex]) {
1692          res_head[rindex] = (RES *)res; /* store first entry */
1693          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1694                res->res_dir.hdr.name, rindex);
1695       } else {
1696          RES *next, *last;
1697          if (res->res_dir.hdr.name == NULL) {
1698             Emsg1(M_ERROR_TERM, 0, _("A Name directive is required in \"%s\" resource, but not found.\n"),
1699                   resources[rindex].name);
1700          }
1701          /* Add new res to end of chain */
1702          for (last=next=res_head[rindex]; next; next=next->next) {
1703             last = next;
1704             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1705                Emsg2(M_ERROR_TERM, 0,
1706                   _("Attempt to define second \"%s\" resource named \"%s\" is not permitted.\n"),
1707                   resources[rindex].name, res->res_dir.hdr.name);
1708             }
1709          }
1710          last->next = (RES *)res;
1711          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1712                res->res_dir.hdr.name, rindex, pass);
1713       }
1714    }
1715 }
1716
1717 void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass)
1718 {
1719    uint32_t *destination = (uint32_t*)item->value;
1720    lex_get_token(lc, T_NAME);
1721    if (strcasecmp(lc->str, "truncate") == 0) {
1722       *destination = (*destination) | ON_PURGE_TRUNCATE;
1723    } else {
1724       scan_err2(lc, _("Expected one of: %s, got: %s"), "Truncate", lc->str);
1725       return;
1726    }
1727    scan_to_eol(lc);
1728    set_bit(index, res_all.hdr.item_present);
1729 }
1730
1731 /*
1732  * Store Device. Note, the resource is created upon the
1733  *  first reference. The details of the resource are obtained
1734  *  later from the SD.
1735  */
1736 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1737 {
1738    URES *res;
1739    int rindex = R_DEVICE - r_first;
1740    int size = sizeof(DEVICE);
1741    bool found = false;
1742
1743    if (pass == 1) {
1744       lex_get_token(lc, T_NAME);
1745       if (!res_head[rindex]) {
1746          res = (URES *)malloc(size);
1747          memset(res, 0, size);
1748          res->res_dev.hdr.name = bstrdup(lc->str);
1749          res_head[rindex] = (RES *)res; /* store first entry */
1750          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1751                res->res_dir.hdr.name, rindex);
1752       } else {
1753          RES *next;
1754          /* See if it is already defined */
1755          for (next=res_head[rindex]; next->next; next=next->next) {
1756             if (strcmp(next->name, lc->str) == 0) {
1757                found = true;
1758                break;
1759             }
1760          }
1761          if (!found) {
1762             res = (URES *)malloc(size);
1763             memset(res, 0, size);
1764             res->res_dev.hdr.name = bstrdup(lc->str);
1765             next->next = (RES *)res;
1766             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1767                res->res_dir.hdr.name, rindex, pass);
1768          }
1769       }
1770
1771       scan_to_eol(lc);
1772       set_bit(index, res_all.hdr.item_present);
1773    } else {
1774       store_alist_res(lc, item, index, pass);
1775    }
1776 }
1777
1778 /*
1779  * Store Migration/Copy type
1780  *
1781  */
1782 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1783 {
1784    int i;
1785
1786    lex_get_token(lc, T_NAME);
1787    /* Store the type both pass 1 and pass 2 */
1788    for (i=0; migtypes[i].type_name; i++) {
1789       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1790          *(uint32_t *)(item->value) = migtypes[i].job_type;
1791          i = 0;
1792          break;
1793       }
1794    }
1795    if (i != 0) {
1796       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1797    }
1798    scan_to_eol(lc);
1799    set_bit(index, res_all.hdr.item_present);
1800 }
1801
1802
1803
1804 /*
1805  * Store JobType (backup, verify, restore)
1806  *
1807  */
1808 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1809 {
1810    int i;
1811
1812    lex_get_token(lc, T_NAME);
1813    /* Store the type both pass 1 and pass 2 */
1814    for (i=0; jobtypes[i].type_name; i++) {
1815       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1816          *(uint32_t *)(item->value) = jobtypes[i].job_type;
1817          i = 0;
1818          break;
1819       }
1820    }
1821    if (i != 0) {
1822       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1823    }
1824    scan_to_eol(lc);
1825    set_bit(index, res_all.hdr.item_present);
1826 }
1827
1828 /*
1829  * Store Job Level (Full, Incremental, ...)
1830  *
1831  */
1832 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1833 {
1834    int i;
1835
1836    lex_get_token(lc, T_NAME);
1837    /* Store the level pass 2 so that type is defined */
1838    for (i=0; joblevels[i].level_name; i++) {
1839       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1840          *(uint32_t *)(item->value) = joblevels[i].level;
1841          i = 0;
1842          break;
1843       }
1844    }
1845    if (i != 0) {
1846       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1847    }
1848    scan_to_eol(lc);
1849    set_bit(index, res_all.hdr.item_present);
1850 }
1851
1852
1853 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1854 {
1855    int i;
1856    lex_get_token(lc, T_NAME);
1857    /* Scan Replacement options */
1858    for (i=0; ReplaceOptions[i].name; i++) {
1859       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1860          *(uint32_t *)(item->value) = ReplaceOptions[i].token;
1861          i = 0;
1862          break;
1863       }
1864    }
1865    if (i != 0) {
1866       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1867    }
1868    scan_to_eol(lc);
1869    set_bit(index, res_all.hdr.item_present);
1870 }
1871
1872 /*
1873  * Store ACL (access control list)
1874  *
1875  */
1876 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1877 {
1878    int token;
1879
1880    for (;;) {
1881       lex_get_token(lc, T_STRING);
1882       if (pass == 1) {
1883          if (((alist **)item->value)[item->code] == NULL) {
1884             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1885             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1886          }
1887          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1888          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1889       }
1890       token = lex_get_token(lc, T_ALL);
1891       if (token == T_COMMA) {
1892          continue;                    /* get another ACL */
1893       }
1894       break;
1895    }
1896    set_bit(index, res_all.hdr.item_present);
1897 }
1898
1899 /* We build RunScripts items here */
1900 static RUNSCRIPT res_runscript;
1901
1902 /* Store a runscript->when in a bit field */
1903 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1904 {
1905    lex_get_token(lc, T_NAME);
1906
1907    if (strcasecmp(lc->str, "before") == 0) {
1908       *(uint32_t *)(item->value) = SCRIPT_Before ;
1909    } else if (strcasecmp(lc->str, "after") == 0) {
1910       *(uint32_t *)(item->value) = SCRIPT_After;
1911    } else if (strcasecmp(lc->str, "aftervss") == 0) {
1912       *(uint32_t *)(item->value) = SCRIPT_AfterVSS;
1913    } else if (strcasecmp(lc->str, "aftersnapshot") == 0) {
1914       *(uint32_t *)(item->value) = SCRIPT_AfterVSS;
1915    } else if (strcasecmp(lc->str, "always") == 0) {
1916       *(uint32_t *)(item->value) = SCRIPT_Any;
1917    } else {
1918       scan_err2(lc, _("Expect %s, got: %s"), "Before, After, AfterVSS or Always", lc->str);
1919    }
1920    scan_to_eol(lc);
1921 }
1922
1923 /* Store a runscript->target
1924  *
1925  */
1926 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1927 {
1928    lex_get_token(lc, T_STRING);
1929
1930    if (pass == 2) {
1931       if (strcmp(lc->str, "%c") == 0) {
1932          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1933       } else if (strcasecmp(lc->str, "yes") == 0) {
1934          ((RUNSCRIPT*) item->value)->set_target("%c");
1935       } else if (strcasecmp(lc->str, "no") == 0) {
1936          ((RUNSCRIPT*) item->value)->set_target("");
1937       } else {
1938          RES *res = GetResWithName(R_CLIENT, lc->str);
1939          if (res == NULL) {
1940             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1941                       lc->str, lc->line_no, lc->line);
1942          }
1943
1944          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1945       }
1946    }
1947    scan_to_eol(lc);
1948 }
1949
1950 /*
1951  * Store a runscript->command as a string and runscript->cmd_type as a pointer
1952  */
1953 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1954 {
1955    lex_get_token(lc, T_STRING);
1956
1957    if (pass == 2) {
1958       Dmsg2(1, "runscript cmd=%s type=%c\n", lc->str, item->code);
1959       POOLMEM *c = get_pool_memory(PM_FNAME);
1960       /* Each runscript command takes 2 entries in commands list */
1961       pm_strcpy(c, lc->str);
1962       ((RUNSCRIPT*) item->value)->commands->prepend(c); /* command line */
1963       ((RUNSCRIPT*) item->value)->commands->prepend((void *)(intptr_t)item->code); /* command type */
1964    }
1965    scan_to_eol(lc);
1966 }
1967
1968 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1969 {
1970    lex_get_token(lc, T_STRING);
1971    alist **runscripts = (alist **)(item->value) ;
1972
1973    if (pass == 2) {
1974       RUNSCRIPT *script = new_runscript();
1975       script->set_job_code_callback(job_code_callback_director);
1976
1977       script->set_command(lc->str);
1978
1979       /* TODO: remove all script->old_proto with bacula 1.42 */
1980
1981       if (strcasecmp(item->name, "runbeforejob") == 0) {
1982          script->when = SCRIPT_Before;
1983          script->fail_on_error = true;
1984          script->set_target("");
1985
1986       } else if (strcasecmp(item->name, "runafterjob") == 0) {
1987          script->when = SCRIPT_After;
1988          script->on_success = true;
1989          script->on_failure = false;
1990          script->set_target("");
1991
1992       } else if (strcasecmp(item->name, "clientrunbeforejob") == 0) {
1993          script->old_proto = true;
1994          script->when = SCRIPT_Before;
1995          script->set_target("%c");
1996          script->fail_on_error = true;
1997
1998       } else if (strcasecmp(item->name, "clientrunafterjob") == 0) {
1999          script->old_proto = true;
2000          script->when = SCRIPT_After;
2001          script->set_target("%c");
2002          script->on_success = true;
2003          script->on_failure = false;
2004
2005       } else if (strcasecmp(item->name, "consolerunbeforejob") == 0) {
2006          script->when = SCRIPT_Before;
2007          script->set_target("");
2008          script->fail_on_error = true;
2009          script->set_command(NPRT(script->command), CONSOLE_CMD);
2010
2011       } else if (strcasecmp(item->name, "consolerunafterjob") == 0) {
2012          script->when = SCRIPT_After;
2013          script->set_target("");
2014          script->on_success = true;
2015          script->on_failure = false;
2016          script->set_command(NPRT(script->command), CONSOLE_CMD);
2017
2018       } else if (strcasecmp(item->name, "runafterfailedjob") == 0) {
2019          script->when = SCRIPT_After;
2020          script->on_failure = true;
2021          script->on_success = false;
2022          script->set_target("");
2023       }
2024
2025       if (*runscripts == NULL) {
2026         *runscripts = New(alist(10, not_owned_by_alist));
2027       }
2028
2029       (*runscripts)->append(script);
2030       script->debug();
2031    }
2032    scan_to_eol(lc);
2033    set_bit(index, res_all.hdr.item_present);
2034 }
2035
2036 /* Store a bool in a bit field without modifing res_all.hdr
2037  * We can also add an option to store_bool to skip res_all.hdr
2038  */
2039 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
2040 {
2041    lex_get_token(lc, T_NAME);
2042    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
2043       *(bool *)(item->value) = true;
2044    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
2045       *(bool *)(item->value) = false;
2046    } else {
2047       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
2048    }
2049    scan_to_eol(lc);
2050 }
2051
2052 /*
2053  * new RunScript items
2054  *   name     handler     value               code flags default_value
2055  */
2056 static RES_ITEM runscript_items[] = {
2057  {"command",        store_runscript_cmd,  {(char **)&res_runscript},     SHELL_CMD, 0, 0},
2058  {"console",        store_runscript_cmd,  {(char **)&res_runscript},     CONSOLE_CMD, 0, 0},
2059  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0},
2060  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
2061  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
2062  {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
2063  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
2064  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
2065  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
2066  {NULL, NULL, {0}, 0, 0, 0}
2067 };
2068
2069 /*
2070  * Store RunScript info
2071  *
2072  *  Note, when this routine is called, we are inside a Job
2073  *  resource.  We treat the RunScript like a sort of
2074  *  mini-resource within the Job resource.
2075  */
2076 void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
2077 {
2078    char *c;
2079    int token, i, t;
2080    alist **runscripts = (alist **)(item->value) ;
2081
2082    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
2083
2084    token = lex_get_token(lc, T_SKIP_EOL);
2085
2086    if (token != T_BOB) {
2087       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
2088    }
2089    /* setting on_success, on_failure, fail_on_error */
2090    res_runscript.reset_default();
2091
2092    if (pass == 2) {
2093       res_runscript.commands = New(alist(10, not_owned_by_alist));
2094    }
2095
2096    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
2097       if (token == T_EOB) {
2098         break;
2099       }
2100       if (token != T_IDENTIFIER) {
2101         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
2102       }
2103       for (i=0; runscript_items[i].name; i++) {
2104         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
2105            token = lex_get_token(lc, T_SKIP_EOL);
2106            if (token != T_EQUALS) {
2107               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
2108            }
2109
2110            /* Call item handler */
2111            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
2112            i = -1;
2113            break;
2114         }
2115       }
2116
2117       if (i >=0) {
2118         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
2119       }
2120    }
2121
2122    if (pass == 2) {
2123       /* run on client by default */
2124       if (res_runscript.target == NULL) {
2125          res_runscript.set_target("%c");
2126       }
2127       if (*runscripts == NULL) {
2128          *runscripts = New(alist(10, not_owned_by_alist));
2129       }
2130       /*
2131        * commands list contains 2 values per command
2132        *  - POOLMEM command string (ex: /bin/true)
2133        *  - int command type (ex: SHELL_CMD)
2134        */
2135       res_runscript.set_job_code_callback(job_code_callback_director);
2136       while ((c=(char*)res_runscript.commands->pop()) != NULL) {
2137          t = (intptr_t)res_runscript.commands->pop();
2138          RUNSCRIPT *script = new_runscript();
2139          memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
2140          script->command = c;
2141          script->cmd_type = t;
2142          /* target is taken from res_runscript, each runscript object have
2143           * a copy
2144           */
2145          script->target = NULL;
2146          script->set_target(res_runscript.target);
2147
2148          (*runscripts)->append(script);
2149          script->debug();
2150       }
2151       delete res_runscript.commands;
2152       /* setting on_success, on_failure... cleanup target field */
2153       res_runscript.reset_default(true);
2154    }
2155
2156    scan_to_eol(lc);
2157    set_bit(index, res_all.hdr.item_present);
2158 }
2159
2160 /* callback function for edit_job_codes */
2161 /* See ../lib/util.c, function edit_job_codes, for more remaining codes */
2162 extern "C" char *job_code_callback_director(JCR *jcr, const char* param)
2163 {
2164    static char yes[] = "yes";
2165    static char no[] = "no";
2166    static char nothing[] = "";
2167
2168    if (jcr == NULL) {
2169       return nothing;
2170    }
2171    switch (param[0]) {
2172       case 'f':
2173          if (jcr->fileset) {
2174             return jcr->fileset->name();
2175          }
2176          break;
2177       case 'h':
2178          if (jcr->client && jcr->client->address) {
2179             return jcr->client->address;
2180          }
2181          break;
2182       case 'p':
2183          if (jcr->pool) {
2184             return jcr->pool->name();
2185          }
2186          break;
2187       case 'w':
2188          if (jcr->wstore) {
2189             return jcr->wstore->name();
2190          }
2191          break;
2192       case 'x':
2193          return jcr->spool_data ? yes : no;
2194       case 'D':
2195          return my_name;
2196       case 'C':
2197          return jcr->cloned ? yes : no;
2198    }
2199    return nothing;
2200 }
2201
2202 bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code)
2203 {
2204    config->init(configfile, NULL, exit_code, (void *)&res_all, res_all_size,
2205       r_first, r_last, resources, res_head);
2206    return config->parse_config();
2207 }