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 {"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;
1317 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1318 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1320 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1323 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1324 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1326 res->res_dir.messages = res_all.res_dir.messages;
1327 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1330 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1331 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1332 res_all.res_dir.hdr.name);
1334 /* we must explicitly copy the device alist pointer */
1335 res->res_store.device = res_all.res_store.device;
1339 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1340 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1341 res_all.res_dir.hdr.name);
1343 res->res_job.messages = res_all.res_job.messages;
1344 res->res_job.schedule = res_all.res_job.schedule;
1345 res->res_job.client = res_all.res_job.client;
1346 res->res_job.fileset = res_all.res_job.fileset;
1347 res->res_job.storage = res_all.res_job.storage;
1348 res->res_job.pool = res_all.res_job.pool;
1349 res->res_job.full_pool = res_all.res_job.full_pool;
1350 res->res_job.inc_pool = res_all.res_job.inc_pool;
1351 res->res_job.diff_pool = res_all.res_job.diff_pool;
1352 res->res_job.verify_job = res_all.res_job.verify_job;
1353 res->res_job.jobdefs = res_all.res_job.jobdefs;
1354 res->res_job.run_cmds = res_all.res_job.run_cmds;
1355 res->res_job.RunScripts = res_all.res_job.RunScripts;
1357 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1358 * is not very useful)
1359 * We have to set_bit(index, res_all.hdr.item_present);
1360 * or something like that
1363 /* we take RegexWhere before all other options */
1364 if (!res->res_job.RegexWhere
1366 (res->res_job.strip_prefix ||
1367 res->res_job.add_suffix ||
1368 res->res_job.add_prefix))
1370 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1371 res->res_job.add_prefix,
1372 res->res_job.add_suffix);
1373 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1374 bregexp_build_where(res->res_job.RegexWhere, len,
1375 res->res_job.strip_prefix,
1376 res->res_job.add_prefix,
1377 res->res_job.add_suffix);
1378 /* TODO: test bregexp */
1381 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1382 free(res->res_job.RestoreWhere);
1383 res->res_job.RestoreWhere = NULL;
1388 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1389 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1391 res->res_counter.Catalog = res_all.res_counter.Catalog;
1392 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1396 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1397 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1399 res->res_client.catalog = res_all.res_client.catalog;
1400 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1404 * Schedule is a bit different in that it contains a RUN record
1405 * chain which isn't a "named" resource. This chain was linked
1406 * in by run_conf.c during pass 2, so here we jam the pointer
1407 * into the Schedule resource.
1409 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1410 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1412 res->res_sch.run = res_all.res_sch.run;
1415 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1419 /* Note, the resource name was already saved during pass 1,
1420 * so here, we can just release it.
1422 if (res_all.res_dir.hdr.name) {
1423 free(res_all.res_dir.hdr.name);
1424 res_all.res_dir.hdr.name = NULL;
1426 if (res_all.res_dir.hdr.desc) {
1427 free(res_all.res_dir.hdr.desc);
1428 res_all.res_dir.hdr.desc = NULL;
1434 * The following code is only executed during pass 1
1438 size = sizeof(DIRRES);
1441 size = sizeof(CONRES);
1444 size =sizeof(CLIENT);
1447 size = sizeof(STORE);
1457 size = sizeof(FILESET);
1460 size = sizeof(SCHED);
1463 size = sizeof(POOL);
1466 size = sizeof(MSGS);
1469 size = sizeof(COUNTER);
1475 printf(_("Unknown resource type %d in save_resource.\n"), type);
1481 res = (URES *)malloc(size);
1482 memcpy(res, &res_all, size);
1483 if (!res_head[rindex]) {
1484 res_head[rindex] = (RES *)res; /* store first entry */
1485 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1486 res->res_dir.hdr.name, rindex);
1489 if (res->res_dir.hdr.name == NULL) {
1490 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1493 /* Add new res to end of chain */
1494 for (last=next=res_head[rindex]; next; next=next->next) {
1496 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1497 Emsg2(M_ERROR_TERM, 0,
1498 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1499 resources[rindex].name, res->res_dir.hdr.name);
1502 last->next = (RES *)res;
1503 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1504 res->res_dir.hdr.name, rindex, pass);
1510 * Store Device. Note, the resource is created upon the
1511 * first reference. The details of the resource are obtained
1512 * later from the SD.
1514 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1518 int rindex = R_DEVICE - r_first;
1519 int size = sizeof(DEVICE);
1523 token = lex_get_token(lc, T_NAME);
1524 if (!res_head[rindex]) {
1525 res = (URES *)malloc(size);
1526 memset(res, 0, size);
1527 res->res_dev.hdr.name = bstrdup(lc->str);
1528 res_head[rindex] = (RES *)res; /* store first entry */
1529 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1530 res->res_dir.hdr.name, rindex);
1533 /* See if it is already defined */
1534 for (next=res_head[rindex]; next->next; next=next->next) {
1535 if (strcmp(next->name, lc->str) == 0) {
1541 res = (URES *)malloc(size);
1542 memset(res, 0, size);
1543 res->res_dev.hdr.name = bstrdup(lc->str);
1544 next->next = (RES *)res;
1545 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1546 res->res_dir.hdr.name, rindex, pass);
1551 set_bit(index, res_all.hdr.item_present);
1553 store_alist_res(lc, item, index, pass);
1558 * Store JobType (backup, verify, restore)
1561 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1565 token = lex_get_token(lc, T_NAME);
1566 /* Store the type both pass 1 and pass 2 */
1567 for (i=0; migtypes[i].type_name; i++) {
1568 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1569 *(int *)(item->value) = migtypes[i].job_type;
1575 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1578 set_bit(index, res_all.hdr.item_present);
1584 * Store JobType (backup, verify, restore)
1587 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1591 token = lex_get_token(lc, T_NAME);
1592 /* Store the type both pass 1 and pass 2 */
1593 for (i=0; jobtypes[i].type_name; i++) {
1594 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1595 *(int *)(item->value) = jobtypes[i].job_type;
1601 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1604 set_bit(index, res_all.hdr.item_present);
1608 * Store Job Level (Full, Incremental, ...)
1611 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1615 token = lex_get_token(lc, T_NAME);
1616 /* Store the level pass 2 so that type is defined */
1617 for (i=0; joblevels[i].level_name; i++) {
1618 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1619 *(int *)(item->value) = joblevels[i].level;
1625 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1628 set_bit(index, res_all.hdr.item_present);
1632 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1635 token = lex_get_token(lc, T_NAME);
1636 /* Scan Replacement options */
1637 for (i=0; ReplaceOptions[i].name; i++) {
1638 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1639 *(int *)(item->value) = ReplaceOptions[i].token;
1645 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1648 set_bit(index, res_all.hdr.item_present);
1652 * Store ACL (access control list)
1655 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1660 token = lex_get_token(lc, T_STRING);
1662 if (((alist **)item->value)[item->code] == NULL) {
1663 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1664 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1666 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1667 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1669 token = lex_get_token(lc, T_ALL);
1670 if (token == T_COMMA) {
1671 continue; /* get another ACL */
1675 set_bit(index, res_all.hdr.item_present);
1678 /* We build RunScripts items here */
1679 static RUNSCRIPT res_runscript;
1681 /* Store a runscript->when in a bit field */
1682 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1684 lex_get_token(lc, T_NAME);
1686 if (strcasecmp(lc->str, "before") == 0) {
1687 *(int *)(item->value) = SCRIPT_Before ;
1688 } else if (strcasecmp(lc->str, "after") == 0) {
1689 *(int *)(item->value) = SCRIPT_After;
1690 } else if (strcasecmp(lc->str, "always") == 0) {
1691 *(int *)(item->value) = SCRIPT_Any;
1693 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1698 /* Store a runscript->target
1701 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1703 lex_get_token(lc, T_STRING);
1706 if (strcmp(lc->str, "%c") == 0) {
1707 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1708 } else if (strcasecmp(lc->str, "yes") == 0) {
1709 ((RUNSCRIPT*) item->value)->set_target("%c");
1710 } else if (strcasecmp(lc->str, "no") == 0) {
1711 ((RUNSCRIPT*) item->value)->set_target("");
1713 RES *res = GetResWithName(R_CLIENT, lc->str);
1715 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1716 lc->str, lc->line_no, lc->line);
1719 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1725 /* Store a runscript->command in a bit field
1728 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1730 lex_get_token(lc, T_STRING);
1733 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1738 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1740 lex_get_token(lc, T_STRING);
1741 alist **runscripts = (alist **)(item->value) ;
1744 RUNSCRIPT *script = new_runscript();
1746 script->set_command(lc->str);
1748 /* TODO: remove all script->old_proto with bacula 1.42 */
1750 if (strcmp(item->name, "runbeforejob") == 0) {
1751 script->when = SCRIPT_Before;
1752 script->fail_on_error = true;
1753 script->set_target("");
1755 } else if (strcmp(item->name, "runafterjob") == 0) {
1756 script->when = SCRIPT_After;
1757 script->on_success = true;
1758 script->on_failure = false;
1759 script->set_target("");
1761 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1762 script->old_proto = true;
1763 script->when = SCRIPT_After;
1764 script->set_target("%c");
1765 script->on_success = true;
1766 script->on_failure = false;
1768 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1769 script->old_proto = true;
1770 script->when = SCRIPT_Before;
1771 script->set_target("%c");
1772 script->fail_on_error = true;
1774 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1775 script->when = SCRIPT_After;
1776 script->on_failure = true;
1777 script->on_success = false;
1778 script->set_target("");
1781 if (*runscripts == NULL) {
1782 *runscripts = New(alist(10, not_owned_by_alist));
1785 (*runscripts)->append(script);
1792 /* Store a bool in a bit field without modifing res_all.hdr
1793 * We can also add an option to store_bool to skip res_all.hdr
1795 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1797 lex_get_token(lc, T_NAME);
1798 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1799 *(bool *)(item->value) = true;
1800 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1801 *(bool *)(item->value) = false;
1803 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1809 * new RunScript items
1810 * name handler value code flags default_value
1812 static RES_ITEM runscript_items[] = {
1813 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1814 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1815 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1816 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1817 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1818 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1819 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1820 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1821 {NULL, NULL, {0}, 0, 0, 0}
1825 * Store RunScript info
1827 * Note, when this routine is called, we are inside a Job
1828 * resource. We treat the RunScript like a sort of
1829 * mini-resource within the Job resource.
1831 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1834 alist **runscripts = (alist **)(item->value) ;
1836 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1838 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1840 token = lex_get_token(lc, T_SKIP_EOL);
1842 if (token != T_BOB) {
1843 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1846 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1847 if (token == T_EOB) {
1850 if (token != T_IDENTIFIER) {
1851 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1853 for (i=0; runscript_items[i].name; i++) {
1854 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1855 token = lex_get_token(lc, T_SKIP_EOL);
1856 if (token != T_EQUALS) {
1857 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1860 /* Call item handler */
1861 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1868 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1873 if (res_runscript.command == NULL) {
1874 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1875 "command", "runscript");
1878 /* run on client by default */
1879 if (res_runscript.target == NULL) {
1880 res_runscript.set_target("%c");
1883 RUNSCRIPT *script = new_runscript();
1884 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1886 if (*runscripts == NULL) {
1887 *runscripts = New(alist(10, not_owned_by_alist));
1890 (*runscripts)->append(script);
1895 set_bit(index, res_all.hdr.item_present);