]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Add shortcut to RunScript console commands. Submitted by Wanderlei Huttel. Fixes...
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2015 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.client) {
717          sendit(sock, _("  --> "));
718          dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
719       }
720       if (res->res_job.fileset) {
721          sendit(sock, _("  --> "));
722          dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
723       }
724       if (res->res_job.schedule) {
725          sendit(sock, _("  --> "));
726          dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
727       }
728       if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
729            sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
730       }
731       if (res->res_job.RegexWhere) {
732            sendit(sock, _("  --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
733       }
734       if (res->res_job.RestoreBootstrap) {
735          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
736       }
737       if (res->res_job.WriteBootstrap) {
738          sendit(sock, _("  --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
739       }
740       if (res->res_job.PluginOptions) {
741          sendit(sock, _("  --> PluginOptions=%s\n"), NPRT(res->res_job.PluginOptions));
742       }
743       if (res->res_job.MaxRunTime) {
744          sendit(sock, _("  --> MaxRunTime=%u\n"), res->res_job.MaxRunTime);
745       }
746       if (res->res_job.MaxWaitTime) {
747          sendit(sock, _("  --> MaxWaitTime=%u\n"), res->res_job.MaxWaitTime);
748       }
749       if (res->res_job.MaxStartDelay) {
750          sendit(sock, _("  --> MaxStartDelay=%u\n"), res->res_job.MaxStartDelay);
751       }
752       if (res->res_job.MaxRunSchedTime) {
753          sendit(sock, _("  --> MaxRunSchedTime=%u\n"), res->res_job.MaxRunSchedTime);
754       }
755       if (res->res_job.storage) {
756          STORE *store;
757          foreach_alist(store, res->res_job.storage) {
758             sendit(sock, _("  --> "));
759             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
760          }
761       }
762       if (res->res_job.base) {
763          JOB *job;
764          foreach_alist(job, res->res_job.base) {
765             sendit(sock, _("  --> Base %s\n"), job->name());
766          }
767       }
768       if (res->res_job.RunScripts) {
769         RUNSCRIPT *script;
770         foreach_alist(script, res->res_job.RunScripts) {
771            sendit(sock, _(" --> RunScript\n"));
772            sendit(sock, _("  --> Command=%s\n"), NPRT(script->command));
773            sendit(sock, _("  --> Target=%s\n"),  NPRT(script->target));
774            sendit(sock, _("  --> RunOnSuccess=%u\n"),  script->on_success);
775            sendit(sock, _("  --> RunOnFailure=%u\n"),  script->on_failure);
776            sendit(sock, _("  --> FailJobOnError=%u\n"),  script->fail_on_error);
777            sendit(sock, _("  --> RunWhen=%u\n"),  script->when);
778         }
779       }
780       if (res->res_job.pool) {
781          sendit(sock, _("  --> "));
782          dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
783       }
784       if (res->res_job.full_pool) {
785          sendit(sock, _("  --> FullBackup"));
786          dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
787       }
788       if (res->res_job.inc_pool) {
789          sendit(sock, _("  --> IncrementalBackup"));
790          dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
791       }
792       if (res->res_job.diff_pool) {
793          sendit(sock, _("  --> DifferentialBackup"));
794          dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
795       }
796       if (res->res_job.verify_job) {
797          sendit(sock, _("  --> "));
798          dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
799       }
800       if (res->res_job.run_cmds) {
801          char *runcmd;
802          foreach_alist(runcmd, res->res_job.run_cmds) {
803             sendit(sock, _("  --> Run=%s\n"), runcmd);
804          }
805       }
806       if (res->res_job.selection_pattern) {
807          sendit(sock, _("  --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
808       }
809       if (res->res_job.messages) {
810          sendit(sock, _("  --> "));
811          dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
812       }
813       break;
814
815    case R_FILESET:
816    {
817       int i, j, k;
818       if (!acl_access_ok(ua, FileSet_ACL, res->res_fs.hdr.name)) {
819          break;
820       }
821       sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
822       for (i=0; i<res->res_fs.num_includes; i++) {
823          INCEXE *incexe = res->res_fs.include_items[i];
824          for (j=0; j<incexe->num_opts; j++) {
825             FOPTS *fo = incexe->opts_list[j];
826             sendit(sock, "      O %s\n", fo->opts);
827
828             bool enhanced_wild = false;
829             for (k=0; fo->opts[k]!='\0'; k++) {
830                if (fo->opts[k]=='W') {
831                   enhanced_wild = true;
832                   break;
833                }
834             }
835
836             for (k=0; k<fo->regex.size(); k++) {
837                sendit(sock, "      R %s\n", fo->regex.get(k));
838             }
839             for (k=0; k<fo->regexdir.size(); k++) {
840                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
841             }
842             for (k=0; k<fo->regexfile.size(); k++) {
843                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
844             }
845             for (k=0; k<fo->wild.size(); k++) {
846                sendit(sock, "      W %s\n", fo->wild.get(k));
847             }
848             for (k=0; k<fo->wilddir.size(); k++) {
849                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
850             }
851             for (k=0; k<fo->wildfile.size(); k++) {
852                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
853             }
854             for (k=0; k<fo->wildbase.size(); k++) {
855                sendit(sock, "      W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
856             }
857             for (k=0; k<fo->base.size(); k++) {
858                sendit(sock, "      B %s\n", fo->base.get(k));
859             }
860             for (k=0; k<fo->fstype.size(); k++) {
861                sendit(sock, "      X %s\n", fo->fstype.get(k));
862             }
863             for (k=0; k<fo->drivetype.size(); k++) {
864                sendit(sock, "      XD %s\n", fo->drivetype.get(k));
865             }
866             if (fo->plugin) {
867                sendit(sock, "      G %s\n", fo->plugin);
868             }
869             if (fo->reader) {
870                sendit(sock, "      D %s\n", fo->reader);
871             }
872             if (fo->writer) {
873                sendit(sock, "      T %s\n", fo->writer);
874             }
875             sendit(sock, "      N\n");
876          }
877          if (incexe->ignoredir) {
878             sendit(sock, "      Z %s\n", incexe->ignoredir);
879          }
880          for (j=0; j<incexe->name_list.size(); j++) {
881             sendit(sock, "      I %s\n", incexe->name_list.get(j));
882          }
883          if (incexe->name_list.size()) {
884             sendit(sock, "      N\n");
885          }
886          for (j=0; j<incexe->plugin_list.size(); j++) {
887             sendit(sock, "      P %s\n", incexe->plugin_list.get(j));
888          }
889          if (incexe->plugin_list.size()) {
890             sendit(sock, "      N\n");
891          }
892       } /* end for over includes */
893
894       for (i=0; i<res->res_fs.num_excludes; i++) {
895          INCEXE *incexe = res->res_fs.exclude_items[i];
896          for (j=0; j<incexe->name_list.size(); j++) {
897             sendit(sock, "      E %s\n", incexe->name_list.get(j));
898          }
899          if (incexe->name_list.size()) {
900             sendit(sock, "      N\n");
901          }
902       }
903       break;
904    } /* end case R_FILESET */
905
906    case R_SCHEDULE:
907       if (!acl_access_ok(ua, Schedule_ACL, res->res_sch.hdr.name)) {
908          break;
909       }
910
911       if (res->res_sch.run) {
912          int i;
913          RUN *run = res->res_sch.run;
914          char buf[1000], num[30];
915          sendit(sock, _("Schedule: Name=%s Enabled=%d\n"), 
916             res->res_sch.hdr.name, res->res_sch.enabled);
917          if (!run) {
918             break;
919          }
920 next_run:
921          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
922          if (run->MaxRunSchedTime) {
923             sendit(sock, _("      MaxRunSchedTime=%u\n"), run->MaxRunSchedTime);
924          }
925          if (run->Priority) {
926             sendit(sock, _("      Priority=%u\n"), run->Priority);
927          }
928          bstrncpy(buf, _("      hour="), sizeof(buf));
929          for (i=0; i<24; i++) {
930             if (bit_is_set(i, run->hour)) {
931                bsnprintf(num, sizeof(num), "%d ", i);
932                bstrncat(buf, num, sizeof(buf));
933             }
934          }
935          bstrncat(buf, "\n", sizeof(buf));
936          sendit(sock, buf);
937          bstrncpy(buf, _("      mday="), sizeof(buf));
938          for (i=0; i<32; i++) {
939             if (bit_is_set(i, run->mday)) {
940                bsnprintf(num, sizeof(num), "%d ", i);
941                bstrncat(buf, num, sizeof(buf));
942             }
943          }
944          bstrncat(buf, "\n", sizeof(buf));
945          sendit(sock, buf);
946          bstrncpy(buf, _("      month="), sizeof(buf));
947          for (i=0; i<12; i++) {
948             if (bit_is_set(i, run->month)) {
949                bsnprintf(num, sizeof(num), "%d ", i);
950                bstrncat(buf, num, sizeof(buf));
951             }
952          }
953          bstrncat(buf, "\n", sizeof(buf));
954          sendit(sock, buf);
955          bstrncpy(buf, _("      wday="), sizeof(buf));
956          for (i=0; i<7; i++) {
957             if (bit_is_set(i, run->wday)) {
958                bsnprintf(num, sizeof(num), "%d ", i);
959                bstrncat(buf, num, sizeof(buf));
960             }
961          }
962          bstrncat(buf, "\n", sizeof(buf));
963          sendit(sock, buf);
964          bstrncpy(buf, _("      wom="), sizeof(buf));
965          for (i=0; i<6; i++) {
966             if (bit_is_set(i, run->wom)) {
967                bsnprintf(num, sizeof(num), "%d ", i);
968                bstrncat(buf, num, sizeof(buf));
969             }
970          }
971          bstrncat(buf, "\n", sizeof(buf));
972          sendit(sock, buf);
973          bstrncpy(buf, _("      woy="), sizeof(buf));
974          for (i=0; i<54; i++) {
975             if (bit_is_set(i, run->woy)) {
976                bsnprintf(num, sizeof(num), "%d ", i);
977                bstrncat(buf, num, sizeof(buf));
978             }
979          }
980          bstrncat(buf, "\n", sizeof(buf));
981          sendit(sock, buf);
982          sendit(sock, _("      mins=%d\n"), run->minute);
983          if (run->pool) {
984             sendit(sock, _("     --> "));
985             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
986          }
987          if (run->storage) {
988             sendit(sock, _("     --> "));
989             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
990          }
991          if (run->msgs) {
992             sendit(sock, _("     --> "));
993             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
994          }
995          /* If another Run record is chained in, go print it */
996          if (run->next) {
997             run = run->next;
998             goto next_run;
999          }
1000       } else {
1001          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
1002       }
1003       break;
1004
1005    case R_POOL:
1006       if (!acl_access_ok(ua, Pool_ACL, res->res_pool.hdr.name)) {
1007          break;
1008       }
1009       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
1010               res->res_pool.pool_type);
1011       sendit(sock, _("      use_cat=%d use_once=%d cat_files=%d\n"),
1012               res->res_pool.use_catalog, res->res_pool.use_volume_once,
1013               res->res_pool.catalog_files);
1014       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
1015               res->res_pool.max_volumes, res->res_pool.AutoPrune,
1016               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
1017       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
1018               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
1019               res->res_pool.Recycle,
1020               NPRT(res->res_pool.label_format));
1021       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
1022               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
1023       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d ActionOnPurge=%d\n"),
1024               res->res_pool.recycle_oldest_volume,
1025               res->res_pool.purge_oldest_volume,
1026               res->res_pool.action_on_purge);
1027       sendit(sock, _("      MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
1028               res->res_pool.MaxVolJobs,
1029               res->res_pool.MaxVolFiles,
1030               edit_uint64(res->res_pool.MaxVolBytes, ed1));
1031       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
1032               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
1033               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
1034               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
1035       sendit(sock, _("      JobRetention=%s FileRetention=%s\n"),
1036          edit_utime(res->res_pool.JobRetention, ed1, sizeof(ed1)),
1037          edit_utime(res->res_pool.FileRetention, ed2, sizeof(ed2)));
1038       if (res->res_pool.NextPool) {
1039          sendit(sock, _("      NextPool=%s\n"), res->res_pool.NextPool->name());
1040       }
1041       if (res->res_pool.RecyclePool) {
1042          sendit(sock, _("      RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
1043       }
1044       if (res->res_pool.ScratchPool) {
1045          sendit(sock, _("      ScratchPool=%s\n"), res->res_pool.ScratchPool->name());
1046       }
1047       if (res->res_pool.catalog) {
1048          sendit(sock, _("      Catalog=%s\n"), res->res_pool.catalog->name());
1049       }
1050       if (res->res_pool.storage) {
1051          STORE *store;
1052          foreach_alist(store, res->res_pool.storage) {
1053             sendit(sock, _("  --> "));
1054             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
1055          }
1056       }
1057       if (res->res_pool.CopyPool) {
1058          POOL *copy;
1059          foreach_alist(copy, res->res_pool.CopyPool) {
1060             sendit(sock, _("  --> "));
1061             dump_resource(-R_POOL, (RES *)copy, sendit, sock);
1062          }
1063       }
1064
1065       break;
1066
1067    case R_MSGS:
1068       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
1069       if (res->res_msgs.mail_cmd)
1070          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
1071       if (res->res_msgs.operator_cmd)
1072          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
1073       break;
1074
1075    default:
1076       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
1077       break;
1078    }
1079    if (recurse && res->res_dir.hdr.next) {
1080       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
1081    }
1082
1083 }
1084
1085 /*
1086  * Free all the members of an INCEXE structure
1087  */
1088 static void free_incexe(INCEXE *incexe)
1089 {
1090    incexe->name_list.destroy();
1091    incexe->plugin_list.destroy();
1092    for (int i=0; i<incexe->num_opts; i++) {
1093       FOPTS *fopt = incexe->opts_list[i];
1094       fopt->regex.destroy();
1095       fopt->regexdir.destroy();
1096       fopt->regexfile.destroy();
1097       fopt->wild.destroy();
1098       fopt->wilddir.destroy();
1099       fopt->wildfile.destroy();
1100       fopt->wildbase.destroy();
1101       fopt->base.destroy();
1102       fopt->fstype.destroy();
1103       fopt->drivetype.destroy();
1104       if (fopt->plugin) {
1105          free(fopt->plugin);
1106       }
1107       if (fopt->reader) {
1108          free(fopt->reader);
1109       }
1110       if (fopt->writer) {
1111          free(fopt->writer);
1112       }
1113       free(fopt);
1114    }
1115    if (incexe->opts_list) {
1116       free(incexe->opts_list);
1117    }
1118    if (incexe->ignoredir) {
1119       free(incexe->ignoredir);
1120    }
1121    free(incexe);
1122 }
1123
1124
1125 /*
1126  * Free memory of resource -- called when daemon terminates.
1127  * NB, we don't need to worry about freeing any references
1128  * to other resources as they will be freed when that
1129  * resource chain is traversed.  Mainly we worry about freeing
1130  * allocated strings (names).
1131  */
1132 void free_resource(RES *rres, int type)
1133 {
1134    int num;
1135    RES *nres;
1136    URES *res = (URES *)rres;
1137
1138    if (res == NULL) {
1139       return;
1140    }
1141
1142    Dmsg3(200, "type=%d res=%p name=%s\n", type, res, res->res_dir.hdr.name);
1143    /* common stuff -- free the resource name and description */
1144    nres = (RES *)res->res_dir.hdr.next;
1145    if (res->res_dir.hdr.name) {
1146       free(res->res_dir.hdr.name);
1147    }
1148    if (res->res_dir.hdr.desc) {
1149       free(res->res_dir.hdr.desc);
1150    }
1151
1152    switch (type) {
1153    case R_DIRECTOR:
1154       if (res->res_dir.working_directory) {
1155          free(res->res_dir.working_directory);
1156       }
1157       if (res->res_dir.scripts_directory) {
1158          free((char *)res->res_dir.scripts_directory);
1159       }
1160       if (res->res_dir.plugin_directory) {
1161          free((char *)res->res_dir.plugin_directory);
1162       }
1163       if (res->res_dir.pid_directory) {
1164          free(res->res_dir.pid_directory);
1165       }
1166       if (res->res_dir.subsys_directory) {
1167          free(res->res_dir.subsys_directory);
1168       }
1169       if (res->res_dir.password) {
1170          free(res->res_dir.password);
1171       }
1172       if (res->res_dir.query_file) {
1173          free(res->res_dir.query_file);
1174       }
1175       if (res->res_dir.DIRaddrs) {
1176          free_addresses(res->res_dir.DIRaddrs);
1177       }
1178       if (res->res_dir.DIRsrc_addr) {
1179          free_addresses(res->res_dir.DIRsrc_addr);
1180       }
1181       if (res->res_dir.tls_ctx) {
1182          free_tls_context(res->res_dir.tls_ctx);
1183       }
1184       if (res->res_dir.tls_ca_certfile) {
1185          free(res->res_dir.tls_ca_certfile);
1186       }
1187       if (res->res_dir.tls_ca_certdir) {
1188          free(res->res_dir.tls_ca_certdir);
1189       }
1190       if (res->res_dir.tls_certfile) {
1191          free(res->res_dir.tls_certfile);
1192       }
1193       if (res->res_dir.tls_keyfile) {
1194          free(res->res_dir.tls_keyfile);
1195       }
1196       if (res->res_dir.tls_dhfile) {
1197          free(res->res_dir.tls_dhfile);
1198       }
1199       if (res->res_dir.tls_allowed_cns) {
1200          delete res->res_dir.tls_allowed_cns;
1201       }
1202       if (res->res_dir.verid) {
1203          free(res->res_dir.verid);
1204       }
1205       break;
1206    case R_DEVICE:
1207    case R_COUNTER:
1208        break;
1209    case R_CONSOLE:
1210       if (res->res_con.password) {
1211          free(res->res_con.password);
1212       }
1213       if (res->res_con.tls_ctx) {
1214          free_tls_context(res->res_con.tls_ctx);
1215       }
1216       if (res->res_con.tls_ca_certfile) {
1217          free(res->res_con.tls_ca_certfile);
1218       }
1219       if (res->res_con.tls_ca_certdir) {
1220          free(res->res_con.tls_ca_certdir);
1221       }
1222       if (res->res_con.tls_certfile) {
1223          free(res->res_con.tls_certfile);
1224       }
1225       if (res->res_con.tls_keyfile) {
1226          free(res->res_con.tls_keyfile);
1227       }
1228       if (res->res_con.tls_dhfile) {
1229          free(res->res_con.tls_dhfile);
1230       }
1231       if (res->res_con.tls_allowed_cns) {
1232          delete res->res_con.tls_allowed_cns;
1233       }
1234       for (int i=0; i<Num_ACL; i++) {
1235          if (res->res_con.ACL_lists[i]) {
1236             delete res->res_con.ACL_lists[i];
1237             res->res_con.ACL_lists[i] = NULL;
1238          }
1239       }
1240       break;
1241    case R_CLIENT:
1242       if (res->res_client.address) {
1243          free(res->res_client.address);
1244       }
1245       if (res->res_client.fd_storage_address) {
1246          free(res->res_client.fd_storage_address);
1247       }
1248       if (res->res_client.password) {
1249          free(res->res_client.password);
1250       }
1251       if (res->res_client.tls_ctx) {
1252          free_tls_context(res->res_client.tls_ctx);
1253       }
1254       if (res->res_client.tls_ca_certfile) {
1255          free(res->res_client.tls_ca_certfile);
1256       }
1257       if (res->res_client.tls_ca_certdir) {
1258          free(res->res_client.tls_ca_certdir);
1259       }
1260       if (res->res_client.tls_certfile) {
1261          free(res->res_client.tls_certfile);
1262       }
1263       if (res->res_client.tls_keyfile) {
1264          free(res->res_client.tls_keyfile);
1265       }
1266       if (res->res_client.tls_allowed_cns) {
1267          delete res->res_client.tls_allowed_cns;
1268       }
1269       break;
1270    case R_STORAGE:
1271       if (res->res_store.address) {
1272          free(res->res_store.address);
1273       }
1274       if (res->res_store.fd_storage_address) {
1275          free(res->res_store.fd_storage_address);
1276       }
1277       if (res->res_store.password) {
1278          free(res->res_store.password);
1279       }
1280       if (res->res_store.media_type) {
1281          free(res->res_store.media_type);
1282       }
1283       if (res->res_store.device) {
1284          delete res->res_store.device;
1285       }
1286       if (res->res_store.tls_ctx) {
1287          free_tls_context(res->res_store.tls_ctx);
1288       }
1289       if (res->res_store.tls_ca_certfile) {
1290          free(res->res_store.tls_ca_certfile);
1291       }
1292       if (res->res_store.tls_ca_certdir) {
1293          free(res->res_store.tls_ca_certdir);
1294       }
1295       if (res->res_store.tls_certfile) {
1296          free(res->res_store.tls_certfile);
1297       }
1298       if (res->res_store.tls_keyfile) {
1299          free(res->res_store.tls_keyfile);
1300       }
1301       break;
1302    case R_CATALOG:
1303       if (res->res_cat.db_address) {
1304          free(res->res_cat.db_address);
1305       }
1306       if (res->res_cat.db_socket) {
1307          free(res->res_cat.db_socket);
1308       }
1309       if (res->res_cat.db_user) {
1310          free(res->res_cat.db_user);
1311       }
1312       if (res->res_cat.db_name) {
1313          free(res->res_cat.db_name);
1314       }
1315       if (res->res_cat.db_driver) {
1316          free(res->res_cat.db_driver);
1317       }
1318       if (res->res_cat.db_password) {
1319          free(res->res_cat.db_password);
1320       }
1321       if (res->res_cat.db_ssl_key) {
1322          free(res->res_cat.db_ssl_key);
1323       }
1324       if (res->res_cat.db_ssl_cert) {
1325          free(res->res_cat.db_ssl_cert);
1326       }
1327       if (res->res_cat.db_ssl_ca) {
1328          free(res->res_cat.db_ssl_ca);
1329       }
1330       if (res->res_cat.db_ssl_capath) {
1331          free(res->res_cat.db_ssl_capath);
1332       }
1333       if (res->res_cat.db_ssl_cipher) {
1334          free(res->res_cat.db_ssl_cipher);
1335       }
1336       break;
1337    case R_FILESET:
1338       if ((num=res->res_fs.num_includes)) {
1339          while (--num >= 0) {
1340             free_incexe(res->res_fs.include_items[num]);
1341          }
1342          free(res->res_fs.include_items);
1343       }
1344       res->res_fs.num_includes = 0;
1345       if ((num=res->res_fs.num_excludes)) {
1346          while (--num >= 0) {
1347             free_incexe(res->res_fs.exclude_items[num]);
1348          }
1349          free(res->res_fs.exclude_items);
1350       }
1351       res->res_fs.num_excludes = 0;
1352       break;
1353    case R_POOL:
1354       if (res->res_pool.pool_type) {
1355          free(res->res_pool.pool_type);
1356       }
1357       if (res->res_pool.label_format) {
1358          free(res->res_pool.label_format);
1359       }
1360       if (res->res_pool.cleaning_prefix) {
1361          free(res->res_pool.cleaning_prefix);
1362       }
1363       if (res->res_pool.storage) {
1364          delete res->res_pool.storage;
1365       }
1366       break;
1367    case R_SCHEDULE:
1368       if (res->res_sch.run) {
1369          RUN *nrun, *next;
1370          nrun = res->res_sch.run;
1371          while (nrun) {
1372             next = nrun->next;
1373             free(nrun);
1374             nrun = next;
1375          }
1376       }
1377       break;
1378    case R_JOB:
1379    case R_JOBDEFS:
1380       if (res->res_job.RestoreWhere) {
1381          free(res->res_job.RestoreWhere);
1382       }
1383       if (res->res_job.RegexWhere) {
1384          free(res->res_job.RegexWhere);
1385       }
1386       if (res->res_job.strip_prefix) {
1387          free(res->res_job.strip_prefix);
1388       }
1389       if (res->res_job.add_prefix) {
1390          free(res->res_job.add_prefix);
1391       }
1392       if (res->res_job.add_suffix) {
1393          free(res->res_job.add_suffix);
1394       }
1395       if (res->res_job.RestoreBootstrap) {
1396          free(res->res_job.RestoreBootstrap);
1397       }
1398       if (res->res_job.WriteBootstrap) {
1399          free(res->res_job.WriteBootstrap);
1400       }
1401       if (res->res_job.PluginOptions) {
1402          free(res->res_job.PluginOptions);
1403       }
1404       if (res->res_job.selection_pattern) {
1405          free(res->res_job.selection_pattern);
1406       }
1407       if (res->res_job.run_cmds) {
1408          delete res->res_job.run_cmds;
1409       }
1410       if (res->res_job.storage) {
1411          delete res->res_job.storage;
1412       }
1413       if (res->res_job.base) {
1414          delete res->res_job.base;
1415       }
1416       if (res->res_job.RunScripts) {
1417          free_runscripts(res->res_job.RunScripts);
1418          delete res->res_job.RunScripts;
1419       }
1420       break;
1421    case R_MSGS:
1422       if (res->res_msgs.mail_cmd) {
1423          free(res->res_msgs.mail_cmd);
1424       }
1425       if (res->res_msgs.operator_cmd) {
1426          free(res->res_msgs.operator_cmd);
1427       }
1428       free_msgs_res((MSGS *)res);  /* free message resource */
1429       res = NULL;
1430       break;
1431    default:
1432       printf(_("Unknown resource type %d in free_resource.\n"), type);
1433    }
1434    /* Common stuff again -- free the resource, recurse to next one */
1435    if (res) {
1436       free(res);
1437    }
1438    if (nres) {
1439       free_resource(nres, type);
1440    }
1441 }
1442
1443 /*
1444  * Save the new resource by chaining it into the head list for
1445  * the resource. If this is pass 2, we update any resource
1446  * pointers because they may not have been defined until
1447  * later in pass 1.
1448  */
1449 void save_resource(int type, RES_ITEM *items, int pass)
1450 {
1451    URES *res;
1452    int rindex = type - r_first;
1453    int i, size = 0;
1454    bool error = false;
1455
1456    /* Check Job requirements after applying JobDefs */
1457    if (type != R_JOB && type != R_JOBDEFS) {
1458       /*
1459        * Ensure that all required items are present
1460        */
1461       for (i=0; items[i].name; i++) {
1462          if (items[i].flags & ITEM_REQUIRED) {
1463             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1464                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1465                     items[i].name, resources[rindex]);
1466             }
1467          }
1468          /* If this triggers, take a look at lib/parse_conf.h */
1469          if (i >= MAX_RES_ITEMS) {
1470             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1471          }
1472       }
1473    } else if (type == R_JOB) {
1474       /*
1475        * Ensure that the name item is present
1476        */
1477       if (items[0].flags & ITEM_REQUIRED) {
1478          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1479              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1480                    items[0].name, resources[rindex]);
1481          }
1482       }
1483    }
1484
1485    /*
1486     * During pass 2 in each "store" routine, we looked up pointers
1487     * to all the resources referrenced in the current resource, now we
1488     * must copy their addresses from the static record to the allocated
1489     * record.
1490     */
1491    if (pass == 2) {
1492       switch (type) {
1493       /* Resources not containing a resource */
1494       case R_CATALOG:
1495       case R_MSGS:
1496       case R_FILESET:
1497       case R_DEVICE:
1498          break;
1499
1500       /*
1501        * Resources containing another resource or alist. First
1502        *  look up the resource which contains another resource. It
1503        *  was written during pass 1.  Then stuff in the pointers to
1504        *  the resources it contains, which were inserted this pass.
1505        *  Finally, it will all be stored back.
1506        */
1507       case R_POOL:
1508          /* Find resource saved in pass 1 */
1509          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1510             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1511          }
1512          /* Explicitly copy resource pointers from this pass (res_all) */
1513          res->res_pool.NextPool = res_all.res_pool.NextPool;
1514          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1515          res->res_pool.ScratchPool = res_all.res_pool.ScratchPool;
1516          res->res_pool.storage    = res_all.res_pool.storage;
1517          res->res_pool.catalog    = res_all.res_pool.catalog;
1518          break;
1519       case R_CONSOLE:
1520          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1521             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1522          }
1523          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1524          break;
1525       case R_DIRECTOR:
1526          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1527             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1528          }
1529          res->res_dir.messages = res_all.res_dir.messages;
1530          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1531          break;
1532       case R_STORAGE:
1533          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1534             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1535                   res_all.res_dir.hdr.name);
1536          }
1537          /* we must explicitly copy the device alist pointer */
1538          res->res_store.device   = res_all.res_store.device;
1539          break;
1540       case R_JOB:
1541       case R_JOBDEFS:
1542          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1543             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1544                   res_all.res_dir.hdr.name);
1545          }
1546          res->res_job.messages   = res_all.res_job.messages;
1547          res->res_job.schedule   = res_all.res_job.schedule;
1548          res->res_job.client     = res_all.res_job.client;
1549          res->res_job.fileset    = res_all.res_job.fileset;
1550          res->res_job.storage    = res_all.res_job.storage;
1551          res->res_job.base       = res_all.res_job.base;
1552          res->res_job.pool       = res_all.res_job.pool;
1553          res->res_job.next_pool  = res_all.res_job.next_pool;
1554          res->res_job.full_pool  = res_all.res_job.full_pool;
1555          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1556          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1557          res->res_job.verify_job = res_all.res_job.verify_job;
1558          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1559          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1560          res->res_job.RunScripts = res_all.res_job.RunScripts;
1561
1562          /* TODO: JobDefs where/regexwhere doesn't work well (but this
1563           * is not very useful)
1564           * We have to set_bit(index, res_all.hdr.item_present);
1565           * or something like that
1566           */
1567
1568          /* we take RegexWhere before all other options */
1569          if (!res->res_job.RegexWhere
1570              &&
1571              (res->res_job.strip_prefix ||
1572               res->res_job.add_suffix   ||
1573               res->res_job.add_prefix))
1574          {
1575             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1576                                                    res->res_job.add_prefix,
1577                                                    res->res_job.add_suffix);
1578             res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1579             bregexp_build_where(res->res_job.RegexWhere, len,
1580                                 res->res_job.strip_prefix,
1581                                 res->res_job.add_prefix,
1582                                 res->res_job.add_suffix);
1583             /* TODO: test bregexp */
1584          }
1585
1586          if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1587             free(res->res_job.RestoreWhere);
1588             res->res_job.RestoreWhere = NULL;
1589          }
1590
1591          break;
1592       case R_COUNTER:
1593          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1594             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1595          }
1596          res->res_counter.Catalog = res_all.res_counter.Catalog;
1597          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1598          break;
1599
1600       case R_CLIENT:
1601          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1602             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1603          }
1604          res->res_client.catalog = res_all.res_client.catalog;
1605          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1606          break;
1607       case R_SCHEDULE:
1608          /*
1609           * Schedule is a bit different in that it contains a RUN record
1610           * chain which isn't a "named" resource. This chain was linked
1611           * in by run_conf.c during pass 2, so here we jam the pointer
1612           * into the Schedule resource.
1613           */
1614          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1615             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1616          }
1617          res->res_sch.run = res_all.res_sch.run;
1618          break;
1619       default:
1620          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1621          error = true;
1622          break;
1623       }
1624       /* Note, the resource name was already saved during pass 1,
1625        * so here, we can just release it.
1626        */
1627       if (res_all.res_dir.hdr.name) {
1628          free(res_all.res_dir.hdr.name);
1629          res_all.res_dir.hdr.name = NULL;
1630       }
1631       if (res_all.res_dir.hdr.desc) {
1632          free(res_all.res_dir.hdr.desc);
1633          res_all.res_dir.hdr.desc = NULL;
1634       }
1635       return;
1636    }
1637
1638    /*
1639     * The following code is only executed during pass 1
1640     */
1641    switch (type) {
1642    case R_DIRECTOR:
1643       size = sizeof(DIRRES);
1644       break;
1645    case R_CONSOLE:
1646       size = sizeof(CONRES);
1647       break;
1648    case R_CLIENT:
1649       size =sizeof(CLIENT);
1650       break;
1651    case R_STORAGE:
1652       size = sizeof(STORE);
1653       break;
1654    case R_CATALOG:
1655       size = sizeof(CAT);
1656       break;
1657    case R_JOB:
1658    case R_JOBDEFS:
1659       size = sizeof(JOB);
1660       break;
1661    case R_FILESET:
1662       size = sizeof(FILESET);
1663       break;
1664    case R_SCHEDULE:
1665       size = sizeof(SCHED);
1666       break;
1667    case R_POOL:
1668       size = sizeof(POOL);
1669       break;
1670    case R_MSGS:
1671       size = sizeof(MSGS);
1672       break;
1673    case R_COUNTER:
1674       size = sizeof(COUNTER);
1675       break;
1676    case R_DEVICE:
1677       error = true;
1678       break;
1679    default:
1680       printf(_("Unknown resource type %d in save_resource.\n"), type);
1681       error = true;
1682       break;
1683    }
1684    /* Common */
1685    if (!error) {
1686       res = (URES *)malloc(size);
1687       memcpy(res, &res_all, size);
1688       if (!res_head[rindex]) {
1689          res_head[rindex] = (RES *)res; /* store first entry */
1690          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1691                res->res_dir.hdr.name, rindex);
1692       } else {
1693          RES *next, *last;
1694          if (res->res_dir.hdr.name == NULL) {
1695             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1696                   resources[rindex]);
1697          }
1698          /* Add new res to end of chain */
1699          for (last=next=res_head[rindex]; next; next=next->next) {
1700             last = next;
1701             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1702                Emsg2(M_ERROR_TERM, 0,
1703                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1704                   resources[rindex].name, res->res_dir.hdr.name);
1705             }
1706          }
1707          last->next = (RES *)res;
1708          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1709                res->res_dir.hdr.name, rindex, pass);
1710       }
1711    }
1712 }
1713
1714 void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass)
1715 {
1716    uint32_t *destination = (uint32_t*)item->value;
1717    lex_get_token(lc, T_NAME);
1718    if (strcasecmp(lc->str, "truncate") == 0) {
1719       *destination = (*destination) | ON_PURGE_TRUNCATE;
1720    } else {
1721       scan_err2(lc, _("Expected one of: %s, got: %s"), "Truncate", lc->str);
1722       return;
1723    }
1724    scan_to_eol(lc);
1725    set_bit(index, res_all.hdr.item_present);
1726 }
1727
1728 /*
1729  * Store Device. Note, the resource is created upon the
1730  *  first reference. The details of the resource are obtained
1731  *  later from the SD.
1732  */
1733 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1734 {
1735    URES *res;
1736    int rindex = R_DEVICE - r_first;
1737    int size = sizeof(DEVICE);
1738    bool found = false;
1739
1740    if (pass == 1) {
1741       lex_get_token(lc, T_NAME);
1742       if (!res_head[rindex]) {
1743          res = (URES *)malloc(size);
1744          memset(res, 0, size);
1745          res->res_dev.hdr.name = bstrdup(lc->str);
1746          res_head[rindex] = (RES *)res; /* store first entry */
1747          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1748                res->res_dir.hdr.name, rindex);
1749       } else {
1750          RES *next;
1751          /* See if it is already defined */
1752          for (next=res_head[rindex]; next->next; next=next->next) {
1753             if (strcmp(next->name, lc->str) == 0) {
1754                found = true;
1755                break;
1756             }
1757          }
1758          if (!found) {
1759             res = (URES *)malloc(size);
1760             memset(res, 0, size);
1761             res->res_dev.hdr.name = bstrdup(lc->str);
1762             next->next = (RES *)res;
1763             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1764                res->res_dir.hdr.name, rindex, pass);
1765          }
1766       }
1767
1768       scan_to_eol(lc);
1769       set_bit(index, res_all.hdr.item_present);
1770    } else {
1771       store_alist_res(lc, item, index, pass);
1772    }
1773 }
1774
1775 /*
1776  * Store Migration/Copy type
1777  *
1778  */
1779 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1780 {
1781    int i;
1782
1783    lex_get_token(lc, T_NAME);
1784    /* Store the type both pass 1 and pass 2 */
1785    for (i=0; migtypes[i].type_name; i++) {
1786       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1787          *(uint32_t *)(item->value) = migtypes[i].job_type;
1788          i = 0;
1789          break;
1790       }
1791    }
1792    if (i != 0) {
1793       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1794    }
1795    scan_to_eol(lc);
1796    set_bit(index, res_all.hdr.item_present);
1797 }
1798
1799
1800
1801 /*
1802  * Store JobType (backup, verify, restore)
1803  *
1804  */
1805 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1806 {
1807    int i;
1808
1809    lex_get_token(lc, T_NAME);
1810    /* Store the type both pass 1 and pass 2 */
1811    for (i=0; jobtypes[i].type_name; i++) {
1812       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1813          *(uint32_t *)(item->value) = jobtypes[i].job_type;
1814          i = 0;
1815          break;
1816       }
1817    }
1818    if (i != 0) {
1819       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1820    }
1821    scan_to_eol(lc);
1822    set_bit(index, res_all.hdr.item_present);
1823 }
1824
1825 /*
1826  * Store Job Level (Full, Incremental, ...)
1827  *
1828  */
1829 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1830 {
1831    int i;
1832
1833    lex_get_token(lc, T_NAME);
1834    /* Store the level pass 2 so that type is defined */
1835    for (i=0; joblevels[i].level_name; i++) {
1836       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1837          *(uint32_t *)(item->value) = joblevels[i].level;
1838          i = 0;
1839          break;
1840       }
1841    }
1842    if (i != 0) {
1843       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1844    }
1845    scan_to_eol(lc);
1846    set_bit(index, res_all.hdr.item_present);
1847 }
1848
1849
1850 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1851 {
1852    int i;
1853    lex_get_token(lc, T_NAME);
1854    /* Scan Replacement options */
1855    for (i=0; ReplaceOptions[i].name; i++) {
1856       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1857          *(uint32_t *)(item->value) = ReplaceOptions[i].token;
1858          i = 0;
1859          break;
1860       }
1861    }
1862    if (i != 0) {
1863       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1864    }
1865    scan_to_eol(lc);
1866    set_bit(index, res_all.hdr.item_present);
1867 }
1868
1869 /*
1870  * Store ACL (access control list)
1871  *
1872  */
1873 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1874 {
1875    int token;
1876
1877    for (;;) {
1878       lex_get_token(lc, T_STRING);
1879       if (pass == 1) {
1880          if (((alist **)item->value)[item->code] == NULL) {
1881             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1882             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1883          }
1884          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1885          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1886       }
1887       token = lex_get_token(lc, T_ALL);
1888       if (token == T_COMMA) {
1889          continue;                    /* get another ACL */
1890       }
1891       break;
1892    }
1893    set_bit(index, res_all.hdr.item_present);
1894 }
1895
1896 /* We build RunScripts items here */
1897 static RUNSCRIPT res_runscript;
1898
1899 /* Store a runscript->when in a bit field */
1900 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1901 {
1902    lex_get_token(lc, T_NAME);
1903
1904    if (strcasecmp(lc->str, "before") == 0) {
1905       *(uint32_t *)(item->value) = SCRIPT_Before ;
1906    } else if (strcasecmp(lc->str, "after") == 0) {
1907       *(uint32_t *)(item->value) = SCRIPT_After;
1908    } else if (strcasecmp(lc->str, "aftervss") == 0) {
1909       *(uint32_t *)(item->value) = SCRIPT_AfterVSS;
1910    } else if (strcasecmp(lc->str, "aftersnapshot") == 0) {
1911       *(uint32_t *)(item->value) = SCRIPT_AfterVSS;
1912    } else if (strcasecmp(lc->str, "always") == 0) {
1913       *(uint32_t *)(item->value) = SCRIPT_Any;
1914    } else {
1915       scan_err2(lc, _("Expect %s, got: %s"), "Before, After, AfterVSS or Always", lc->str);
1916    }
1917    scan_to_eol(lc);
1918 }
1919
1920 /* Store a runscript->target
1921  *
1922  */
1923 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1924 {
1925    lex_get_token(lc, T_STRING);
1926
1927    if (pass == 2) {
1928       if (strcmp(lc->str, "%c") == 0) {
1929          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1930       } else if (strcasecmp(lc->str, "yes") == 0) {
1931          ((RUNSCRIPT*) item->value)->set_target("%c");
1932       } else if (strcasecmp(lc->str, "no") == 0) {
1933          ((RUNSCRIPT*) item->value)->set_target("");
1934       } else {
1935          RES *res = GetResWithName(R_CLIENT, lc->str);
1936          if (res == NULL) {
1937             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1938                       lc->str, lc->line_no, lc->line);
1939          }
1940
1941          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1942       }
1943    }
1944    scan_to_eol(lc);
1945 }
1946
1947 /*
1948  * Store a runscript->command as a string and runscript->cmd_type as a pointer
1949  */
1950 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1951 {
1952    lex_get_token(lc, T_STRING);
1953
1954    if (pass == 2) {
1955       Dmsg2(1, "runscript cmd=%s type=%c\n", lc->str, item->code);
1956       POOLMEM *c = get_pool_memory(PM_FNAME);
1957       /* Each runscript command takes 2 entries in commands list */
1958       pm_strcpy(c, lc->str);
1959       ((RUNSCRIPT*) item->value)->commands->prepend(c); /* command line */
1960       ((RUNSCRIPT*) item->value)->commands->prepend((void *)(intptr_t)item->code); /* command type */
1961    }
1962    scan_to_eol(lc);
1963 }
1964
1965 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1966 {
1967    lex_get_token(lc, T_STRING);
1968    alist **runscripts = (alist **)(item->value) ;
1969
1970    if (pass == 2) {
1971       RUNSCRIPT *script = new_runscript();
1972       script->set_job_code_callback(job_code_callback_director);
1973
1974       script->set_command(lc->str);
1975
1976       /* TODO: remove all script->old_proto with bacula 1.42 */
1977
1978       if (strcasecmp(item->name, "runbeforejob") == 0) {
1979          script->when = SCRIPT_Before;
1980          script->fail_on_error = true;
1981          script->set_target("");
1982
1983       } else if (strcasecmp(item->name, "runafterjob") == 0) {
1984          script->when = SCRIPT_After;
1985          script->on_success = true;
1986          script->on_failure = false;
1987          script->set_target("");
1988
1989       } else if (strcasecmp(item->name, "clientrunbeforejob") == 0) {
1990          script->old_proto = true;
1991          script->when = SCRIPT_Before;
1992          script->set_target("%c");
1993          script->fail_on_error = true;
1994
1995       } else if (strcasecmp(item->name, "clientrunafterjob") == 0) {
1996          script->old_proto = true;
1997          script->when = SCRIPT_After;
1998          script->set_target("%c");
1999          script->on_success = true;
2000          script->on_failure = false;
2001
2002       } else if (strcasecmp(item->name, "consolerunbeforejob") == 0) {
2003          script->when = SCRIPT_Before;
2004          script->set_target("");
2005          script->fail_on_error = true;
2006          script->set_command(NPRT(script->command), CONSOLE_CMD);
2007
2008       } else if (strcasecmp(item->name, "consolerunafterjob") == 0) {
2009          script->when = SCRIPT_After;
2010          script->set_target("");
2011          script->on_success = true;
2012          script->on_failure = false;
2013          script->set_command(NPRT(script->command), CONSOLE_CMD);
2014
2015       } else if (strcasecmp(item->name, "runafterfailedjob") == 0) {
2016          script->when = SCRIPT_After;
2017          script->on_failure = true;
2018          script->on_success = false;
2019          script->set_target("");
2020       }
2021
2022       if (*runscripts == NULL) {
2023         *runscripts = New(alist(10, not_owned_by_alist));
2024       }
2025
2026       (*runscripts)->append(script);
2027       script->debug();
2028    }
2029    scan_to_eol(lc);
2030    set_bit(index, res_all.hdr.item_present);
2031 }
2032
2033 /* Store a bool in a bit field without modifing res_all.hdr
2034  * We can also add an option to store_bool to skip res_all.hdr
2035  */
2036 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
2037 {
2038    lex_get_token(lc, T_NAME);
2039    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
2040       *(bool *)(item->value) = true;
2041    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
2042       *(bool *)(item->value) = false;
2043    } else {
2044       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
2045    }
2046    scan_to_eol(lc);
2047 }
2048
2049 /*
2050  * new RunScript items
2051  *   name     handler     value               code flags default_value
2052  */
2053 static RES_ITEM runscript_items[] = {
2054  {"command",        store_runscript_cmd,  {(char **)&res_runscript},     SHELL_CMD, 0, 0},
2055  {"console",        store_runscript_cmd,  {(char **)&res_runscript},     CONSOLE_CMD, 0, 0},
2056  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0},
2057  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
2058  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
2059  {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
2060  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
2061  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
2062  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
2063  {NULL, NULL, {0}, 0, 0, 0}
2064 };
2065
2066 /*
2067  * Store RunScript info
2068  *
2069  *  Note, when this routine is called, we are inside a Job
2070  *  resource.  We treat the RunScript like a sort of
2071  *  mini-resource within the Job resource.
2072  */
2073 void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
2074 {
2075    char *c;
2076    int token, i, t;
2077    alist **runscripts = (alist **)(item->value) ;
2078
2079    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
2080
2081    token = lex_get_token(lc, T_SKIP_EOL);
2082
2083    if (token != T_BOB) {
2084       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
2085    }
2086    /* setting on_success, on_failure, fail_on_error */
2087    res_runscript.reset_default();
2088
2089    if (pass == 2) {
2090       res_runscript.commands = New(alist(10, not_owned_by_alist));
2091    }
2092
2093    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
2094       if (token == T_EOB) {
2095         break;
2096       }
2097       if (token != T_IDENTIFIER) {
2098         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
2099       }
2100       for (i=0; runscript_items[i].name; i++) {
2101         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
2102            token = lex_get_token(lc, T_SKIP_EOL);
2103            if (token != T_EQUALS) {
2104               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
2105            }
2106
2107            /* Call item handler */
2108            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
2109            i = -1;
2110            break;
2111         }
2112       }
2113
2114       if (i >=0) {
2115         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
2116       }
2117    }
2118
2119    if (pass == 2) {
2120       /* run on client by default */
2121       if (res_runscript.target == NULL) {
2122          res_runscript.set_target("%c");
2123       }
2124       if (*runscripts == NULL) {
2125          *runscripts = New(alist(10, not_owned_by_alist));
2126       }
2127       /*
2128        * commands list contains 2 values per command
2129        *  - POOLMEM command string (ex: /bin/true)
2130        *  - int command type (ex: SHELL_CMD)
2131        */
2132       res_runscript.set_job_code_callback(job_code_callback_director);
2133       while ((c=(char*)res_runscript.commands->pop()) != NULL) {
2134          t = (intptr_t)res_runscript.commands->pop();
2135          RUNSCRIPT *script = new_runscript();
2136          memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
2137          script->command = c;
2138          script->cmd_type = t;
2139          /* target is taken from res_runscript, each runscript object have
2140           * a copy
2141           */
2142          script->target = NULL;
2143          script->set_target(res_runscript.target);
2144
2145          (*runscripts)->append(script);
2146          script->debug();
2147       }
2148       delete res_runscript.commands;
2149       /* setting on_success, on_failure... cleanup target field */
2150       res_runscript.reset_default(true);
2151    }
2152
2153    scan_to_eol(lc);
2154    set_bit(index, res_all.hdr.item_present);
2155 }
2156
2157 /* callback function for edit_job_codes */
2158 /* See ../lib/util.c, function edit_job_codes, for more remaining codes */
2159 extern "C" char *job_code_callback_director(JCR *jcr, const char* param)
2160 {
2161    static char yes[] = "yes";
2162    static char no[] = "no";
2163    switch (param[0]) {
2164       case 'f':
2165          if (jcr->fileset) {
2166             return jcr->fileset->name();
2167          }
2168          break;
2169       case 'h':
2170          if (jcr->client && jcr->client->address) {
2171             return jcr->client->address;
2172          }
2173          break;
2174       case 'p':
2175          if (jcr->pool) {
2176             return jcr->pool->name();
2177          }
2178          break;
2179       case 'w':
2180          if (jcr->wstore) {
2181             return jcr->wstore->name();
2182          }
2183          break;
2184       case 'x':
2185          return jcr->spool_data ? yes : no;
2186       case 'D':
2187          return my_name;
2188       case 'C':
2189          return jcr->cloned ? yes : no;
2190    }
2191    return NULL;
2192 }
2193
2194 bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code)
2195 {
2196    config->init(configfile, NULL, exit_code, (void *)&res_all, res_all_size,
2197       r_first, r_last, resources, res_head);
2198    return config->parse_config();
2199 }