2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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 {"maxfullage", store_time, ITEM(res_job.MaxFullAge), 0, 0, 0},
297 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
298 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
299 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
300 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
301 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
302 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
303 {"optimizejobscheduling",store_bool, ITEM(res_job.OptimizeJobScheduling), 0, ITEM_DEFAULT, false},
304 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
305 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
306 {"spoolsize", store_size, ITEM(res_job.spool_size), 0, 0, 0},
307 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
308 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
309 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
310 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
311 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
312 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
313 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
314 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
315 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
316 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
317 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
318 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
319 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
320 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
321 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
322 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
323 {"accurate", store_bool, ITEM(res_job.accurate), 0,0,0},
324 {NULL, NULL, {0}, 0, 0, 0}
329 * name handler value code flags default_value
331 static RES_ITEM fs_items[] = {
332 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
333 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
334 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
335 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
336 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
337 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
338 {NULL, NULL, {0}, 0, 0, 0}
341 /* Schedule -- see run_conf.c */
344 * name handler value code flags default_value
346 static RES_ITEM sch_items[] = {
347 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
348 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
349 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
350 {NULL, NULL, {0}, 0, 0, 0}
355 * name handler value code flags default_value
357 static RES_ITEM pool_items[] = {
358 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
359 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
360 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
361 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
362 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
363 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
364 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
365 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
366 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
367 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
368 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
369 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
370 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
371 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
372 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
373 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
374 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
375 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
376 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
377 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
378 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
379 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
380 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
381 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
382 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
383 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
384 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
385 {"catalog", store_res, ITEM(res_pool.catalog), R_CATALOG, 0, 0},
386 {NULL, NULL, {0}, 0, 0, 0}
391 * name handler value code flags default_value
393 static RES_ITEM counter_items[] = {
394 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
395 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
396 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
397 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
398 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
399 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
400 {NULL, NULL, {0}, 0, 0, 0}
404 /* Message resource */
405 extern RES_ITEM msgs_items[];
408 * This is the master resource definition.
409 * It must have one item for each of the resources.
411 * NOTE!!! keep it in the same order as the R_codes
412 * or eliminate all resources[rindex].name
414 * name items rcode res_head
416 RES_TABLE resources[] = {
417 {"director", dir_items, R_DIRECTOR},
418 {"client", cli_items, R_CLIENT},
419 {"job", job_items, R_JOB},
420 {"storage", store_items, R_STORAGE},
421 {"catalog", cat_items, R_CATALOG},
422 {"schedule", sch_items, R_SCHEDULE},
423 {"fileset", fs_items, R_FILESET},
424 {"pool", pool_items, R_POOL},
425 {"messages", msgs_items, R_MSGS},
426 {"counter", counter_items, R_COUNTER},
427 {"console", con_items, R_CONSOLE},
428 {"jobdefs", job_items, R_JOBDEFS},
429 {"device", NULL, R_DEVICE}, /* info obtained from SD */
434 /* Keywords (RHS) permitted in Job Level records
436 * level_name level job_type
438 struct s_jl joblevels[] = {
439 {"Full", L_FULL, JT_BACKUP},
440 {"Base", L_BASE, JT_BACKUP},
441 {"Incremental", L_INCREMENTAL, JT_BACKUP},
442 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
443 {"Since", L_SINCE, JT_BACKUP},
444 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
445 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
446 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
447 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
448 {"Data", L_VERIFY_DATA, JT_VERIFY},
449 {" ", L_NONE, JT_ADMIN},
450 {" ", L_NONE, JT_RESTORE},
454 /* Keywords (RHS) permitted in Job type records
458 struct s_jt jobtypes[] = {
459 {"backup", JT_BACKUP},
461 {"verify", JT_VERIFY},
462 {"restore", JT_RESTORE},
463 {"migrate", JT_MIGRATE},
469 /* Keywords (RHS) permitted in Selection type records
473 struct s_jt migtypes[] = {
474 {"smallestvolume", MT_SMALLEST_VOL},
475 {"oldestvolume", MT_OLDEST_VOL},
476 {"pooloccupancy", MT_POOL_OCCUPANCY},
477 {"pooltime", MT_POOL_TIME},
478 {"client", MT_CLIENT},
479 {"volume", MT_VOLUME},
481 {"sqlquery", MT_SQLQUERY},
487 /* Options permitted in Restore replace= */
488 struct s_kw ReplaceOptions[] = {
489 {"always", REPLACE_ALWAYS},
490 {"ifnewer", REPLACE_IFNEWER},
491 {"ifolder", REPLACE_IFOLDER},
492 {"never", REPLACE_NEVER},
496 const char *level_to_str(int level)
499 static char level_no[30];
500 const char *str = level_no;
502 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
503 for (i=0; joblevels[i].level_name; i++) {
504 if (level == joblevels[i].level) {
505 str = joblevels[i].level_name;
512 /* Dump contents of resource */
513 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
515 URES *res = (URES *)reshdr;
517 char ed1[100], ed2[100], ed3[100];
521 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
524 if (type < 0) { /* no recursion */
530 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
531 reshdr->name, res->res_dir.MaxConcurrentJobs,
532 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
533 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
534 if (res->res_dir.query_file) {
535 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
537 if (res->res_dir.messages) {
538 sendit(sock, _(" --> "));
539 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
543 sendit(sock, _("Console: name=%s SSL=%d\n"),
544 res->res_con.hdr.name, res->res_con.tls_enable);
547 if (res->res_counter.WrapCounter) {
548 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
549 res->res_counter.hdr.name, res->res_counter.MinValue,
550 res->res_counter.MaxValue, res->res_counter.CurrentValue,
551 res->res_counter.WrapCounter->hdr.name);
553 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
554 res->res_counter.hdr.name, res->res_counter.MinValue,
555 res->res_counter.MaxValue);
557 if (res->res_counter.Catalog) {
558 sendit(sock, _(" --> "));
559 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
564 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
565 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
566 res->res_client.MaxConcurrentJobs);
567 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
568 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
569 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
570 res->res_client.AutoPrune);
571 if (res->res_client.catalog) {
572 sendit(sock, _(" --> "));
573 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
580 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
581 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
582 " poolid=%s volname=%s MediaType=%s\n"),
583 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
584 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
585 dev->offline, dev->autochanger,
586 edit_uint64(dev->PoolId, ed1),
587 dev->VolumeName, dev->MediaType);
591 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
592 " DeviceName=%s MediaType=%s StorageId=%s\n"),
593 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
594 res->res_store.MaxConcurrentJobs,
595 res->res_store.dev_name(),
596 res->res_store.media_type,
597 edit_int64(res->res_store.StorageId, ed1));
601 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
602 " db_driver=%s db_user=%s MutliDBConn=%d\n"),
603 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
604 res->res_cat.db_port, res->res_cat.db_name,
605 NPRT(res->res_cat.db_driver), NPRT(res->res_cat.db_user),
606 res->res_cat.mult_db_connections);
611 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
612 type == R_JOB ? _("Job") : _("JobDefs"),
613 res->res_job.hdr.name, res->res_job.JobType,
614 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
615 res->res_job.enabled);
616 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
617 res->res_job.MaxConcurrentJobs,
618 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
619 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
620 res->res_job.spool_data, res->res_job.write_part_after_job);
621 if (res->res_job.spool_size) {
622 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
624 if (res->res_job.JobType == JT_BACKUP) {
625 sendit(sock, _(" Accurate=%d\n"), res->res_job.accurate);
627 if (res->res_job.JobType == JT_MIGRATE || res->res_job.JobType == JT_COPY) {
628 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
630 if (res->res_job.client) {
631 sendit(sock, _(" --> "));
632 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
634 if (res->res_job.fileset) {
635 sendit(sock, _(" --> "));
636 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
638 if (res->res_job.schedule) {
639 sendit(sock, _(" --> "));
640 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
642 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
643 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
645 if (res->res_job.RegexWhere) {
646 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
648 if (res->res_job.RestoreBootstrap) {
649 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
651 if (res->res_job.WriteBootstrap) {
652 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
654 if (res->res_job.storage) {
656 foreach_alist(store, res->res_job.storage) {
657 sendit(sock, _(" --> "));
658 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
661 if (res->res_job.RunScripts) {
663 foreach_alist(script, res->res_job.RunScripts) {
664 sendit(sock, _(" --> RunScript\n"));
665 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
666 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
667 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
668 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
669 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
670 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
673 if (res->res_job.pool) {
674 sendit(sock, _(" --> "));
675 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
677 if (res->res_job.full_pool) {
678 sendit(sock, _(" --> "));
679 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
681 if (res->res_job.inc_pool) {
682 sendit(sock, _(" --> "));
683 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
685 if (res->res_job.diff_pool) {
686 sendit(sock, _(" --> "));
687 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
689 if (res->res_job.verify_job) {
690 sendit(sock, _(" --> "));
691 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
693 if (res->res_job.run_cmds) {
695 foreach_alist(runcmd, res->res_job.run_cmds) {
696 sendit(sock, _(" --> Run=%s\n"), runcmd);
699 if (res->res_job.selection_pattern) {
700 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
702 if (res->res_job.messages) {
703 sendit(sock, _(" --> "));
704 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
711 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
712 for (i=0; i<res->res_fs.num_includes; i++) {
713 INCEXE *incexe = res->res_fs.include_items[i];
714 for (j=0; j<incexe->num_opts; j++) {
715 FOPTS *fo = incexe->opts_list[j];
716 sendit(sock, " O %s\n", fo->opts);
718 bool enhanced_wild = false;
719 for (k=0; fo->opts[k]!='\0'; k++) {
720 if (fo->opts[k]=='W') {
721 enhanced_wild = true;
726 for (k=0; k<fo->regex.size(); k++) {
727 sendit(sock, " R %s\n", fo->regex.get(k));
729 for (k=0; k<fo->regexdir.size(); k++) {
730 sendit(sock, " RD %s\n", fo->regexdir.get(k));
732 for (k=0; k<fo->regexfile.size(); k++) {
733 sendit(sock, " RF %s\n", fo->regexfile.get(k));
735 for (k=0; k<fo->wild.size(); k++) {
736 sendit(sock, " W %s\n", fo->wild.get(k));
738 for (k=0; k<fo->wilddir.size(); k++) {
739 sendit(sock, " WD %s\n", fo->wilddir.get(k));
741 for (k=0; k<fo->wildfile.size(); k++) {
742 sendit(sock, " WF %s\n", fo->wildfile.get(k));
744 for (k=0; k<fo->wildbase.size(); k++) {
745 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
747 for (k=0; k<fo->base.size(); k++) {
748 sendit(sock, " B %s\n", fo->base.get(k));
750 for (k=0; k<fo->fstype.size(); k++) {
751 sendit(sock, " X %s\n", fo->fstype.get(k));
753 for (k=0; k<fo->drivetype.size(); k++) {
754 sendit(sock, " XD %s\n", fo->drivetype.get(k));
757 sendit(sock, " G %s\n", fo->plugin);
760 sendit(sock, " D %s\n", fo->reader);
763 sendit(sock, " T %s\n", fo->writer);
765 sendit(sock, " N\n");
767 for (j=0; j<incexe->name_list.size(); j++) {
768 sendit(sock, " I %s\n", incexe->name_list.get(j));
770 if (incexe->name_list.size()) {
771 sendit(sock, " N\n");
773 for (j=0; j<incexe->plugin_list.size(); j++) {
774 sendit(sock, " P %s\n", incexe->plugin_list.get(j));
776 if (incexe->plugin_list.size()) {
777 sendit(sock, " N\n");
782 for (i=0; i<res->res_fs.num_excludes; i++) {
783 INCEXE *incexe = res->res_fs.exclude_items[i];
784 for (j=0; j<incexe->name_list.size(); j++) {
785 sendit(sock, " E %s\n", incexe->name_list.get(j));
787 if (incexe->name_list.size()) {
788 sendit(sock, " N\n");
795 if (res->res_sch.run) {
797 RUN *run = res->res_sch.run;
798 char buf[1000], num[30];
799 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
804 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
805 bstrncpy(buf, _(" hour="), sizeof(buf));
806 for (i=0; i<24; i++) {
807 if (bit_is_set(i, run->hour)) {
808 bsnprintf(num, sizeof(num), "%d ", i);
809 bstrncat(buf, num, sizeof(buf));
812 bstrncat(buf, "\n", sizeof(buf));
814 bstrncpy(buf, _(" mday="), sizeof(buf));
815 for (i=0; i<31; i++) {
816 if (bit_is_set(i, run->mday)) {
817 bsnprintf(num, sizeof(num), "%d ", i);
818 bstrncat(buf, num, sizeof(buf));
821 bstrncat(buf, "\n", sizeof(buf));
823 bstrncpy(buf, _(" month="), sizeof(buf));
824 for (i=0; i<12; i++) {
825 if (bit_is_set(i, run->month)) {
826 bsnprintf(num, sizeof(num), "%d ", i);
827 bstrncat(buf, num, sizeof(buf));
830 bstrncat(buf, "\n", sizeof(buf));
832 bstrncpy(buf, _(" wday="), sizeof(buf));
833 for (i=0; i<7; i++) {
834 if (bit_is_set(i, run->wday)) {
835 bsnprintf(num, sizeof(num), "%d ", i);
836 bstrncat(buf, num, sizeof(buf));
839 bstrncat(buf, "\n", sizeof(buf));
841 bstrncpy(buf, _(" wom="), sizeof(buf));
842 for (i=0; i<5; i++) {
843 if (bit_is_set(i, run->wom)) {
844 bsnprintf(num, sizeof(num), "%d ", i);
845 bstrncat(buf, num, sizeof(buf));
848 bstrncat(buf, "\n", sizeof(buf));
850 bstrncpy(buf, _(" woy="), sizeof(buf));
851 for (i=0; i<54; i++) {
852 if (bit_is_set(i, run->woy)) {
853 bsnprintf(num, sizeof(num), "%d ", i);
854 bstrncat(buf, num, sizeof(buf));
857 bstrncat(buf, "\n", sizeof(buf));
859 sendit(sock, _(" mins=%d\n"), run->minute);
861 sendit(sock, _(" --> "));
862 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
865 sendit(sock, _(" --> "));
866 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
869 sendit(sock, _(" --> "));
870 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
872 /* If another Run record is chained in, go print it */
878 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
883 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
884 res->res_pool.pool_type);
885 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
886 res->res_pool.use_catalog, res->res_pool.use_volume_once,
887 res->res_pool.catalog_files);
888 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
889 res->res_pool.max_volumes, res->res_pool.AutoPrune,
890 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
891 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
892 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
893 res->res_pool.Recycle,
894 NPRT(res->res_pool.label_format));
895 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
896 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
897 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
898 res->res_pool.recycle_oldest_volume,
899 res->res_pool.purge_oldest_volume);
900 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
901 res->res_pool.MaxVolJobs,
902 res->res_pool.MaxVolFiles,
903 edit_uint64(res->res_pool.MaxVolFiles, ed1));
904 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
905 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
906 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
907 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
908 if (res->res_pool.NextPool) {
909 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
911 if (res->res_pool.RecyclePool) {
912 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
914 if (res->res_pool.catalog) {
915 sendit(sock, _(" Catalog=%s\n"), res->res_pool.catalog->name());
917 if (res->res_pool.storage) {
919 foreach_alist(store, res->res_pool.storage) {
920 sendit(sock, _(" --> "));
921 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
924 if (res->res_pool.CopyPool) {
926 foreach_alist(copy, res->res_pool.CopyPool) {
927 sendit(sock, _(" --> "));
928 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
935 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
936 if (res->res_msgs.mail_cmd)
937 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
938 if (res->res_msgs.operator_cmd)
939 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
943 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
946 if (recurse && res->res_dir.hdr.next) {
947 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
952 * Free all the members of an INCEXE structure
954 static void free_incexe(INCEXE *incexe)
956 incexe->name_list.destroy();
957 incexe->plugin_list.destroy();
958 for (int i=0; i<incexe->num_opts; i++) {
959 FOPTS *fopt = incexe->opts_list[i];
960 fopt->regex.destroy();
961 fopt->regexdir.destroy();
962 fopt->regexfile.destroy();
963 fopt->wild.destroy();
964 fopt->wilddir.destroy();
965 fopt->wildfile.destroy();
966 fopt->wildbase.destroy();
967 fopt->base.destroy();
968 fopt->fstype.destroy();
969 fopt->drivetype.destroy();
981 if (incexe->opts_list) {
982 free(incexe->opts_list);
988 * Free memory of resource -- called when daemon terminates.
989 * NB, we don't need to worry about freeing any references
990 * to other resources as they will be freed when that
991 * resource chain is traversed. Mainly we worry about freeing
992 * allocated strings (names).
994 void free_resource(RES *sres, int type)
997 RES *nres; /* next resource if linked */
998 URES *res = (URES *)sres;
1003 /* common stuff -- free the resource name and description */
1004 nres = (RES *)res->res_dir.hdr.next;
1005 if (res->res_dir.hdr.name) {
1006 free(res->res_dir.hdr.name);
1008 if (res->res_dir.hdr.desc) {
1009 free(res->res_dir.hdr.desc);
1014 if (res->res_dir.working_directory) {
1015 free(res->res_dir.working_directory);
1017 if (res->res_dir.scripts_directory) {
1018 free((char *)res->res_dir.scripts_directory);
1020 if (res->res_dir.plugin_directory) {
1021 free((char *)res->res_dir.plugin_directory);
1023 if (res->res_dir.pid_directory) {
1024 free(res->res_dir.pid_directory);
1026 if (res->res_dir.subsys_directory) {
1027 free(res->res_dir.subsys_directory);
1029 if (res->res_dir.password) {
1030 free(res->res_dir.password);
1032 if (res->res_dir.query_file) {
1033 free(res->res_dir.query_file);
1035 if (res->res_dir.DIRaddrs) {
1036 free_addresses(res->res_dir.DIRaddrs);
1038 if (res->res_dir.tls_ctx) {
1039 free_tls_context(res->res_dir.tls_ctx);
1041 if (res->res_dir.tls_ca_certfile) {
1042 free(res->res_dir.tls_ca_certfile);
1044 if (res->res_dir.tls_ca_certdir) {
1045 free(res->res_dir.tls_ca_certdir);
1047 if (res->res_dir.tls_certfile) {
1048 free(res->res_dir.tls_certfile);
1050 if (res->res_dir.tls_keyfile) {
1051 free(res->res_dir.tls_keyfile);
1053 if (res->res_dir.tls_dhfile) {
1054 free(res->res_dir.tls_dhfile);
1056 if (res->res_dir.tls_allowed_cns) {
1057 delete res->res_dir.tls_allowed_cns;
1064 if (res->res_con.password) {
1065 free(res->res_con.password);
1067 if (res->res_con.tls_ctx) {
1068 free_tls_context(res->res_con.tls_ctx);
1070 if (res->res_con.tls_ca_certfile) {
1071 free(res->res_con.tls_ca_certfile);
1073 if (res->res_con.tls_ca_certdir) {
1074 free(res->res_con.tls_ca_certdir);
1076 if (res->res_con.tls_certfile) {
1077 free(res->res_con.tls_certfile);
1079 if (res->res_con.tls_keyfile) {
1080 free(res->res_con.tls_keyfile);
1082 if (res->res_con.tls_dhfile) {
1083 free(res->res_con.tls_dhfile);
1085 if (res->res_con.tls_allowed_cns) {
1086 delete res->res_con.tls_allowed_cns;
1088 for (int i=0; i<Num_ACL; i++) {
1089 if (res->res_con.ACL_lists[i]) {
1090 delete res->res_con.ACL_lists[i];
1091 res->res_con.ACL_lists[i] = NULL;
1096 if (res->res_client.address) {
1097 free(res->res_client.address);
1099 if (res->res_client.password) {
1100 free(res->res_client.password);
1102 if (res->res_client.tls_ctx) {
1103 free_tls_context(res->res_client.tls_ctx);
1105 if (res->res_client.tls_ca_certfile) {
1106 free(res->res_client.tls_ca_certfile);
1108 if (res->res_client.tls_ca_certdir) {
1109 free(res->res_client.tls_ca_certdir);
1111 if (res->res_client.tls_certfile) {
1112 free(res->res_client.tls_certfile);
1114 if (res->res_client.tls_keyfile) {
1115 free(res->res_client.tls_keyfile);
1117 if (res->res_client.tls_allowed_cns) {
1118 delete res->res_client.tls_allowed_cns;
1122 if (res->res_store.address) {
1123 free(res->res_store.address);
1125 if (res->res_store.password) {
1126 free(res->res_store.password);
1128 if (res->res_store.media_type) {
1129 free(res->res_store.media_type);
1131 if (res->res_store.device) {
1132 delete res->res_store.device;
1134 if (res->res_store.tls_ctx) {
1135 free_tls_context(res->res_store.tls_ctx);
1137 if (res->res_store.tls_ca_certfile) {
1138 free(res->res_store.tls_ca_certfile);
1140 if (res->res_store.tls_ca_certdir) {
1141 free(res->res_store.tls_ca_certdir);
1143 if (res->res_store.tls_certfile) {
1144 free(res->res_store.tls_certfile);
1146 if (res->res_store.tls_keyfile) {
1147 free(res->res_store.tls_keyfile);
1151 if (res->res_cat.db_address) {
1152 free(res->res_cat.db_address);
1154 if (res->res_cat.db_socket) {
1155 free(res->res_cat.db_socket);
1157 if (res->res_cat.db_user) {
1158 free(res->res_cat.db_user);
1160 if (res->res_cat.db_name) {
1161 free(res->res_cat.db_name);
1163 if (res->res_cat.db_driver) {
1164 free(res->res_cat.db_driver);
1166 if (res->res_cat.db_password) {
1167 free(res->res_cat.db_password);
1171 if ((num=res->res_fs.num_includes)) {
1172 while (--num >= 0) {
1173 free_incexe(res->res_fs.include_items[num]);
1175 free(res->res_fs.include_items);
1177 res->res_fs.num_includes = 0;
1178 if ((num=res->res_fs.num_excludes)) {
1179 while (--num >= 0) {
1180 free_incexe(res->res_fs.exclude_items[num]);
1182 free(res->res_fs.exclude_items);
1184 res->res_fs.num_excludes = 0;
1187 if (res->res_pool.pool_type) {
1188 free(res->res_pool.pool_type);
1190 if (res->res_pool.label_format) {
1191 free(res->res_pool.label_format);
1193 if (res->res_pool.cleaning_prefix) {
1194 free(res->res_pool.cleaning_prefix);
1196 if (res->res_pool.storage) {
1197 delete res->res_pool.storage;
1201 if (res->res_sch.run) {
1203 nrun = res->res_sch.run;
1213 if (res->res_job.RestoreWhere) {
1214 free(res->res_job.RestoreWhere);
1216 if (res->res_job.RegexWhere) {
1217 free(res->res_job.RegexWhere);
1219 if (res->res_job.strip_prefix) {
1220 free(res->res_job.strip_prefix);
1222 if (res->res_job.add_prefix) {
1223 free(res->res_job.add_prefix);
1225 if (res->res_job.add_suffix) {
1226 free(res->res_job.add_suffix);
1228 if (res->res_job.RestoreBootstrap) {
1229 free(res->res_job.RestoreBootstrap);
1231 if (res->res_job.WriteBootstrap) {
1232 free(res->res_job.WriteBootstrap);
1234 if (res->res_job.selection_pattern) {
1235 free(res->res_job.selection_pattern);
1237 if (res->res_job.run_cmds) {
1238 delete res->res_job.run_cmds;
1240 if (res->res_job.storage) {
1241 delete res->res_job.storage;
1243 if (res->res_job.RunScripts) {
1244 free_runscripts(res->res_job.RunScripts);
1245 delete res->res_job.RunScripts;
1249 if (res->res_msgs.mail_cmd) {
1250 free(res->res_msgs.mail_cmd);
1252 if (res->res_msgs.operator_cmd) {
1253 free(res->res_msgs.operator_cmd);
1255 free_msgs_res((MSGS *)res); /* free message resource */
1259 printf(_("Unknown resource type %d in free_resource.\n"), type);
1261 /* Common stuff again -- free the resource, recurse to next one */
1266 free_resource(nres, type);
1271 * Save the new resource by chaining it into the head list for
1272 * the resource. If this is pass 2, we update any resource
1273 * pointers because they may not have been defined until
1276 void save_resource(int type, RES_ITEM *items, int pass)
1279 int rindex = type - r_first;
1283 /* Check Job requirements after applying JobDefs */
1284 if (type != R_JOB && type != R_JOBDEFS) {
1286 * Ensure that all required items are present
1288 for (i=0; items[i].name; i++) {
1289 if (items[i].flags & ITEM_REQUIRED) {
1290 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1291 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1292 items[i].name, resources[rindex]);
1295 /* If this triggers, take a look at lib/parse_conf.h */
1296 if (i >= MAX_RES_ITEMS) {
1297 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1300 } else if (type == R_JOB) {
1302 * Ensure that the name item is present
1304 if (items[0].flags & ITEM_REQUIRED) {
1305 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1306 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1307 items[0].name, resources[rindex]);
1313 * During pass 2 in each "store" routine, we looked up pointers
1314 * to all the resources referrenced in the current resource, now we
1315 * must copy their addresses from the static record to the allocated
1320 /* Resources not containing a resource */
1328 * Resources containing another resource or alist. First
1329 * look up the resource which contains another resource. It
1330 * was written during pass 1. Then stuff in the pointers to
1331 * the resources it contains, which were inserted this pass.
1332 * Finally, it will all be stored back.
1335 /* Find resource saved in pass 1 */
1336 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1337 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1339 /* Explicitly copy resource pointers from this pass (res_all) */
1340 res->res_pool.NextPool = res_all.res_pool.NextPool;
1341 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1342 res->res_pool.storage = res_all.res_pool.storage;
1343 res->res_pool.catalog = res_all.res_pool.catalog;
1346 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1347 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1349 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1352 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1353 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1355 res->res_dir.messages = res_all.res_dir.messages;
1356 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1359 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1360 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1361 res_all.res_dir.hdr.name);
1363 /* we must explicitly copy the device alist pointer */
1364 res->res_store.device = res_all.res_store.device;
1368 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1369 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1370 res_all.res_dir.hdr.name);
1372 res->res_job.messages = res_all.res_job.messages;
1373 res->res_job.schedule = res_all.res_job.schedule;
1374 res->res_job.client = res_all.res_job.client;
1375 res->res_job.fileset = res_all.res_job.fileset;
1376 res->res_job.storage = res_all.res_job.storage;
1377 res->res_job.pool = res_all.res_job.pool;
1378 res->res_job.full_pool = res_all.res_job.full_pool;
1379 res->res_job.inc_pool = res_all.res_job.inc_pool;
1380 res->res_job.diff_pool = res_all.res_job.diff_pool;
1381 res->res_job.verify_job = res_all.res_job.verify_job;
1382 res->res_job.jobdefs = res_all.res_job.jobdefs;
1383 res->res_job.run_cmds = res_all.res_job.run_cmds;
1384 res->res_job.RunScripts = res_all.res_job.RunScripts;
1386 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1387 * is not very useful)
1388 * We have to set_bit(index, res_all.hdr.item_present);
1389 * or something like that
1392 /* we take RegexWhere before all other options */
1393 if (!res->res_job.RegexWhere
1395 (res->res_job.strip_prefix ||
1396 res->res_job.add_suffix ||
1397 res->res_job.add_prefix))
1399 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1400 res->res_job.add_prefix,
1401 res->res_job.add_suffix);
1402 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1403 bregexp_build_where(res->res_job.RegexWhere, len,
1404 res->res_job.strip_prefix,
1405 res->res_job.add_prefix,
1406 res->res_job.add_suffix);
1407 /* TODO: test bregexp */
1410 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1411 free(res->res_job.RestoreWhere);
1412 res->res_job.RestoreWhere = NULL;
1417 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1418 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1420 res->res_counter.Catalog = res_all.res_counter.Catalog;
1421 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1425 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1426 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1428 res->res_client.catalog = res_all.res_client.catalog;
1429 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1433 * Schedule is a bit different in that it contains a RUN record
1434 * chain which isn't a "named" resource. This chain was linked
1435 * in by run_conf.c during pass 2, so here we jam the pointer
1436 * into the Schedule resource.
1438 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1439 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1441 res->res_sch.run = res_all.res_sch.run;
1444 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1448 /* Note, the resource name was already saved during pass 1,
1449 * so here, we can just release it.
1451 if (res_all.res_dir.hdr.name) {
1452 free(res_all.res_dir.hdr.name);
1453 res_all.res_dir.hdr.name = NULL;
1455 if (res_all.res_dir.hdr.desc) {
1456 free(res_all.res_dir.hdr.desc);
1457 res_all.res_dir.hdr.desc = NULL;
1463 * The following code is only executed during pass 1
1467 size = sizeof(DIRRES);
1470 size = sizeof(CONRES);
1473 size =sizeof(CLIENT);
1476 size = sizeof(STORE);
1486 size = sizeof(FILESET);
1489 size = sizeof(SCHED);
1492 size = sizeof(POOL);
1495 size = sizeof(MSGS);
1498 size = sizeof(COUNTER);
1504 printf(_("Unknown resource type %d in save_resource.\n"), type);
1510 res = (URES *)malloc(size);
1511 memcpy(res, &res_all, size);
1512 if (!res_head[rindex]) {
1513 res_head[rindex] = (RES *)res; /* store first entry */
1514 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1515 res->res_dir.hdr.name, rindex);
1518 if (res->res_dir.hdr.name == NULL) {
1519 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1522 /* Add new res to end of chain */
1523 for (last=next=res_head[rindex]; next; next=next->next) {
1525 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1526 Emsg2(M_ERROR_TERM, 0,
1527 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1528 resources[rindex].name, res->res_dir.hdr.name);
1531 last->next = (RES *)res;
1532 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1533 res->res_dir.hdr.name, rindex, pass);
1539 * Store Device. Note, the resource is created upon the
1540 * first reference. The details of the resource are obtained
1541 * later from the SD.
1543 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1547 int rindex = R_DEVICE - r_first;
1548 int size = sizeof(DEVICE);
1552 token = lex_get_token(lc, T_NAME);
1553 if (!res_head[rindex]) {
1554 res = (URES *)malloc(size);
1555 memset(res, 0, size);
1556 res->res_dev.hdr.name = bstrdup(lc->str);
1557 res_head[rindex] = (RES *)res; /* store first entry */
1558 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1559 res->res_dir.hdr.name, rindex);
1562 /* See if it is already defined */
1563 for (next=res_head[rindex]; next->next; next=next->next) {
1564 if (strcmp(next->name, lc->str) == 0) {
1570 res = (URES *)malloc(size);
1571 memset(res, 0, size);
1572 res->res_dev.hdr.name = bstrdup(lc->str);
1573 next->next = (RES *)res;
1574 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1575 res->res_dir.hdr.name, rindex, pass);
1580 set_bit(index, res_all.hdr.item_present);
1582 store_alist_res(lc, item, index, pass);
1587 * Store JobType (backup, verify, restore)
1590 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1594 token = lex_get_token(lc, T_NAME);
1595 /* Store the type both pass 1 and pass 2 */
1596 for (i=0; migtypes[i].type_name; i++) {
1597 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1598 *(int *)(item->value) = migtypes[i].job_type;
1604 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1607 set_bit(index, res_all.hdr.item_present);
1613 * Store JobType (backup, verify, restore)
1616 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1620 token = lex_get_token(lc, T_NAME);
1621 /* Store the type both pass 1 and pass 2 */
1622 for (i=0; jobtypes[i].type_name; i++) {
1623 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1624 *(int *)(item->value) = jobtypes[i].job_type;
1630 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1633 set_bit(index, res_all.hdr.item_present);
1637 * Store Job Level (Full, Incremental, ...)
1640 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1644 token = lex_get_token(lc, T_NAME);
1645 /* Store the level pass 2 so that type is defined */
1646 for (i=0; joblevels[i].level_name; i++) {
1647 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1648 *(int *)(item->value) = joblevels[i].level;
1654 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1657 set_bit(index, res_all.hdr.item_present);
1661 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1664 token = lex_get_token(lc, T_NAME);
1665 /* Scan Replacement options */
1666 for (i=0; ReplaceOptions[i].name; i++) {
1667 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1668 *(int *)(item->value) = ReplaceOptions[i].token;
1674 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1677 set_bit(index, res_all.hdr.item_present);
1681 * Store ACL (access control list)
1684 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1689 token = lex_get_token(lc, T_STRING);
1691 if (((alist **)item->value)[item->code] == NULL) {
1692 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1693 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1695 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1696 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1698 token = lex_get_token(lc, T_ALL);
1699 if (token == T_COMMA) {
1700 continue; /* get another ACL */
1704 set_bit(index, res_all.hdr.item_present);
1707 /* We build RunScripts items here */
1708 static RUNSCRIPT res_runscript;
1710 /* Store a runscript->when in a bit field */
1711 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1713 lex_get_token(lc, T_NAME);
1715 if (strcasecmp(lc->str, "before") == 0) {
1716 *(int *)(item->value) = SCRIPT_Before ;
1717 } else if (strcasecmp(lc->str, "after") == 0) {
1718 *(int *)(item->value) = SCRIPT_After;
1719 } else if (strcasecmp(lc->str, "always") == 0) {
1720 *(int *)(item->value) = SCRIPT_Any;
1722 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1727 /* Store a runscript->target
1730 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1732 lex_get_token(lc, T_STRING);
1735 if (strcmp(lc->str, "%c") == 0) {
1736 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1737 } else if (strcasecmp(lc->str, "yes") == 0) {
1738 ((RUNSCRIPT*) item->value)->set_target("%c");
1739 } else if (strcasecmp(lc->str, "no") == 0) {
1740 ((RUNSCRIPT*) item->value)->set_target("");
1742 RES *res = GetResWithName(R_CLIENT, lc->str);
1744 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1745 lc->str, lc->line_no, lc->line);
1748 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1755 * Store a runscript->command as a string
1757 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1759 lex_get_token(lc, T_STRING);
1762 ((RUNSCRIPT*)item->value)->set_command(lc->str, item->code);
1767 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1769 lex_get_token(lc, T_STRING);
1770 alist **runscripts = (alist **)(item->value) ;
1773 RUNSCRIPT *script = new_runscript();
1774 script->set_job_code_callback(job_code_callback_filesetname);
1776 script->set_command(lc->str);
1778 /* TODO: remove all script->old_proto with bacula 1.42 */
1780 if (strcmp(item->name, "runbeforejob") == 0) {
1781 script->when = SCRIPT_Before;
1782 script->fail_on_error = true;
1783 script->set_target("");
1785 } else if (strcmp(item->name, "runafterjob") == 0) {
1786 script->when = SCRIPT_After;
1787 script->on_success = true;
1788 script->on_failure = false;
1789 script->set_target("");
1791 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1792 script->old_proto = true;
1793 script->when = SCRIPT_After;
1794 script->set_target("%c");
1795 script->on_success = true;
1796 script->on_failure = false;
1798 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1799 script->old_proto = true;
1800 script->when = SCRIPT_Before;
1801 script->set_target("%c");
1802 script->fail_on_error = true;
1804 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1805 script->when = SCRIPT_After;
1806 script->on_failure = true;
1807 script->on_success = false;
1808 script->set_target("");
1811 if (*runscripts == NULL) {
1812 *runscripts = New(alist(10, not_owned_by_alist));
1815 (*runscripts)->append(script);
1822 /* Store a bool in a bit field without modifing res_all.hdr
1823 * We can also add an option to store_bool to skip res_all.hdr
1825 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1827 lex_get_token(lc, T_NAME);
1828 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1829 *(bool *)(item->value) = true;
1830 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1831 *(bool *)(item->value) = false;
1833 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1839 * new RunScript items
1840 * name handler value code flags default_value
1842 static RES_ITEM runscript_items[] = {
1843 {"command", store_runscript_cmd, {(char **)&res_runscript}, SHELL_CMD, 0, 0},
1844 {"console", store_runscript_cmd, {(char **)&res_runscript}, CONSOLE_CMD, 0, 0},
1845 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1846 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1847 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1848 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1849 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1850 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1851 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1852 {NULL, NULL, {0}, 0, 0, 0}
1856 * Store RunScript info
1858 * Note, when this routine is called, we are inside a Job
1859 * resource. We treat the RunScript like a sort of
1860 * mini-resource within the Job resource.
1862 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1865 alist **runscripts = (alist **)(item->value) ;
1867 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1869 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1871 token = lex_get_token(lc, T_SKIP_EOL);
1873 if (token != T_BOB) {
1874 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1877 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1878 if (token == T_EOB) {
1881 if (token != T_IDENTIFIER) {
1882 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1884 for (i=0; runscript_items[i].name; i++) {
1885 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1886 token = lex_get_token(lc, T_SKIP_EOL);
1887 if (token != T_EQUALS) {
1888 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1891 /* Call item handler */
1892 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1899 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1904 if (res_runscript.command == NULL) {
1905 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1906 "command", "runscript");
1909 /* run on client by default */
1910 if (res_runscript.target == NULL) {
1911 res_runscript.set_target("%c");
1914 RUNSCRIPT *script = new_runscript();
1915 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1916 script->set_job_code_callback(job_code_callback_filesetname);
1918 if (*runscripts == NULL) {
1919 *runscripts = New(alist(10, not_owned_by_alist));
1922 (*runscripts)->append(script);
1927 set_bit(index, res_all.hdr.item_present);
1930 /* callback function for edit_job_codes */
1931 extern "C" char *job_code_callback_filesetname(JCR *jcr, const char* param)
1933 if (param[0] == 'f') {
1934 return jcr->fileset->name();