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 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
297 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
298 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
299 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
300 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
301 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
302 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
303 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
304 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
305 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
306 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
307 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
308 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
309 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
310 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
311 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
312 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
313 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
314 {NULL, NULL, {0}, 0, 0, 0}
319 * name handler value code flags default_value
321 static RES_ITEM fs_items[] = {
322 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
323 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
324 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
325 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
326 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
327 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
328 {NULL, NULL, {0}, 0, 0, 0}
331 /* Schedule -- see run_conf.c */
334 * name handler value code flags default_value
336 static RES_ITEM sch_items[] = {
337 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
338 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
339 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
340 {NULL, NULL, {0}, 0, 0, 0}
345 * name handler value code flags default_value
347 static RES_ITEM pool_items[] = {
348 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
349 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
350 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
351 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
352 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
353 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
354 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
355 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
356 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
357 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
358 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
359 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
360 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
361 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
362 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
363 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
364 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
365 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
366 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
367 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
368 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
369 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
370 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
371 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
372 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
373 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
374 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
375 {"catalog", store_res, ITEM(res_pool.Catalog), R_CATALOG, 0, 0},
376 {NULL, NULL, {0}, 0, 0, 0}
381 * name handler value code flags default_value
383 static RES_ITEM counter_items[] = {
384 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
385 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
386 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
387 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
388 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
389 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
390 {NULL, NULL, {0}, 0, 0, 0}
394 /* Message resource */
395 extern RES_ITEM msgs_items[];
398 * This is the master resource definition.
399 * It must have one item for each of the resources.
401 * NOTE!!! keep it in the same order as the R_codes
402 * or eliminate all resources[rindex].name
404 * name items rcode res_head
406 RES_TABLE resources[] = {
407 {"director", dir_items, R_DIRECTOR},
408 {"client", cli_items, R_CLIENT},
409 {"job", job_items, R_JOB},
410 {"storage", store_items, R_STORAGE},
411 {"catalog", cat_items, R_CATALOG},
412 {"schedule", sch_items, R_SCHEDULE},
413 {"fileset", fs_items, R_FILESET},
414 {"pool", pool_items, R_POOL},
415 {"messages", msgs_items, R_MSGS},
416 {"counter", counter_items, R_COUNTER},
417 {"console", con_items, R_CONSOLE},
418 {"jobdefs", job_items, R_JOBDEFS},
419 {"device", NULL, R_DEVICE}, /* info obtained from SD */
424 /* Keywords (RHS) permitted in Job Level records
426 * level_name level job_type
428 struct s_jl joblevels[] = {
429 {"Full", L_FULL, JT_BACKUP},
430 {"Base", L_BASE, JT_BACKUP},
431 {"Incremental", L_INCREMENTAL, JT_BACKUP},
432 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
433 {"Since", L_SINCE, JT_BACKUP},
434 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
435 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
436 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
437 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
438 {"Data", L_VERIFY_DATA, JT_VERIFY},
439 {" ", L_NONE, JT_ADMIN},
440 {" ", L_NONE, JT_RESTORE},
444 /* Keywords (RHS) permitted in Job type records
448 struct s_jt jobtypes[] = {
449 {"backup", JT_BACKUP},
451 {"verify", JT_VERIFY},
452 {"restore", JT_RESTORE},
453 {"migrate", JT_MIGRATE},
458 /* Keywords (RHS) permitted in Selection type records
462 struct s_jt migtypes[] = {
463 {"smallestvolume", MT_SMALLEST_VOL},
464 {"oldestvolume", MT_OLDEST_VOL},
465 {"pooloccupancy", MT_POOL_OCCUPANCY},
466 {"pooltime", MT_POOL_TIME},
467 {"client", MT_CLIENT},
468 {"volume", MT_VOLUME},
470 {"sqlquery", MT_SQLQUERY},
476 /* Options permitted in Restore replace= */
477 struct s_kw ReplaceOptions[] = {
478 {"always", REPLACE_ALWAYS},
479 {"ifnewer", REPLACE_IFNEWER},
480 {"ifolder", REPLACE_IFOLDER},
481 {"never", REPLACE_NEVER},
485 const char *level_to_str(int level)
488 static char level_no[30];
489 const char *str = level_no;
491 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
492 for (i=0; joblevels[i].level_name; i++) {
493 if (level == joblevels[i].level) {
494 str = joblevels[i].level_name;
501 /* Dump contents of resource */
502 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
504 URES *res = (URES *)reshdr;
506 char ed1[100], ed2[100], ed3[100];
510 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
513 if (type < 0) { /* no recursion */
519 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
520 reshdr->name, res->res_dir.MaxConcurrentJobs,
521 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
522 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
523 if (res->res_dir.query_file) {
524 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
526 if (res->res_dir.messages) {
527 sendit(sock, _(" --> "));
528 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
532 sendit(sock, _("Console: name=%s SSL=%d\n"),
533 res->res_con.hdr.name, res->res_con.tls_enable);
536 if (res->res_counter.WrapCounter) {
537 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
538 res->res_counter.hdr.name, res->res_counter.MinValue,
539 res->res_counter.MaxValue, res->res_counter.CurrentValue,
540 res->res_counter.WrapCounter->hdr.name);
542 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
543 res->res_counter.hdr.name, res->res_counter.MinValue,
544 res->res_counter.MaxValue);
546 if (res->res_counter.Catalog) {
547 sendit(sock, _(" --> "));
548 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
553 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
554 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
555 res->res_client.MaxConcurrentJobs);
556 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
557 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
558 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
559 res->res_client.AutoPrune);
560 if (res->res_client.catalog) {
561 sendit(sock, _(" --> "));
562 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
569 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
570 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
571 " poolid=%s volname=%s MediaType=%s\n"),
572 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
573 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
574 dev->offline, dev->autochanger,
575 edit_uint64(dev->PoolId, ed1),
576 dev->VolumeName, dev->MediaType);
580 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
581 " DeviceName=%s MediaType=%s StorageId=%s\n"),
582 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
583 res->res_store.MaxConcurrentJobs,
584 res->res_store.dev_name(),
585 res->res_store.media_type,
586 edit_int64(res->res_store.StorageId, ed1));
590 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
591 " db_user=%s MutliDBConn=%d\n"),
592 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
593 res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
594 res->res_cat.mult_db_connections);
599 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
600 type == R_JOB ? _("Job") : _("JobDefs"),
601 res->res_job.hdr.name, res->res_job.JobType,
602 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
603 res->res_job.enabled);
604 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
605 res->res_job.MaxConcurrentJobs,
606 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
607 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
608 res->res_job.spool_data, res->res_job.write_part_after_job);
609 if (res->res_job.JobType == JT_MIGRATE) {
610 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
612 if (res->res_job.client) {
613 sendit(sock, _(" --> "));
614 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
616 if (res->res_job.fileset) {
617 sendit(sock, _(" --> "));
618 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
620 if (res->res_job.schedule) {
621 sendit(sock, _(" --> "));
622 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
624 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
625 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
627 if (res->res_job.RegexWhere) {
628 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
630 if (res->res_job.RestoreBootstrap) {
631 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
633 if (res->res_job.WriteBootstrap) {
634 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
636 if (res->res_job.storage) {
638 foreach_alist(store, res->res_job.storage) {
639 sendit(sock, _(" --> "));
640 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
643 if (res->res_job.RunScripts) {
645 foreach_alist(script, res->res_job.RunScripts) {
646 sendit(sock, _(" --> RunScript\n"));
647 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
648 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
649 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
650 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
651 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
652 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
655 if (res->res_job.pool) {
656 sendit(sock, _(" --> "));
657 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
659 if (res->res_job.full_pool) {
660 sendit(sock, _(" --> "));
661 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
663 if (res->res_job.inc_pool) {
664 sendit(sock, _(" --> "));
665 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
667 if (res->res_job.diff_pool) {
668 sendit(sock, _(" --> "));
669 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
671 if (res->res_job.verify_job) {
672 sendit(sock, _(" --> "));
673 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
675 if (res->res_job.run_cmds) {
677 foreach_alist(runcmd, res->res_job.run_cmds) {
678 sendit(sock, _(" --> Run=%s\n"), runcmd);
681 if (res->res_job.selection_pattern) {
682 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
684 if (res->res_job.messages) {
685 sendit(sock, _(" --> "));
686 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
693 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
694 for (i=0; i<res->res_fs.num_includes; i++) {
695 INCEXE *incexe = res->res_fs.include_items[i];
696 for (j=0; j<incexe->num_opts; j++) {
697 FOPTS *fo = incexe->opts_list[j];
698 sendit(sock, " O %s\n", fo->opts);
700 bool enhanced_wild = false;
701 for (k=0; fo->opts[k]!='\0'; k++) {
702 if (fo->opts[k]=='W') {
703 enhanced_wild = true;
708 for (k=0; k<fo->regex.size(); k++) {
709 sendit(sock, " R %s\n", fo->regex.get(k));
711 for (k=0; k<fo->regexdir.size(); k++) {
712 sendit(sock, " RD %s\n", fo->regexdir.get(k));
714 for (k=0; k<fo->regexfile.size(); k++) {
715 sendit(sock, " RF %s\n", fo->regexfile.get(k));
717 for (k=0; k<fo->wild.size(); k++) {
718 sendit(sock, " W %s\n", fo->wild.get(k));
720 for (k=0; k<fo->wilddir.size(); k++) {
721 sendit(sock, " WD %s\n", fo->wilddir.get(k));
723 for (k=0; k<fo->wildfile.size(); k++) {
724 sendit(sock, " WF %s\n", fo->wildfile.get(k));
726 for (k=0; k<fo->wildbase.size(); k++) {
727 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
729 for (k=0; k<fo->base.size(); k++) {
730 sendit(sock, " B %s\n", fo->base.get(k));
732 for (k=0; k<fo->fstype.size(); k++) {
733 sendit(sock, " X %s\n", fo->fstype.get(k));
735 for (k=0; k<fo->drivetype.size(); k++) {
736 sendit(sock, " XD %s\n", fo->drivetype.get(k));
739 sendit(sock, " D %s\n", fo->reader);
742 sendit(sock, " T %s\n", fo->writer);
744 sendit(sock, " N\n");
746 for (j=0; j<incexe->name_list.size(); j++) {
747 sendit(sock, " I %s\n", incexe->name_list.get(j));
749 if (incexe->name_list.size()) {
750 sendit(sock, " N\n");
754 for (i=0; i<res->res_fs.num_excludes; i++) {
755 INCEXE *incexe = res->res_fs.exclude_items[i];
756 for (j=0; j<incexe->name_list.size(); j++) {
757 sendit(sock, " E %s\n", incexe->name_list.get(j));
759 if (incexe->name_list.size()) {
760 sendit(sock, " N\n");
767 if (res->res_sch.run) {
769 RUN *run = res->res_sch.run;
770 char buf[1000], num[30];
771 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
776 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
777 bstrncpy(buf, _(" hour="), sizeof(buf));
778 for (i=0; i<24; i++) {
779 if (bit_is_set(i, run->hour)) {
780 bsnprintf(num, sizeof(num), "%d ", i);
781 bstrncat(buf, num, sizeof(buf));
784 bstrncat(buf, "\n", sizeof(buf));
786 bstrncpy(buf, _(" mday="), sizeof(buf));
787 for (i=0; i<31; i++) {
788 if (bit_is_set(i, run->mday)) {
789 bsnprintf(num, sizeof(num), "%d ", i);
790 bstrncat(buf, num, sizeof(buf));
793 bstrncat(buf, "\n", sizeof(buf));
795 bstrncpy(buf, _(" month="), sizeof(buf));
796 for (i=0; i<12; i++) {
797 if (bit_is_set(i, run->month)) {
798 bsnprintf(num, sizeof(num), "%d ", i);
799 bstrncat(buf, num, sizeof(buf));
802 bstrncat(buf, "\n", sizeof(buf));
804 bstrncpy(buf, _(" wday="), sizeof(buf));
805 for (i=0; i<7; i++) {
806 if (bit_is_set(i, run->wday)) {
807 bsnprintf(num, sizeof(num), "%d ", i);
808 bstrncat(buf, num, sizeof(buf));
811 bstrncat(buf, "\n", sizeof(buf));
813 bstrncpy(buf, _(" wom="), sizeof(buf));
814 for (i=0; i<5; i++) {
815 if (bit_is_set(i, run->wom)) {
816 bsnprintf(num, sizeof(num), "%d ", i);
817 bstrncat(buf, num, sizeof(buf));
820 bstrncat(buf, "\n", sizeof(buf));
822 bstrncpy(buf, _(" woy="), sizeof(buf));
823 for (i=0; i<54; i++) {
824 if (bit_is_set(i, run->woy)) {
825 bsnprintf(num, sizeof(num), "%d ", i);
826 bstrncat(buf, num, sizeof(buf));
829 bstrncat(buf, "\n", sizeof(buf));
831 sendit(sock, _(" mins=%d\n"), run->minute);
833 sendit(sock, _(" --> "));
834 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
837 sendit(sock, _(" --> "));
838 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
841 sendit(sock, _(" --> "));
842 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
844 /* If another Run record is chained in, go print it */
850 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
855 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
856 res->res_pool.pool_type);
857 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
858 res->res_pool.use_catalog, res->res_pool.use_volume_once,
859 res->res_pool.catalog_files);
860 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
861 res->res_pool.max_volumes, res->res_pool.AutoPrune,
862 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
863 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
864 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
865 res->res_pool.Recycle,
866 NPRT(res->res_pool.label_format));
867 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
868 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
869 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
870 res->res_pool.recycle_oldest_volume,
871 res->res_pool.purge_oldest_volume);
872 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
873 res->res_pool.MaxVolJobs,
874 res->res_pool.MaxVolFiles,
875 edit_uint64(res->res_pool.MaxVolFiles, ed1));
876 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
877 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
878 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
879 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
880 if (res->res_pool.NextPool) {
881 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
883 if (res->res_pool.RecyclePool) {
884 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
886 if (res->res_pool.Catalog) {
887 sendit(sock, _(" Catalog=%s\n"), res->res_pool.Catalog->name());
889 if (res->res_pool.storage) {
891 foreach_alist(store, res->res_pool.storage) {
892 sendit(sock, _(" --> "));
893 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
896 if (res->res_pool.CopyPool) {
898 foreach_alist(copy, res->res_pool.CopyPool) {
899 sendit(sock, _(" --> "));
900 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
907 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
908 if (res->res_msgs.mail_cmd)
909 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
910 if (res->res_msgs.operator_cmd)
911 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
915 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
918 if (recurse && res->res_dir.hdr.next) {
919 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
924 * Free all the members of an INCEXE structure
926 static void free_incexe(INCEXE *incexe)
928 incexe->name_list.destroy();
929 for (int i=0; i<incexe->num_opts; i++) {
930 FOPTS *fopt = incexe->opts_list[i];
931 fopt->regex.destroy();
932 fopt->regexdir.destroy();
933 fopt->regexfile.destroy();
934 fopt->wild.destroy();
935 fopt->wilddir.destroy();
936 fopt->wildfile.destroy();
937 fopt->wildbase.destroy();
938 fopt->base.destroy();
939 fopt->fstype.destroy();
940 fopt->drivetype.destroy();
949 if (incexe->opts_list) {
950 free(incexe->opts_list);
956 * Free memory of resource -- called when daemon terminates.
957 * NB, we don't need to worry about freeing any references
958 * to other resources as they will be freed when that
959 * resource chain is traversed. Mainly we worry about freeing
960 * allocated strings (names).
962 void free_resource(RES *sres, int type)
965 RES *nres; /* next resource if linked */
966 URES *res = (URES *)sres;
971 /* common stuff -- free the resource name and description */
972 nres = (RES *)res->res_dir.hdr.next;
973 if (res->res_dir.hdr.name) {
974 free(res->res_dir.hdr.name);
976 if (res->res_dir.hdr.desc) {
977 free(res->res_dir.hdr.desc);
982 if (res->res_dir.working_directory) {
983 free(res->res_dir.working_directory);
985 if (res->res_dir.scripts_directory) {
986 free((char *)res->res_dir.scripts_directory);
988 if (res->res_dir.pid_directory) {
989 free(res->res_dir.pid_directory);
991 if (res->res_dir.subsys_directory) {
992 free(res->res_dir.subsys_directory);
994 if (res->res_dir.password) {
995 free(res->res_dir.password);
997 if (res->res_dir.query_file) {
998 free(res->res_dir.query_file);
1000 if (res->res_dir.DIRaddrs) {
1001 free_addresses(res->res_dir.DIRaddrs);
1003 if (res->res_dir.tls_ctx) {
1004 free_tls_context(res->res_dir.tls_ctx);
1006 if (res->res_dir.tls_ca_certfile) {
1007 free(res->res_dir.tls_ca_certfile);
1009 if (res->res_dir.tls_ca_certdir) {
1010 free(res->res_dir.tls_ca_certdir);
1012 if (res->res_dir.tls_certfile) {
1013 free(res->res_dir.tls_certfile);
1015 if (res->res_dir.tls_keyfile) {
1016 free(res->res_dir.tls_keyfile);
1018 if (res->res_dir.tls_dhfile) {
1019 free(res->res_dir.tls_dhfile);
1021 if (res->res_dir.tls_allowed_cns) {
1022 delete res->res_dir.tls_allowed_cns;
1029 if (res->res_con.password) {
1030 free(res->res_con.password);
1032 if (res->res_con.tls_ctx) {
1033 free_tls_context(res->res_con.tls_ctx);
1035 if (res->res_con.tls_ca_certfile) {
1036 free(res->res_con.tls_ca_certfile);
1038 if (res->res_con.tls_ca_certdir) {
1039 free(res->res_con.tls_ca_certdir);
1041 if (res->res_con.tls_certfile) {
1042 free(res->res_con.tls_certfile);
1044 if (res->res_con.tls_keyfile) {
1045 free(res->res_con.tls_keyfile);
1047 if (res->res_con.tls_dhfile) {
1048 free(res->res_con.tls_dhfile);
1050 if (res->res_con.tls_allowed_cns) {
1051 delete res->res_con.tls_allowed_cns;
1053 for (int i=0; i<Num_ACL; i++) {
1054 if (res->res_con.ACL_lists[i]) {
1055 delete res->res_con.ACL_lists[i];
1056 res->res_con.ACL_lists[i] = NULL;
1061 if (res->res_client.address) {
1062 free(res->res_client.address);
1064 if (res->res_client.password) {
1065 free(res->res_client.password);
1067 if (res->res_client.tls_ctx) {
1068 free_tls_context(res->res_client.tls_ctx);
1070 if (res->res_client.tls_ca_certfile) {
1071 free(res->res_client.tls_ca_certfile);
1073 if (res->res_client.tls_ca_certdir) {
1074 free(res->res_client.tls_ca_certdir);
1076 if (res->res_client.tls_certfile) {
1077 free(res->res_client.tls_certfile);
1079 if (res->res_client.tls_keyfile) {
1080 free(res->res_client.tls_keyfile);
1082 if (res->res_client.tls_allowed_cns) {
1083 delete res->res_client.tls_allowed_cns;
1087 if (res->res_store.address) {
1088 free(res->res_store.address);
1090 if (res->res_store.password) {
1091 free(res->res_store.password);
1093 if (res->res_store.media_type) {
1094 free(res->res_store.media_type);
1096 if (res->res_store.device) {
1097 delete res->res_store.device;
1099 if (res->res_store.tls_ctx) {
1100 free_tls_context(res->res_store.tls_ctx);
1102 if (res->res_store.tls_ca_certfile) {
1103 free(res->res_store.tls_ca_certfile);
1105 if (res->res_store.tls_ca_certdir) {
1106 free(res->res_store.tls_ca_certdir);
1108 if (res->res_store.tls_certfile) {
1109 free(res->res_store.tls_certfile);
1111 if (res->res_store.tls_keyfile) {
1112 free(res->res_store.tls_keyfile);
1116 if (res->res_cat.db_address) {
1117 free(res->res_cat.db_address);
1119 if (res->res_cat.db_socket) {
1120 free(res->res_cat.db_socket);
1122 if (res->res_cat.db_user) {
1123 free(res->res_cat.db_user);
1125 if (res->res_cat.db_name) {
1126 free(res->res_cat.db_name);
1128 if (res->res_cat.db_password) {
1129 free(res->res_cat.db_password);
1133 if ((num=res->res_fs.num_includes)) {
1134 while (--num >= 0) {
1135 free_incexe(res->res_fs.include_items[num]);
1137 free(res->res_fs.include_items);
1139 res->res_fs.num_includes = 0;
1140 if ((num=res->res_fs.num_excludes)) {
1141 while (--num >= 0) {
1142 free_incexe(res->res_fs.exclude_items[num]);
1144 free(res->res_fs.exclude_items);
1146 res->res_fs.num_excludes = 0;
1149 if (res->res_pool.pool_type) {
1150 free(res->res_pool.pool_type);
1152 if (res->res_pool.label_format) {
1153 free(res->res_pool.label_format);
1155 if (res->res_pool.cleaning_prefix) {
1156 free(res->res_pool.cleaning_prefix);
1158 if (res->res_pool.storage) {
1159 delete res->res_pool.storage;
1163 if (res->res_sch.run) {
1165 nrun = res->res_sch.run;
1175 if (res->res_job.RestoreWhere) {
1176 free(res->res_job.RestoreWhere);
1178 if (res->res_job.RegexWhere) {
1179 free(res->res_job.RegexWhere);
1181 if (res->res_job.strip_prefix) {
1182 free(res->res_job.strip_prefix);
1184 if (res->res_job.add_prefix) {
1185 free(res->res_job.add_prefix);
1187 if (res->res_job.add_suffix) {
1188 free(res->res_job.add_suffix);
1190 if (res->res_job.RestoreBootstrap) {
1191 free(res->res_job.RestoreBootstrap);
1193 if (res->res_job.WriteBootstrap) {
1194 free(res->res_job.WriteBootstrap);
1196 if (res->res_job.selection_pattern) {
1197 free(res->res_job.selection_pattern);
1199 if (res->res_job.run_cmds) {
1200 delete res->res_job.run_cmds;
1202 if (res->res_job.storage) {
1203 delete res->res_job.storage;
1205 if (res->res_job.RunScripts) {
1206 free_runscripts(res->res_job.RunScripts);
1207 delete res->res_job.RunScripts;
1211 if (res->res_msgs.mail_cmd) {
1212 free(res->res_msgs.mail_cmd);
1214 if (res->res_msgs.operator_cmd) {
1215 free(res->res_msgs.operator_cmd);
1217 free_msgs_res((MSGS *)res); /* free message resource */
1221 printf(_("Unknown resource type %d in free_resource.\n"), type);
1223 /* Common stuff again -- free the resource, recurse to next one */
1228 free_resource(nres, type);
1233 * Save the new resource by chaining it into the head list for
1234 * the resource. If this is pass 2, we update any resource
1235 * pointers because they may not have been defined until
1238 void save_resource(int type, RES_ITEM *items, int pass)
1241 int rindex = type - r_first;
1245 /* Check Job requirements after applying JobDefs */
1246 if (type != R_JOB && type != R_JOBDEFS) {
1248 * Ensure that all required items are present
1250 for (i=0; items[i].name; i++) {
1251 if (items[i].flags & ITEM_REQUIRED) {
1252 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1253 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1254 items[i].name, resources[rindex]);
1257 /* If this triggers, take a look at lib/parse_conf.h */
1258 if (i >= MAX_RES_ITEMS) {
1259 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1262 } else if (type == R_JOB) {
1264 * Ensure that the name item is present
1266 if (items[0].flags & ITEM_REQUIRED) {
1267 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1268 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1269 items[0].name, resources[rindex]);
1275 * During pass 2 in each "store" routine, we looked up pointers
1276 * to all the resources referrenced in the current resource, now we
1277 * must copy their addresses from the static record to the allocated
1282 /* Resources not containing a resource */
1290 * Resources containing another resource or alist. First
1291 * look up the resource which contains another resource. It
1292 * was written during pass 1. Then stuff in the pointers to
1293 * the resources it contains, which were inserted this pass.
1294 * Finally, it will all be stored back.
1297 /* Find resource saved in pass 1 */
1298 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1299 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1301 /* Explicitly copy resource pointers from this pass (res_all) */
1302 res->res_pool.NextPool = res_all.res_pool.NextPool;
1303 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1304 res->res_pool.storage = res_all.res_pool.storage;
1307 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1308 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1310 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1313 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1314 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1316 res->res_dir.messages = res_all.res_dir.messages;
1317 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1320 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1321 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1322 res_all.res_dir.hdr.name);
1324 /* we must explicitly copy the device alist pointer */
1325 res->res_store.device = res_all.res_store.device;
1329 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1330 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1331 res_all.res_dir.hdr.name);
1333 res->res_job.messages = res_all.res_job.messages;
1334 res->res_job.schedule = res_all.res_job.schedule;
1335 res->res_job.client = res_all.res_job.client;
1336 res->res_job.fileset = res_all.res_job.fileset;
1337 res->res_job.storage = res_all.res_job.storage;
1338 res->res_job.pool = res_all.res_job.pool;
1339 res->res_job.full_pool = res_all.res_job.full_pool;
1340 res->res_job.inc_pool = res_all.res_job.inc_pool;
1341 res->res_job.diff_pool = res_all.res_job.diff_pool;
1342 res->res_job.verify_job = res_all.res_job.verify_job;
1343 res->res_job.jobdefs = res_all.res_job.jobdefs;
1344 res->res_job.run_cmds = res_all.res_job.run_cmds;
1345 res->res_job.RunScripts = res_all.res_job.RunScripts;
1347 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1348 * is not very useful)
1349 * We have to set_bit(index, res_all.hdr.item_present);
1350 * or something like that
1353 /* we take RegexWhere before all other options */
1354 if (!res->res_job.RegexWhere
1356 (res->res_job.strip_prefix ||
1357 res->res_job.add_suffix ||
1358 res->res_job.add_prefix))
1360 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1361 res->res_job.add_prefix,
1362 res->res_job.add_suffix);
1363 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1364 bregexp_build_where(res->res_job.RegexWhere, len,
1365 res->res_job.strip_prefix,
1366 res->res_job.add_prefix,
1367 res->res_job.add_suffix);
1368 /* TODO: test bregexp */
1371 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1372 free(res->res_job.RestoreWhere);
1373 res->res_job.RestoreWhere = NULL;
1378 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1379 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1381 res->res_counter.Catalog = res_all.res_counter.Catalog;
1382 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1386 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1387 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1389 res->res_client.catalog = res_all.res_client.catalog;
1390 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1394 * Schedule is a bit different in that it contains a RUN record
1395 * chain which isn't a "named" resource. This chain was linked
1396 * in by run_conf.c during pass 2, so here we jam the pointer
1397 * into the Schedule resource.
1399 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1400 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1402 res->res_sch.run = res_all.res_sch.run;
1405 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1409 /* Note, the resource name was already saved during pass 1,
1410 * so here, we can just release it.
1412 if (res_all.res_dir.hdr.name) {
1413 free(res_all.res_dir.hdr.name);
1414 res_all.res_dir.hdr.name = NULL;
1416 if (res_all.res_dir.hdr.desc) {
1417 free(res_all.res_dir.hdr.desc);
1418 res_all.res_dir.hdr.desc = NULL;
1424 * The following code is only executed during pass 1
1428 size = sizeof(DIRRES);
1431 size = sizeof(CONRES);
1434 size =sizeof(CLIENT);
1437 size = sizeof(STORE);
1447 size = sizeof(FILESET);
1450 size = sizeof(SCHED);
1453 size = sizeof(POOL);
1456 size = sizeof(MSGS);
1459 size = sizeof(COUNTER);
1465 printf(_("Unknown resource type %d in save_resource.\n"), type);
1471 res = (URES *)malloc(size);
1472 memcpy(res, &res_all, size);
1473 if (!res_head[rindex]) {
1474 res_head[rindex] = (RES *)res; /* store first entry */
1475 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1476 res->res_dir.hdr.name, rindex);
1479 if (res->res_dir.hdr.name == NULL) {
1480 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1483 /* Add new res to end of chain */
1484 for (last=next=res_head[rindex]; next; next=next->next) {
1486 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1487 Emsg2(M_ERROR_TERM, 0,
1488 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1489 resources[rindex].name, res->res_dir.hdr.name);
1492 last->next = (RES *)res;
1493 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1494 res->res_dir.hdr.name, rindex, pass);
1500 * Store Device. Note, the resource is created upon the
1501 * first reference. The details of the resource are obtained
1502 * later from the SD.
1504 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1508 int rindex = R_DEVICE - r_first;
1509 int size = sizeof(DEVICE);
1513 token = lex_get_token(lc, T_NAME);
1514 if (!res_head[rindex]) {
1515 res = (URES *)malloc(size);
1516 memset(res, 0, size);
1517 res->res_dev.hdr.name = bstrdup(lc->str);
1518 res_head[rindex] = (RES *)res; /* store first entry */
1519 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1520 res->res_dir.hdr.name, rindex);
1523 /* See if it is already defined */
1524 for (next=res_head[rindex]; next->next; next=next->next) {
1525 if (strcmp(next->name, lc->str) == 0) {
1531 res = (URES *)malloc(size);
1532 memset(res, 0, size);
1533 res->res_dev.hdr.name = bstrdup(lc->str);
1534 next->next = (RES *)res;
1535 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1536 res->res_dir.hdr.name, rindex, pass);
1541 set_bit(index, res_all.hdr.item_present);
1543 store_alist_res(lc, item, index, pass);
1548 * Store JobType (backup, verify, restore)
1551 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1555 token = lex_get_token(lc, T_NAME);
1556 /* Store the type both pass 1 and pass 2 */
1557 for (i=0; migtypes[i].type_name; i++) {
1558 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1559 *(int *)(item->value) = migtypes[i].job_type;
1565 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1568 set_bit(index, res_all.hdr.item_present);
1574 * Store JobType (backup, verify, restore)
1577 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1581 token = lex_get_token(lc, T_NAME);
1582 /* Store the type both pass 1 and pass 2 */
1583 for (i=0; jobtypes[i].type_name; i++) {
1584 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1585 *(int *)(item->value) = jobtypes[i].job_type;
1591 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1594 set_bit(index, res_all.hdr.item_present);
1598 * Store Job Level (Full, Incremental, ...)
1601 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1605 token = lex_get_token(lc, T_NAME);
1606 /* Store the level pass 2 so that type is defined */
1607 for (i=0; joblevels[i].level_name; i++) {
1608 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1609 *(int *)(item->value) = joblevels[i].level;
1615 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1618 set_bit(index, res_all.hdr.item_present);
1622 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1625 token = lex_get_token(lc, T_NAME);
1626 /* Scan Replacement options */
1627 for (i=0; ReplaceOptions[i].name; i++) {
1628 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1629 *(int *)(item->value) = ReplaceOptions[i].token;
1635 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1638 set_bit(index, res_all.hdr.item_present);
1642 * Store ACL (access control list)
1645 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1650 token = lex_get_token(lc, T_STRING);
1652 if (((alist **)item->value)[item->code] == NULL) {
1653 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1654 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1656 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1657 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1659 token = lex_get_token(lc, T_ALL);
1660 if (token == T_COMMA) {
1661 continue; /* get another ACL */
1665 set_bit(index, res_all.hdr.item_present);
1668 /* We build RunScripts items here */
1669 static RUNSCRIPT res_runscript;
1671 /* Store a runscript->when in a bit field */
1672 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1674 lex_get_token(lc, T_NAME);
1676 if (strcasecmp(lc->str, "before") == 0) {
1677 *(int *)(item->value) = SCRIPT_Before ;
1678 } else if (strcasecmp(lc->str, "after") == 0) {
1679 *(int *)(item->value) = SCRIPT_After;
1680 } else if (strcasecmp(lc->str, "always") == 0) {
1681 *(int *)(item->value) = SCRIPT_Any;
1683 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1688 /* Store a runscript->target
1691 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1693 lex_get_token(lc, T_STRING);
1696 if (strcmp(lc->str, "%c") == 0) {
1697 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1698 } else if (strcasecmp(lc->str, "yes") == 0) {
1699 ((RUNSCRIPT*) item->value)->set_target("%c");
1700 } else if (strcasecmp(lc->str, "no") == 0) {
1701 ((RUNSCRIPT*) item->value)->set_target("");
1703 RES *res = GetResWithName(R_CLIENT, lc->str);
1705 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1706 lc->str, lc->line_no, lc->line);
1709 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1715 /* Store a runscript->command in a bit field
1718 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1720 lex_get_token(lc, T_STRING);
1723 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1728 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1730 lex_get_token(lc, T_STRING);
1731 alist **runscripts = (alist **)(item->value) ;
1734 RUNSCRIPT *script = new_runscript();
1736 script->set_command(lc->str);
1738 /* TODO: remove all script->old_proto with bacula 1.42 */
1740 if (strcmp(item->name, "runbeforejob") == 0) {
1741 script->when = SCRIPT_Before;
1742 script->fail_on_error = true;
1743 script->set_target("");
1745 } else if (strcmp(item->name, "runafterjob") == 0) {
1746 script->when = SCRIPT_After;
1747 script->on_success = true;
1748 script->on_failure = false;
1749 script->set_target("");
1751 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1752 script->old_proto = true;
1753 script->when = SCRIPT_After;
1754 script->set_target("%c");
1755 script->on_success = true;
1756 script->on_failure = false;
1758 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1759 script->old_proto = true;
1760 script->when = SCRIPT_Before;
1761 script->set_target("%c");
1762 script->fail_on_error = true;
1764 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1765 script->when = SCRIPT_After;
1766 script->on_failure = true;
1767 script->on_success = false;
1768 script->set_target("");
1771 if (*runscripts == NULL) {
1772 *runscripts = New(alist(10, not_owned_by_alist));
1775 (*runscripts)->append(script);
1782 /* Store a bool in a bit field without modifing res_all.hdr
1783 * We can also add an option to store_bool to skip res_all.hdr
1785 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1787 lex_get_token(lc, T_NAME);
1788 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1789 *(bool *)(item->value) = true;
1790 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1791 *(bool *)(item->value) = false;
1793 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1799 * new RunScript items
1800 * name handler value code flags default_value
1802 static RES_ITEM runscript_items[] = {
1803 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1804 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1805 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1806 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1807 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1808 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1809 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1810 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1811 {NULL, NULL, {0}, 0, 0, 0}
1815 * Store RunScript info
1817 * Note, when this routine is called, we are inside a Job
1818 * resource. We treat the RunScript like a sort of
1819 * mini-resource within the Job resource.
1821 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1824 alist **runscripts = (alist **)(item->value) ;
1826 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1828 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1830 token = lex_get_token(lc, T_SKIP_EOL);
1832 if (token != T_BOB) {
1833 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1836 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1837 if (token == T_EOB) {
1840 if (token != T_IDENTIFIER) {
1841 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1843 for (i=0; runscript_items[i].name; i++) {
1844 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1845 token = lex_get_token(lc, T_SKIP_EOL);
1846 if (token != T_EQUALS) {
1847 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1850 /* Call item handler */
1851 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1858 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1863 if (res_runscript.command == NULL) {
1864 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1865 "command", "runscript");
1868 /* run on client by default */
1869 if (res_runscript.target == NULL) {
1870 res_runscript.set_target("%c");
1873 RUNSCRIPT *script = new_runscript();
1874 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1876 if (*runscripts == NULL) {
1877 *runscripts = New(alist(10, not_owned_by_alist));
1880 (*runscripts)->append(script);
1885 set_bit(index, res_all.hdr.item_present);