2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Main configuration file parser for Bacula Directors,
30 * some parts may be split into separate files such as
31 * the schedule configuration (run_config.c).
33 * Note, the configuration file parser consists of three parts
35 * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
37 * 2. The generic config scanner in lib/parse_config.c and
39 * These files contain the parser code, some utility
40 * routines, and the common store routines (name, int,
43 * 3. The daemon specific file, which contains the Resource
44 * definitions as well as any specific store routines
45 * for the resource records.
47 * Kern Sibbald, January MM
56 /* Define the first and last resource ID record
57 * types. Note, these should be unique for each
58 * daemon though not a requirement.
60 int r_first = R_FIRST;
62 static RES *sres_head[R_LAST - R_FIRST + 1];
63 RES **res_head = sres_head;
65 /* Imported subroutines */
66 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
67 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
68 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
71 /* Forward referenced subroutines */
73 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
74 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
75 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
76 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
77 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
79 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
80 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
81 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
82 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
84 /* We build the current resource here as we are
85 * scanning the resource configuration definition,
86 * then move it to allocated memory when the resource
90 extern "C" { // work around visual compiler mangling variables
96 int res_all_size = sizeof(res_all);
99 /* Definition of records permitted within each
100 * resource with the routine to process the record
101 * information. NOTE! quoted names must be in lower case.
106 * name handler value code flags default_value
108 static RES_ITEM dir_items[] = {
109 {"name", store_name, ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
110 {"description", store_str, ITEM(res_dir.hdr.desc), 0, 0, 0},
111 {"messages", store_res, ITEM(res_dir.messages), R_MSGS, 0, 0},
112 {"dirport", store_addresses_port, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
113 {"diraddress", store_addresses_address, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
114 {"diraddresses",store_addresses, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
115 {"queryfile", store_dir, ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
116 {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
117 {"plugindirectory", store_dir, ITEM(res_dir.plugin_directory), 0, 0, 0},
118 {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
119 {"piddirectory",store_dir, ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
120 {"subsysdirectory", store_dir, ITEM(res_dir.subsys_directory), 0, 0, 0},
121 {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
122 {"password", store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
123 {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
124 {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
125 {"heartbeatinterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 0},
126 {"tlsauthenticate", store_bool, ITEM(res_dir.tls_authenticate), 0, 0, 0},
127 {"tlsenable", store_bool, ITEM(res_dir.tls_enable), 0, 0, 0},
128 {"tlsrequire", store_bool, ITEM(res_dir.tls_require), 0, 0, 0},
129 {"tlsverifypeer", store_bool, ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
130 {"tlscacertificatefile", store_dir, ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
131 {"tlscacertificatedir", store_dir, ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
132 {"tlscertificate", store_dir, ITEM(res_dir.tls_certfile), 0, 0, 0},
133 {"tlskey", store_dir, ITEM(res_dir.tls_keyfile), 0, 0, 0},
134 {"tlsdhfile", store_dir, ITEM(res_dir.tls_dhfile), 0, 0, 0},
135 {"tlsallowedcn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
136 {NULL, NULL, {0}, 0, 0, 0}
142 * name handler value code flags default_value
144 static RES_ITEM con_items[] = {
145 {"name", store_name, ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
146 {"description", store_str, ITEM(res_con.hdr.desc), 0, 0, 0},
147 {"password", store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
148 {"jobacl", store_acl, ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
149 {"clientacl", store_acl, ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
150 {"storageacl", store_acl, ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
151 {"scheduleacl", store_acl, ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
152 {"runacl", store_acl, ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
153 {"poolacl", store_acl, ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
154 {"commandacl", store_acl, ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
155 {"filesetacl", store_acl, ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
156 {"catalogacl", store_acl, ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
157 {"whereacl", store_acl, ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
158 {"tlsauthenticate", store_bool, ITEM(res_con.tls_authenticate), 0, 0, 0},
159 {"tlsenable", store_bool, ITEM(res_con.tls_enable), 0, 0, 0},
160 {"tlsrequire", store_bool, ITEM(res_con.tls_require), 0, 0, 0},
161 {"tlsverifypeer", store_bool, ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
162 {"tlscacertificatefile", store_dir, ITEM(res_con.tls_ca_certfile), 0, 0, 0},
163 {"tlscacertificatedir", store_dir, ITEM(res_con.tls_ca_certdir), 0, 0, 0},
164 {"tlscertificate", store_dir, ITEM(res_con.tls_certfile), 0, 0, 0},
165 {"tlskey", store_dir, ITEM(res_con.tls_keyfile), 0, 0, 0},
166 {"tlsdhfile", store_dir, ITEM(res_con.tls_dhfile), 0, 0, 0},
167 {"tlsallowedcn", store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
168 {NULL, NULL, {0}, 0, 0, 0}
173 * Client or File daemon resource
175 * name handler value code flags default_value
178 static RES_ITEM cli_items[] = {
179 {"name", store_name, ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
180 {"description", store_str, ITEM(res_client.hdr.desc), 0, 0, 0},
181 {"address", store_str, ITEM(res_client.address), 0, ITEM_REQUIRED, 0},
182 {"fdaddress", store_str, ITEM(res_client.address), 0, 0, 0},
183 {"fdport", store_pint, ITEM(res_client.FDport), 0, ITEM_DEFAULT, 9102},
184 {"password", store_password, ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
185 {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
186 {"catalog", store_res, ITEM(res_client.catalog), R_CATALOG, ITEM_REQUIRED, 0},
187 {"fileretention", store_time, ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
188 {"jobretention", store_time, ITEM(res_client.JobRetention), 0, ITEM_DEFAULT, 60*60*24*180},
189 {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0},
190 {"autoprune", store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
191 {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
192 {"tlsauthenticate", store_bool, ITEM(res_client.tls_authenticate), 0, 0, 0},
193 {"tlsenable", store_bool, ITEM(res_client.tls_enable), 0, 0, 0},
194 {"tlsrequire", store_bool, ITEM(res_client.tls_require), 0, 0, 0},
195 {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0},
196 {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0},
197 {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0},
198 {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0},
199 {"tlsallowedcn", store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
200 {NULL, NULL, {0}, 0, 0, 0}
203 /* Storage daemon resource
205 * name handler value code flags default_value
207 static RES_ITEM store_items[] = {
208 {"name", store_name, ITEM(res_store.hdr.name), 0, ITEM_REQUIRED, 0},
209 {"description", store_str, ITEM(res_store.hdr.desc), 0, 0, 0},
210 {"sdport", store_pint, ITEM(res_store.SDport), 0, ITEM_DEFAULT, 9103},
211 {"address", store_str, ITEM(res_store.address), 0, ITEM_REQUIRED, 0},
212 {"sdaddress", store_str, ITEM(res_store.address), 0, 0, 0},
213 {"password", store_password, ITEM(res_store.password), 0, ITEM_REQUIRED, 0},
214 {"sdpassword", store_password, ITEM(res_store.password), 0, 0, 0},
215 {"device", store_device, ITEM(res_store.device), R_DEVICE, ITEM_REQUIRED, 0},
216 {"mediatype", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
217 {"autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
218 {"enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true},
219 {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
220 {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
221 {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
222 {"tlsauthenticate", store_bool, ITEM(res_store.tls_authenticate), 0, 0, 0},
223 {"tlsenable", store_bool, ITEM(res_store.tls_enable), 0, 0, 0},
224 {"tlsrequire", store_bool, ITEM(res_store.tls_require), 0, 0, 0},
225 {"tlscacertificatefile", store_dir, ITEM(res_store.tls_ca_certfile), 0, 0, 0},
226 {"tlscacertificatedir", store_dir, ITEM(res_store.tls_ca_certdir), 0, 0, 0},
227 {"tlscertificate", store_dir, ITEM(res_store.tls_certfile), 0, 0, 0},
228 {"tlskey", store_dir, ITEM(res_store.tls_keyfile), 0, 0, 0},
229 {NULL, NULL, {0}, 0, 0, 0}
233 * Catalog Resource Directives
235 * name handler value code flags default_value
237 static RES_ITEM cat_items[] = {
238 {"name", store_name, ITEM(res_cat.hdr.name), 0, ITEM_REQUIRED, 0},
239 {"description", store_str, ITEM(res_cat.hdr.desc), 0, 0, 0},
240 {"address", store_str, ITEM(res_cat.db_address), 0, 0, 0},
241 {"dbaddress", store_str, ITEM(res_cat.db_address), 0, 0, 0},
242 {"dbport", store_pint, ITEM(res_cat.db_port), 0, 0, 0},
243 /* keep this password as store_str for the moment */
244 {"password", store_str, ITEM(res_cat.db_password), 0, 0, 0},
245 {"dbpassword", store_str, ITEM(res_cat.db_password), 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 {NULL, NULL, {0}, 0, 0, 0}
256 * Job Resource Directives
258 * name handler value code flags default_value
260 RES_ITEM job_items[] = {
261 {"name", store_name, ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
262 {"description", store_str, ITEM(res_job.hdr.desc), 0, 0, 0},
263 {"type", store_jobtype, ITEM(res_job.JobType), 0, ITEM_REQUIRED, 0},
264 {"level", store_level, ITEM(res_job.JobLevel), 0, 0, 0},
265 {"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
266 {"storage", store_alist_res, ITEM(res_job.storage), R_STORAGE, 0, 0},
267 {"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
268 {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
269 {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
270 {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
271 {"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
272 {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
273 {"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
274 {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
275 {"jobtoverify", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
276 {"jobdefs", store_res, ITEM(res_job.jobdefs), R_JOBDEFS, 0, 0},
277 {"run", store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
278 /* Root of where to restore files */
279 {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
280 {"regexwhere", store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
281 {"stripprefix", store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
282 {"addprefix", store_str, ITEM(res_job.add_prefix), 0, 0, 0},
283 {"addsuffix", store_str, ITEM(res_job.add_suffix), 0, 0, 0},
284 /* Where to find bootstrap during restore */
285 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
286 /* Where to write bootstrap file during backup */
287 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
288 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
289 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
290 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
291 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
292 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
293 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
294 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
295 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
296 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
297 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
298 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
299 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
300 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
301 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
302 {"optimizejobscheduling",store_bool, ITEM(res_job.OptimizeJobScheduling), 0, ITEM_DEFAULT, false},
303 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
304 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
305 {"spoolsize", store_size, ITEM(res_job.spool_size), 0, 0, 0},
306 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
307 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
308 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
309 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
310 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
311 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
312 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
313 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
314 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
315 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
316 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
317 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
318 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
319 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
320 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
321 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
322 {NULL, NULL, {0}, 0, 0, 0}
327 * name handler value code flags default_value
329 static RES_ITEM fs_items[] = {
330 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
331 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
332 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
333 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
334 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
335 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
336 {NULL, NULL, {0}, 0, 0, 0}
339 /* Schedule -- see run_conf.c */
342 * name handler value code flags default_value
344 static RES_ITEM sch_items[] = {
345 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
346 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
347 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
348 {NULL, NULL, {0}, 0, 0, 0}
353 * name handler value code flags default_value
355 static RES_ITEM pool_items[] = {
356 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
357 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
358 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
359 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
360 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
361 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
362 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
363 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
364 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
365 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
366 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
367 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
368 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
369 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
370 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
371 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
372 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
373 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
374 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
375 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
376 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
377 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
378 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
379 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
380 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
381 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
382 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
383 {"catalog", store_res, ITEM(res_pool.Catalog), R_CATALOG, 0, 0},
384 {NULL, NULL, {0}, 0, 0, 0}
389 * name handler value code flags default_value
391 static RES_ITEM counter_items[] = {
392 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
393 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
394 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
395 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
396 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
397 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
398 {NULL, NULL, {0}, 0, 0, 0}
402 /* Message resource */
403 extern RES_ITEM msgs_items[];
406 * This is the master resource definition.
407 * It must have one item for each of the resources.
409 * NOTE!!! keep it in the same order as the R_codes
410 * or eliminate all resources[rindex].name
412 * name items rcode res_head
414 RES_TABLE resources[] = {
415 {"director", dir_items, R_DIRECTOR},
416 {"client", cli_items, R_CLIENT},
417 {"job", job_items, R_JOB},
418 {"storage", store_items, R_STORAGE},
419 {"catalog", cat_items, R_CATALOG},
420 {"schedule", sch_items, R_SCHEDULE},
421 {"fileset", fs_items, R_FILESET},
422 {"pool", pool_items, R_POOL},
423 {"messages", msgs_items, R_MSGS},
424 {"counter", counter_items, R_COUNTER},
425 {"console", con_items, R_CONSOLE},
426 {"jobdefs", job_items, R_JOBDEFS},
427 {"device", NULL, R_DEVICE}, /* info obtained from SD */
432 /* Keywords (RHS) permitted in Job Level records
434 * level_name level job_type
436 struct s_jl joblevels[] = {
437 {"Full", L_FULL, JT_BACKUP},
438 {"Base", L_BASE, JT_BACKUP},
439 {"Incremental", L_INCREMENTAL, JT_BACKUP},
440 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
441 {"Since", L_SINCE, JT_BACKUP},
442 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
443 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
444 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
445 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
446 {"Data", L_VERIFY_DATA, JT_VERIFY},
447 {" ", L_NONE, JT_ADMIN},
448 {" ", L_NONE, JT_RESTORE},
452 /* Keywords (RHS) permitted in Job type records
456 struct s_jt jobtypes[] = {
457 {"backup", JT_BACKUP},
459 {"verify", JT_VERIFY},
460 {"restore", JT_RESTORE},
461 {"migrate", JT_MIGRATE},
466 /* Keywords (RHS) permitted in Selection type records
470 struct s_jt migtypes[] = {
471 {"smallestvolume", MT_SMALLEST_VOL},
472 {"oldestvolume", MT_OLDEST_VOL},
473 {"pooloccupancy", MT_POOL_OCCUPANCY},
474 {"pooltime", MT_POOL_TIME},
475 {"client", MT_CLIENT},
476 {"volume", MT_VOLUME},
478 {"sqlquery", MT_SQLQUERY},
484 /* Options permitted in Restore replace= */
485 struct s_kw ReplaceOptions[] = {
486 {"always", REPLACE_ALWAYS},
487 {"ifnewer", REPLACE_IFNEWER},
488 {"ifolder", REPLACE_IFOLDER},
489 {"never", REPLACE_NEVER},
493 const char *level_to_str(int level)
496 static char level_no[30];
497 const char *str = level_no;
499 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
500 for (i=0; joblevels[i].level_name; i++) {
501 if (level == joblevels[i].level) {
502 str = joblevels[i].level_name;
509 /* Dump contents of resource */
510 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
512 URES *res = (URES *)reshdr;
514 char ed1[100], ed2[100], ed3[100];
518 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
521 if (type < 0) { /* no recursion */
527 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
528 reshdr->name, res->res_dir.MaxConcurrentJobs,
529 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
530 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
531 if (res->res_dir.query_file) {
532 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
534 if (res->res_dir.messages) {
535 sendit(sock, _(" --> "));
536 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
540 sendit(sock, _("Console: name=%s SSL=%d\n"),
541 res->res_con.hdr.name, res->res_con.tls_enable);
544 if (res->res_counter.WrapCounter) {
545 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
546 res->res_counter.hdr.name, res->res_counter.MinValue,
547 res->res_counter.MaxValue, res->res_counter.CurrentValue,
548 res->res_counter.WrapCounter->hdr.name);
550 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
551 res->res_counter.hdr.name, res->res_counter.MinValue,
552 res->res_counter.MaxValue);
554 if (res->res_counter.Catalog) {
555 sendit(sock, _(" --> "));
556 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
561 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
562 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
563 res->res_client.MaxConcurrentJobs);
564 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
565 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
566 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
567 res->res_client.AutoPrune);
568 if (res->res_client.catalog) {
569 sendit(sock, _(" --> "));
570 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
577 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
578 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
579 " poolid=%s volname=%s MediaType=%s\n"),
580 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
581 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
582 dev->offline, dev->autochanger,
583 edit_uint64(dev->PoolId, ed1),
584 dev->VolumeName, dev->MediaType);
588 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
589 " DeviceName=%s MediaType=%s StorageId=%s\n"),
590 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
591 res->res_store.MaxConcurrentJobs,
592 res->res_store.dev_name(),
593 res->res_store.media_type,
594 edit_int64(res->res_store.StorageId, ed1));
598 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
599 " db_driver=%s db_user=%s MutliDBConn=%d\n"),
600 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
601 res->res_cat.db_port, res->res_cat.db_name,
602 NPRT(res->res_cat.db_driver), NPRT(res->res_cat.db_user),
603 res->res_cat.mult_db_connections);
608 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
609 type == R_JOB ? _("Job") : _("JobDefs"),
610 res->res_job.hdr.name, res->res_job.JobType,
611 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
612 res->res_job.enabled);
613 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
614 res->res_job.MaxConcurrentJobs,
615 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
616 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
617 res->res_job.spool_data, res->res_job.write_part_after_job);
618 if (res->res_job.spool_size) {
619 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
621 if (res->res_job.JobType == JT_MIGRATE) {
622 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
624 if (res->res_job.client) {
625 sendit(sock, _(" --> "));
626 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
628 if (res->res_job.fileset) {
629 sendit(sock, _(" --> "));
630 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
632 if (res->res_job.schedule) {
633 sendit(sock, _(" --> "));
634 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
636 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
637 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
639 if (res->res_job.RegexWhere) {
640 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
642 if (res->res_job.RestoreBootstrap) {
643 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
645 if (res->res_job.WriteBootstrap) {
646 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
648 if (res->res_job.storage) {
650 foreach_alist(store, res->res_job.storage) {
651 sendit(sock, _(" --> "));
652 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
655 if (res->res_job.RunScripts) {
657 foreach_alist(script, res->res_job.RunScripts) {
658 sendit(sock, _(" --> RunScript\n"));
659 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
660 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
661 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
662 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
663 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
664 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
667 if (res->res_job.pool) {
668 sendit(sock, _(" --> "));
669 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
671 if (res->res_job.full_pool) {
672 sendit(sock, _(" --> "));
673 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
675 if (res->res_job.inc_pool) {
676 sendit(sock, _(" --> "));
677 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
679 if (res->res_job.diff_pool) {
680 sendit(sock, _(" --> "));
681 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
683 if (res->res_job.verify_job) {
684 sendit(sock, _(" --> "));
685 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
687 if (res->res_job.run_cmds) {
689 foreach_alist(runcmd, res->res_job.run_cmds) {
690 sendit(sock, _(" --> Run=%s\n"), runcmd);
693 if (res->res_job.selection_pattern) {
694 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
696 if (res->res_job.messages) {
697 sendit(sock, _(" --> "));
698 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
705 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
706 for (i=0; i<res->res_fs.num_includes; i++) {
707 INCEXE *incexe = res->res_fs.include_items[i];
708 for (j=0; j<incexe->num_opts; j++) {
709 FOPTS *fo = incexe->opts_list[j];
710 sendit(sock, " O %s\n", fo->opts);
712 bool enhanced_wild = false;
713 for (k=0; fo->opts[k]!='\0'; k++) {
714 if (fo->opts[k]=='W') {
715 enhanced_wild = true;
720 for (k=0; k<fo->regex.size(); k++) {
721 sendit(sock, " R %s\n", fo->regex.get(k));
723 for (k=0; k<fo->regexdir.size(); k++) {
724 sendit(sock, " RD %s\n", fo->regexdir.get(k));
726 for (k=0; k<fo->regexfile.size(); k++) {
727 sendit(sock, " RF %s\n", fo->regexfile.get(k));
729 for (k=0; k<fo->wild.size(); k++) {
730 sendit(sock, " W %s\n", fo->wild.get(k));
732 for (k=0; k<fo->wilddir.size(); k++) {
733 sendit(sock, " WD %s\n", fo->wilddir.get(k));
735 for (k=0; k<fo->wildfile.size(); k++) {
736 sendit(sock, " WF %s\n", fo->wildfile.get(k));
738 for (k=0; k<fo->wildbase.size(); k++) {
739 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
741 for (k=0; k<fo->base.size(); k++) {
742 sendit(sock, " B %s\n", fo->base.get(k));
744 for (k=0; k<fo->fstype.size(); k++) {
745 sendit(sock, " X %s\n", fo->fstype.get(k));
747 for (k=0; k<fo->drivetype.size(); k++) {
748 sendit(sock, " XD %s\n", fo->drivetype.get(k));
751 sendit(sock, " G %s\n", fo->plugin);
754 sendit(sock, " D %s\n", fo->reader);
757 sendit(sock, " T %s\n", fo->writer);
759 sendit(sock, " N\n");
761 for (j=0; j<incexe->name_list.size(); j++) {
762 sendit(sock, " I %s\n", incexe->name_list.get(j));
764 if (incexe->name_list.size()) {
765 sendit(sock, " N\n");
767 for (j=0; j<incexe->plugin_list.size(); j++) {
768 sendit(sock, " P %s\n", incexe->plugin_list.get(j));
770 if (incexe->plugin_list.size()) {
771 sendit(sock, " N\n");
776 for (i=0; i<res->res_fs.num_excludes; i++) {
777 INCEXE *incexe = res->res_fs.exclude_items[i];
778 for (j=0; j<incexe->name_list.size(); j++) {
779 sendit(sock, " E %s\n", incexe->name_list.get(j));
781 if (incexe->name_list.size()) {
782 sendit(sock, " N\n");
789 if (res->res_sch.run) {
791 RUN *run = res->res_sch.run;
792 char buf[1000], num[30];
793 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
798 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
799 bstrncpy(buf, _(" hour="), sizeof(buf));
800 for (i=0; i<24; i++) {
801 if (bit_is_set(i, run->hour)) {
802 bsnprintf(num, sizeof(num), "%d ", i);
803 bstrncat(buf, num, sizeof(buf));
806 bstrncat(buf, "\n", sizeof(buf));
808 bstrncpy(buf, _(" mday="), sizeof(buf));
809 for (i=0; i<31; i++) {
810 if (bit_is_set(i, run->mday)) {
811 bsnprintf(num, sizeof(num), "%d ", i);
812 bstrncat(buf, num, sizeof(buf));
815 bstrncat(buf, "\n", sizeof(buf));
817 bstrncpy(buf, _(" month="), sizeof(buf));
818 for (i=0; i<12; i++) {
819 if (bit_is_set(i, run->month)) {
820 bsnprintf(num, sizeof(num), "%d ", i);
821 bstrncat(buf, num, sizeof(buf));
824 bstrncat(buf, "\n", sizeof(buf));
826 bstrncpy(buf, _(" wday="), sizeof(buf));
827 for (i=0; i<7; i++) {
828 if (bit_is_set(i, run->wday)) {
829 bsnprintf(num, sizeof(num), "%d ", i);
830 bstrncat(buf, num, sizeof(buf));
833 bstrncat(buf, "\n", sizeof(buf));
835 bstrncpy(buf, _(" wom="), sizeof(buf));
836 for (i=0; i<5; i++) {
837 if (bit_is_set(i, run->wom)) {
838 bsnprintf(num, sizeof(num), "%d ", i);
839 bstrncat(buf, num, sizeof(buf));
842 bstrncat(buf, "\n", sizeof(buf));
844 bstrncpy(buf, _(" woy="), sizeof(buf));
845 for (i=0; i<54; i++) {
846 if (bit_is_set(i, run->woy)) {
847 bsnprintf(num, sizeof(num), "%d ", i);
848 bstrncat(buf, num, sizeof(buf));
851 bstrncat(buf, "\n", sizeof(buf));
853 sendit(sock, _(" mins=%d\n"), run->minute);
855 sendit(sock, _(" --> "));
856 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
859 sendit(sock, _(" --> "));
860 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
863 sendit(sock, _(" --> "));
864 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
866 /* If another Run record is chained in, go print it */
872 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
877 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
878 res->res_pool.pool_type);
879 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
880 res->res_pool.use_catalog, res->res_pool.use_volume_once,
881 res->res_pool.catalog_files);
882 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
883 res->res_pool.max_volumes, res->res_pool.AutoPrune,
884 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
885 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
886 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
887 res->res_pool.Recycle,
888 NPRT(res->res_pool.label_format));
889 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
890 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
891 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
892 res->res_pool.recycle_oldest_volume,
893 res->res_pool.purge_oldest_volume);
894 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
895 res->res_pool.MaxVolJobs,
896 res->res_pool.MaxVolFiles,
897 edit_uint64(res->res_pool.MaxVolFiles, ed1));
898 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
899 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
900 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
901 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
902 if (res->res_pool.NextPool) {
903 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
905 if (res->res_pool.RecyclePool) {
906 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
908 if (res->res_pool.Catalog) {
909 sendit(sock, _(" Catalog=%s\n"), res->res_pool.Catalog->name());
911 if (res->res_pool.storage) {
913 foreach_alist(store, res->res_pool.storage) {
914 sendit(sock, _(" --> "));
915 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
918 if (res->res_pool.CopyPool) {
920 foreach_alist(copy, res->res_pool.CopyPool) {
921 sendit(sock, _(" --> "));
922 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
929 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
930 if (res->res_msgs.mail_cmd)
931 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
932 if (res->res_msgs.operator_cmd)
933 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
937 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
940 if (recurse && res->res_dir.hdr.next) {
941 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
946 * Free all the members of an INCEXE structure
948 static void free_incexe(INCEXE *incexe)
950 incexe->name_list.destroy();
951 incexe->plugin_list.destroy();
952 for (int i=0; i<incexe->num_opts; i++) {
953 FOPTS *fopt = incexe->opts_list[i];
954 fopt->regex.destroy();
955 fopt->regexdir.destroy();
956 fopt->regexfile.destroy();
957 fopt->wild.destroy();
958 fopt->wilddir.destroy();
959 fopt->wildfile.destroy();
960 fopt->wildbase.destroy();
961 fopt->base.destroy();
962 fopt->fstype.destroy();
963 fopt->drivetype.destroy();
975 if (incexe->opts_list) {
976 free(incexe->opts_list);
982 * Free memory of resource -- called when daemon terminates.
983 * NB, we don't need to worry about freeing any references
984 * to other resources as they will be freed when that
985 * resource chain is traversed. Mainly we worry about freeing
986 * allocated strings (names).
988 void free_resource(RES *sres, int type)
991 RES *nres; /* next resource if linked */
992 URES *res = (URES *)sres;
997 /* common stuff -- free the resource name and description */
998 nres = (RES *)res->res_dir.hdr.next;
999 if (res->res_dir.hdr.name) {
1000 free(res->res_dir.hdr.name);
1002 if (res->res_dir.hdr.desc) {
1003 free(res->res_dir.hdr.desc);
1008 if (res->res_dir.working_directory) {
1009 free(res->res_dir.working_directory);
1011 if (res->res_dir.scripts_directory) {
1012 free((char *)res->res_dir.scripts_directory);
1014 if (res->res_dir.pid_directory) {
1015 free(res->res_dir.pid_directory);
1017 if (res->res_dir.subsys_directory) {
1018 free(res->res_dir.subsys_directory);
1020 if (res->res_dir.password) {
1021 free(res->res_dir.password);
1023 if (res->res_dir.query_file) {
1024 free(res->res_dir.query_file);
1026 if (res->res_dir.DIRaddrs) {
1027 free_addresses(res->res_dir.DIRaddrs);
1029 if (res->res_dir.tls_ctx) {
1030 free_tls_context(res->res_dir.tls_ctx);
1032 if (res->res_dir.tls_ca_certfile) {
1033 free(res->res_dir.tls_ca_certfile);
1035 if (res->res_dir.tls_ca_certdir) {
1036 free(res->res_dir.tls_ca_certdir);
1038 if (res->res_dir.tls_certfile) {
1039 free(res->res_dir.tls_certfile);
1041 if (res->res_dir.tls_keyfile) {
1042 free(res->res_dir.tls_keyfile);
1044 if (res->res_dir.tls_dhfile) {
1045 free(res->res_dir.tls_dhfile);
1047 if (res->res_dir.tls_allowed_cns) {
1048 delete res->res_dir.tls_allowed_cns;
1055 if (res->res_con.password) {
1056 free(res->res_con.password);
1058 if (res->res_con.tls_ctx) {
1059 free_tls_context(res->res_con.tls_ctx);
1061 if (res->res_con.tls_ca_certfile) {
1062 free(res->res_con.tls_ca_certfile);
1064 if (res->res_con.tls_ca_certdir) {
1065 free(res->res_con.tls_ca_certdir);
1067 if (res->res_con.tls_certfile) {
1068 free(res->res_con.tls_certfile);
1070 if (res->res_con.tls_keyfile) {
1071 free(res->res_con.tls_keyfile);
1073 if (res->res_con.tls_dhfile) {
1074 free(res->res_con.tls_dhfile);
1076 if (res->res_con.tls_allowed_cns) {
1077 delete res->res_con.tls_allowed_cns;
1079 for (int i=0; i<Num_ACL; i++) {
1080 if (res->res_con.ACL_lists[i]) {
1081 delete res->res_con.ACL_lists[i];
1082 res->res_con.ACL_lists[i] = NULL;
1087 if (res->res_client.address) {
1088 free(res->res_client.address);
1090 if (res->res_client.password) {
1091 free(res->res_client.password);
1093 if (res->res_client.tls_ctx) {
1094 free_tls_context(res->res_client.tls_ctx);
1096 if (res->res_client.tls_ca_certfile) {
1097 free(res->res_client.tls_ca_certfile);
1099 if (res->res_client.tls_ca_certdir) {
1100 free(res->res_client.tls_ca_certdir);
1102 if (res->res_client.tls_certfile) {
1103 free(res->res_client.tls_certfile);
1105 if (res->res_client.tls_keyfile) {
1106 free(res->res_client.tls_keyfile);
1108 if (res->res_client.tls_allowed_cns) {
1109 delete res->res_client.tls_allowed_cns;
1113 if (res->res_store.address) {
1114 free(res->res_store.address);
1116 if (res->res_store.password) {
1117 free(res->res_store.password);
1119 if (res->res_store.media_type) {
1120 free(res->res_store.media_type);
1122 if (res->res_store.device) {
1123 delete res->res_store.device;
1125 if (res->res_store.tls_ctx) {
1126 free_tls_context(res->res_store.tls_ctx);
1128 if (res->res_store.tls_ca_certfile) {
1129 free(res->res_store.tls_ca_certfile);
1131 if (res->res_store.tls_ca_certdir) {
1132 free(res->res_store.tls_ca_certdir);
1134 if (res->res_store.tls_certfile) {
1135 free(res->res_store.tls_certfile);
1137 if (res->res_store.tls_keyfile) {
1138 free(res->res_store.tls_keyfile);
1142 if (res->res_cat.db_address) {
1143 free(res->res_cat.db_address);
1145 if (res->res_cat.db_socket) {
1146 free(res->res_cat.db_socket);
1148 if (res->res_cat.db_user) {
1149 free(res->res_cat.db_user);
1151 if (res->res_cat.db_name) {
1152 free(res->res_cat.db_name);
1154 if (res->res_cat.db_driver) {
1155 free(res->res_cat.db_driver);
1157 if (res->res_cat.db_password) {
1158 free(res->res_cat.db_password);
1162 if ((num=res->res_fs.num_includes)) {
1163 while (--num >= 0) {
1164 free_incexe(res->res_fs.include_items[num]);
1166 free(res->res_fs.include_items);
1168 res->res_fs.num_includes = 0;
1169 if ((num=res->res_fs.num_excludes)) {
1170 while (--num >= 0) {
1171 free_incexe(res->res_fs.exclude_items[num]);
1173 free(res->res_fs.exclude_items);
1175 res->res_fs.num_excludes = 0;
1178 if (res->res_pool.pool_type) {
1179 free(res->res_pool.pool_type);
1181 if (res->res_pool.label_format) {
1182 free(res->res_pool.label_format);
1184 if (res->res_pool.cleaning_prefix) {
1185 free(res->res_pool.cleaning_prefix);
1187 if (res->res_pool.storage) {
1188 delete res->res_pool.storage;
1192 if (res->res_sch.run) {
1194 nrun = res->res_sch.run;
1204 if (res->res_job.RestoreWhere) {
1205 free(res->res_job.RestoreWhere);
1207 if (res->res_job.RegexWhere) {
1208 free(res->res_job.RegexWhere);
1210 if (res->res_job.strip_prefix) {
1211 free(res->res_job.strip_prefix);
1213 if (res->res_job.add_prefix) {
1214 free(res->res_job.add_prefix);
1216 if (res->res_job.add_suffix) {
1217 free(res->res_job.add_suffix);
1219 if (res->res_job.RestoreBootstrap) {
1220 free(res->res_job.RestoreBootstrap);
1222 if (res->res_job.WriteBootstrap) {
1223 free(res->res_job.WriteBootstrap);
1225 if (res->res_job.selection_pattern) {
1226 free(res->res_job.selection_pattern);
1228 if (res->res_job.run_cmds) {
1229 delete res->res_job.run_cmds;
1231 if (res->res_job.storage) {
1232 delete res->res_job.storage;
1234 if (res->res_job.RunScripts) {
1235 free_runscripts(res->res_job.RunScripts);
1236 delete res->res_job.RunScripts;
1240 if (res->res_msgs.mail_cmd) {
1241 free(res->res_msgs.mail_cmd);
1243 if (res->res_msgs.operator_cmd) {
1244 free(res->res_msgs.operator_cmd);
1246 free_msgs_res((MSGS *)res); /* free message resource */
1250 printf(_("Unknown resource type %d in free_resource.\n"), type);
1252 /* Common stuff again -- free the resource, recurse to next one */
1257 free_resource(nres, type);
1262 * Save the new resource by chaining it into the head list for
1263 * the resource. If this is pass 2, we update any resource
1264 * pointers because they may not have been defined until
1267 void save_resource(int type, RES_ITEM *items, int pass)
1270 int rindex = type - r_first;
1274 /* Check Job requirements after applying JobDefs */
1275 if (type != R_JOB && type != R_JOBDEFS) {
1277 * Ensure that all required items are present
1279 for (i=0; items[i].name; i++) {
1280 if (items[i].flags & ITEM_REQUIRED) {
1281 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1282 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1283 items[i].name, resources[rindex]);
1286 /* If this triggers, take a look at lib/parse_conf.h */
1287 if (i >= MAX_RES_ITEMS) {
1288 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1291 } else if (type == R_JOB) {
1293 * Ensure that the name item is present
1295 if (items[0].flags & ITEM_REQUIRED) {
1296 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1297 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1298 items[0].name, resources[rindex]);
1304 * During pass 2 in each "store" routine, we looked up pointers
1305 * to all the resources referrenced in the current resource, now we
1306 * must copy their addresses from the static record to the allocated
1311 /* Resources not containing a resource */
1319 * Resources containing another resource or alist. First
1320 * look up the resource which contains another resource. It
1321 * was written during pass 1. Then stuff in the pointers to
1322 * the resources it contains, which were inserted this pass.
1323 * Finally, it will all be stored back.
1326 /* Find resource saved in pass 1 */
1327 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1328 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1330 /* Explicitly copy resource pointers from this pass (res_all) */
1331 res->res_pool.NextPool = res_all.res_pool.NextPool;
1332 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1333 res->res_pool.storage = res_all.res_pool.storage;
1334 res->res_pool.Catalog = res_all.res_pool.Catalog;
1337 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1338 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1340 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1343 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1344 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1346 res->res_dir.messages = res_all.res_dir.messages;
1347 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1350 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1351 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1352 res_all.res_dir.hdr.name);
1354 /* we must explicitly copy the device alist pointer */
1355 res->res_store.device = res_all.res_store.device;
1359 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1360 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1361 res_all.res_dir.hdr.name);
1363 res->res_job.messages = res_all.res_job.messages;
1364 res->res_job.schedule = res_all.res_job.schedule;
1365 res->res_job.client = res_all.res_job.client;
1366 res->res_job.fileset = res_all.res_job.fileset;
1367 res->res_job.storage = res_all.res_job.storage;
1368 res->res_job.pool = res_all.res_job.pool;
1369 res->res_job.full_pool = res_all.res_job.full_pool;
1370 res->res_job.inc_pool = res_all.res_job.inc_pool;
1371 res->res_job.diff_pool = res_all.res_job.diff_pool;
1372 res->res_job.verify_job = res_all.res_job.verify_job;
1373 res->res_job.jobdefs = res_all.res_job.jobdefs;
1374 res->res_job.run_cmds = res_all.res_job.run_cmds;
1375 res->res_job.RunScripts = res_all.res_job.RunScripts;
1377 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1378 * is not very useful)
1379 * We have to set_bit(index, res_all.hdr.item_present);
1380 * or something like that
1383 /* we take RegexWhere before all other options */
1384 if (!res->res_job.RegexWhere
1386 (res->res_job.strip_prefix ||
1387 res->res_job.add_suffix ||
1388 res->res_job.add_prefix))
1390 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1391 res->res_job.add_prefix,
1392 res->res_job.add_suffix);
1393 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1394 bregexp_build_where(res->res_job.RegexWhere, len,
1395 res->res_job.strip_prefix,
1396 res->res_job.add_prefix,
1397 res->res_job.add_suffix);
1398 /* TODO: test bregexp */
1401 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1402 free(res->res_job.RestoreWhere);
1403 res->res_job.RestoreWhere = NULL;
1408 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1409 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1411 res->res_counter.Catalog = res_all.res_counter.Catalog;
1412 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1416 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1417 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1419 res->res_client.catalog = res_all.res_client.catalog;
1420 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1424 * Schedule is a bit different in that it contains a RUN record
1425 * chain which isn't a "named" resource. This chain was linked
1426 * in by run_conf.c during pass 2, so here we jam the pointer
1427 * into the Schedule resource.
1429 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1430 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1432 res->res_sch.run = res_all.res_sch.run;
1435 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1439 /* Note, the resource name was already saved during pass 1,
1440 * so here, we can just release it.
1442 if (res_all.res_dir.hdr.name) {
1443 free(res_all.res_dir.hdr.name);
1444 res_all.res_dir.hdr.name = NULL;
1446 if (res_all.res_dir.hdr.desc) {
1447 free(res_all.res_dir.hdr.desc);
1448 res_all.res_dir.hdr.desc = NULL;
1454 * The following code is only executed during pass 1
1458 size = sizeof(DIRRES);
1461 size = sizeof(CONRES);
1464 size =sizeof(CLIENT);
1467 size = sizeof(STORE);
1477 size = sizeof(FILESET);
1480 size = sizeof(SCHED);
1483 size = sizeof(POOL);
1486 size = sizeof(MSGS);
1489 size = sizeof(COUNTER);
1495 printf(_("Unknown resource type %d in save_resource.\n"), type);
1501 res = (URES *)malloc(size);
1502 memcpy(res, &res_all, size);
1503 if (!res_head[rindex]) {
1504 res_head[rindex] = (RES *)res; /* store first entry */
1505 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1506 res->res_dir.hdr.name, rindex);
1509 if (res->res_dir.hdr.name == NULL) {
1510 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1513 /* Add new res to end of chain */
1514 for (last=next=res_head[rindex]; next; next=next->next) {
1516 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1517 Emsg2(M_ERROR_TERM, 0,
1518 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1519 resources[rindex].name, res->res_dir.hdr.name);
1522 last->next = (RES *)res;
1523 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1524 res->res_dir.hdr.name, rindex, pass);
1530 * Store Device. Note, the resource is created upon the
1531 * first reference. The details of the resource are obtained
1532 * later from the SD.
1534 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1538 int rindex = R_DEVICE - r_first;
1539 int size = sizeof(DEVICE);
1543 token = lex_get_token(lc, T_NAME);
1544 if (!res_head[rindex]) {
1545 res = (URES *)malloc(size);
1546 memset(res, 0, size);
1547 res->res_dev.hdr.name = bstrdup(lc->str);
1548 res_head[rindex] = (RES *)res; /* store first entry */
1549 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1550 res->res_dir.hdr.name, rindex);
1553 /* See if it is already defined */
1554 for (next=res_head[rindex]; next->next; next=next->next) {
1555 if (strcmp(next->name, lc->str) == 0) {
1561 res = (URES *)malloc(size);
1562 memset(res, 0, size);
1563 res->res_dev.hdr.name = bstrdup(lc->str);
1564 next->next = (RES *)res;
1565 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1566 res->res_dir.hdr.name, rindex, pass);
1571 set_bit(index, res_all.hdr.item_present);
1573 store_alist_res(lc, item, index, pass);
1578 * Store JobType (backup, verify, restore)
1581 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1585 token = lex_get_token(lc, T_NAME);
1586 /* Store the type both pass 1 and pass 2 */
1587 for (i=0; migtypes[i].type_name; i++) {
1588 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1589 *(int *)(item->value) = migtypes[i].job_type;
1595 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1598 set_bit(index, res_all.hdr.item_present);
1604 * Store JobType (backup, verify, restore)
1607 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1611 token = lex_get_token(lc, T_NAME);
1612 /* Store the type both pass 1 and pass 2 */
1613 for (i=0; jobtypes[i].type_name; i++) {
1614 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1615 *(int *)(item->value) = jobtypes[i].job_type;
1621 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1624 set_bit(index, res_all.hdr.item_present);
1628 * Store Job Level (Full, Incremental, ...)
1631 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1635 token = lex_get_token(lc, T_NAME);
1636 /* Store the level pass 2 so that type is defined */
1637 for (i=0; joblevels[i].level_name; i++) {
1638 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1639 *(int *)(item->value) = joblevels[i].level;
1645 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1648 set_bit(index, res_all.hdr.item_present);
1652 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1655 token = lex_get_token(lc, T_NAME);
1656 /* Scan Replacement options */
1657 for (i=0; ReplaceOptions[i].name; i++) {
1658 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1659 *(int *)(item->value) = ReplaceOptions[i].token;
1665 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1668 set_bit(index, res_all.hdr.item_present);
1672 * Store ACL (access control list)
1675 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1680 token = lex_get_token(lc, T_STRING);
1682 if (((alist **)item->value)[item->code] == NULL) {
1683 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1684 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1686 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1687 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1689 token = lex_get_token(lc, T_ALL);
1690 if (token == T_COMMA) {
1691 continue; /* get another ACL */
1695 set_bit(index, res_all.hdr.item_present);
1698 /* We build RunScripts items here */
1699 static RUNSCRIPT res_runscript;
1701 /* Store a runscript->when in a bit field */
1702 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1704 lex_get_token(lc, T_NAME);
1706 if (strcasecmp(lc->str, "before") == 0) {
1707 *(int *)(item->value) = SCRIPT_Before ;
1708 } else if (strcasecmp(lc->str, "after") == 0) {
1709 *(int *)(item->value) = SCRIPT_After;
1710 } else if (strcasecmp(lc->str, "always") == 0) {
1711 *(int *)(item->value) = SCRIPT_Any;
1713 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1718 /* Store a runscript->target
1721 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1723 lex_get_token(lc, T_STRING);
1726 if (strcmp(lc->str, "%c") == 0) {
1727 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1728 } else if (strcasecmp(lc->str, "yes") == 0) {
1729 ((RUNSCRIPT*) item->value)->set_target("%c");
1730 } else if (strcasecmp(lc->str, "no") == 0) {
1731 ((RUNSCRIPT*) item->value)->set_target("");
1733 RES *res = GetResWithName(R_CLIENT, lc->str);
1735 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1736 lc->str, lc->line_no, lc->line);
1739 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1746 * Store a runscript->command as a string
1748 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1750 lex_get_token(lc, T_STRING);
1753 ((RUNSCRIPT*)item->value)->set_command(lc->str, item->code);
1758 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1760 lex_get_token(lc, T_STRING);
1761 alist **runscripts = (alist **)(item->value) ;
1764 RUNSCRIPT *script = new_runscript();
1765 script->set_job_code_callback(job_code_callback_filesetname);
1767 script->set_command(lc->str);
1769 /* TODO: remove all script->old_proto with bacula 1.42 */
1771 if (strcmp(item->name, "runbeforejob") == 0) {
1772 script->when = SCRIPT_Before;
1773 script->fail_on_error = true;
1774 script->set_target("");
1776 } else if (strcmp(item->name, "runafterjob") == 0) {
1777 script->when = SCRIPT_After;
1778 script->on_success = true;
1779 script->on_failure = false;
1780 script->set_target("");
1782 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1783 script->old_proto = true;
1784 script->when = SCRIPT_After;
1785 script->set_target("%c");
1786 script->on_success = true;
1787 script->on_failure = false;
1789 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1790 script->old_proto = true;
1791 script->when = SCRIPT_Before;
1792 script->set_target("%c");
1793 script->fail_on_error = true;
1795 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1796 script->when = SCRIPT_After;
1797 script->on_failure = true;
1798 script->on_success = false;
1799 script->set_target("");
1802 if (*runscripts == NULL) {
1803 *runscripts = New(alist(10, not_owned_by_alist));
1806 (*runscripts)->append(script);
1813 /* Store a bool in a bit field without modifing res_all.hdr
1814 * We can also add an option to store_bool to skip res_all.hdr
1816 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1818 lex_get_token(lc, T_NAME);
1819 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1820 *(bool *)(item->value) = true;
1821 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1822 *(bool *)(item->value) = false;
1824 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1830 * new RunScript items
1831 * name handler value code flags default_value
1833 static RES_ITEM runscript_items[] = {
1834 {"command", store_runscript_cmd, {(char **)&res_runscript}, SHELL_CMD, 0, 0},
1835 {"console", store_runscript_cmd, {(char **)&res_runscript}, CONSOLE_CMD, 0, 0},
1836 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1837 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1838 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1839 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1840 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1841 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1842 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1843 {NULL, NULL, {0}, 0, 0, 0}
1847 * Store RunScript info
1849 * Note, when this routine is called, we are inside a Job
1850 * resource. We treat the RunScript like a sort of
1851 * mini-resource within the Job resource.
1853 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1856 alist **runscripts = (alist **)(item->value) ;
1858 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1860 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1862 token = lex_get_token(lc, T_SKIP_EOL);
1864 if (token != T_BOB) {
1865 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1868 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1869 if (token == T_EOB) {
1872 if (token != T_IDENTIFIER) {
1873 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1875 for (i=0; runscript_items[i].name; i++) {
1876 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1877 token = lex_get_token(lc, T_SKIP_EOL);
1878 if (token != T_EQUALS) {
1879 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1882 /* Call item handler */
1883 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1890 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1895 if (res_runscript.command == NULL) {
1896 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1897 "command", "runscript");
1900 /* run on client by default */
1901 if (res_runscript.target == NULL) {
1902 res_runscript.set_target("%c");
1905 RUNSCRIPT *script = new_runscript();
1906 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1907 script->set_job_code_callback(job_code_callback_filesetname);
1909 if (*runscripts == NULL) {
1910 *runscripts = New(alist(10, not_owned_by_alist));
1913 (*runscripts)->append(script);
1918 set_bit(index, res_all.hdr.item_present);
1921 /* callback function for edit_job_codes */
1922 extern "C" char *job_code_callback_filesetname(JCR *jcr, const char* param)
1924 if (param[0] == 'f') {
1925 return jcr->fileset->name();