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 {"tlsenable", store_bool, ITEM(res_dir.tls_enable), 0, 0, 0},
126 {"tlsrequire", store_bool, ITEM(res_dir.tls_require), 0, 0, 0},
127 {"tlsverifypeer", store_bool, ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
128 {"tlscacertificatefile", store_dir, ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
129 {"tlscacertificatedir", store_dir, ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
130 {"tlscertificate", store_dir, ITEM(res_dir.tls_certfile), 0, 0, 0},
131 {"tlskey", store_dir, ITEM(res_dir.tls_keyfile), 0, 0, 0},
132 {"tlsdhfile", store_dir, ITEM(res_dir.tls_dhfile), 0, 0, 0},
133 {"tlsallowedcn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
134 {NULL, NULL, {0}, 0, 0, 0}
140 * name handler value code flags default_value
142 static RES_ITEM con_items[] = {
143 {"name", store_name, ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
144 {"description", store_str, ITEM(res_con.hdr.desc), 0, 0, 0},
145 {"password", store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
146 {"jobacl", store_acl, ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
147 {"clientacl", store_acl, ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
148 {"storageacl", store_acl, ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
149 {"scheduleacl", store_acl, ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
150 {"runacl", store_acl, ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
151 {"poolacl", store_acl, ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
152 {"commandacl", store_acl, ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
153 {"filesetacl", store_acl, ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
154 {"catalogacl", store_acl, ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
155 {"whereacl", store_acl, ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
156 {"tlsenable", store_bool, ITEM(res_con.tls_enable), 0, 0, 0},
157 {"tlsrequire", store_bool, ITEM(res_con.tls_require), 0, 0, 0},
158 {"tlsverifypeer", store_bool, ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
159 {"tlscacertificatefile", store_dir, ITEM(res_con.tls_ca_certfile), 0, 0, 0},
160 {"tlscacertificatedir", store_dir, ITEM(res_con.tls_ca_certdir), 0, 0, 0},
161 {"tlscertificate", store_dir, ITEM(res_con.tls_certfile), 0, 0, 0},
162 {"tlskey", store_dir, ITEM(res_con.tls_keyfile), 0, 0, 0},
163 {"tlsdhfile", store_dir, ITEM(res_con.tls_dhfile), 0, 0, 0},
164 {"tlsallowedcn", store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
165 {NULL, NULL, {0}, 0, 0, 0}
170 * Client or File daemon resource
172 * name handler value code flags default_value
175 static RES_ITEM cli_items[] = {
176 {"name", store_name, ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
177 {"description", store_str, ITEM(res_client.hdr.desc), 0, 0, 0},
178 {"address", store_str, ITEM(res_client.address), 0, ITEM_REQUIRED, 0},
179 {"fdaddress", store_str, ITEM(res_client.address), 0, 0, 0},
180 {"fdport", store_pint, ITEM(res_client.FDport), 0, ITEM_DEFAULT, 9102},
181 {"password", store_password, ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
182 {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
183 {"catalog", store_res, ITEM(res_client.catalog), R_CATALOG, ITEM_REQUIRED, 0},
184 {"fileretention", store_time, ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
185 {"jobretention", store_time, ITEM(res_client.JobRetention), 0, ITEM_DEFAULT, 60*60*24*180},
186 {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0},
187 {"autoprune", store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
188 {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
189 {"tlsenable", store_bool, ITEM(res_client.tls_enable), 0, 0, 0},
190 {"tlsrequire", store_bool, ITEM(res_client.tls_require), 0, 0, 0},
191 {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0},
192 {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0},
193 {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0},
194 {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0},
195 {"tlsallowedcn", store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
196 {NULL, NULL, {0}, 0, 0, 0}
199 /* Storage daemon resource
201 * name handler value code flags default_value
203 static RES_ITEM store_items[] = {
204 {"name", store_name, ITEM(res_store.hdr.name), 0, ITEM_REQUIRED, 0},
205 {"description", store_str, ITEM(res_store.hdr.desc), 0, 0, 0},
206 {"sdport", store_pint, ITEM(res_store.SDport), 0, ITEM_DEFAULT, 9103},
207 {"address", store_str, ITEM(res_store.address), 0, ITEM_REQUIRED, 0},
208 {"sdaddress", store_str, ITEM(res_store.address), 0, 0, 0},
209 {"password", store_password, ITEM(res_store.password), 0, ITEM_REQUIRED, 0},
210 {"sdpassword", store_password, ITEM(res_store.password), 0, 0, 0},
211 {"device", store_device, ITEM(res_store.device), R_DEVICE, ITEM_REQUIRED, 0},
212 {"mediatype", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
213 {"autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
214 {"enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true},
215 {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
216 {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
217 {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
218 {"tlsenable", store_bool, ITEM(res_store.tls_enable), 0, 0, 0},
219 {"tlsrequire", store_bool, ITEM(res_store.tls_require), 0, 0, 0},
220 {"tlscacertificatefile", store_dir, ITEM(res_store.tls_ca_certfile), 0, 0, 0},
221 {"tlscacertificatedir", store_dir, ITEM(res_store.tls_ca_certdir), 0, 0, 0},
222 {"tlscertificate", store_dir, ITEM(res_store.tls_certfile), 0, 0, 0},
223 {"tlskey", store_dir, ITEM(res_store.tls_keyfile), 0, 0, 0},
224 {NULL, NULL, {0}, 0, 0, 0}
228 * Catalog Resource Directives
230 * name handler value code flags default_value
232 static RES_ITEM cat_items[] = {
233 {"name", store_name, ITEM(res_cat.hdr.name), 0, ITEM_REQUIRED, 0},
234 {"description", store_str, ITEM(res_cat.hdr.desc), 0, 0, 0},
235 {"address", store_str, ITEM(res_cat.db_address), 0, 0, 0},
236 {"dbaddress", store_str, ITEM(res_cat.db_address), 0, 0, 0},
237 {"dbport", store_pint, ITEM(res_cat.db_port), 0, 0, 0},
238 /* keep this password as store_str for the moment */
239 {"password", store_str, ITEM(res_cat.db_password), 0, 0, 0},
240 {"dbpassword", store_str, ITEM(res_cat.db_password), 0, 0, 0},
241 {"user", store_str, ITEM(res_cat.db_user), 0, 0, 0},
242 {"dbname", store_str, ITEM(res_cat.db_name), 0, ITEM_REQUIRED, 0},
243 {"dbsocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0},
244 /* Turned off for the moment */
245 {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
246 {NULL, NULL, {0}, 0, 0, 0}
250 * Job Resource Directives
252 * name handler value code flags default_value
254 RES_ITEM job_items[] = {
255 {"name", store_name, ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
256 {"description", store_str, ITEM(res_job.hdr.desc), 0, 0, 0},
257 {"type", store_jobtype, ITEM(res_job.JobType), 0, ITEM_REQUIRED, 0},
258 {"level", store_level, ITEM(res_job.JobLevel), 0, 0, 0},
259 {"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
260 {"storage", store_alist_res, ITEM(res_job.storage), R_STORAGE, 0, 0},
261 {"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
262 {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
263 {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
264 {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
265 {"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
266 {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
267 {"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
268 {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
269 {"jobtoverify", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
270 {"jobdefs", store_res, ITEM(res_job.jobdefs), R_JOBDEFS, 0, 0},
271 {"run", store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
272 /* Root of where to restore files */
273 {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
274 {"regexwhere", store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
275 {"stripprefix", store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
276 {"addprefix", store_str, ITEM(res_job.add_prefix), 0, 0, 0},
277 {"addsuffix", store_str, ITEM(res_job.add_suffix), 0, 0, 0},
278 /* Where to find bootstrap during restore */
279 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
280 /* Where to write bootstrap file during backup */
281 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
282 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
283 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
284 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
285 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
286 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
287 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
288 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
289 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
290 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
291 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
292 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
293 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
294 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
295 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
296 {"optimizejobscheduling",store_bool, ITEM(res_job.OptimizeJobScheduling), 0, ITEM_DEFAULT, false},
297 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
298 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
299 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
300 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
301 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
302 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
303 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
304 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
305 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
306 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
307 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
308 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
309 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
310 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
311 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
312 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
313 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
314 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
315 {NULL, NULL, {0}, 0, 0, 0}
320 * name handler value code flags default_value
322 static RES_ITEM fs_items[] = {
323 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
324 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
325 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
326 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
327 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
328 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
329 {NULL, NULL, {0}, 0, 0, 0}
332 /* Schedule -- see run_conf.c */
335 * name handler value code flags default_value
337 static RES_ITEM sch_items[] = {
338 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
339 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
340 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
341 {NULL, NULL, {0}, 0, 0, 0}
346 * name handler value code flags default_value
348 static RES_ITEM pool_items[] = {
349 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
350 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
351 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
352 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
353 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
354 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
355 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
356 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
357 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
358 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
359 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
360 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
361 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
362 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
363 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
364 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
365 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
366 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
367 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
368 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
369 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
370 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
371 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
372 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
373 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
374 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
375 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
376 {"catalog", store_res, ITEM(res_pool.Catalog), R_CATALOG, 0, 0},
377 {NULL, NULL, {0}, 0, 0, 0}
382 * name handler value code flags default_value
384 static RES_ITEM counter_items[] = {
385 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
386 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
387 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
388 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
389 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
390 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
391 {NULL, NULL, {0}, 0, 0, 0}
395 /* Message resource */
396 extern RES_ITEM msgs_items[];
399 * This is the master resource definition.
400 * It must have one item for each of the resources.
402 * NOTE!!! keep it in the same order as the R_codes
403 * or eliminate all resources[rindex].name
405 * name items rcode res_head
407 RES_TABLE resources[] = {
408 {"director", dir_items, R_DIRECTOR},
409 {"client", cli_items, R_CLIENT},
410 {"job", job_items, R_JOB},
411 {"storage", store_items, R_STORAGE},
412 {"catalog", cat_items, R_CATALOG},
413 {"schedule", sch_items, R_SCHEDULE},
414 {"fileset", fs_items, R_FILESET},
415 {"pool", pool_items, R_POOL},
416 {"messages", msgs_items, R_MSGS},
417 {"counter", counter_items, R_COUNTER},
418 {"console", con_items, R_CONSOLE},
419 {"jobdefs", job_items, R_JOBDEFS},
420 {"device", NULL, R_DEVICE}, /* info obtained from SD */
425 /* Keywords (RHS) permitted in Job Level records
427 * level_name level job_type
429 struct s_jl joblevels[] = {
430 {"Full", L_FULL, JT_BACKUP},
431 {"Base", L_BASE, JT_BACKUP},
432 {"Incremental", L_INCREMENTAL, JT_BACKUP},
433 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
434 {"Since", L_SINCE, JT_BACKUP},
435 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
436 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
437 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
438 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
439 {"Data", L_VERIFY_DATA, JT_VERIFY},
440 {" ", L_NONE, JT_ADMIN},
441 {" ", L_NONE, JT_RESTORE},
445 /* Keywords (RHS) permitted in Job type records
449 struct s_jt jobtypes[] = {
450 {"backup", JT_BACKUP},
452 {"verify", JT_VERIFY},
453 {"restore", JT_RESTORE},
454 {"migrate", JT_MIGRATE},
459 /* Keywords (RHS) permitted in Selection type records
463 struct s_jt migtypes[] = {
464 {"smallestvolume", MT_SMALLEST_VOL},
465 {"oldestvolume", MT_OLDEST_VOL},
466 {"pooloccupancy", MT_POOL_OCCUPANCY},
467 {"pooltime", MT_POOL_TIME},
468 {"client", MT_CLIENT},
469 {"volume", MT_VOLUME},
471 {"sqlquery", MT_SQLQUERY},
477 /* Options permitted in Restore replace= */
478 struct s_kw ReplaceOptions[] = {
479 {"always", REPLACE_ALWAYS},
480 {"ifnewer", REPLACE_IFNEWER},
481 {"ifolder", REPLACE_IFOLDER},
482 {"never", REPLACE_NEVER},
486 const char *level_to_str(int level)
489 static char level_no[30];
490 const char *str = level_no;
492 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
493 for (i=0; joblevels[i].level_name; i++) {
494 if (level == joblevels[i].level) {
495 str = joblevels[i].level_name;
502 /* Dump contents of resource */
503 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
505 URES *res = (URES *)reshdr;
507 char ed1[100], ed2[100], ed3[100];
511 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
514 if (type < 0) { /* no recursion */
520 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
521 reshdr->name, res->res_dir.MaxConcurrentJobs,
522 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
523 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
524 if (res->res_dir.query_file) {
525 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
527 if (res->res_dir.messages) {
528 sendit(sock, _(" --> "));
529 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
533 sendit(sock, _("Console: name=%s SSL=%d\n"),
534 res->res_con.hdr.name, res->res_con.tls_enable);
537 if (res->res_counter.WrapCounter) {
538 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
539 res->res_counter.hdr.name, res->res_counter.MinValue,
540 res->res_counter.MaxValue, res->res_counter.CurrentValue,
541 res->res_counter.WrapCounter->hdr.name);
543 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
544 res->res_counter.hdr.name, res->res_counter.MinValue,
545 res->res_counter.MaxValue);
547 if (res->res_counter.Catalog) {
548 sendit(sock, _(" --> "));
549 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
554 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
555 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
556 res->res_client.MaxConcurrentJobs);
557 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
558 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
559 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
560 res->res_client.AutoPrune);
561 if (res->res_client.catalog) {
562 sendit(sock, _(" --> "));
563 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
570 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
571 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
572 " poolid=%s volname=%s MediaType=%s\n"),
573 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
574 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
575 dev->offline, dev->autochanger,
576 edit_uint64(dev->PoolId, ed1),
577 dev->VolumeName, dev->MediaType);
581 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
582 " DeviceName=%s MediaType=%s StorageId=%s\n"),
583 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
584 res->res_store.MaxConcurrentJobs,
585 res->res_store.dev_name(),
586 res->res_store.media_type,
587 edit_int64(res->res_store.StorageId, ed1));
591 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
592 " db_user=%s MutliDBConn=%d\n"),
593 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
594 res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
595 res->res_cat.mult_db_connections);
600 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
601 type == R_JOB ? _("Job") : _("JobDefs"),
602 res->res_job.hdr.name, res->res_job.JobType,
603 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
604 res->res_job.enabled);
605 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
606 res->res_job.MaxConcurrentJobs,
607 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
608 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
609 res->res_job.spool_data, res->res_job.write_part_after_job);
610 if (res->res_job.JobType == JT_MIGRATE) {
611 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
613 if (res->res_job.client) {
614 sendit(sock, _(" --> "));
615 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
617 if (res->res_job.fileset) {
618 sendit(sock, _(" --> "));
619 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
621 if (res->res_job.schedule) {
622 sendit(sock, _(" --> "));
623 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
625 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
626 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
628 if (res->res_job.RegexWhere) {
629 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
631 if (res->res_job.RestoreBootstrap) {
632 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
634 if (res->res_job.WriteBootstrap) {
635 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
637 if (res->res_job.storage) {
639 foreach_alist(store, res->res_job.storage) {
640 sendit(sock, _(" --> "));
641 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
644 if (res->res_job.RunScripts) {
646 foreach_alist(script, res->res_job.RunScripts) {
647 sendit(sock, _(" --> RunScript\n"));
648 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
649 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
650 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
651 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
652 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
653 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
656 if (res->res_job.pool) {
657 sendit(sock, _(" --> "));
658 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
660 if (res->res_job.full_pool) {
661 sendit(sock, _(" --> "));
662 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
664 if (res->res_job.inc_pool) {
665 sendit(sock, _(" --> "));
666 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
668 if (res->res_job.diff_pool) {
669 sendit(sock, _(" --> "));
670 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
672 if (res->res_job.verify_job) {
673 sendit(sock, _(" --> "));
674 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
676 if (res->res_job.run_cmds) {
678 foreach_alist(runcmd, res->res_job.run_cmds) {
679 sendit(sock, _(" --> Run=%s\n"), runcmd);
682 if (res->res_job.selection_pattern) {
683 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
685 if (res->res_job.messages) {
686 sendit(sock, _(" --> "));
687 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
694 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
695 for (i=0; i<res->res_fs.num_includes; i++) {
696 INCEXE *incexe = res->res_fs.include_items[i];
697 for (j=0; j<incexe->num_opts; j++) {
698 FOPTS *fo = incexe->opts_list[j];
699 sendit(sock, " O %s\n", fo->opts);
701 bool enhanced_wild = false;
702 for (k=0; fo->opts[k]!='\0'; k++) {
703 if (fo->opts[k]=='W') {
704 enhanced_wild = true;
709 for (k=0; k<fo->regex.size(); k++) {
710 sendit(sock, " R %s\n", fo->regex.get(k));
712 for (k=0; k<fo->regexdir.size(); k++) {
713 sendit(sock, " RD %s\n", fo->regexdir.get(k));
715 for (k=0; k<fo->regexfile.size(); k++) {
716 sendit(sock, " RF %s\n", fo->regexfile.get(k));
718 for (k=0; k<fo->wild.size(); k++) {
719 sendit(sock, " W %s\n", fo->wild.get(k));
721 for (k=0; k<fo->wilddir.size(); k++) {
722 sendit(sock, " WD %s\n", fo->wilddir.get(k));
724 for (k=0; k<fo->wildfile.size(); k++) {
725 sendit(sock, " WF %s\n", fo->wildfile.get(k));
727 for (k=0; k<fo->wildbase.size(); k++) {
728 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
730 for (k=0; k<fo->base.size(); k++) {
731 sendit(sock, " B %s\n", fo->base.get(k));
733 for (k=0; k<fo->fstype.size(); k++) {
734 sendit(sock, " X %s\n", fo->fstype.get(k));
736 for (k=0; k<fo->drivetype.size(); k++) {
737 sendit(sock, " XD %s\n", fo->drivetype.get(k));
740 sendit(sock, " D %s\n", fo->reader);
743 sendit(sock, " T %s\n", fo->writer);
745 sendit(sock, " N\n");
747 for (j=0; j<incexe->name_list.size(); j++) {
748 sendit(sock, " I %s\n", incexe->name_list.get(j));
750 if (incexe->name_list.size()) {
751 sendit(sock, " N\n");
755 for (i=0; i<res->res_fs.num_excludes; i++) {
756 INCEXE *incexe = res->res_fs.exclude_items[i];
757 for (j=0; j<incexe->name_list.size(); j++) {
758 sendit(sock, " E %s\n", incexe->name_list.get(j));
760 if (incexe->name_list.size()) {
761 sendit(sock, " N\n");
768 if (res->res_sch.run) {
770 RUN *run = res->res_sch.run;
771 char buf[1000], num[30];
772 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
777 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
778 bstrncpy(buf, _(" hour="), sizeof(buf));
779 for (i=0; i<24; i++) {
780 if (bit_is_set(i, run->hour)) {
781 bsnprintf(num, sizeof(num), "%d ", i);
782 bstrncat(buf, num, sizeof(buf));
785 bstrncat(buf, "\n", sizeof(buf));
787 bstrncpy(buf, _(" mday="), sizeof(buf));
788 for (i=0; i<31; i++) {
789 if (bit_is_set(i, run->mday)) {
790 bsnprintf(num, sizeof(num), "%d ", i);
791 bstrncat(buf, num, sizeof(buf));
794 bstrncat(buf, "\n", sizeof(buf));
796 bstrncpy(buf, _(" month="), sizeof(buf));
797 for (i=0; i<12; i++) {
798 if (bit_is_set(i, run->month)) {
799 bsnprintf(num, sizeof(num), "%d ", i);
800 bstrncat(buf, num, sizeof(buf));
803 bstrncat(buf, "\n", sizeof(buf));
805 bstrncpy(buf, _(" wday="), sizeof(buf));
806 for (i=0; i<7; i++) {
807 if (bit_is_set(i, run->wday)) {
808 bsnprintf(num, sizeof(num), "%d ", i);
809 bstrncat(buf, num, sizeof(buf));
812 bstrncat(buf, "\n", sizeof(buf));
814 bstrncpy(buf, _(" wom="), sizeof(buf));
815 for (i=0; i<5; i++) {
816 if (bit_is_set(i, run->wom)) {
817 bsnprintf(num, sizeof(num), "%d ", i);
818 bstrncat(buf, num, sizeof(buf));
821 bstrncat(buf, "\n", sizeof(buf));
823 bstrncpy(buf, _(" woy="), sizeof(buf));
824 for (i=0; i<54; i++) {
825 if (bit_is_set(i, run->woy)) {
826 bsnprintf(num, sizeof(num), "%d ", i);
827 bstrncat(buf, num, sizeof(buf));
830 bstrncat(buf, "\n", sizeof(buf));
832 sendit(sock, _(" mins=%d\n"), run->minute);
834 sendit(sock, _(" --> "));
835 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
838 sendit(sock, _(" --> "));
839 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
842 sendit(sock, _(" --> "));
843 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
845 /* If another Run record is chained in, go print it */
851 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
856 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
857 res->res_pool.pool_type);
858 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
859 res->res_pool.use_catalog, res->res_pool.use_volume_once,
860 res->res_pool.catalog_files);
861 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
862 res->res_pool.max_volumes, res->res_pool.AutoPrune,
863 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
864 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
865 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
866 res->res_pool.Recycle,
867 NPRT(res->res_pool.label_format));
868 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
869 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
870 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
871 res->res_pool.recycle_oldest_volume,
872 res->res_pool.purge_oldest_volume);
873 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
874 res->res_pool.MaxVolJobs,
875 res->res_pool.MaxVolFiles,
876 edit_uint64(res->res_pool.MaxVolFiles, ed1));
877 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
878 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
879 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
880 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
881 if (res->res_pool.NextPool) {
882 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
884 if (res->res_pool.RecyclePool) {
885 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
887 if (res->res_pool.Catalog) {
888 sendit(sock, _(" Catalog=%s\n"), res->res_pool.Catalog->name());
890 if (res->res_pool.storage) {
892 foreach_alist(store, res->res_pool.storage) {
893 sendit(sock, _(" --> "));
894 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
897 if (res->res_pool.CopyPool) {
899 foreach_alist(copy, res->res_pool.CopyPool) {
900 sendit(sock, _(" --> "));
901 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
908 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
909 if (res->res_msgs.mail_cmd)
910 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
911 if (res->res_msgs.operator_cmd)
912 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
916 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
919 if (recurse && res->res_dir.hdr.next) {
920 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
925 * Free all the members of an INCEXE structure
927 static void free_incexe(INCEXE *incexe)
929 incexe->name_list.destroy();
930 for (int i=0; i<incexe->num_opts; i++) {
931 FOPTS *fopt = incexe->opts_list[i];
932 fopt->regex.destroy();
933 fopt->regexdir.destroy();
934 fopt->regexfile.destroy();
935 fopt->wild.destroy();
936 fopt->wilddir.destroy();
937 fopt->wildfile.destroy();
938 fopt->wildbase.destroy();
939 fopt->base.destroy();
940 fopt->fstype.destroy();
941 fopt->drivetype.destroy();
950 if (incexe->opts_list) {
951 free(incexe->opts_list);
957 * Free memory of resource -- called when daemon terminates.
958 * NB, we don't need to worry about freeing any references
959 * to other resources as they will be freed when that
960 * resource chain is traversed. Mainly we worry about freeing
961 * allocated strings (names).
963 void free_resource(RES *sres, int type)
966 RES *nres; /* next resource if linked */
967 URES *res = (URES *)sres;
972 /* common stuff -- free the resource name and description */
973 nres = (RES *)res->res_dir.hdr.next;
974 if (res->res_dir.hdr.name) {
975 free(res->res_dir.hdr.name);
977 if (res->res_dir.hdr.desc) {
978 free(res->res_dir.hdr.desc);
983 if (res->res_dir.working_directory) {
984 free(res->res_dir.working_directory);
986 if (res->res_dir.scripts_directory) {
987 free((char *)res->res_dir.scripts_directory);
989 if (res->res_dir.pid_directory) {
990 free(res->res_dir.pid_directory);
992 if (res->res_dir.subsys_directory) {
993 free(res->res_dir.subsys_directory);
995 if (res->res_dir.password) {
996 free(res->res_dir.password);
998 if (res->res_dir.query_file) {
999 free(res->res_dir.query_file);
1001 if (res->res_dir.DIRaddrs) {
1002 free_addresses(res->res_dir.DIRaddrs);
1004 if (res->res_dir.tls_ctx) {
1005 free_tls_context(res->res_dir.tls_ctx);
1007 if (res->res_dir.tls_ca_certfile) {
1008 free(res->res_dir.tls_ca_certfile);
1010 if (res->res_dir.tls_ca_certdir) {
1011 free(res->res_dir.tls_ca_certdir);
1013 if (res->res_dir.tls_certfile) {
1014 free(res->res_dir.tls_certfile);
1016 if (res->res_dir.tls_keyfile) {
1017 free(res->res_dir.tls_keyfile);
1019 if (res->res_dir.tls_dhfile) {
1020 free(res->res_dir.tls_dhfile);
1022 if (res->res_dir.tls_allowed_cns) {
1023 delete res->res_dir.tls_allowed_cns;
1030 if (res->res_con.password) {
1031 free(res->res_con.password);
1033 if (res->res_con.tls_ctx) {
1034 free_tls_context(res->res_con.tls_ctx);
1036 if (res->res_con.tls_ca_certfile) {
1037 free(res->res_con.tls_ca_certfile);
1039 if (res->res_con.tls_ca_certdir) {
1040 free(res->res_con.tls_ca_certdir);
1042 if (res->res_con.tls_certfile) {
1043 free(res->res_con.tls_certfile);
1045 if (res->res_con.tls_keyfile) {
1046 free(res->res_con.tls_keyfile);
1048 if (res->res_con.tls_dhfile) {
1049 free(res->res_con.tls_dhfile);
1051 if (res->res_con.tls_allowed_cns) {
1052 delete res->res_con.tls_allowed_cns;
1054 for (int i=0; i<Num_ACL; i++) {
1055 if (res->res_con.ACL_lists[i]) {
1056 delete res->res_con.ACL_lists[i];
1057 res->res_con.ACL_lists[i] = NULL;
1062 if (res->res_client.address) {
1063 free(res->res_client.address);
1065 if (res->res_client.password) {
1066 free(res->res_client.password);
1068 if (res->res_client.tls_ctx) {
1069 free_tls_context(res->res_client.tls_ctx);
1071 if (res->res_client.tls_ca_certfile) {
1072 free(res->res_client.tls_ca_certfile);
1074 if (res->res_client.tls_ca_certdir) {
1075 free(res->res_client.tls_ca_certdir);
1077 if (res->res_client.tls_certfile) {
1078 free(res->res_client.tls_certfile);
1080 if (res->res_client.tls_keyfile) {
1081 free(res->res_client.tls_keyfile);
1083 if (res->res_client.tls_allowed_cns) {
1084 delete res->res_client.tls_allowed_cns;
1088 if (res->res_store.address) {
1089 free(res->res_store.address);
1091 if (res->res_store.password) {
1092 free(res->res_store.password);
1094 if (res->res_store.media_type) {
1095 free(res->res_store.media_type);
1097 if (res->res_store.device) {
1098 delete res->res_store.device;
1100 if (res->res_store.tls_ctx) {
1101 free_tls_context(res->res_store.tls_ctx);
1103 if (res->res_store.tls_ca_certfile) {
1104 free(res->res_store.tls_ca_certfile);
1106 if (res->res_store.tls_ca_certdir) {
1107 free(res->res_store.tls_ca_certdir);
1109 if (res->res_store.tls_certfile) {
1110 free(res->res_store.tls_certfile);
1112 if (res->res_store.tls_keyfile) {
1113 free(res->res_store.tls_keyfile);
1117 if (res->res_cat.db_address) {
1118 free(res->res_cat.db_address);
1120 if (res->res_cat.db_socket) {
1121 free(res->res_cat.db_socket);
1123 if (res->res_cat.db_user) {
1124 free(res->res_cat.db_user);
1126 if (res->res_cat.db_name) {
1127 free(res->res_cat.db_name);
1129 if (res->res_cat.db_password) {
1130 free(res->res_cat.db_password);
1134 if ((num=res->res_fs.num_includes)) {
1135 while (--num >= 0) {
1136 free_incexe(res->res_fs.include_items[num]);
1138 free(res->res_fs.include_items);
1140 res->res_fs.num_includes = 0;
1141 if ((num=res->res_fs.num_excludes)) {
1142 while (--num >= 0) {
1143 free_incexe(res->res_fs.exclude_items[num]);
1145 free(res->res_fs.exclude_items);
1147 res->res_fs.num_excludes = 0;
1150 if (res->res_pool.pool_type) {
1151 free(res->res_pool.pool_type);
1153 if (res->res_pool.label_format) {
1154 free(res->res_pool.label_format);
1156 if (res->res_pool.cleaning_prefix) {
1157 free(res->res_pool.cleaning_prefix);
1159 if (res->res_pool.storage) {
1160 delete res->res_pool.storage;
1164 if (res->res_sch.run) {
1166 nrun = res->res_sch.run;
1176 if (res->res_job.RestoreWhere) {
1177 free(res->res_job.RestoreWhere);
1179 if (res->res_job.RegexWhere) {
1180 free(res->res_job.RegexWhere);
1182 if (res->res_job.strip_prefix) {
1183 free(res->res_job.strip_prefix);
1185 if (res->res_job.add_prefix) {
1186 free(res->res_job.add_prefix);
1188 if (res->res_job.add_suffix) {
1189 free(res->res_job.add_suffix);
1191 if (res->res_job.RestoreBootstrap) {
1192 free(res->res_job.RestoreBootstrap);
1194 if (res->res_job.WriteBootstrap) {
1195 free(res->res_job.WriteBootstrap);
1197 if (res->res_job.selection_pattern) {
1198 free(res->res_job.selection_pattern);
1200 if (res->res_job.run_cmds) {
1201 delete res->res_job.run_cmds;
1203 if (res->res_job.storage) {
1204 delete res->res_job.storage;
1206 if (res->res_job.RunScripts) {
1207 free_runscripts(res->res_job.RunScripts);
1208 delete res->res_job.RunScripts;
1212 if (res->res_msgs.mail_cmd) {
1213 free(res->res_msgs.mail_cmd);
1215 if (res->res_msgs.operator_cmd) {
1216 free(res->res_msgs.operator_cmd);
1218 free_msgs_res((MSGS *)res); /* free message resource */
1222 printf(_("Unknown resource type %d in free_resource.\n"), type);
1224 /* Common stuff again -- free the resource, recurse to next one */
1229 free_resource(nres, type);
1234 * Save the new resource by chaining it into the head list for
1235 * the resource. If this is pass 2, we update any resource
1236 * pointers because they may not have been defined until
1239 void save_resource(int type, RES_ITEM *items, int pass)
1242 int rindex = type - r_first;
1246 /* Check Job requirements after applying JobDefs */
1247 if (type != R_JOB && type != R_JOBDEFS) {
1249 * Ensure that all required items are present
1251 for (i=0; items[i].name; i++) {
1252 if (items[i].flags & ITEM_REQUIRED) {
1253 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1254 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1255 items[i].name, resources[rindex]);
1258 /* If this triggers, take a look at lib/parse_conf.h */
1259 if (i >= MAX_RES_ITEMS) {
1260 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1263 } else if (type == R_JOB) {
1265 * Ensure that the name item is present
1267 if (items[0].flags & ITEM_REQUIRED) {
1268 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1269 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1270 items[0].name, resources[rindex]);
1276 * During pass 2 in each "store" routine, we looked up pointers
1277 * to all the resources referrenced in the current resource, now we
1278 * must copy their addresses from the static record to the allocated
1283 /* Resources not containing a resource */
1291 * Resources containing another resource or alist. First
1292 * look up the resource which contains another resource. It
1293 * was written during pass 1. Then stuff in the pointers to
1294 * the resources it contains, which were inserted this pass.
1295 * Finally, it will all be stored back.
1298 /* Find resource saved in pass 1 */
1299 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1300 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1302 /* Explicitly copy resource pointers from this pass (res_all) */
1303 res->res_pool.NextPool = res_all.res_pool.NextPool;
1304 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1305 res->res_pool.storage = res_all.res_pool.storage;
1308 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1309 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1311 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1314 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1315 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1317 res->res_dir.messages = res_all.res_dir.messages;
1318 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1321 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1322 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1323 res_all.res_dir.hdr.name);
1325 /* we must explicitly copy the device alist pointer */
1326 res->res_store.device = res_all.res_store.device;
1330 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1331 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1332 res_all.res_dir.hdr.name);
1334 res->res_job.messages = res_all.res_job.messages;
1335 res->res_job.schedule = res_all.res_job.schedule;
1336 res->res_job.client = res_all.res_job.client;
1337 res->res_job.fileset = res_all.res_job.fileset;
1338 res->res_job.storage = res_all.res_job.storage;
1339 res->res_job.pool = res_all.res_job.pool;
1340 res->res_job.full_pool = res_all.res_job.full_pool;
1341 res->res_job.inc_pool = res_all.res_job.inc_pool;
1342 res->res_job.diff_pool = res_all.res_job.diff_pool;
1343 res->res_job.verify_job = res_all.res_job.verify_job;
1344 res->res_job.jobdefs = res_all.res_job.jobdefs;
1345 res->res_job.run_cmds = res_all.res_job.run_cmds;
1346 res->res_job.RunScripts = res_all.res_job.RunScripts;
1348 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1349 * is not very useful)
1350 * We have to set_bit(index, res_all.hdr.item_present);
1351 * or something like that
1354 /* we take RegexWhere before all other options */
1355 if (!res->res_job.RegexWhere
1357 (res->res_job.strip_prefix ||
1358 res->res_job.add_suffix ||
1359 res->res_job.add_prefix))
1361 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1362 res->res_job.add_prefix,
1363 res->res_job.add_suffix);
1364 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1365 bregexp_build_where(res->res_job.RegexWhere, len,
1366 res->res_job.strip_prefix,
1367 res->res_job.add_prefix,
1368 res->res_job.add_suffix);
1369 /* TODO: test bregexp */
1372 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1373 free(res->res_job.RestoreWhere);
1374 res->res_job.RestoreWhere = NULL;
1379 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1380 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1382 res->res_counter.Catalog = res_all.res_counter.Catalog;
1383 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1387 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1388 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1390 res->res_client.catalog = res_all.res_client.catalog;
1391 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1395 * Schedule is a bit different in that it contains a RUN record
1396 * chain which isn't a "named" resource. This chain was linked
1397 * in by run_conf.c during pass 2, so here we jam the pointer
1398 * into the Schedule resource.
1400 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1401 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1403 res->res_sch.run = res_all.res_sch.run;
1406 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1410 /* Note, the resource name was already saved during pass 1,
1411 * so here, we can just release it.
1413 if (res_all.res_dir.hdr.name) {
1414 free(res_all.res_dir.hdr.name);
1415 res_all.res_dir.hdr.name = NULL;
1417 if (res_all.res_dir.hdr.desc) {
1418 free(res_all.res_dir.hdr.desc);
1419 res_all.res_dir.hdr.desc = NULL;
1425 * The following code is only executed during pass 1
1429 size = sizeof(DIRRES);
1432 size = sizeof(CONRES);
1435 size =sizeof(CLIENT);
1438 size = sizeof(STORE);
1448 size = sizeof(FILESET);
1451 size = sizeof(SCHED);
1454 size = sizeof(POOL);
1457 size = sizeof(MSGS);
1460 size = sizeof(COUNTER);
1466 printf(_("Unknown resource type %d in save_resource.\n"), type);
1472 res = (URES *)malloc(size);
1473 memcpy(res, &res_all, size);
1474 if (!res_head[rindex]) {
1475 res_head[rindex] = (RES *)res; /* store first entry */
1476 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1477 res->res_dir.hdr.name, rindex);
1480 if (res->res_dir.hdr.name == NULL) {
1481 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1484 /* Add new res to end of chain */
1485 for (last=next=res_head[rindex]; next; next=next->next) {
1487 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1488 Emsg2(M_ERROR_TERM, 0,
1489 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1490 resources[rindex].name, res->res_dir.hdr.name);
1493 last->next = (RES *)res;
1494 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1495 res->res_dir.hdr.name, rindex, pass);
1501 * Store Device. Note, the resource is created upon the
1502 * first reference. The details of the resource are obtained
1503 * later from the SD.
1505 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1509 int rindex = R_DEVICE - r_first;
1510 int size = sizeof(DEVICE);
1514 token = lex_get_token(lc, T_NAME);
1515 if (!res_head[rindex]) {
1516 res = (URES *)malloc(size);
1517 memset(res, 0, size);
1518 res->res_dev.hdr.name = bstrdup(lc->str);
1519 res_head[rindex] = (RES *)res; /* store first entry */
1520 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1521 res->res_dir.hdr.name, rindex);
1524 /* See if it is already defined */
1525 for (next=res_head[rindex]; next->next; next=next->next) {
1526 if (strcmp(next->name, lc->str) == 0) {
1532 res = (URES *)malloc(size);
1533 memset(res, 0, size);
1534 res->res_dev.hdr.name = bstrdup(lc->str);
1535 next->next = (RES *)res;
1536 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1537 res->res_dir.hdr.name, rindex, pass);
1542 set_bit(index, res_all.hdr.item_present);
1544 store_alist_res(lc, item, index, pass);
1549 * Store JobType (backup, verify, restore)
1552 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1556 token = lex_get_token(lc, T_NAME);
1557 /* Store the type both pass 1 and pass 2 */
1558 for (i=0; migtypes[i].type_name; i++) {
1559 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1560 *(int *)(item->value) = migtypes[i].job_type;
1566 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1569 set_bit(index, res_all.hdr.item_present);
1575 * Store JobType (backup, verify, restore)
1578 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1582 token = lex_get_token(lc, T_NAME);
1583 /* Store the type both pass 1 and pass 2 */
1584 for (i=0; jobtypes[i].type_name; i++) {
1585 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1586 *(int *)(item->value) = jobtypes[i].job_type;
1592 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1595 set_bit(index, res_all.hdr.item_present);
1599 * Store Job Level (Full, Incremental, ...)
1602 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1606 token = lex_get_token(lc, T_NAME);
1607 /* Store the level pass 2 so that type is defined */
1608 for (i=0; joblevels[i].level_name; i++) {
1609 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1610 *(int *)(item->value) = joblevels[i].level;
1616 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1619 set_bit(index, res_all.hdr.item_present);
1623 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1626 token = lex_get_token(lc, T_NAME);
1627 /* Scan Replacement options */
1628 for (i=0; ReplaceOptions[i].name; i++) {
1629 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1630 *(int *)(item->value) = ReplaceOptions[i].token;
1636 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1639 set_bit(index, res_all.hdr.item_present);
1643 * Store ACL (access control list)
1646 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1651 token = lex_get_token(lc, T_STRING);
1653 if (((alist **)item->value)[item->code] == NULL) {
1654 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1655 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1657 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1658 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1660 token = lex_get_token(lc, T_ALL);
1661 if (token == T_COMMA) {
1662 continue; /* get another ACL */
1666 set_bit(index, res_all.hdr.item_present);
1669 /* We build RunScripts items here */
1670 static RUNSCRIPT res_runscript;
1672 /* Store a runscript->when in a bit field */
1673 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1675 lex_get_token(lc, T_NAME);
1677 if (strcasecmp(lc->str, "before") == 0) {
1678 *(int *)(item->value) = SCRIPT_Before ;
1679 } else if (strcasecmp(lc->str, "after") == 0) {
1680 *(int *)(item->value) = SCRIPT_After;
1681 } else if (strcasecmp(lc->str, "always") == 0) {
1682 *(int *)(item->value) = SCRIPT_Any;
1684 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1689 /* Store a runscript->target
1692 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1694 lex_get_token(lc, T_STRING);
1697 if (strcmp(lc->str, "%c") == 0) {
1698 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1699 } else if (strcasecmp(lc->str, "yes") == 0) {
1700 ((RUNSCRIPT*) item->value)->set_target("%c");
1701 } else if (strcasecmp(lc->str, "no") == 0) {
1702 ((RUNSCRIPT*) item->value)->set_target("");
1704 RES *res = GetResWithName(R_CLIENT, lc->str);
1706 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1707 lc->str, lc->line_no, lc->line);
1710 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1716 /* Store a runscript->command in a bit field
1719 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1721 lex_get_token(lc, T_STRING);
1724 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1729 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1731 lex_get_token(lc, T_STRING);
1732 alist **runscripts = (alist **)(item->value) ;
1735 RUNSCRIPT *script = new_runscript();
1737 script->set_command(lc->str);
1739 /* TODO: remove all script->old_proto with bacula 1.42 */
1741 if (strcmp(item->name, "runbeforejob") == 0) {
1742 script->when = SCRIPT_Before;
1743 script->fail_on_error = true;
1744 script->set_target("");
1746 } else if (strcmp(item->name, "runafterjob") == 0) {
1747 script->when = SCRIPT_After;
1748 script->on_success = true;
1749 script->on_failure = false;
1750 script->set_target("");
1752 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1753 script->old_proto = true;
1754 script->when = SCRIPT_After;
1755 script->set_target("%c");
1756 script->on_success = true;
1757 script->on_failure = false;
1759 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1760 script->old_proto = true;
1761 script->when = SCRIPT_Before;
1762 script->set_target("%c");
1763 script->fail_on_error = true;
1765 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1766 script->when = SCRIPT_After;
1767 script->on_failure = true;
1768 script->on_success = false;
1769 script->set_target("");
1772 if (*runscripts == NULL) {
1773 *runscripts = New(alist(10, not_owned_by_alist));
1776 (*runscripts)->append(script);
1783 /* Store a bool in a bit field without modifing res_all.hdr
1784 * We can also add an option to store_bool to skip res_all.hdr
1786 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1788 lex_get_token(lc, T_NAME);
1789 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1790 *(bool *)(item->value) = true;
1791 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1792 *(bool *)(item->value) = false;
1794 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1800 * new RunScript items
1801 * name handler value code flags default_value
1803 static RES_ITEM runscript_items[] = {
1804 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1805 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1806 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1807 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1808 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1809 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1810 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1811 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1812 {NULL, NULL, {0}, 0, 0, 0}
1816 * Store RunScript info
1818 * Note, when this routine is called, we are inside a Job
1819 * resource. We treat the RunScript like a sort of
1820 * mini-resource within the Job resource.
1822 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1825 alist **runscripts = (alist **)(item->value) ;
1827 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1829 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1831 token = lex_get_token(lc, T_SKIP_EOL);
1833 if (token != T_BOB) {
1834 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1837 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1838 if (token == T_EOB) {
1841 if (token != T_IDENTIFIER) {
1842 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1844 for (i=0; runscript_items[i].name; i++) {
1845 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1846 token = lex_get_token(lc, T_SKIP_EOL);
1847 if (token != T_EQUALS) {
1848 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1851 /* Call item handler */
1852 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1859 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1864 if (res_runscript.command == NULL) {
1865 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1866 "command", "runscript");
1869 /* run on client by default */
1870 if (res_runscript.target == NULL) {
1871 res_runscript.set_target("%c");
1874 RUNSCRIPT *script = new_runscript();
1875 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1877 if (*runscripts == NULL) {
1878 *runscripts = New(alist(10, not_owned_by_alist));
1881 (*runscripts)->append(script);
1886 set_bit(index, res_all.hdr.item_present);