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