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