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 {"dbsocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0},
249 /* Turned off for the moment */
250 {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
251 {NULL, NULL, {0}, 0, 0, 0}
255 * Job Resource Directives
257 * name handler value code flags default_value
259 RES_ITEM job_items[] = {
260 {"name", store_name, ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
261 {"description", store_str, ITEM(res_job.hdr.desc), 0, 0, 0},
262 {"type", store_jobtype, ITEM(res_job.JobType), 0, ITEM_REQUIRED, 0},
263 {"level", store_level, ITEM(res_job.JobLevel), 0, 0, 0},
264 {"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
265 {"storage", store_alist_res, ITEM(res_job.storage), R_STORAGE, 0, 0},
266 {"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
267 {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
268 {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
269 {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
270 {"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
271 {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
272 {"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
273 {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
274 {"jobtoverify", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
275 {"jobdefs", store_res, ITEM(res_job.jobdefs), R_JOBDEFS, 0, 0},
276 {"run", store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
277 /* Root of where to restore files */
278 {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
279 {"regexwhere", store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
280 {"stripprefix", store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
281 {"addprefix", store_str, ITEM(res_job.add_prefix), 0, 0, 0},
282 {"addsuffix", store_str, ITEM(res_job.add_suffix), 0, 0, 0},
283 /* Where to find bootstrap during restore */
284 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
285 /* Where to write bootstrap file during backup */
286 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
287 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
288 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
289 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
290 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
291 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
292 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
293 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
294 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
295 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
296 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
297 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
298 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
299 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
300 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
301 {"optimizejobscheduling",store_bool, ITEM(res_job.OptimizeJobScheduling), 0, ITEM_DEFAULT, false},
302 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
303 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
304 {"spoolsize", store_size, ITEM(res_job.spool_size), 0, 0, 0},
305 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
306 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
307 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
308 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
309 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
310 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
311 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
312 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
313 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
314 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
315 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
316 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
317 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
318 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
319 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
320 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
321 {NULL, NULL, {0}, 0, 0, 0}
326 * name handler value code flags default_value
328 static RES_ITEM fs_items[] = {
329 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
330 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
331 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
332 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
333 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
334 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
335 {NULL, NULL, {0}, 0, 0, 0}
338 /* Schedule -- see run_conf.c */
341 * name handler value code flags default_value
343 static RES_ITEM sch_items[] = {
344 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
345 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
346 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
347 {NULL, NULL, {0}, 0, 0, 0}
352 * name handler value code flags default_value
354 static RES_ITEM pool_items[] = {
355 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
356 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
357 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
358 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
359 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
360 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
361 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
362 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
363 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
364 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
365 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
366 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
367 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
368 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
369 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
370 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
371 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
372 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
373 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
374 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
375 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
376 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
377 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
378 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
379 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
380 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
381 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
382 {"catalog", store_res, ITEM(res_pool.Catalog), R_CATALOG, 0, 0},
383 {NULL, NULL, {0}, 0, 0, 0}
388 * name handler value code flags default_value
390 static RES_ITEM counter_items[] = {
391 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
392 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
393 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
394 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
395 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
396 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
397 {NULL, NULL, {0}, 0, 0, 0}
401 /* Message resource */
402 extern RES_ITEM msgs_items[];
405 * This is the master resource definition.
406 * It must have one item for each of the resources.
408 * NOTE!!! keep it in the same order as the R_codes
409 * or eliminate all resources[rindex].name
411 * name items rcode res_head
413 RES_TABLE resources[] = {
414 {"director", dir_items, R_DIRECTOR},
415 {"client", cli_items, R_CLIENT},
416 {"job", job_items, R_JOB},
417 {"storage", store_items, R_STORAGE},
418 {"catalog", cat_items, R_CATALOG},
419 {"schedule", sch_items, R_SCHEDULE},
420 {"fileset", fs_items, R_FILESET},
421 {"pool", pool_items, R_POOL},
422 {"messages", msgs_items, R_MSGS},
423 {"counter", counter_items, R_COUNTER},
424 {"console", con_items, R_CONSOLE},
425 {"jobdefs", job_items, R_JOBDEFS},
426 {"device", NULL, R_DEVICE}, /* info obtained from SD */
431 /* Keywords (RHS) permitted in Job Level records
433 * level_name level job_type
435 struct s_jl joblevels[] = {
436 {"Full", L_FULL, JT_BACKUP},
437 {"Base", L_BASE, JT_BACKUP},
438 {"Incremental", L_INCREMENTAL, JT_BACKUP},
439 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
440 {"Since", L_SINCE, JT_BACKUP},
441 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
442 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
443 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
444 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
445 {"Data", L_VERIFY_DATA, JT_VERIFY},
446 {" ", L_NONE, JT_ADMIN},
447 {" ", L_NONE, JT_RESTORE},
451 /* Keywords (RHS) permitted in Job type records
455 struct s_jt jobtypes[] = {
456 {"backup", JT_BACKUP},
458 {"verify", JT_VERIFY},
459 {"restore", JT_RESTORE},
460 {"migrate", JT_MIGRATE},
465 /* Keywords (RHS) permitted in Selection type records
469 struct s_jt migtypes[] = {
470 {"smallestvolume", MT_SMALLEST_VOL},
471 {"oldestvolume", MT_OLDEST_VOL},
472 {"pooloccupancy", MT_POOL_OCCUPANCY},
473 {"pooltime", MT_POOL_TIME},
474 {"client", MT_CLIENT},
475 {"volume", MT_VOLUME},
477 {"sqlquery", MT_SQLQUERY},
483 /* Options permitted in Restore replace= */
484 struct s_kw ReplaceOptions[] = {
485 {"always", REPLACE_ALWAYS},
486 {"ifnewer", REPLACE_IFNEWER},
487 {"ifolder", REPLACE_IFOLDER},
488 {"never", REPLACE_NEVER},
492 const char *level_to_str(int level)
495 static char level_no[30];
496 const char *str = level_no;
498 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
499 for (i=0; joblevels[i].level_name; i++) {
500 if (level == joblevels[i].level) {
501 str = joblevels[i].level_name;
508 /* Dump contents of resource */
509 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
511 URES *res = (URES *)reshdr;
513 char ed1[100], ed2[100], ed3[100];
517 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
520 if (type < 0) { /* no recursion */
526 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
527 reshdr->name, res->res_dir.MaxConcurrentJobs,
528 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
529 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
530 if (res->res_dir.query_file) {
531 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
533 if (res->res_dir.messages) {
534 sendit(sock, _(" --> "));
535 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
539 sendit(sock, _("Console: name=%s SSL=%d\n"),
540 res->res_con.hdr.name, res->res_con.tls_enable);
543 if (res->res_counter.WrapCounter) {
544 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
545 res->res_counter.hdr.name, res->res_counter.MinValue,
546 res->res_counter.MaxValue, res->res_counter.CurrentValue,
547 res->res_counter.WrapCounter->hdr.name);
549 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
550 res->res_counter.hdr.name, res->res_counter.MinValue,
551 res->res_counter.MaxValue);
553 if (res->res_counter.Catalog) {
554 sendit(sock, _(" --> "));
555 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
560 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
561 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
562 res->res_client.MaxConcurrentJobs);
563 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
564 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
565 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
566 res->res_client.AutoPrune);
567 if (res->res_client.catalog) {
568 sendit(sock, _(" --> "));
569 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
576 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
577 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
578 " poolid=%s volname=%s MediaType=%s\n"),
579 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
580 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
581 dev->offline, dev->autochanger,
582 edit_uint64(dev->PoolId, ed1),
583 dev->VolumeName, dev->MediaType);
587 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
588 " DeviceName=%s MediaType=%s StorageId=%s\n"),
589 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
590 res->res_store.MaxConcurrentJobs,
591 res->res_store.dev_name(),
592 res->res_store.media_type,
593 edit_int64(res->res_store.StorageId, ed1));
597 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
598 " db_user=%s MutliDBConn=%d\n"),
599 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
600 res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
601 res->res_cat.mult_db_connections);
606 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
607 type == R_JOB ? _("Job") : _("JobDefs"),
608 res->res_job.hdr.name, res->res_job.JobType,
609 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
610 res->res_job.enabled);
611 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
612 res->res_job.MaxConcurrentJobs,
613 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
614 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
615 res->res_job.spool_data, res->res_job.write_part_after_job);
616 if (res->res_job.spool_size) {
617 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
619 if (res->res_job.JobType == JT_MIGRATE) {
620 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
622 if (res->res_job.client) {
623 sendit(sock, _(" --> "));
624 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
626 if (res->res_job.fileset) {
627 sendit(sock, _(" --> "));
628 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
630 if (res->res_job.schedule) {
631 sendit(sock, _(" --> "));
632 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
634 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
635 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
637 if (res->res_job.RegexWhere) {
638 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
640 if (res->res_job.RestoreBootstrap) {
641 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
643 if (res->res_job.WriteBootstrap) {
644 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
646 if (res->res_job.storage) {
648 foreach_alist(store, res->res_job.storage) {
649 sendit(sock, _(" --> "));
650 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
653 if (res->res_job.RunScripts) {
655 foreach_alist(script, res->res_job.RunScripts) {
656 sendit(sock, _(" --> RunScript\n"));
657 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
658 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
659 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
660 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
661 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
662 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
665 if (res->res_job.pool) {
666 sendit(sock, _(" --> "));
667 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
669 if (res->res_job.full_pool) {
670 sendit(sock, _(" --> "));
671 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
673 if (res->res_job.inc_pool) {
674 sendit(sock, _(" --> "));
675 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
677 if (res->res_job.diff_pool) {
678 sendit(sock, _(" --> "));
679 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
681 if (res->res_job.verify_job) {
682 sendit(sock, _(" --> "));
683 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
685 if (res->res_job.run_cmds) {
687 foreach_alist(runcmd, res->res_job.run_cmds) {
688 sendit(sock, _(" --> Run=%s\n"), runcmd);
691 if (res->res_job.selection_pattern) {
692 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
694 if (res->res_job.messages) {
695 sendit(sock, _(" --> "));
696 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
703 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
704 for (i=0; i<res->res_fs.num_includes; i++) {
705 INCEXE *incexe = res->res_fs.include_items[i];
706 for (j=0; j<incexe->num_opts; j++) {
707 FOPTS *fo = incexe->opts_list[j];
708 sendit(sock, " O %s\n", fo->opts);
710 bool enhanced_wild = false;
711 for (k=0; fo->opts[k]!='\0'; k++) {
712 if (fo->opts[k]=='W') {
713 enhanced_wild = true;
718 for (k=0; k<fo->regex.size(); k++) {
719 sendit(sock, " R %s\n", fo->regex.get(k));
721 for (k=0; k<fo->regexdir.size(); k++) {
722 sendit(sock, " RD %s\n", fo->regexdir.get(k));
724 for (k=0; k<fo->regexfile.size(); k++) {
725 sendit(sock, " RF %s\n", fo->regexfile.get(k));
727 for (k=0; k<fo->wild.size(); k++) {
728 sendit(sock, " W %s\n", fo->wild.get(k));
730 for (k=0; k<fo->wilddir.size(); k++) {
731 sendit(sock, " WD %s\n", fo->wilddir.get(k));
733 for (k=0; k<fo->wildfile.size(); k++) {
734 sendit(sock, " WF %s\n", fo->wildfile.get(k));
736 for (k=0; k<fo->wildbase.size(); k++) {
737 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
739 for (k=0; k<fo->base.size(); k++) {
740 sendit(sock, " B %s\n", fo->base.get(k));
742 for (k=0; k<fo->fstype.size(); k++) {
743 sendit(sock, " X %s\n", fo->fstype.get(k));
745 for (k=0; k<fo->drivetype.size(); k++) {
746 sendit(sock, " XD %s\n", fo->drivetype.get(k));
749 sendit(sock, " D %s\n", fo->reader);
752 sendit(sock, " T %s\n", fo->writer);
754 sendit(sock, " N\n");
756 for (j=0; j<incexe->name_list.size(); j++) {
757 sendit(sock, " I %s\n", incexe->name_list.get(j));
759 if (incexe->name_list.size()) {
760 sendit(sock, " N\n");
764 for (i=0; i<res->res_fs.num_excludes; i++) {
765 INCEXE *incexe = res->res_fs.exclude_items[i];
766 for (j=0; j<incexe->name_list.size(); j++) {
767 sendit(sock, " E %s\n", incexe->name_list.get(j));
769 if (incexe->name_list.size()) {
770 sendit(sock, " N\n");
777 if (res->res_sch.run) {
779 RUN *run = res->res_sch.run;
780 char buf[1000], num[30];
781 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
786 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
787 bstrncpy(buf, _(" hour="), sizeof(buf));
788 for (i=0; i<24; i++) {
789 if (bit_is_set(i, run->hour)) {
790 bsnprintf(num, sizeof(num), "%d ", i);
791 bstrncat(buf, num, sizeof(buf));
794 bstrncat(buf, "\n", sizeof(buf));
796 bstrncpy(buf, _(" mday="), sizeof(buf));
797 for (i=0; i<31; i++) {
798 if (bit_is_set(i, run->mday)) {
799 bsnprintf(num, sizeof(num), "%d ", i);
800 bstrncat(buf, num, sizeof(buf));
803 bstrncat(buf, "\n", sizeof(buf));
805 bstrncpy(buf, _(" month="), sizeof(buf));
806 for (i=0; i<12; i++) {
807 if (bit_is_set(i, run->month)) {
808 bsnprintf(num, sizeof(num), "%d ", i);
809 bstrncat(buf, num, sizeof(buf));
812 bstrncat(buf, "\n", sizeof(buf));
814 bstrncpy(buf, _(" wday="), sizeof(buf));
815 for (i=0; i<7; i++) {
816 if (bit_is_set(i, run->wday)) {
817 bsnprintf(num, sizeof(num), "%d ", i);
818 bstrncat(buf, num, sizeof(buf));
821 bstrncat(buf, "\n", sizeof(buf));
823 bstrncpy(buf, _(" wom="), sizeof(buf));
824 for (i=0; i<5; i++) {
825 if (bit_is_set(i, run->wom)) {
826 bsnprintf(num, sizeof(num), "%d ", i);
827 bstrncat(buf, num, sizeof(buf));
830 bstrncat(buf, "\n", sizeof(buf));
832 bstrncpy(buf, _(" woy="), sizeof(buf));
833 for (i=0; i<54; i++) {
834 if (bit_is_set(i, run->woy)) {
835 bsnprintf(num, sizeof(num), "%d ", i);
836 bstrncat(buf, num, sizeof(buf));
839 bstrncat(buf, "\n", sizeof(buf));
841 sendit(sock, _(" mins=%d\n"), run->minute);
843 sendit(sock, _(" --> "));
844 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
847 sendit(sock, _(" --> "));
848 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
851 sendit(sock, _(" --> "));
852 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
854 /* If another Run record is chained in, go print it */
860 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
865 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
866 res->res_pool.pool_type);
867 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
868 res->res_pool.use_catalog, res->res_pool.use_volume_once,
869 res->res_pool.catalog_files);
870 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
871 res->res_pool.max_volumes, res->res_pool.AutoPrune,
872 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
873 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
874 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
875 res->res_pool.Recycle,
876 NPRT(res->res_pool.label_format));
877 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
878 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
879 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
880 res->res_pool.recycle_oldest_volume,
881 res->res_pool.purge_oldest_volume);
882 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
883 res->res_pool.MaxVolJobs,
884 res->res_pool.MaxVolFiles,
885 edit_uint64(res->res_pool.MaxVolFiles, ed1));
886 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
887 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
888 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
889 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
890 if (res->res_pool.NextPool) {
891 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
893 if (res->res_pool.RecyclePool) {
894 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
896 if (res->res_pool.Catalog) {
897 sendit(sock, _(" Catalog=%s\n"), res->res_pool.Catalog->name());
899 if (res->res_pool.storage) {
901 foreach_alist(store, res->res_pool.storage) {
902 sendit(sock, _(" --> "));
903 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
906 if (res->res_pool.CopyPool) {
908 foreach_alist(copy, res->res_pool.CopyPool) {
909 sendit(sock, _(" --> "));
910 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
917 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
918 if (res->res_msgs.mail_cmd)
919 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
920 if (res->res_msgs.operator_cmd)
921 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
925 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
928 if (recurse && res->res_dir.hdr.next) {
929 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
934 * Free all the members of an INCEXE structure
936 static void free_incexe(INCEXE *incexe)
938 incexe->name_list.destroy();
939 for (int i=0; i<incexe->num_opts; i++) {
940 FOPTS *fopt = incexe->opts_list[i];
941 fopt->regex.destroy();
942 fopt->regexdir.destroy();
943 fopt->regexfile.destroy();
944 fopt->wild.destroy();
945 fopt->wilddir.destroy();
946 fopt->wildfile.destroy();
947 fopt->wildbase.destroy();
948 fopt->base.destroy();
949 fopt->fstype.destroy();
950 fopt->drivetype.destroy();
959 if (incexe->opts_list) {
960 free(incexe->opts_list);
966 * Free memory of resource -- called when daemon terminates.
967 * NB, we don't need to worry about freeing any references
968 * to other resources as they will be freed when that
969 * resource chain is traversed. Mainly we worry about freeing
970 * allocated strings (names).
972 void free_resource(RES *sres, int type)
975 RES *nres; /* next resource if linked */
976 URES *res = (URES *)sres;
981 /* common stuff -- free the resource name and description */
982 nres = (RES *)res->res_dir.hdr.next;
983 if (res->res_dir.hdr.name) {
984 free(res->res_dir.hdr.name);
986 if (res->res_dir.hdr.desc) {
987 free(res->res_dir.hdr.desc);
992 if (res->res_dir.working_directory) {
993 free(res->res_dir.working_directory);
995 if (res->res_dir.scripts_directory) {
996 free((char *)res->res_dir.scripts_directory);
998 if (res->res_dir.pid_directory) {
999 free(res->res_dir.pid_directory);
1001 if (res->res_dir.subsys_directory) {
1002 free(res->res_dir.subsys_directory);
1004 if (res->res_dir.password) {
1005 free(res->res_dir.password);
1007 if (res->res_dir.query_file) {
1008 free(res->res_dir.query_file);
1010 if (res->res_dir.DIRaddrs) {
1011 free_addresses(res->res_dir.DIRaddrs);
1013 if (res->res_dir.tls_ctx) {
1014 free_tls_context(res->res_dir.tls_ctx);
1016 if (res->res_dir.tls_ca_certfile) {
1017 free(res->res_dir.tls_ca_certfile);
1019 if (res->res_dir.tls_ca_certdir) {
1020 free(res->res_dir.tls_ca_certdir);
1022 if (res->res_dir.tls_certfile) {
1023 free(res->res_dir.tls_certfile);
1025 if (res->res_dir.tls_keyfile) {
1026 free(res->res_dir.tls_keyfile);
1028 if (res->res_dir.tls_dhfile) {
1029 free(res->res_dir.tls_dhfile);
1031 if (res->res_dir.tls_allowed_cns) {
1032 delete res->res_dir.tls_allowed_cns;
1039 if (res->res_con.password) {
1040 free(res->res_con.password);
1042 if (res->res_con.tls_ctx) {
1043 free_tls_context(res->res_con.tls_ctx);
1045 if (res->res_con.tls_ca_certfile) {
1046 free(res->res_con.tls_ca_certfile);
1048 if (res->res_con.tls_ca_certdir) {
1049 free(res->res_con.tls_ca_certdir);
1051 if (res->res_con.tls_certfile) {
1052 free(res->res_con.tls_certfile);
1054 if (res->res_con.tls_keyfile) {
1055 free(res->res_con.tls_keyfile);
1057 if (res->res_con.tls_dhfile) {
1058 free(res->res_con.tls_dhfile);
1060 if (res->res_con.tls_allowed_cns) {
1061 delete res->res_con.tls_allowed_cns;
1063 for (int i=0; i<Num_ACL; i++) {
1064 if (res->res_con.ACL_lists[i]) {
1065 delete res->res_con.ACL_lists[i];
1066 res->res_con.ACL_lists[i] = NULL;
1071 if (res->res_client.address) {
1072 free(res->res_client.address);
1074 if (res->res_client.password) {
1075 free(res->res_client.password);
1077 if (res->res_client.tls_ctx) {
1078 free_tls_context(res->res_client.tls_ctx);
1080 if (res->res_client.tls_ca_certfile) {
1081 free(res->res_client.tls_ca_certfile);
1083 if (res->res_client.tls_ca_certdir) {
1084 free(res->res_client.tls_ca_certdir);
1086 if (res->res_client.tls_certfile) {
1087 free(res->res_client.tls_certfile);
1089 if (res->res_client.tls_keyfile) {
1090 free(res->res_client.tls_keyfile);
1092 if (res->res_client.tls_allowed_cns) {
1093 delete res->res_client.tls_allowed_cns;
1097 if (res->res_store.address) {
1098 free(res->res_store.address);
1100 if (res->res_store.password) {
1101 free(res->res_store.password);
1103 if (res->res_store.media_type) {
1104 free(res->res_store.media_type);
1106 if (res->res_store.device) {
1107 delete res->res_store.device;
1109 if (res->res_store.tls_ctx) {
1110 free_tls_context(res->res_store.tls_ctx);
1112 if (res->res_store.tls_ca_certfile) {
1113 free(res->res_store.tls_ca_certfile);
1115 if (res->res_store.tls_ca_certdir) {
1116 free(res->res_store.tls_ca_certdir);
1118 if (res->res_store.tls_certfile) {
1119 free(res->res_store.tls_certfile);
1121 if (res->res_store.tls_keyfile) {
1122 free(res->res_store.tls_keyfile);
1126 if (res->res_cat.db_address) {
1127 free(res->res_cat.db_address);
1129 if (res->res_cat.db_socket) {
1130 free(res->res_cat.db_socket);
1132 if (res->res_cat.db_user) {
1133 free(res->res_cat.db_user);
1135 if (res->res_cat.db_name) {
1136 free(res->res_cat.db_name);
1138 if (res->res_cat.db_password) {
1139 free(res->res_cat.db_password);
1143 if ((num=res->res_fs.num_includes)) {
1144 while (--num >= 0) {
1145 free_incexe(res->res_fs.include_items[num]);
1147 free(res->res_fs.include_items);
1149 res->res_fs.num_includes = 0;
1150 if ((num=res->res_fs.num_excludes)) {
1151 while (--num >= 0) {
1152 free_incexe(res->res_fs.exclude_items[num]);
1154 free(res->res_fs.exclude_items);
1156 res->res_fs.num_excludes = 0;
1159 if (res->res_pool.pool_type) {
1160 free(res->res_pool.pool_type);
1162 if (res->res_pool.label_format) {
1163 free(res->res_pool.label_format);
1165 if (res->res_pool.cleaning_prefix) {
1166 free(res->res_pool.cleaning_prefix);
1168 if (res->res_pool.storage) {
1169 delete res->res_pool.storage;
1173 if (res->res_sch.run) {
1175 nrun = res->res_sch.run;
1185 if (res->res_job.RestoreWhere) {
1186 free(res->res_job.RestoreWhere);
1188 if (res->res_job.RegexWhere) {
1189 free(res->res_job.RegexWhere);
1191 if (res->res_job.strip_prefix) {
1192 free(res->res_job.strip_prefix);
1194 if (res->res_job.add_prefix) {
1195 free(res->res_job.add_prefix);
1197 if (res->res_job.add_suffix) {
1198 free(res->res_job.add_suffix);
1200 if (res->res_job.RestoreBootstrap) {
1201 free(res->res_job.RestoreBootstrap);
1203 if (res->res_job.WriteBootstrap) {
1204 free(res->res_job.WriteBootstrap);
1206 if (res->res_job.selection_pattern) {
1207 free(res->res_job.selection_pattern);
1209 if (res->res_job.run_cmds) {
1210 delete res->res_job.run_cmds;
1212 if (res->res_job.storage) {
1213 delete res->res_job.storage;
1215 if (res->res_job.RunScripts) {
1216 free_runscripts(res->res_job.RunScripts);
1217 delete res->res_job.RunScripts;
1221 if (res->res_msgs.mail_cmd) {
1222 free(res->res_msgs.mail_cmd);
1224 if (res->res_msgs.operator_cmd) {
1225 free(res->res_msgs.operator_cmd);
1227 free_msgs_res((MSGS *)res); /* free message resource */
1231 printf(_("Unknown resource type %d in free_resource.\n"), type);
1233 /* Common stuff again -- free the resource, recurse to next one */
1238 free_resource(nres, type);
1243 * Save the new resource by chaining it into the head list for
1244 * the resource. If this is pass 2, we update any resource
1245 * pointers because they may not have been defined until
1248 void save_resource(int type, RES_ITEM *items, int pass)
1251 int rindex = type - r_first;
1255 /* Check Job requirements after applying JobDefs */
1256 if (type != R_JOB && type != R_JOBDEFS) {
1258 * Ensure that all required items are present
1260 for (i=0; items[i].name; i++) {
1261 if (items[i].flags & ITEM_REQUIRED) {
1262 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1263 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1264 items[i].name, resources[rindex]);
1267 /* If this triggers, take a look at lib/parse_conf.h */
1268 if (i >= MAX_RES_ITEMS) {
1269 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1272 } else if (type == R_JOB) {
1274 * Ensure that the name item is present
1276 if (items[0].flags & ITEM_REQUIRED) {
1277 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1278 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1279 items[0].name, resources[rindex]);
1285 * During pass 2 in each "store" routine, we looked up pointers
1286 * to all the resources referrenced in the current resource, now we
1287 * must copy their addresses from the static record to the allocated
1292 /* Resources not containing a resource */
1300 * Resources containing another resource or alist. First
1301 * look up the resource which contains another resource. It
1302 * was written during pass 1. Then stuff in the pointers to
1303 * the resources it contains, which were inserted this pass.
1304 * Finally, it will all be stored back.
1307 /* Find resource saved in pass 1 */
1308 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1309 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1311 /* Explicitly copy resource pointers from this pass (res_all) */
1312 res->res_pool.NextPool = res_all.res_pool.NextPool;
1313 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1314 res->res_pool.storage = res_all.res_pool.storage;
1315 res->res_pool.Catalog = res_all.res_pool.Catalog;
1318 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1319 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1321 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1324 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1325 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1327 res->res_dir.messages = res_all.res_dir.messages;
1328 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1331 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1332 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1333 res_all.res_dir.hdr.name);
1335 /* we must explicitly copy the device alist pointer */
1336 res->res_store.device = res_all.res_store.device;
1340 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1341 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1342 res_all.res_dir.hdr.name);
1344 res->res_job.messages = res_all.res_job.messages;
1345 res->res_job.schedule = res_all.res_job.schedule;
1346 res->res_job.client = res_all.res_job.client;
1347 res->res_job.fileset = res_all.res_job.fileset;
1348 res->res_job.storage = res_all.res_job.storage;
1349 res->res_job.pool = res_all.res_job.pool;
1350 res->res_job.full_pool = res_all.res_job.full_pool;
1351 res->res_job.inc_pool = res_all.res_job.inc_pool;
1352 res->res_job.diff_pool = res_all.res_job.diff_pool;
1353 res->res_job.verify_job = res_all.res_job.verify_job;
1354 res->res_job.jobdefs = res_all.res_job.jobdefs;
1355 res->res_job.run_cmds = res_all.res_job.run_cmds;
1356 res->res_job.RunScripts = res_all.res_job.RunScripts;
1358 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1359 * is not very useful)
1360 * We have to set_bit(index, res_all.hdr.item_present);
1361 * or something like that
1364 /* we take RegexWhere before all other options */
1365 if (!res->res_job.RegexWhere
1367 (res->res_job.strip_prefix ||
1368 res->res_job.add_suffix ||
1369 res->res_job.add_prefix))
1371 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1372 res->res_job.add_prefix,
1373 res->res_job.add_suffix);
1374 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1375 bregexp_build_where(res->res_job.RegexWhere, len,
1376 res->res_job.strip_prefix,
1377 res->res_job.add_prefix,
1378 res->res_job.add_suffix);
1379 /* TODO: test bregexp */
1382 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1383 free(res->res_job.RestoreWhere);
1384 res->res_job.RestoreWhere = NULL;
1389 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1390 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1392 res->res_counter.Catalog = res_all.res_counter.Catalog;
1393 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1397 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1398 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1400 res->res_client.catalog = res_all.res_client.catalog;
1401 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1405 * Schedule is a bit different in that it contains a RUN record
1406 * chain which isn't a "named" resource. This chain was linked
1407 * in by run_conf.c during pass 2, so here we jam the pointer
1408 * into the Schedule resource.
1410 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1411 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1413 res->res_sch.run = res_all.res_sch.run;
1416 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1420 /* Note, the resource name was already saved during pass 1,
1421 * so here, we can just release it.
1423 if (res_all.res_dir.hdr.name) {
1424 free(res_all.res_dir.hdr.name);
1425 res_all.res_dir.hdr.name = NULL;
1427 if (res_all.res_dir.hdr.desc) {
1428 free(res_all.res_dir.hdr.desc);
1429 res_all.res_dir.hdr.desc = NULL;
1435 * The following code is only executed during pass 1
1439 size = sizeof(DIRRES);
1442 size = sizeof(CONRES);
1445 size =sizeof(CLIENT);
1448 size = sizeof(STORE);
1458 size = sizeof(FILESET);
1461 size = sizeof(SCHED);
1464 size = sizeof(POOL);
1467 size = sizeof(MSGS);
1470 size = sizeof(COUNTER);
1476 printf(_("Unknown resource type %d in save_resource.\n"), type);
1482 res = (URES *)malloc(size);
1483 memcpy(res, &res_all, size);
1484 if (!res_head[rindex]) {
1485 res_head[rindex] = (RES *)res; /* store first entry */
1486 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1487 res->res_dir.hdr.name, rindex);
1490 if (res->res_dir.hdr.name == NULL) {
1491 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1494 /* Add new res to end of chain */
1495 for (last=next=res_head[rindex]; next; next=next->next) {
1497 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1498 Emsg2(M_ERROR_TERM, 0,
1499 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1500 resources[rindex].name, res->res_dir.hdr.name);
1503 last->next = (RES *)res;
1504 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1505 res->res_dir.hdr.name, rindex, pass);
1511 * Store Device. Note, the resource is created upon the
1512 * first reference. The details of the resource are obtained
1513 * later from the SD.
1515 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1519 int rindex = R_DEVICE - r_first;
1520 int size = sizeof(DEVICE);
1524 token = lex_get_token(lc, T_NAME);
1525 if (!res_head[rindex]) {
1526 res = (URES *)malloc(size);
1527 memset(res, 0, size);
1528 res->res_dev.hdr.name = bstrdup(lc->str);
1529 res_head[rindex] = (RES *)res; /* store first entry */
1530 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1531 res->res_dir.hdr.name, rindex);
1534 /* See if it is already defined */
1535 for (next=res_head[rindex]; next->next; next=next->next) {
1536 if (strcmp(next->name, lc->str) == 0) {
1542 res = (URES *)malloc(size);
1543 memset(res, 0, size);
1544 res->res_dev.hdr.name = bstrdup(lc->str);
1545 next->next = (RES *)res;
1546 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1547 res->res_dir.hdr.name, rindex, pass);
1552 set_bit(index, res_all.hdr.item_present);
1554 store_alist_res(lc, item, index, pass);
1559 * Store JobType (backup, verify, restore)
1562 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1566 token = lex_get_token(lc, T_NAME);
1567 /* Store the type both pass 1 and pass 2 */
1568 for (i=0; migtypes[i].type_name; i++) {
1569 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1570 *(int *)(item->value) = migtypes[i].job_type;
1576 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1579 set_bit(index, res_all.hdr.item_present);
1585 * Store JobType (backup, verify, restore)
1588 void store_jobtype(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; jobtypes[i].type_name; i++) {
1595 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1596 *(int *)(item->value) = jobtypes[i].job_type;
1602 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1605 set_bit(index, res_all.hdr.item_present);
1609 * Store Job Level (Full, Incremental, ...)
1612 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1616 token = lex_get_token(lc, T_NAME);
1617 /* Store the level pass 2 so that type is defined */
1618 for (i=0; joblevels[i].level_name; i++) {
1619 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1620 *(int *)(item->value) = joblevels[i].level;
1626 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1629 set_bit(index, res_all.hdr.item_present);
1633 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1636 token = lex_get_token(lc, T_NAME);
1637 /* Scan Replacement options */
1638 for (i=0; ReplaceOptions[i].name; i++) {
1639 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1640 *(int *)(item->value) = ReplaceOptions[i].token;
1646 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1649 set_bit(index, res_all.hdr.item_present);
1653 * Store ACL (access control list)
1656 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1661 token = lex_get_token(lc, T_STRING);
1663 if (((alist **)item->value)[item->code] == NULL) {
1664 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1665 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1667 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1668 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1670 token = lex_get_token(lc, T_ALL);
1671 if (token == T_COMMA) {
1672 continue; /* get another ACL */
1676 set_bit(index, res_all.hdr.item_present);
1679 /* We build RunScripts items here */
1680 static RUNSCRIPT res_runscript;
1682 /* Store a runscript->when in a bit field */
1683 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1685 lex_get_token(lc, T_NAME);
1687 if (strcasecmp(lc->str, "before") == 0) {
1688 *(int *)(item->value) = SCRIPT_Before ;
1689 } else if (strcasecmp(lc->str, "after") == 0) {
1690 *(int *)(item->value) = SCRIPT_After;
1691 } else if (strcasecmp(lc->str, "always") == 0) {
1692 *(int *)(item->value) = SCRIPT_Any;
1694 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1699 /* Store a runscript->target
1702 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1704 lex_get_token(lc, T_STRING);
1707 if (strcmp(lc->str, "%c") == 0) {
1708 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1709 } else if (strcasecmp(lc->str, "yes") == 0) {
1710 ((RUNSCRIPT*) item->value)->set_target("%c");
1711 } else if (strcasecmp(lc->str, "no") == 0) {
1712 ((RUNSCRIPT*) item->value)->set_target("");
1714 RES *res = GetResWithName(R_CLIENT, lc->str);
1716 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1717 lc->str, lc->line_no, lc->line);
1720 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1727 * Store a runscript->command as a string
1729 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1731 lex_get_token(lc, T_STRING);
1734 ((RUNSCRIPT*)item->value)->set_command(lc->str, item->code);
1739 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1741 lex_get_token(lc, T_STRING);
1742 alist **runscripts = (alist **)(item->value) ;
1745 RUNSCRIPT *script = new_runscript();
1746 script->set_job_code_callback(job_code_callback_filesetname);
1748 script->set_command(lc->str);
1750 /* TODO: remove all script->old_proto with bacula 1.42 */
1752 if (strcmp(item->name, "runbeforejob") == 0) {
1753 script->when = SCRIPT_Before;
1754 script->fail_on_error = true;
1755 script->set_target("");
1757 } else if (strcmp(item->name, "runafterjob") == 0) {
1758 script->when = SCRIPT_After;
1759 script->on_success = true;
1760 script->on_failure = false;
1761 script->set_target("");
1763 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1764 script->old_proto = true;
1765 script->when = SCRIPT_After;
1766 script->set_target("%c");
1767 script->on_success = true;
1768 script->on_failure = false;
1770 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1771 script->old_proto = true;
1772 script->when = SCRIPT_Before;
1773 script->set_target("%c");
1774 script->fail_on_error = true;
1776 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1777 script->when = SCRIPT_After;
1778 script->on_failure = true;
1779 script->on_success = false;
1780 script->set_target("");
1783 if (*runscripts == NULL) {
1784 *runscripts = New(alist(10, not_owned_by_alist));
1787 (*runscripts)->append(script);
1794 /* Store a bool in a bit field without modifing res_all.hdr
1795 * We can also add an option to store_bool to skip res_all.hdr
1797 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1799 lex_get_token(lc, T_NAME);
1800 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1801 *(bool *)(item->value) = true;
1802 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1803 *(bool *)(item->value) = false;
1805 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1811 * new RunScript items
1812 * name handler value code flags default_value
1814 static RES_ITEM runscript_items[] = {
1815 {"command", store_runscript_cmd, {(char **)&res_runscript}, SHELL_CMD, 0, 0},
1816 {"console", store_runscript_cmd, {(char **)&res_runscript}, CONSOLE_CMD, 0, 0},
1817 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1818 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1819 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1820 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1821 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1822 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1823 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1824 {NULL, NULL, {0}, 0, 0, 0}
1828 * Store RunScript info
1830 * Note, when this routine is called, we are inside a Job
1831 * resource. We treat the RunScript like a sort of
1832 * mini-resource within the Job resource.
1834 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1837 alist **runscripts = (alist **)(item->value) ;
1839 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1841 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1843 token = lex_get_token(lc, T_SKIP_EOL);
1845 if (token != T_BOB) {
1846 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1849 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1850 if (token == T_EOB) {
1853 if (token != T_IDENTIFIER) {
1854 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1856 for (i=0; runscript_items[i].name; i++) {
1857 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1858 token = lex_get_token(lc, T_SKIP_EOL);
1859 if (token != T_EQUALS) {
1860 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1863 /* Call item handler */
1864 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1871 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1876 if (res_runscript.command == NULL) {
1877 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1878 "command", "runscript");
1881 /* run on client by default */
1882 if (res_runscript.target == NULL) {
1883 res_runscript.set_target("%c");
1886 RUNSCRIPT *script = new_runscript();
1887 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1888 script->set_job_code_callback(job_code_callback_filesetname);
1890 if (*runscripts == NULL) {
1891 *runscripts = New(alist(10, not_owned_by_alist));
1894 (*runscripts)->append(script);
1899 set_bit(index, res_all.hdr.item_present);
1902 /* callback function for edit_job_codes */
1903 extern "C" char *job_code_callback_filesetname(JCR *jcr, const char* param)
1905 if (param[0] == 'f') {
1906 return jcr->fileset->name();