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