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 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_migtype(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 {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
118 {"piddirectory",store_dir, ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
119 {"subsysdirectory", store_dir, ITEM(res_dir.subsys_directory), 0, 0, 0},
120 {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
121 {"password", store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
122 {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
123 {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
124 {"heartbeatinterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 0},
125 {"tlsauthenticate", store_bool, ITEM(res_dir.tls_authenticate), 0, 0, 0},
126 {"tlsenable", store_bool, ITEM(res_dir.tls_enable), 0, 0, 0},
127 {"tlsrequire", store_bool, ITEM(res_dir.tls_require), 0, 0, 0},
128 {"tlsverifypeer", store_bool, ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
129 {"tlscacertificatefile", store_dir, ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
130 {"tlscacertificatedir", store_dir, ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
131 {"tlscertificate", store_dir, ITEM(res_dir.tls_certfile), 0, 0, 0},
132 {"tlskey", store_dir, ITEM(res_dir.tls_keyfile), 0, 0, 0},
133 {"tlsdhfile", store_dir, ITEM(res_dir.tls_dhfile), 0, 0, 0},
134 {"tlsallowedcn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
135 {NULL, NULL, {0}, 0, 0, 0}
141 * name handler value code flags default_value
143 static RES_ITEM con_items[] = {
144 {"name", store_name, ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
145 {"description", store_str, ITEM(res_con.hdr.desc), 0, 0, 0},
146 {"password", store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
147 {"jobacl", store_acl, ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
148 {"clientacl", store_acl, ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
149 {"storageacl", store_acl, ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
150 {"scheduleacl", store_acl, ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
151 {"runacl", store_acl, ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
152 {"poolacl", store_acl, ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
153 {"commandacl", store_acl, ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
154 {"filesetacl", store_acl, ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
155 {"catalogacl", store_acl, ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
156 {"whereacl", store_acl, ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
157 {"tlsauthenticate", store_bool, ITEM(res_con.tls_authenticate), 0, 0, 0},
158 {"tlsenable", store_bool, ITEM(res_con.tls_enable), 0, 0, 0},
159 {"tlsrequire", store_bool, ITEM(res_con.tls_require), 0, 0, 0},
160 {"tlsverifypeer", store_bool, ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
161 {"tlscacertificatefile", store_dir, ITEM(res_con.tls_ca_certfile), 0, 0, 0},
162 {"tlscacertificatedir", store_dir, ITEM(res_con.tls_ca_certdir), 0, 0, 0},
163 {"tlscertificate", store_dir, ITEM(res_con.tls_certfile), 0, 0, 0},
164 {"tlskey", store_dir, ITEM(res_con.tls_keyfile), 0, 0, 0},
165 {"tlsdhfile", store_dir, ITEM(res_con.tls_dhfile), 0, 0, 0},
166 {"tlsallowedcn", store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
167 {NULL, NULL, {0}, 0, 0, 0}
172 * Client or File daemon resource
174 * name handler value code flags default_value
177 static RES_ITEM cli_items[] = {
178 {"name", store_name, ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
179 {"description", store_str, ITEM(res_client.hdr.desc), 0, 0, 0},
180 {"address", store_str, ITEM(res_client.address), 0, ITEM_REQUIRED, 0},
181 {"fdaddress", store_str, ITEM(res_client.address), 0, 0, 0},
182 {"fdport", store_pint, ITEM(res_client.FDport), 0, ITEM_DEFAULT, 9102},
183 {"password", store_password, ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
184 {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
185 {"catalog", store_res, ITEM(res_client.catalog), R_CATALOG, ITEM_REQUIRED, 0},
186 {"fileretention", store_time, ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
187 {"jobretention", store_time, ITEM(res_client.JobRetention), 0, ITEM_DEFAULT, 60*60*24*180},
188 {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0},
189 {"autoprune", store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
190 {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
191 {"tlsauthenticate", store_bool, ITEM(res_client.tls_authenticate), 0, 0, 0},
192 {"tlsenable", store_bool, ITEM(res_client.tls_enable), 0, 0, 0},
193 {"tlsrequire", store_bool, ITEM(res_client.tls_require), 0, 0, 0},
194 {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0},
195 {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0},
196 {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0},
197 {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0},
198 {"tlsallowedcn", store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
199 {NULL, NULL, {0}, 0, 0, 0}
202 /* Storage daemon resource
204 * name handler value code flags default_value
206 static RES_ITEM store_items[] = {
207 {"name", store_name, ITEM(res_store.hdr.name), 0, ITEM_REQUIRED, 0},
208 {"description", store_str, ITEM(res_store.hdr.desc), 0, 0, 0},
209 {"sdport", store_pint, ITEM(res_store.SDport), 0, ITEM_DEFAULT, 9103},
210 {"address", store_str, ITEM(res_store.address), 0, ITEM_REQUIRED, 0},
211 {"sdaddress", store_str, ITEM(res_store.address), 0, 0, 0},
212 {"password", store_password, ITEM(res_store.password), 0, ITEM_REQUIRED, 0},
213 {"sdpassword", store_password, ITEM(res_store.password), 0, 0, 0},
214 {"device", store_device, ITEM(res_store.device), R_DEVICE, ITEM_REQUIRED, 0},
215 {"mediatype", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
216 {"autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
217 {"enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true},
218 {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
219 {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
220 {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
221 {"tlsauthenticate", store_bool, ITEM(res_store.tls_authenticate), 0, 0, 0},
222 {"tlsenable", store_bool, ITEM(res_store.tls_enable), 0, 0, 0},
223 {"tlsrequire", store_bool, ITEM(res_store.tls_require), 0, 0, 0},
224 {"tlscacertificatefile", store_dir, ITEM(res_store.tls_ca_certfile), 0, 0, 0},
225 {"tlscacertificatedir", store_dir, ITEM(res_store.tls_ca_certdir), 0, 0, 0},
226 {"tlscertificate", store_dir, ITEM(res_store.tls_certfile), 0, 0, 0},
227 {"tlskey", store_dir, ITEM(res_store.tls_keyfile), 0, 0, 0},
228 {NULL, NULL, {0}, 0, 0, 0}
232 * Catalog Resource Directives
234 * name handler value code flags default_value
236 static RES_ITEM cat_items[] = {
237 {"name", store_name, ITEM(res_cat.hdr.name), 0, ITEM_REQUIRED, 0},
238 {"description", store_str, ITEM(res_cat.hdr.desc), 0, 0, 0},
239 {"address", store_str, ITEM(res_cat.db_address), 0, 0, 0},
240 {"dbaddress", store_str, ITEM(res_cat.db_address), 0, 0, 0},
241 {"dbport", store_pint, ITEM(res_cat.db_port), 0, 0, 0},
242 /* keep this password as store_str for the moment */
243 {"password", store_str, ITEM(res_cat.db_password), 0, 0, 0},
244 {"dbpassword", store_str, ITEM(res_cat.db_password), 0, 0, 0},
245 {"user", store_str, ITEM(res_cat.db_user), 0, 0, 0},
246 {"dbname", store_str, ITEM(res_cat.db_name), 0, ITEM_REQUIRED, 0},
247 {"dbsocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0},
248 /* Turned off for the moment */
249 {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
250 {NULL, NULL, {0}, 0, 0, 0}
254 * Job Resource Directives
256 * name handler value code flags default_value
258 RES_ITEM job_items[] = {
259 {"name", store_name, ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
260 {"description", store_str, ITEM(res_job.hdr.desc), 0, 0, 0},
261 {"type", store_jobtype, ITEM(res_job.JobType), 0, ITEM_REQUIRED, 0},
262 {"level", store_level, ITEM(res_job.JobLevel), 0, 0, 0},
263 {"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
264 {"storage", store_alist_res, ITEM(res_job.storage), R_STORAGE, 0, 0},
265 {"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
266 {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
267 {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
268 {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
269 {"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
270 {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
271 {"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
272 {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
273 {"jobtoverify", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
274 {"jobdefs", store_res, ITEM(res_job.jobdefs), R_JOBDEFS, 0, 0},
275 {"run", store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
276 /* Root of where to restore files */
277 {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
278 {"regexwhere", store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
279 {"stripprefix", store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
280 {"addprefix", store_str, ITEM(res_job.add_prefix), 0, 0, 0},
281 {"addsuffix", store_str, ITEM(res_job.add_suffix), 0, 0, 0},
282 /* Where to find bootstrap during restore */
283 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
284 /* Where to write bootstrap file during backup */
285 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
286 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
287 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
288 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
289 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
290 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
291 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
292 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
293 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
294 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
295 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
296 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
297 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
298 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
299 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
300 {"optimizejobscheduling",store_bool, ITEM(res_job.OptimizeJobScheduling), 0, ITEM_DEFAULT, false},
301 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
302 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
303 {"spoolsize", store_size, ITEM(res_job.spool_size), 0, 0, 0},
304 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
305 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
306 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
307 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
308 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
309 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
310 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
311 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
312 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
313 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
314 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
315 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
316 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
317 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
318 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
319 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
320 {NULL, NULL, {0}, 0, 0, 0}
325 * name handler value code flags default_value
327 static RES_ITEM fs_items[] = {
328 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
329 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
330 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
331 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
332 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
333 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
334 {NULL, NULL, {0}, 0, 0, 0}
337 /* Schedule -- see run_conf.c */
340 * name handler value code flags default_value
342 static RES_ITEM sch_items[] = {
343 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
344 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
345 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
346 {NULL, NULL, {0}, 0, 0, 0}
351 * name handler value code flags default_value
353 static RES_ITEM pool_items[] = {
354 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
355 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
356 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
357 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
358 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
359 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
360 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
361 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
362 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
363 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
364 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
365 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
366 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
367 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
368 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
369 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
370 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
371 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
372 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
373 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
374 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
375 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
376 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
377 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
378 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
379 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
380 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
381 {"catalog", store_res, ITEM(res_pool.Catalog), R_CATALOG, 0, 0},
382 {NULL, NULL, {0}, 0, 0, 0}
387 * name handler value code flags default_value
389 static RES_ITEM counter_items[] = {
390 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
391 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
392 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
393 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
394 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
395 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
396 {NULL, NULL, {0}, 0, 0, 0}
400 /* Message resource */
401 extern RES_ITEM msgs_items[];
404 * This is the master resource definition.
405 * It must have one item for each of the resources.
407 * NOTE!!! keep it in the same order as the R_codes
408 * or eliminate all resources[rindex].name
410 * name items rcode res_head
412 RES_TABLE resources[] = {
413 {"director", dir_items, R_DIRECTOR},
414 {"client", cli_items, R_CLIENT},
415 {"job", job_items, R_JOB},
416 {"storage", store_items, R_STORAGE},
417 {"catalog", cat_items, R_CATALOG},
418 {"schedule", sch_items, R_SCHEDULE},
419 {"fileset", fs_items, R_FILESET},
420 {"pool", pool_items, R_POOL},
421 {"messages", msgs_items, R_MSGS},
422 {"counter", counter_items, R_COUNTER},
423 {"console", con_items, R_CONSOLE},
424 {"jobdefs", job_items, R_JOBDEFS},
425 {"device", NULL, R_DEVICE}, /* info obtained from SD */
430 /* Keywords (RHS) permitted in Job Level records
432 * level_name level job_type
434 struct s_jl joblevels[] = {
435 {"Full", L_FULL, JT_BACKUP},
436 {"Base", L_BASE, JT_BACKUP},
437 {"Incremental", L_INCREMENTAL, JT_BACKUP},
438 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
439 {"Since", L_SINCE, JT_BACKUP},
440 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
441 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
442 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
443 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
444 {"Data", L_VERIFY_DATA, JT_VERIFY},
445 {" ", L_NONE, JT_ADMIN},
446 {" ", L_NONE, JT_RESTORE},
450 /* Keywords (RHS) permitted in Job type records
454 struct s_jt jobtypes[] = {
455 {"backup", JT_BACKUP},
457 {"verify", JT_VERIFY},
458 {"restore", JT_RESTORE},
459 {"migrate", JT_MIGRATE},
464 /* Keywords (RHS) permitted in Selection type records
468 struct s_jt migtypes[] = {
469 {"smallestvolume", MT_SMALLEST_VOL},
470 {"oldestvolume", MT_OLDEST_VOL},
471 {"pooloccupancy", MT_POOL_OCCUPANCY},
472 {"pooltime", MT_POOL_TIME},
473 {"client", MT_CLIENT},
474 {"volume", MT_VOLUME},
476 {"sqlquery", MT_SQLQUERY},
482 /* Options permitted in Restore replace= */
483 struct s_kw ReplaceOptions[] = {
484 {"always", REPLACE_ALWAYS},
485 {"ifnewer", REPLACE_IFNEWER},
486 {"ifolder", REPLACE_IFOLDER},
487 {"never", REPLACE_NEVER},
491 const char *level_to_str(int level)
494 static char level_no[30];
495 const char *str = level_no;
497 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
498 for (i=0; joblevels[i].level_name; i++) {
499 if (level == joblevels[i].level) {
500 str = joblevels[i].level_name;
507 /* Dump contents of resource */
508 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
510 URES *res = (URES *)reshdr;
512 char ed1[100], ed2[100], ed3[100];
516 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
519 if (type < 0) { /* no recursion */
525 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
526 reshdr->name, res->res_dir.MaxConcurrentJobs,
527 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
528 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
529 if (res->res_dir.query_file) {
530 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
532 if (res->res_dir.messages) {
533 sendit(sock, _(" --> "));
534 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
538 sendit(sock, _("Console: name=%s SSL=%d\n"),
539 res->res_con.hdr.name, res->res_con.tls_enable);
542 if (res->res_counter.WrapCounter) {
543 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
544 res->res_counter.hdr.name, res->res_counter.MinValue,
545 res->res_counter.MaxValue, res->res_counter.CurrentValue,
546 res->res_counter.WrapCounter->hdr.name);
548 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
549 res->res_counter.hdr.name, res->res_counter.MinValue,
550 res->res_counter.MaxValue);
552 if (res->res_counter.Catalog) {
553 sendit(sock, _(" --> "));
554 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
559 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
560 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
561 res->res_client.MaxConcurrentJobs);
562 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
563 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
564 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
565 res->res_client.AutoPrune);
566 if (res->res_client.catalog) {
567 sendit(sock, _(" --> "));
568 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
575 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
576 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
577 " poolid=%s volname=%s MediaType=%s\n"),
578 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
579 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
580 dev->offline, dev->autochanger,
581 edit_uint64(dev->PoolId, ed1),
582 dev->VolumeName, dev->MediaType);
586 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
587 " DeviceName=%s MediaType=%s StorageId=%s\n"),
588 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
589 res->res_store.MaxConcurrentJobs,
590 res->res_store.dev_name(),
591 res->res_store.media_type,
592 edit_int64(res->res_store.StorageId, ed1));
596 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
597 " db_user=%s MutliDBConn=%d\n"),
598 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
599 res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
600 res->res_cat.mult_db_connections);
605 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
606 type == R_JOB ? _("Job") : _("JobDefs"),
607 res->res_job.hdr.name, res->res_job.JobType,
608 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
609 res->res_job.enabled);
610 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
611 res->res_job.MaxConcurrentJobs,
612 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
613 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
614 res->res_job.spool_data, res->res_job.write_part_after_job);
615 if (res->res_job.spool_size) {
616 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
618 if (res->res_job.JobType == JT_MIGRATE) {
619 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
621 if (res->res_job.client) {
622 sendit(sock, _(" --> "));
623 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
625 if (res->res_job.fileset) {
626 sendit(sock, _(" --> "));
627 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
629 if (res->res_job.schedule) {
630 sendit(sock, _(" --> "));
631 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
633 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
634 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
636 if (res->res_job.RegexWhere) {
637 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
639 if (res->res_job.RestoreBootstrap) {
640 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
642 if (res->res_job.WriteBootstrap) {
643 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
645 if (res->res_job.storage) {
647 foreach_alist(store, res->res_job.storage) {
648 sendit(sock, _(" --> "));
649 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
652 if (res->res_job.RunScripts) {
654 foreach_alist(script, res->res_job.RunScripts) {
655 sendit(sock, _(" --> RunScript\n"));
656 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
657 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
658 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
659 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
660 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
661 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
664 if (res->res_job.pool) {
665 sendit(sock, _(" --> "));
666 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
668 if (res->res_job.full_pool) {
669 sendit(sock, _(" --> "));
670 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
672 if (res->res_job.inc_pool) {
673 sendit(sock, _(" --> "));
674 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
676 if (res->res_job.diff_pool) {
677 sendit(sock, _(" --> "));
678 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
680 if (res->res_job.verify_job) {
681 sendit(sock, _(" --> "));
682 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
684 if (res->res_job.run_cmds) {
686 foreach_alist(runcmd, res->res_job.run_cmds) {
687 sendit(sock, _(" --> Run=%s\n"), runcmd);
690 if (res->res_job.selection_pattern) {
691 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
693 if (res->res_job.messages) {
694 sendit(sock, _(" --> "));
695 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
702 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
703 for (i=0; i<res->res_fs.num_includes; i++) {
704 INCEXE *incexe = res->res_fs.include_items[i];
705 for (j=0; j<incexe->num_opts; j++) {
706 FOPTS *fo = incexe->opts_list[j];
707 sendit(sock, " O %s\n", fo->opts);
709 bool enhanced_wild = false;
710 for (k=0; fo->opts[k]!='\0'; k++) {
711 if (fo->opts[k]=='W') {
712 enhanced_wild = true;
717 for (k=0; k<fo->regex.size(); k++) {
718 sendit(sock, " R %s\n", fo->regex.get(k));
720 for (k=0; k<fo->regexdir.size(); k++) {
721 sendit(sock, " RD %s\n", fo->regexdir.get(k));
723 for (k=0; k<fo->regexfile.size(); k++) {
724 sendit(sock, " RF %s\n", fo->regexfile.get(k));
726 for (k=0; k<fo->wild.size(); k++) {
727 sendit(sock, " W %s\n", fo->wild.get(k));
729 for (k=0; k<fo->wilddir.size(); k++) {
730 sendit(sock, " WD %s\n", fo->wilddir.get(k));
732 for (k=0; k<fo->wildfile.size(); k++) {
733 sendit(sock, " WF %s\n", fo->wildfile.get(k));
735 for (k=0; k<fo->wildbase.size(); k++) {
736 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
738 for (k=0; k<fo->base.size(); k++) {
739 sendit(sock, " B %s\n", fo->base.get(k));
741 for (k=0; k<fo->fstype.size(); k++) {
742 sendit(sock, " X %s\n", fo->fstype.get(k));
744 for (k=0; k<fo->drivetype.size(); k++) {
745 sendit(sock, " XD %s\n", fo->drivetype.get(k));
748 sendit(sock, " D %s\n", fo->reader);
751 sendit(sock, " T %s\n", fo->writer);
753 sendit(sock, " N\n");
755 for (j=0; j<incexe->name_list.size(); j++) {
756 sendit(sock, " I %s\n", incexe->name_list.get(j));
758 if (incexe->name_list.size()) {
759 sendit(sock, " N\n");
763 for (i=0; i<res->res_fs.num_excludes; i++) {
764 INCEXE *incexe = res->res_fs.exclude_items[i];
765 for (j=0; j<incexe->name_list.size(); j++) {
766 sendit(sock, " E %s\n", incexe->name_list.get(j));
768 if (incexe->name_list.size()) {
769 sendit(sock, " N\n");
776 if (res->res_sch.run) {
778 RUN *run = res->res_sch.run;
779 char buf[1000], num[30];
780 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
785 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
786 bstrncpy(buf, _(" hour="), sizeof(buf));
787 for (i=0; i<24; i++) {
788 if (bit_is_set(i, run->hour)) {
789 bsnprintf(num, sizeof(num), "%d ", i);
790 bstrncat(buf, num, sizeof(buf));
793 bstrncat(buf, "\n", sizeof(buf));
795 bstrncpy(buf, _(" mday="), sizeof(buf));
796 for (i=0; i<31; i++) {
797 if (bit_is_set(i, run->mday)) {
798 bsnprintf(num, sizeof(num), "%d ", i);
799 bstrncat(buf, num, sizeof(buf));
802 bstrncat(buf, "\n", sizeof(buf));
804 bstrncpy(buf, _(" month="), sizeof(buf));
805 for (i=0; i<12; i++) {
806 if (bit_is_set(i, run->month)) {
807 bsnprintf(num, sizeof(num), "%d ", i);
808 bstrncat(buf, num, sizeof(buf));
811 bstrncat(buf, "\n", sizeof(buf));
813 bstrncpy(buf, _(" wday="), sizeof(buf));
814 for (i=0; i<7; i++) {
815 if (bit_is_set(i, run->wday)) {
816 bsnprintf(num, sizeof(num), "%d ", i);
817 bstrncat(buf, num, sizeof(buf));
820 bstrncat(buf, "\n", sizeof(buf));
822 bstrncpy(buf, _(" wom="), sizeof(buf));
823 for (i=0; i<5; i++) {
824 if (bit_is_set(i, run->wom)) {
825 bsnprintf(num, sizeof(num), "%d ", i);
826 bstrncat(buf, num, sizeof(buf));
829 bstrncat(buf, "\n", sizeof(buf));
831 bstrncpy(buf, _(" woy="), sizeof(buf));
832 for (i=0; i<54; i++) {
833 if (bit_is_set(i, run->woy)) {
834 bsnprintf(num, sizeof(num), "%d ", i);
835 bstrncat(buf, num, sizeof(buf));
838 bstrncat(buf, "\n", sizeof(buf));
840 sendit(sock, _(" mins=%d\n"), run->minute);
842 sendit(sock, _(" --> "));
843 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
846 sendit(sock, _(" --> "));
847 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
850 sendit(sock, _(" --> "));
851 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
853 /* If another Run record is chained in, go print it */
859 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
864 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
865 res->res_pool.pool_type);
866 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
867 res->res_pool.use_catalog, res->res_pool.use_volume_once,
868 res->res_pool.catalog_files);
869 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
870 res->res_pool.max_volumes, res->res_pool.AutoPrune,
871 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
872 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
873 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
874 res->res_pool.Recycle,
875 NPRT(res->res_pool.label_format));
876 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
877 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
878 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
879 res->res_pool.recycle_oldest_volume,
880 res->res_pool.purge_oldest_volume);
881 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
882 res->res_pool.MaxVolJobs,
883 res->res_pool.MaxVolFiles,
884 edit_uint64(res->res_pool.MaxVolFiles, ed1));
885 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
886 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
887 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
888 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
889 if (res->res_pool.NextPool) {
890 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
892 if (res->res_pool.RecyclePool) {
893 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
895 if (res->res_pool.Catalog) {
896 sendit(sock, _(" Catalog=%s\n"), res->res_pool.Catalog->name());
898 if (res->res_pool.storage) {
900 foreach_alist(store, res->res_pool.storage) {
901 sendit(sock, _(" --> "));
902 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
905 if (res->res_pool.CopyPool) {
907 foreach_alist(copy, res->res_pool.CopyPool) {
908 sendit(sock, _(" --> "));
909 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
916 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
917 if (res->res_msgs.mail_cmd)
918 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
919 if (res->res_msgs.operator_cmd)
920 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
924 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
927 if (recurse && res->res_dir.hdr.next) {
928 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
933 * Free all the members of an INCEXE structure
935 static void free_incexe(INCEXE *incexe)
937 incexe->name_list.destroy();
938 for (int i=0; i<incexe->num_opts; i++) {
939 FOPTS *fopt = incexe->opts_list[i];
940 fopt->regex.destroy();
941 fopt->regexdir.destroy();
942 fopt->regexfile.destroy();
943 fopt->wild.destroy();
944 fopt->wilddir.destroy();
945 fopt->wildfile.destroy();
946 fopt->wildbase.destroy();
947 fopt->base.destroy();
948 fopt->fstype.destroy();
949 fopt->drivetype.destroy();
958 if (incexe->opts_list) {
959 free(incexe->opts_list);
965 * Free memory of resource -- called when daemon terminates.
966 * NB, we don't need to worry about freeing any references
967 * to other resources as they will be freed when that
968 * resource chain is traversed. Mainly we worry about freeing
969 * allocated strings (names).
971 void free_resource(RES *sres, int type)
974 RES *nres; /* next resource if linked */
975 URES *res = (URES *)sres;
980 /* common stuff -- free the resource name and description */
981 nres = (RES *)res->res_dir.hdr.next;
982 if (res->res_dir.hdr.name) {
983 free(res->res_dir.hdr.name);
985 if (res->res_dir.hdr.desc) {
986 free(res->res_dir.hdr.desc);
991 if (res->res_dir.working_directory) {
992 free(res->res_dir.working_directory);
994 if (res->res_dir.scripts_directory) {
995 free((char *)res->res_dir.scripts_directory);
997 if (res->res_dir.pid_directory) {
998 free(res->res_dir.pid_directory);
1000 if (res->res_dir.subsys_directory) {
1001 free(res->res_dir.subsys_directory);
1003 if (res->res_dir.password) {
1004 free(res->res_dir.password);
1006 if (res->res_dir.query_file) {
1007 free(res->res_dir.query_file);
1009 if (res->res_dir.DIRaddrs) {
1010 free_addresses(res->res_dir.DIRaddrs);
1012 if (res->res_dir.tls_ctx) {
1013 free_tls_context(res->res_dir.tls_ctx);
1015 if (res->res_dir.tls_ca_certfile) {
1016 free(res->res_dir.tls_ca_certfile);
1018 if (res->res_dir.tls_ca_certdir) {
1019 free(res->res_dir.tls_ca_certdir);
1021 if (res->res_dir.tls_certfile) {
1022 free(res->res_dir.tls_certfile);
1024 if (res->res_dir.tls_keyfile) {
1025 free(res->res_dir.tls_keyfile);
1027 if (res->res_dir.tls_dhfile) {
1028 free(res->res_dir.tls_dhfile);
1030 if (res->res_dir.tls_allowed_cns) {
1031 delete res->res_dir.tls_allowed_cns;
1038 if (res->res_con.password) {
1039 free(res->res_con.password);
1041 if (res->res_con.tls_ctx) {
1042 free_tls_context(res->res_con.tls_ctx);
1044 if (res->res_con.tls_ca_certfile) {
1045 free(res->res_con.tls_ca_certfile);
1047 if (res->res_con.tls_ca_certdir) {
1048 free(res->res_con.tls_ca_certdir);
1050 if (res->res_con.tls_certfile) {
1051 free(res->res_con.tls_certfile);
1053 if (res->res_con.tls_keyfile) {
1054 free(res->res_con.tls_keyfile);
1056 if (res->res_con.tls_dhfile) {
1057 free(res->res_con.tls_dhfile);
1059 if (res->res_con.tls_allowed_cns) {
1060 delete res->res_con.tls_allowed_cns;
1062 for (int i=0; i<Num_ACL; i++) {
1063 if (res->res_con.ACL_lists[i]) {
1064 delete res->res_con.ACL_lists[i];
1065 res->res_con.ACL_lists[i] = NULL;
1070 if (res->res_client.address) {
1071 free(res->res_client.address);
1073 if (res->res_client.password) {
1074 free(res->res_client.password);
1076 if (res->res_client.tls_ctx) {
1077 free_tls_context(res->res_client.tls_ctx);
1079 if (res->res_client.tls_ca_certfile) {
1080 free(res->res_client.tls_ca_certfile);
1082 if (res->res_client.tls_ca_certdir) {
1083 free(res->res_client.tls_ca_certdir);
1085 if (res->res_client.tls_certfile) {
1086 free(res->res_client.tls_certfile);
1088 if (res->res_client.tls_keyfile) {
1089 free(res->res_client.tls_keyfile);
1091 if (res->res_client.tls_allowed_cns) {
1092 delete res->res_client.tls_allowed_cns;
1096 if (res->res_store.address) {
1097 free(res->res_store.address);
1099 if (res->res_store.password) {
1100 free(res->res_store.password);
1102 if (res->res_store.media_type) {
1103 free(res->res_store.media_type);
1105 if (res->res_store.device) {
1106 delete res->res_store.device;
1108 if (res->res_store.tls_ctx) {
1109 free_tls_context(res->res_store.tls_ctx);
1111 if (res->res_store.tls_ca_certfile) {
1112 free(res->res_store.tls_ca_certfile);
1114 if (res->res_store.tls_ca_certdir) {
1115 free(res->res_store.tls_ca_certdir);
1117 if (res->res_store.tls_certfile) {
1118 free(res->res_store.tls_certfile);
1120 if (res->res_store.tls_keyfile) {
1121 free(res->res_store.tls_keyfile);
1125 if (res->res_cat.db_address) {
1126 free(res->res_cat.db_address);
1128 if (res->res_cat.db_socket) {
1129 free(res->res_cat.db_socket);
1131 if (res->res_cat.db_user) {
1132 free(res->res_cat.db_user);
1134 if (res->res_cat.db_name) {
1135 free(res->res_cat.db_name);
1137 if (res->res_cat.db_password) {
1138 free(res->res_cat.db_password);
1142 if ((num=res->res_fs.num_includes)) {
1143 while (--num >= 0) {
1144 free_incexe(res->res_fs.include_items[num]);
1146 free(res->res_fs.include_items);
1148 res->res_fs.num_includes = 0;
1149 if ((num=res->res_fs.num_excludes)) {
1150 while (--num >= 0) {
1151 free_incexe(res->res_fs.exclude_items[num]);
1153 free(res->res_fs.exclude_items);
1155 res->res_fs.num_excludes = 0;
1158 if (res->res_pool.pool_type) {
1159 free(res->res_pool.pool_type);
1161 if (res->res_pool.label_format) {
1162 free(res->res_pool.label_format);
1164 if (res->res_pool.cleaning_prefix) {
1165 free(res->res_pool.cleaning_prefix);
1167 if (res->res_pool.storage) {
1168 delete res->res_pool.storage;
1172 if (res->res_sch.run) {
1174 nrun = res->res_sch.run;
1184 if (res->res_job.RestoreWhere) {
1185 free(res->res_job.RestoreWhere);
1187 if (res->res_job.RegexWhere) {
1188 free(res->res_job.RegexWhere);
1190 if (res->res_job.strip_prefix) {
1191 free(res->res_job.strip_prefix);
1193 if (res->res_job.add_prefix) {
1194 free(res->res_job.add_prefix);
1196 if (res->res_job.add_suffix) {
1197 free(res->res_job.add_suffix);
1199 if (res->res_job.RestoreBootstrap) {
1200 free(res->res_job.RestoreBootstrap);
1202 if (res->res_job.WriteBootstrap) {
1203 free(res->res_job.WriteBootstrap);
1205 if (res->res_job.selection_pattern) {
1206 free(res->res_job.selection_pattern);
1208 if (res->res_job.run_cmds) {
1209 delete res->res_job.run_cmds;
1211 if (res->res_job.storage) {
1212 delete res->res_job.storage;
1214 if (res->res_job.RunScripts) {
1215 free_runscripts(res->res_job.RunScripts);
1216 delete res->res_job.RunScripts;
1220 if (res->res_msgs.mail_cmd) {
1221 free(res->res_msgs.mail_cmd);
1223 if (res->res_msgs.operator_cmd) {
1224 free(res->res_msgs.operator_cmd);
1226 free_msgs_res((MSGS *)res); /* free message resource */
1230 printf(_("Unknown resource type %d in free_resource.\n"), type);
1232 /* Common stuff again -- free the resource, recurse to next one */
1237 free_resource(nres, type);
1242 * Save the new resource by chaining it into the head list for
1243 * the resource. If this is pass 2, we update any resource
1244 * pointers because they may not have been defined until
1247 void save_resource(int type, RES_ITEM *items, int pass)
1250 int rindex = type - r_first;
1254 /* Check Job requirements after applying JobDefs */
1255 if (type != R_JOB && type != R_JOBDEFS) {
1257 * Ensure that all required items are present
1259 for (i=0; items[i].name; i++) {
1260 if (items[i].flags & ITEM_REQUIRED) {
1261 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1262 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1263 items[i].name, resources[rindex]);
1266 /* If this triggers, take a look at lib/parse_conf.h */
1267 if (i >= MAX_RES_ITEMS) {
1268 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1271 } else if (type == R_JOB) {
1273 * Ensure that the name item is present
1275 if (items[0].flags & ITEM_REQUIRED) {
1276 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1277 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1278 items[0].name, resources[rindex]);
1284 * During pass 2 in each "store" routine, we looked up pointers
1285 * to all the resources referrenced in the current resource, now we
1286 * must copy their addresses from the static record to the allocated
1291 /* Resources not containing a resource */
1299 * Resources containing another resource or alist. First
1300 * look up the resource which contains another resource. It
1301 * was written during pass 1. Then stuff in the pointers to
1302 * the resources it contains, which were inserted this pass.
1303 * Finally, it will all be stored back.
1306 /* Find resource saved in pass 1 */
1307 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1308 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1310 /* Explicitly copy resource pointers from this pass (res_all) */
1311 res->res_pool.NextPool = res_all.res_pool.NextPool;
1312 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1313 res->res_pool.storage = res_all.res_pool.storage;
1316 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1317 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1319 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1322 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1323 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1325 res->res_dir.messages = res_all.res_dir.messages;
1326 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1329 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1330 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1331 res_all.res_dir.hdr.name);
1333 /* we must explicitly copy the device alist pointer */
1334 res->res_store.device = res_all.res_store.device;
1338 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1339 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1340 res_all.res_dir.hdr.name);
1342 res->res_job.messages = res_all.res_job.messages;
1343 res->res_job.schedule = res_all.res_job.schedule;
1344 res->res_job.client = res_all.res_job.client;
1345 res->res_job.fileset = res_all.res_job.fileset;
1346 res->res_job.storage = res_all.res_job.storage;
1347 res->res_job.pool = res_all.res_job.pool;
1348 res->res_job.full_pool = res_all.res_job.full_pool;
1349 res->res_job.inc_pool = res_all.res_job.inc_pool;
1350 res->res_job.diff_pool = res_all.res_job.diff_pool;
1351 res->res_job.verify_job = res_all.res_job.verify_job;
1352 res->res_job.jobdefs = res_all.res_job.jobdefs;
1353 res->res_job.run_cmds = res_all.res_job.run_cmds;
1354 res->res_job.RunScripts = res_all.res_job.RunScripts;
1356 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1357 * is not very useful)
1358 * We have to set_bit(index, res_all.hdr.item_present);
1359 * or something like that
1362 /* we take RegexWhere before all other options */
1363 if (!res->res_job.RegexWhere
1365 (res->res_job.strip_prefix ||
1366 res->res_job.add_suffix ||
1367 res->res_job.add_prefix))
1369 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1370 res->res_job.add_prefix,
1371 res->res_job.add_suffix);
1372 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1373 bregexp_build_where(res->res_job.RegexWhere, len,
1374 res->res_job.strip_prefix,
1375 res->res_job.add_prefix,
1376 res->res_job.add_suffix);
1377 /* TODO: test bregexp */
1380 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1381 free(res->res_job.RestoreWhere);
1382 res->res_job.RestoreWhere = NULL;
1387 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1388 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1390 res->res_counter.Catalog = res_all.res_counter.Catalog;
1391 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1395 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1396 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1398 res->res_client.catalog = res_all.res_client.catalog;
1399 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1403 * Schedule is a bit different in that it contains a RUN record
1404 * chain which isn't a "named" resource. This chain was linked
1405 * in by run_conf.c during pass 2, so here we jam the pointer
1406 * into the Schedule resource.
1408 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1409 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1411 res->res_sch.run = res_all.res_sch.run;
1414 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1418 /* Note, the resource name was already saved during pass 1,
1419 * so here, we can just release it.
1421 if (res_all.res_dir.hdr.name) {
1422 free(res_all.res_dir.hdr.name);
1423 res_all.res_dir.hdr.name = NULL;
1425 if (res_all.res_dir.hdr.desc) {
1426 free(res_all.res_dir.hdr.desc);
1427 res_all.res_dir.hdr.desc = NULL;
1433 * The following code is only executed during pass 1
1437 size = sizeof(DIRRES);
1440 size = sizeof(CONRES);
1443 size =sizeof(CLIENT);
1446 size = sizeof(STORE);
1456 size = sizeof(FILESET);
1459 size = sizeof(SCHED);
1462 size = sizeof(POOL);
1465 size = sizeof(MSGS);
1468 size = sizeof(COUNTER);
1474 printf(_("Unknown resource type %d in save_resource.\n"), type);
1480 res = (URES *)malloc(size);
1481 memcpy(res, &res_all, size);
1482 if (!res_head[rindex]) {
1483 res_head[rindex] = (RES *)res; /* store first entry */
1484 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1485 res->res_dir.hdr.name, rindex);
1488 if (res->res_dir.hdr.name == NULL) {
1489 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1492 /* Add new res to end of chain */
1493 for (last=next=res_head[rindex]; next; next=next->next) {
1495 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1496 Emsg2(M_ERROR_TERM, 0,
1497 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1498 resources[rindex].name, res->res_dir.hdr.name);
1501 last->next = (RES *)res;
1502 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1503 res->res_dir.hdr.name, rindex, pass);
1509 * Store Device. Note, the resource is created upon the
1510 * first reference. The details of the resource are obtained
1511 * later from the SD.
1513 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1517 int rindex = R_DEVICE - r_first;
1518 int size = sizeof(DEVICE);
1522 token = lex_get_token(lc, T_NAME);
1523 if (!res_head[rindex]) {
1524 res = (URES *)malloc(size);
1525 memset(res, 0, size);
1526 res->res_dev.hdr.name = bstrdup(lc->str);
1527 res_head[rindex] = (RES *)res; /* store first entry */
1528 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1529 res->res_dir.hdr.name, rindex);
1532 /* See if it is already defined */
1533 for (next=res_head[rindex]; next->next; next=next->next) {
1534 if (strcmp(next->name, lc->str) == 0) {
1540 res = (URES *)malloc(size);
1541 memset(res, 0, size);
1542 res->res_dev.hdr.name = bstrdup(lc->str);
1543 next->next = (RES *)res;
1544 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1545 res->res_dir.hdr.name, rindex, pass);
1550 set_bit(index, res_all.hdr.item_present);
1552 store_alist_res(lc, item, index, pass);
1557 * Store JobType (backup, verify, restore)
1560 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1564 token = lex_get_token(lc, T_NAME);
1565 /* Store the type both pass 1 and pass 2 */
1566 for (i=0; migtypes[i].type_name; i++) {
1567 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1568 *(int *)(item->value) = migtypes[i].job_type;
1574 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1577 set_bit(index, res_all.hdr.item_present);
1583 * Store JobType (backup, verify, restore)
1586 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1590 token = lex_get_token(lc, T_NAME);
1591 /* Store the type both pass 1 and pass 2 */
1592 for (i=0; jobtypes[i].type_name; i++) {
1593 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1594 *(int *)(item->value) = jobtypes[i].job_type;
1600 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1603 set_bit(index, res_all.hdr.item_present);
1607 * Store Job Level (Full, Incremental, ...)
1610 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1614 token = lex_get_token(lc, T_NAME);
1615 /* Store the level pass 2 so that type is defined */
1616 for (i=0; joblevels[i].level_name; i++) {
1617 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1618 *(int *)(item->value) = joblevels[i].level;
1624 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1627 set_bit(index, res_all.hdr.item_present);
1631 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1634 token = lex_get_token(lc, T_NAME);
1635 /* Scan Replacement options */
1636 for (i=0; ReplaceOptions[i].name; i++) {
1637 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1638 *(int *)(item->value) = ReplaceOptions[i].token;
1644 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1647 set_bit(index, res_all.hdr.item_present);
1651 * Store ACL (access control list)
1654 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1659 token = lex_get_token(lc, T_STRING);
1661 if (((alist **)item->value)[item->code] == NULL) {
1662 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1663 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1665 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1666 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1668 token = lex_get_token(lc, T_ALL);
1669 if (token == T_COMMA) {
1670 continue; /* get another ACL */
1674 set_bit(index, res_all.hdr.item_present);
1677 /* We build RunScripts items here */
1678 static RUNSCRIPT res_runscript;
1680 /* Store a runscript->when in a bit field */
1681 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1683 lex_get_token(lc, T_NAME);
1685 if (strcasecmp(lc->str, "before") == 0) {
1686 *(int *)(item->value) = SCRIPT_Before ;
1687 } else if (strcasecmp(lc->str, "after") == 0) {
1688 *(int *)(item->value) = SCRIPT_After;
1689 } else if (strcasecmp(lc->str, "always") == 0) {
1690 *(int *)(item->value) = SCRIPT_Any;
1692 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1697 /* Store a runscript->target
1700 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1702 lex_get_token(lc, T_STRING);
1705 if (strcmp(lc->str, "%c") == 0) {
1706 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1707 } else if (strcasecmp(lc->str, "yes") == 0) {
1708 ((RUNSCRIPT*) item->value)->set_target("%c");
1709 } else if (strcasecmp(lc->str, "no") == 0) {
1710 ((RUNSCRIPT*) item->value)->set_target("");
1712 RES *res = GetResWithName(R_CLIENT, lc->str);
1714 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1715 lc->str, lc->line_no, lc->line);
1718 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1724 /* Store a runscript->command in a bit field
1727 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1729 lex_get_token(lc, T_STRING);
1732 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1737 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1739 lex_get_token(lc, T_STRING);
1740 alist **runscripts = (alist **)(item->value) ;
1743 RUNSCRIPT *script = new_runscript();
1745 script->set_command(lc->str);
1747 /* TODO: remove all script->old_proto with bacula 1.42 */
1749 if (strcmp(item->name, "runbeforejob") == 0) {
1750 script->when = SCRIPT_Before;
1751 script->fail_on_error = true;
1752 script->set_target("");
1754 } else if (strcmp(item->name, "runafterjob") == 0) {
1755 script->when = SCRIPT_After;
1756 script->on_success = true;
1757 script->on_failure = false;
1758 script->set_target("");
1760 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1761 script->old_proto = true;
1762 script->when = SCRIPT_After;
1763 script->set_target("%c");
1764 script->on_success = true;
1765 script->on_failure = false;
1767 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1768 script->old_proto = true;
1769 script->when = SCRIPT_Before;
1770 script->set_target("%c");
1771 script->fail_on_error = true;
1773 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1774 script->when = SCRIPT_After;
1775 script->on_failure = true;
1776 script->on_success = false;
1777 script->set_target("");
1780 if (*runscripts == NULL) {
1781 *runscripts = New(alist(10, not_owned_by_alist));
1784 (*runscripts)->append(script);
1791 /* Store a bool in a bit field without modifing res_all.hdr
1792 * We can also add an option to store_bool to skip res_all.hdr
1794 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1796 lex_get_token(lc, T_NAME);
1797 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1798 *(bool *)(item->value) = true;
1799 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1800 *(bool *)(item->value) = false;
1802 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1808 * new RunScript items
1809 * name handler value code flags default_value
1811 static RES_ITEM runscript_items[] = {
1812 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1813 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1814 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1815 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1816 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1817 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1818 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1819 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1820 {NULL, NULL, {0}, 0, 0, 0}
1824 * Store RunScript info
1826 * Note, when this routine is called, we are inside a Job
1827 * resource. We treat the RunScript like a sort of
1828 * mini-resource within the Job resource.
1830 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1833 alist **runscripts = (alist **)(item->value) ;
1835 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1837 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1839 token = lex_get_token(lc, T_SKIP_EOL);
1841 if (token != T_BOB) {
1842 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1845 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1846 if (token == T_EOB) {
1849 if (token != T_IDENTIFIER) {
1850 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1852 for (i=0; runscript_items[i].name; i++) {
1853 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1854 token = lex_get_token(lc, T_SKIP_EOL);
1855 if (token != T_EQUALS) {
1856 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1859 /* Call item handler */
1860 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1867 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1872 if (res_runscript.command == NULL) {
1873 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1874 "command", "runscript");
1877 /* run on client by default */
1878 if (res_runscript.target == NULL) {
1879 res_runscript.set_target("%c");
1882 RUNSCRIPT *script = new_runscript();
1883 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1885 if (*runscripts == NULL) {
1886 *runscripts = New(alist(10, not_owned_by_alist));
1889 (*runscripts)->append(script);
1894 set_bit(index, res_all.hdr.item_present);