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