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 {"accurate", store_bool, ITEM(res_job.accurate), 0,0,0},
323 {NULL, NULL, {0}, 0, 0, 0}
328 * name handler value code flags default_value
330 static RES_ITEM fs_items[] = {
331 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
332 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
333 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
334 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
335 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
336 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
337 {NULL, NULL, {0}, 0, 0, 0}
340 /* Schedule -- see run_conf.c */
343 * name handler value code flags default_value
345 static RES_ITEM sch_items[] = {
346 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
347 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
348 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
349 {NULL, NULL, {0}, 0, 0, 0}
354 * name handler value code flags default_value
356 static RES_ITEM pool_items[] = {
357 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
358 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
359 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
360 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
361 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
362 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
363 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
364 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
365 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
366 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
367 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
368 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
369 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
370 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
371 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
372 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
373 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
374 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
375 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
376 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
377 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
378 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
379 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
380 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
381 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
382 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
383 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
384 {"catalog", store_res, ITEM(res_pool.Catalog), R_CATALOG, 0, 0},
385 {NULL, NULL, {0}, 0, 0, 0}
390 * name handler value code flags default_value
392 static RES_ITEM counter_items[] = {
393 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
394 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
395 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
396 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
397 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
398 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
399 {NULL, NULL, {0}, 0, 0, 0}
403 /* Message resource */
404 extern RES_ITEM msgs_items[];
407 * This is the master resource definition.
408 * It must have one item for each of the resources.
410 * NOTE!!! keep it in the same order as the R_codes
411 * or eliminate all resources[rindex].name
413 * name items rcode res_head
415 RES_TABLE resources[] = {
416 {"director", dir_items, R_DIRECTOR},
417 {"client", cli_items, R_CLIENT},
418 {"job", job_items, R_JOB},
419 {"storage", store_items, R_STORAGE},
420 {"catalog", cat_items, R_CATALOG},
421 {"schedule", sch_items, R_SCHEDULE},
422 {"fileset", fs_items, R_FILESET},
423 {"pool", pool_items, R_POOL},
424 {"messages", msgs_items, R_MSGS},
425 {"counter", counter_items, R_COUNTER},
426 {"console", con_items, R_CONSOLE},
427 {"jobdefs", job_items, R_JOBDEFS},
428 {"device", NULL, R_DEVICE}, /* info obtained from SD */
433 /* Keywords (RHS) permitted in Job Level records
435 * level_name level job_type
437 struct s_jl joblevels[] = {
438 {"Full", L_FULL, JT_BACKUP},
439 {"Base", L_BASE, JT_BACKUP},
440 {"Incremental", L_INCREMENTAL, JT_BACKUP},
441 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
442 {"Since", L_SINCE, JT_BACKUP},
443 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
444 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
445 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
446 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
447 {"Data", L_VERIFY_DATA, JT_VERIFY},
448 {" ", L_NONE, JT_ADMIN},
449 {" ", L_NONE, JT_RESTORE},
453 /* Keywords (RHS) permitted in Job type records
457 struct s_jt jobtypes[] = {
458 {"backup", JT_BACKUP},
460 {"verify", JT_VERIFY},
461 {"restore", JT_RESTORE},
462 {"migrate", JT_MIGRATE},
467 /* Keywords (RHS) permitted in Selection type records
471 struct s_jt migtypes[] = {
472 {"smallestvolume", MT_SMALLEST_VOL},
473 {"oldestvolume", MT_OLDEST_VOL},
474 {"pooloccupancy", MT_POOL_OCCUPANCY},
475 {"pooltime", MT_POOL_TIME},
476 {"client", MT_CLIENT},
477 {"volume", MT_VOLUME},
479 {"sqlquery", MT_SQLQUERY},
485 /* Options permitted in Restore replace= */
486 struct s_kw ReplaceOptions[] = {
487 {"always", REPLACE_ALWAYS},
488 {"ifnewer", REPLACE_IFNEWER},
489 {"ifolder", REPLACE_IFOLDER},
490 {"never", REPLACE_NEVER},
494 const char *level_to_str(int level)
497 static char level_no[30];
498 const char *str = level_no;
500 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
501 for (i=0; joblevels[i].level_name; i++) {
502 if (level == joblevels[i].level) {
503 str = joblevels[i].level_name;
510 /* Dump contents of resource */
511 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
513 URES *res = (URES *)reshdr;
515 char ed1[100], ed2[100], ed3[100];
519 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
522 if (type < 0) { /* no recursion */
528 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
529 reshdr->name, res->res_dir.MaxConcurrentJobs,
530 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
531 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
532 if (res->res_dir.query_file) {
533 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
535 if (res->res_dir.messages) {
536 sendit(sock, _(" --> "));
537 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
541 sendit(sock, _("Console: name=%s SSL=%d\n"),
542 res->res_con.hdr.name, res->res_con.tls_enable);
545 if (res->res_counter.WrapCounter) {
546 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
547 res->res_counter.hdr.name, res->res_counter.MinValue,
548 res->res_counter.MaxValue, res->res_counter.CurrentValue,
549 res->res_counter.WrapCounter->hdr.name);
551 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
552 res->res_counter.hdr.name, res->res_counter.MinValue,
553 res->res_counter.MaxValue);
555 if (res->res_counter.Catalog) {
556 sendit(sock, _(" --> "));
557 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
562 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
563 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
564 res->res_client.MaxConcurrentJobs);
565 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
566 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
567 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
568 res->res_client.AutoPrune);
569 if (res->res_client.catalog) {
570 sendit(sock, _(" --> "));
571 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
578 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
579 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
580 " poolid=%s volname=%s MediaType=%s\n"),
581 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
582 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
583 dev->offline, dev->autochanger,
584 edit_uint64(dev->PoolId, ed1),
585 dev->VolumeName, dev->MediaType);
589 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
590 " DeviceName=%s MediaType=%s StorageId=%s\n"),
591 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
592 res->res_store.MaxConcurrentJobs,
593 res->res_store.dev_name(),
594 res->res_store.media_type,
595 edit_int64(res->res_store.StorageId, ed1));
599 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
600 " db_driver=%s db_user=%s MutliDBConn=%d\n"),
601 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
602 res->res_cat.db_port, res->res_cat.db_name,
603 NPRT(res->res_cat.db_driver), NPRT(res->res_cat.db_user),
604 res->res_cat.mult_db_connections);
609 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
610 type == R_JOB ? _("Job") : _("JobDefs"),
611 res->res_job.hdr.name, res->res_job.JobType,
612 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
613 res->res_job.enabled);
614 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
615 res->res_job.MaxConcurrentJobs,
616 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
617 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
618 res->res_job.spool_data, res->res_job.write_part_after_job);
619 if (res->res_job.spool_size) {
620 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
622 if (res->res_job.JobType == JT_BACKUP) {
623 sendit(sock, _(" Accurate=%d\n"), res->res_job.accurate);
625 if (res->res_job.JobType == JT_MIGRATE) {
626 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
628 if (res->res_job.client) {
629 sendit(sock, _(" --> "));
630 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
632 if (res->res_job.fileset) {
633 sendit(sock, _(" --> "));
634 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
636 if (res->res_job.schedule) {
637 sendit(sock, _(" --> "));
638 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
640 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
641 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
643 if (res->res_job.RegexWhere) {
644 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
646 if (res->res_job.RestoreBootstrap) {
647 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
649 if (res->res_job.WriteBootstrap) {
650 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
652 if (res->res_job.storage) {
654 foreach_alist(store, res->res_job.storage) {
655 sendit(sock, _(" --> "));
656 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
659 if (res->res_job.RunScripts) {
661 foreach_alist(script, res->res_job.RunScripts) {
662 sendit(sock, _(" --> RunScript\n"));
663 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
664 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
665 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
666 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
667 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
668 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
671 if (res->res_job.pool) {
672 sendit(sock, _(" --> "));
673 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
675 if (res->res_job.full_pool) {
676 sendit(sock, _(" --> "));
677 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
679 if (res->res_job.inc_pool) {
680 sendit(sock, _(" --> "));
681 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
683 if (res->res_job.diff_pool) {
684 sendit(sock, _(" --> "));
685 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
687 if (res->res_job.verify_job) {
688 sendit(sock, _(" --> "));
689 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
691 if (res->res_job.run_cmds) {
693 foreach_alist(runcmd, res->res_job.run_cmds) {
694 sendit(sock, _(" --> Run=%s\n"), runcmd);
697 if (res->res_job.selection_pattern) {
698 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
700 if (res->res_job.messages) {
701 sendit(sock, _(" --> "));
702 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
709 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
710 for (i=0; i<res->res_fs.num_includes; i++) {
711 INCEXE *incexe = res->res_fs.include_items[i];
712 for (j=0; j<incexe->num_opts; j++) {
713 FOPTS *fo = incexe->opts_list[j];
714 sendit(sock, " O %s\n", fo->opts);
716 bool enhanced_wild = false;
717 for (k=0; fo->opts[k]!='\0'; k++) {
718 if (fo->opts[k]=='W') {
719 enhanced_wild = true;
724 for (k=0; k<fo->regex.size(); k++) {
725 sendit(sock, " R %s\n", fo->regex.get(k));
727 for (k=0; k<fo->regexdir.size(); k++) {
728 sendit(sock, " RD %s\n", fo->regexdir.get(k));
730 for (k=0; k<fo->regexfile.size(); k++) {
731 sendit(sock, " RF %s\n", fo->regexfile.get(k));
733 for (k=0; k<fo->wild.size(); k++) {
734 sendit(sock, " W %s\n", fo->wild.get(k));
736 for (k=0; k<fo->wilddir.size(); k++) {
737 sendit(sock, " WD %s\n", fo->wilddir.get(k));
739 for (k=0; k<fo->wildfile.size(); k++) {
740 sendit(sock, " WF %s\n", fo->wildfile.get(k));
742 for (k=0; k<fo->wildbase.size(); k++) {
743 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
745 for (k=0; k<fo->base.size(); k++) {
746 sendit(sock, " B %s\n", fo->base.get(k));
748 for (k=0; k<fo->fstype.size(); k++) {
749 sendit(sock, " X %s\n", fo->fstype.get(k));
751 for (k=0; k<fo->drivetype.size(); k++) {
752 sendit(sock, " XD %s\n", fo->drivetype.get(k));
755 sendit(sock, " G %s\n", fo->plugin);
758 sendit(sock, " D %s\n", fo->reader);
761 sendit(sock, " T %s\n", fo->writer);
763 sendit(sock, " N\n");
765 for (j=0; j<incexe->name_list.size(); j++) {
766 sendit(sock, " I %s\n", incexe->name_list.get(j));
768 if (incexe->name_list.size()) {
769 sendit(sock, " N\n");
771 for (j=0; j<incexe->plugin_list.size(); j++) {
772 sendit(sock, " P %s\n", incexe->plugin_list.get(j));
774 if (incexe->plugin_list.size()) {
775 sendit(sock, " N\n");
780 for (i=0; i<res->res_fs.num_excludes; i++) {
781 INCEXE *incexe = res->res_fs.exclude_items[i];
782 for (j=0; j<incexe->name_list.size(); j++) {
783 sendit(sock, " E %s\n", incexe->name_list.get(j));
785 if (incexe->name_list.size()) {
786 sendit(sock, " N\n");
793 if (res->res_sch.run) {
795 RUN *run = res->res_sch.run;
796 char buf[1000], num[30];
797 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
802 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
803 bstrncpy(buf, _(" hour="), sizeof(buf));
804 for (i=0; i<24; i++) {
805 if (bit_is_set(i, run->hour)) {
806 bsnprintf(num, sizeof(num), "%d ", i);
807 bstrncat(buf, num, sizeof(buf));
810 bstrncat(buf, "\n", sizeof(buf));
812 bstrncpy(buf, _(" mday="), sizeof(buf));
813 for (i=0; i<31; i++) {
814 if (bit_is_set(i, run->mday)) {
815 bsnprintf(num, sizeof(num), "%d ", i);
816 bstrncat(buf, num, sizeof(buf));
819 bstrncat(buf, "\n", sizeof(buf));
821 bstrncpy(buf, _(" month="), sizeof(buf));
822 for (i=0; i<12; i++) {
823 if (bit_is_set(i, run->month)) {
824 bsnprintf(num, sizeof(num), "%d ", i);
825 bstrncat(buf, num, sizeof(buf));
828 bstrncat(buf, "\n", sizeof(buf));
830 bstrncpy(buf, _(" wday="), sizeof(buf));
831 for (i=0; i<7; i++) {
832 if (bit_is_set(i, run->wday)) {
833 bsnprintf(num, sizeof(num), "%d ", i);
834 bstrncat(buf, num, sizeof(buf));
837 bstrncat(buf, "\n", sizeof(buf));
839 bstrncpy(buf, _(" wom="), sizeof(buf));
840 for (i=0; i<5; i++) {
841 if (bit_is_set(i, run->wom)) {
842 bsnprintf(num, sizeof(num), "%d ", i);
843 bstrncat(buf, num, sizeof(buf));
846 bstrncat(buf, "\n", sizeof(buf));
848 bstrncpy(buf, _(" woy="), sizeof(buf));
849 for (i=0; i<54; i++) {
850 if (bit_is_set(i, run->woy)) {
851 bsnprintf(num, sizeof(num), "%d ", i);
852 bstrncat(buf, num, sizeof(buf));
855 bstrncat(buf, "\n", sizeof(buf));
857 sendit(sock, _(" mins=%d\n"), run->minute);
859 sendit(sock, _(" --> "));
860 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
863 sendit(sock, _(" --> "));
864 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
867 sendit(sock, _(" --> "));
868 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
870 /* If another Run record is chained in, go print it */
876 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
881 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
882 res->res_pool.pool_type);
883 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
884 res->res_pool.use_catalog, res->res_pool.use_volume_once,
885 res->res_pool.catalog_files);
886 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
887 res->res_pool.max_volumes, res->res_pool.AutoPrune,
888 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
889 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
890 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
891 res->res_pool.Recycle,
892 NPRT(res->res_pool.label_format));
893 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
894 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
895 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
896 res->res_pool.recycle_oldest_volume,
897 res->res_pool.purge_oldest_volume);
898 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
899 res->res_pool.MaxVolJobs,
900 res->res_pool.MaxVolFiles,
901 edit_uint64(res->res_pool.MaxVolFiles, ed1));
902 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
903 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
904 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
905 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
906 if (res->res_pool.NextPool) {
907 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
909 if (res->res_pool.RecyclePool) {
910 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
912 if (res->res_pool.Catalog) {
913 sendit(sock, _(" Catalog=%s\n"), res->res_pool.Catalog->name());
915 if (res->res_pool.storage) {
917 foreach_alist(store, res->res_pool.storage) {
918 sendit(sock, _(" --> "));
919 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
922 if (res->res_pool.CopyPool) {
924 foreach_alist(copy, res->res_pool.CopyPool) {
925 sendit(sock, _(" --> "));
926 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
933 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
934 if (res->res_msgs.mail_cmd)
935 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
936 if (res->res_msgs.operator_cmd)
937 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
941 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
944 if (recurse && res->res_dir.hdr.next) {
945 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
950 * Free all the members of an INCEXE structure
952 static void free_incexe(INCEXE *incexe)
954 incexe->name_list.destroy();
955 incexe->plugin_list.destroy();
956 for (int i=0; i<incexe->num_opts; i++) {
957 FOPTS *fopt = incexe->opts_list[i];
958 fopt->regex.destroy();
959 fopt->regexdir.destroy();
960 fopt->regexfile.destroy();
961 fopt->wild.destroy();
962 fopt->wilddir.destroy();
963 fopt->wildfile.destroy();
964 fopt->wildbase.destroy();
965 fopt->base.destroy();
966 fopt->fstype.destroy();
967 fopt->drivetype.destroy();
979 if (incexe->opts_list) {
980 free(incexe->opts_list);
986 * Free memory of resource -- called when daemon terminates.
987 * NB, we don't need to worry about freeing any references
988 * to other resources as they will be freed when that
989 * resource chain is traversed. Mainly we worry about freeing
990 * allocated strings (names).
992 void free_resource(RES *sres, int type)
995 RES *nres; /* next resource if linked */
996 URES *res = (URES *)sres;
1001 /* common stuff -- free the resource name and description */
1002 nres = (RES *)res->res_dir.hdr.next;
1003 if (res->res_dir.hdr.name) {
1004 free(res->res_dir.hdr.name);
1006 if (res->res_dir.hdr.desc) {
1007 free(res->res_dir.hdr.desc);
1012 if (res->res_dir.working_directory) {
1013 free(res->res_dir.working_directory);
1015 if (res->res_dir.scripts_directory) {
1016 free((char *)res->res_dir.scripts_directory);
1018 if (res->res_dir.plugin_directory) {
1019 free((char *)res->res_dir.plugin_directory);
1021 if (res->res_dir.pid_directory) {
1022 free(res->res_dir.pid_directory);
1024 if (res->res_dir.subsys_directory) {
1025 free(res->res_dir.subsys_directory);
1027 if (res->res_dir.password) {
1028 free(res->res_dir.password);
1030 if (res->res_dir.query_file) {
1031 free(res->res_dir.query_file);
1033 if (res->res_dir.DIRaddrs) {
1034 free_addresses(res->res_dir.DIRaddrs);
1036 if (res->res_dir.tls_ctx) {
1037 free_tls_context(res->res_dir.tls_ctx);
1039 if (res->res_dir.tls_ca_certfile) {
1040 free(res->res_dir.tls_ca_certfile);
1042 if (res->res_dir.tls_ca_certdir) {
1043 free(res->res_dir.tls_ca_certdir);
1045 if (res->res_dir.tls_certfile) {
1046 free(res->res_dir.tls_certfile);
1048 if (res->res_dir.tls_keyfile) {
1049 free(res->res_dir.tls_keyfile);
1051 if (res->res_dir.tls_dhfile) {
1052 free(res->res_dir.tls_dhfile);
1054 if (res->res_dir.tls_allowed_cns) {
1055 delete res->res_dir.tls_allowed_cns;
1062 if (res->res_con.password) {
1063 free(res->res_con.password);
1065 if (res->res_con.tls_ctx) {
1066 free_tls_context(res->res_con.tls_ctx);
1068 if (res->res_con.tls_ca_certfile) {
1069 free(res->res_con.tls_ca_certfile);
1071 if (res->res_con.tls_ca_certdir) {
1072 free(res->res_con.tls_ca_certdir);
1074 if (res->res_con.tls_certfile) {
1075 free(res->res_con.tls_certfile);
1077 if (res->res_con.tls_keyfile) {
1078 free(res->res_con.tls_keyfile);
1080 if (res->res_con.tls_dhfile) {
1081 free(res->res_con.tls_dhfile);
1083 if (res->res_con.tls_allowed_cns) {
1084 delete res->res_con.tls_allowed_cns;
1086 for (int i=0; i<Num_ACL; i++) {
1087 if (res->res_con.ACL_lists[i]) {
1088 delete res->res_con.ACL_lists[i];
1089 res->res_con.ACL_lists[i] = NULL;
1094 if (res->res_client.address) {
1095 free(res->res_client.address);
1097 if (res->res_client.password) {
1098 free(res->res_client.password);
1100 if (res->res_client.tls_ctx) {
1101 free_tls_context(res->res_client.tls_ctx);
1103 if (res->res_client.tls_ca_certfile) {
1104 free(res->res_client.tls_ca_certfile);
1106 if (res->res_client.tls_ca_certdir) {
1107 free(res->res_client.tls_ca_certdir);
1109 if (res->res_client.tls_certfile) {
1110 free(res->res_client.tls_certfile);
1112 if (res->res_client.tls_keyfile) {
1113 free(res->res_client.tls_keyfile);
1115 if (res->res_client.tls_allowed_cns) {
1116 delete res->res_client.tls_allowed_cns;
1120 if (res->res_store.address) {
1121 free(res->res_store.address);
1123 if (res->res_store.password) {
1124 free(res->res_store.password);
1126 if (res->res_store.media_type) {
1127 free(res->res_store.media_type);
1129 if (res->res_store.device) {
1130 delete res->res_store.device;
1132 if (res->res_store.tls_ctx) {
1133 free_tls_context(res->res_store.tls_ctx);
1135 if (res->res_store.tls_ca_certfile) {
1136 free(res->res_store.tls_ca_certfile);
1138 if (res->res_store.tls_ca_certdir) {
1139 free(res->res_store.tls_ca_certdir);
1141 if (res->res_store.tls_certfile) {
1142 free(res->res_store.tls_certfile);
1144 if (res->res_store.tls_keyfile) {
1145 free(res->res_store.tls_keyfile);
1149 if (res->res_cat.db_address) {
1150 free(res->res_cat.db_address);
1152 if (res->res_cat.db_socket) {
1153 free(res->res_cat.db_socket);
1155 if (res->res_cat.db_user) {
1156 free(res->res_cat.db_user);
1158 if (res->res_cat.db_name) {
1159 free(res->res_cat.db_name);
1161 if (res->res_cat.db_driver) {
1162 free(res->res_cat.db_driver);
1164 if (res->res_cat.db_password) {
1165 free(res->res_cat.db_password);
1169 if ((num=res->res_fs.num_includes)) {
1170 while (--num >= 0) {
1171 free_incexe(res->res_fs.include_items[num]);
1173 free(res->res_fs.include_items);
1175 res->res_fs.num_includes = 0;
1176 if ((num=res->res_fs.num_excludes)) {
1177 while (--num >= 0) {
1178 free_incexe(res->res_fs.exclude_items[num]);
1180 free(res->res_fs.exclude_items);
1182 res->res_fs.num_excludes = 0;
1185 if (res->res_pool.pool_type) {
1186 free(res->res_pool.pool_type);
1188 if (res->res_pool.label_format) {
1189 free(res->res_pool.label_format);
1191 if (res->res_pool.cleaning_prefix) {
1192 free(res->res_pool.cleaning_prefix);
1194 if (res->res_pool.storage) {
1195 delete res->res_pool.storage;
1199 if (res->res_sch.run) {
1201 nrun = res->res_sch.run;
1211 if (res->res_job.RestoreWhere) {
1212 free(res->res_job.RestoreWhere);
1214 if (res->res_job.RegexWhere) {
1215 free(res->res_job.RegexWhere);
1217 if (res->res_job.strip_prefix) {
1218 free(res->res_job.strip_prefix);
1220 if (res->res_job.add_prefix) {
1221 free(res->res_job.add_prefix);
1223 if (res->res_job.add_suffix) {
1224 free(res->res_job.add_suffix);
1226 if (res->res_job.RestoreBootstrap) {
1227 free(res->res_job.RestoreBootstrap);
1229 if (res->res_job.WriteBootstrap) {
1230 free(res->res_job.WriteBootstrap);
1232 if (res->res_job.selection_pattern) {
1233 free(res->res_job.selection_pattern);
1235 if (res->res_job.run_cmds) {
1236 delete res->res_job.run_cmds;
1238 if (res->res_job.storage) {
1239 delete res->res_job.storage;
1241 if (res->res_job.RunScripts) {
1242 free_runscripts(res->res_job.RunScripts);
1243 delete res->res_job.RunScripts;
1247 if (res->res_msgs.mail_cmd) {
1248 free(res->res_msgs.mail_cmd);
1250 if (res->res_msgs.operator_cmd) {
1251 free(res->res_msgs.operator_cmd);
1253 free_msgs_res((MSGS *)res); /* free message resource */
1257 printf(_("Unknown resource type %d in free_resource.\n"), type);
1259 /* Common stuff again -- free the resource, recurse to next one */
1264 free_resource(nres, type);
1269 * Save the new resource by chaining it into the head list for
1270 * the resource. If this is pass 2, we update any resource
1271 * pointers because they may not have been defined until
1274 void save_resource(int type, RES_ITEM *items, int pass)
1277 int rindex = type - r_first;
1281 /* Check Job requirements after applying JobDefs */
1282 if (type != R_JOB && type != R_JOBDEFS) {
1284 * Ensure that all required items are present
1286 for (i=0; items[i].name; i++) {
1287 if (items[i].flags & ITEM_REQUIRED) {
1288 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1289 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1290 items[i].name, resources[rindex]);
1293 /* If this triggers, take a look at lib/parse_conf.h */
1294 if (i >= MAX_RES_ITEMS) {
1295 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1298 } else if (type == R_JOB) {
1300 * Ensure that the name item is present
1302 if (items[0].flags & ITEM_REQUIRED) {
1303 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1304 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1305 items[0].name, resources[rindex]);
1311 * During pass 2 in each "store" routine, we looked up pointers
1312 * to all the resources referrenced in the current resource, now we
1313 * must copy their addresses from the static record to the allocated
1318 /* Resources not containing a resource */
1326 * Resources containing another resource or alist. First
1327 * look up the resource which contains another resource. It
1328 * was written during pass 1. Then stuff in the pointers to
1329 * the resources it contains, which were inserted this pass.
1330 * Finally, it will all be stored back.
1333 /* Find resource saved in pass 1 */
1334 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1335 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1337 /* Explicitly copy resource pointers from this pass (res_all) */
1338 res->res_pool.NextPool = res_all.res_pool.NextPool;
1339 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1340 res->res_pool.storage = res_all.res_pool.storage;
1341 res->res_pool.Catalog = res_all.res_pool.Catalog;
1344 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1345 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1347 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1350 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1351 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1353 res->res_dir.messages = res_all.res_dir.messages;
1354 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1357 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1358 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1359 res_all.res_dir.hdr.name);
1361 /* we must explicitly copy the device alist pointer */
1362 res->res_store.device = res_all.res_store.device;
1366 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1367 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1368 res_all.res_dir.hdr.name);
1370 res->res_job.messages = res_all.res_job.messages;
1371 res->res_job.schedule = res_all.res_job.schedule;
1372 res->res_job.client = res_all.res_job.client;
1373 res->res_job.fileset = res_all.res_job.fileset;
1374 res->res_job.storage = res_all.res_job.storage;
1375 res->res_job.pool = res_all.res_job.pool;
1376 res->res_job.full_pool = res_all.res_job.full_pool;
1377 res->res_job.inc_pool = res_all.res_job.inc_pool;
1378 res->res_job.diff_pool = res_all.res_job.diff_pool;
1379 res->res_job.verify_job = res_all.res_job.verify_job;
1380 res->res_job.jobdefs = res_all.res_job.jobdefs;
1381 res->res_job.run_cmds = res_all.res_job.run_cmds;
1382 res->res_job.RunScripts = res_all.res_job.RunScripts;
1384 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1385 * is not very useful)
1386 * We have to set_bit(index, res_all.hdr.item_present);
1387 * or something like that
1390 /* we take RegexWhere before all other options */
1391 if (!res->res_job.RegexWhere
1393 (res->res_job.strip_prefix ||
1394 res->res_job.add_suffix ||
1395 res->res_job.add_prefix))
1397 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1398 res->res_job.add_prefix,
1399 res->res_job.add_suffix);
1400 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1401 bregexp_build_where(res->res_job.RegexWhere, len,
1402 res->res_job.strip_prefix,
1403 res->res_job.add_prefix,
1404 res->res_job.add_suffix);
1405 /* TODO: test bregexp */
1408 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1409 free(res->res_job.RestoreWhere);
1410 res->res_job.RestoreWhere = NULL;
1415 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1416 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1418 res->res_counter.Catalog = res_all.res_counter.Catalog;
1419 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1423 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1424 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1426 res->res_client.catalog = res_all.res_client.catalog;
1427 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1431 * Schedule is a bit different in that it contains a RUN record
1432 * chain which isn't a "named" resource. This chain was linked
1433 * in by run_conf.c during pass 2, so here we jam the pointer
1434 * into the Schedule resource.
1436 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1437 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1439 res->res_sch.run = res_all.res_sch.run;
1442 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1446 /* Note, the resource name was already saved during pass 1,
1447 * so here, we can just release it.
1449 if (res_all.res_dir.hdr.name) {
1450 free(res_all.res_dir.hdr.name);
1451 res_all.res_dir.hdr.name = NULL;
1453 if (res_all.res_dir.hdr.desc) {
1454 free(res_all.res_dir.hdr.desc);
1455 res_all.res_dir.hdr.desc = NULL;
1461 * The following code is only executed during pass 1
1465 size = sizeof(DIRRES);
1468 size = sizeof(CONRES);
1471 size =sizeof(CLIENT);
1474 size = sizeof(STORE);
1484 size = sizeof(FILESET);
1487 size = sizeof(SCHED);
1490 size = sizeof(POOL);
1493 size = sizeof(MSGS);
1496 size = sizeof(COUNTER);
1502 printf(_("Unknown resource type %d in save_resource.\n"), type);
1508 res = (URES *)malloc(size);
1509 memcpy(res, &res_all, size);
1510 if (!res_head[rindex]) {
1511 res_head[rindex] = (RES *)res; /* store first entry */
1512 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1513 res->res_dir.hdr.name, rindex);
1516 if (res->res_dir.hdr.name == NULL) {
1517 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1520 /* Add new res to end of chain */
1521 for (last=next=res_head[rindex]; next; next=next->next) {
1523 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1524 Emsg2(M_ERROR_TERM, 0,
1525 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1526 resources[rindex].name, res->res_dir.hdr.name);
1529 last->next = (RES *)res;
1530 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1531 res->res_dir.hdr.name, rindex, pass);
1537 * Store Device. Note, the resource is created upon the
1538 * first reference. The details of the resource are obtained
1539 * later from the SD.
1541 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1545 int rindex = R_DEVICE - r_first;
1546 int size = sizeof(DEVICE);
1550 token = lex_get_token(lc, T_NAME);
1551 if (!res_head[rindex]) {
1552 res = (URES *)malloc(size);
1553 memset(res, 0, size);
1554 res->res_dev.hdr.name = bstrdup(lc->str);
1555 res_head[rindex] = (RES *)res; /* store first entry */
1556 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1557 res->res_dir.hdr.name, rindex);
1560 /* See if it is already defined */
1561 for (next=res_head[rindex]; next->next; next=next->next) {
1562 if (strcmp(next->name, lc->str) == 0) {
1568 res = (URES *)malloc(size);
1569 memset(res, 0, size);
1570 res->res_dev.hdr.name = bstrdup(lc->str);
1571 next->next = (RES *)res;
1572 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1573 res->res_dir.hdr.name, rindex, pass);
1578 set_bit(index, res_all.hdr.item_present);
1580 store_alist_res(lc, item, index, pass);
1585 * Store JobType (backup, verify, restore)
1588 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1592 token = lex_get_token(lc, T_NAME);
1593 /* Store the type both pass 1 and pass 2 */
1594 for (i=0; migtypes[i].type_name; i++) {
1595 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1596 *(int *)(item->value) = migtypes[i].job_type;
1602 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1605 set_bit(index, res_all.hdr.item_present);
1611 * Store JobType (backup, verify, restore)
1614 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1618 token = lex_get_token(lc, T_NAME);
1619 /* Store the type both pass 1 and pass 2 */
1620 for (i=0; jobtypes[i].type_name; i++) {
1621 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1622 *(int *)(item->value) = jobtypes[i].job_type;
1628 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1631 set_bit(index, res_all.hdr.item_present);
1635 * Store Job Level (Full, Incremental, ...)
1638 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1642 token = lex_get_token(lc, T_NAME);
1643 /* Store the level pass 2 so that type is defined */
1644 for (i=0; joblevels[i].level_name; i++) {
1645 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1646 *(int *)(item->value) = joblevels[i].level;
1652 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1655 set_bit(index, res_all.hdr.item_present);
1659 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1662 token = lex_get_token(lc, T_NAME);
1663 /* Scan Replacement options */
1664 for (i=0; ReplaceOptions[i].name; i++) {
1665 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1666 *(int *)(item->value) = ReplaceOptions[i].token;
1672 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1675 set_bit(index, res_all.hdr.item_present);
1679 * Store ACL (access control list)
1682 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1687 token = lex_get_token(lc, T_STRING);
1689 if (((alist **)item->value)[item->code] == NULL) {
1690 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1691 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1693 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1694 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1696 token = lex_get_token(lc, T_ALL);
1697 if (token == T_COMMA) {
1698 continue; /* get another ACL */
1702 set_bit(index, res_all.hdr.item_present);
1705 /* We build RunScripts items here */
1706 static RUNSCRIPT res_runscript;
1708 /* Store a runscript->when in a bit field */
1709 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1711 lex_get_token(lc, T_NAME);
1713 if (strcasecmp(lc->str, "before") == 0) {
1714 *(int *)(item->value) = SCRIPT_Before ;
1715 } else if (strcasecmp(lc->str, "after") == 0) {
1716 *(int *)(item->value) = SCRIPT_After;
1717 } else if (strcasecmp(lc->str, "always") == 0) {
1718 *(int *)(item->value) = SCRIPT_Any;
1720 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1725 /* Store a runscript->target
1728 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1730 lex_get_token(lc, T_STRING);
1733 if (strcmp(lc->str, "%c") == 0) {
1734 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1735 } else if (strcasecmp(lc->str, "yes") == 0) {
1736 ((RUNSCRIPT*) item->value)->set_target("%c");
1737 } else if (strcasecmp(lc->str, "no") == 0) {
1738 ((RUNSCRIPT*) item->value)->set_target("");
1740 RES *res = GetResWithName(R_CLIENT, lc->str);
1742 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1743 lc->str, lc->line_no, lc->line);
1746 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1753 * Store a runscript->command as a string
1755 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1757 lex_get_token(lc, T_STRING);
1760 ((RUNSCRIPT*)item->value)->set_command(lc->str, item->code);
1765 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1767 lex_get_token(lc, T_STRING);
1768 alist **runscripts = (alist **)(item->value) ;
1771 RUNSCRIPT *script = new_runscript();
1772 script->set_job_code_callback(job_code_callback_filesetname);
1774 script->set_command(lc->str);
1776 /* TODO: remove all script->old_proto with bacula 1.42 */
1778 if (strcmp(item->name, "runbeforejob") == 0) {
1779 script->when = SCRIPT_Before;
1780 script->fail_on_error = true;
1781 script->set_target("");
1783 } else if (strcmp(item->name, "runafterjob") == 0) {
1784 script->when = SCRIPT_After;
1785 script->on_success = true;
1786 script->on_failure = false;
1787 script->set_target("");
1789 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1790 script->old_proto = true;
1791 script->when = SCRIPT_After;
1792 script->set_target("%c");
1793 script->on_success = true;
1794 script->on_failure = false;
1796 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1797 script->old_proto = true;
1798 script->when = SCRIPT_Before;
1799 script->set_target("%c");
1800 script->fail_on_error = true;
1802 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1803 script->when = SCRIPT_After;
1804 script->on_failure = true;
1805 script->on_success = false;
1806 script->set_target("");
1809 if (*runscripts == NULL) {
1810 *runscripts = New(alist(10, not_owned_by_alist));
1813 (*runscripts)->append(script);
1820 /* Store a bool in a bit field without modifing res_all.hdr
1821 * We can also add an option to store_bool to skip res_all.hdr
1823 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1825 lex_get_token(lc, T_NAME);
1826 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1827 *(bool *)(item->value) = true;
1828 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1829 *(bool *)(item->value) = false;
1831 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1837 * new RunScript items
1838 * name handler value code flags default_value
1840 static RES_ITEM runscript_items[] = {
1841 {"command", store_runscript_cmd, {(char **)&res_runscript}, SHELL_CMD, 0, 0},
1842 {"console", store_runscript_cmd, {(char **)&res_runscript}, CONSOLE_CMD, 0, 0},
1843 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1844 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1845 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1846 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1847 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1848 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1849 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1850 {NULL, NULL, {0}, 0, 0, 0}
1854 * Store RunScript info
1856 * Note, when this routine is called, we are inside a Job
1857 * resource. We treat the RunScript like a sort of
1858 * mini-resource within the Job resource.
1860 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1863 alist **runscripts = (alist **)(item->value) ;
1865 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1867 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1869 token = lex_get_token(lc, T_SKIP_EOL);
1871 if (token != T_BOB) {
1872 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1875 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1876 if (token == T_EOB) {
1879 if (token != T_IDENTIFIER) {
1880 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1882 for (i=0; runscript_items[i].name; i++) {
1883 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1884 token = lex_get_token(lc, T_SKIP_EOL);
1885 if (token != T_EQUALS) {
1886 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1889 /* Call item handler */
1890 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1897 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1902 if (res_runscript.command == NULL) {
1903 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1904 "command", "runscript");
1907 /* run on client by default */
1908 if (res_runscript.target == NULL) {
1909 res_runscript.set_target("%c");
1912 RUNSCRIPT *script = new_runscript();
1913 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1914 script->set_job_code_callback(job_code_callback_filesetname);
1916 if (*runscripts == NULL) {
1917 *runscripts = New(alist(10, not_owned_by_alist));
1920 (*runscripts)->append(script);
1925 set_bit(index, res_all.hdr.item_present);
1928 /* callback function for edit_job_codes */
1929 extern "C" char *job_code_callback_filesetname(JCR *jcr, const char* param)
1931 if (param[0] == 'f') {
1932 return jcr->fileset->name();