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 plus additions
11 that are listed in the file LICENSE.
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 {"tlsenable", store_bool, ITEM(res_dir.tls_enable), 0, 0, 0},
125 {"tlsrequire", store_bool, ITEM(res_dir.tls_require), 0, 0, 0},
126 {"tlsverifypeer", store_bool, ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
127 {"tlscacertificatefile", store_dir, ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
128 {"tlscacertificatedir", store_dir, ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
129 {"tlscertificate", store_dir, ITEM(res_dir.tls_certfile), 0, 0, 0},
130 {"tlskey", store_dir, ITEM(res_dir.tls_keyfile), 0, 0, 0},
131 {"tlsdhfile", store_dir, ITEM(res_dir.tls_dhfile), 0, 0, 0},
132 {"tlsallowedcn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
133 {NULL, NULL, {0}, 0, 0, 0}
139 * name handler value code flags default_value
141 static RES_ITEM con_items[] = {
142 {"name", store_name, ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
143 {"description", store_str, ITEM(res_con.hdr.desc), 0, 0, 0},
144 {"password", store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
145 {"jobacl", store_acl, ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
146 {"clientacl", store_acl, ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
147 {"storageacl", store_acl, ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
148 {"scheduleacl", store_acl, ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
149 {"runacl", store_acl, ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
150 {"poolacl", store_acl, ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
151 {"commandacl", store_acl, ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
152 {"filesetacl", store_acl, ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
153 {"catalogacl", store_acl, ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
154 {"whereacl", store_acl, ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
155 {"tlsenable", store_bool, ITEM(res_con.tls_enable), 0, 0, 0},
156 {"tlsrequire", store_bool, ITEM(res_con.tls_require), 0, 0, 0},
157 {"tlsverifypeer", store_bool, ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
158 {"tlscacertificatefile", store_dir, ITEM(res_con.tls_ca_certfile), 0, 0, 0},
159 {"tlscacertificatedir", store_dir, ITEM(res_con.tls_ca_certdir), 0, 0, 0},
160 {"tlscertificate", store_dir, ITEM(res_con.tls_certfile), 0, 0, 0},
161 {"tlskey", store_dir, ITEM(res_con.tls_keyfile), 0, 0, 0},
162 {"tlsdhfile", store_dir, ITEM(res_con.tls_dhfile), 0, 0, 0},
163 {"tlsallowedcn", store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
164 {NULL, NULL, {0}, 0, 0, 0}
169 * Client or File daemon resource
171 * name handler value code flags default_value
174 static RES_ITEM cli_items[] = {
175 {"name", store_name, ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
176 {"description", store_str, ITEM(res_client.hdr.desc), 0, 0, 0},
177 {"address", store_str, ITEM(res_client.address), 0, ITEM_REQUIRED, 0},
178 {"fdaddress", store_str, ITEM(res_client.address), 0, 0, 0},
179 {"fdport", store_pint, ITEM(res_client.FDport), 0, ITEM_DEFAULT, 9102},
180 {"password", store_password, ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
181 {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
182 {"catalog", store_res, ITEM(res_client.catalog), R_CATALOG, ITEM_REQUIRED, 0},
183 {"fileretention", store_time, ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
184 {"jobretention", store_time, ITEM(res_client.JobRetention), 0, ITEM_DEFAULT, 60*60*24*180},
185 {"autoprune", store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
186 {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
187 {"tlsenable", store_bool, ITEM(res_client.tls_enable), 0, 0, 0},
188 {"tlsrequire", store_bool, ITEM(res_client.tls_require), 0, 0, 0},
189 {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0},
190 {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0},
191 {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0},
192 {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0},
193 {"tlsallowedcn", store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
194 {NULL, NULL, {0}, 0, 0, 0}
197 /* Storage daemon resource
199 * name handler value code flags default_value
201 static RES_ITEM store_items[] = {
202 {"name", store_name, ITEM(res_store.hdr.name), 0, ITEM_REQUIRED, 0},
203 {"description", store_str, ITEM(res_store.hdr.desc), 0, 0, 0},
204 {"sdport", store_pint, ITEM(res_store.SDport), 0, ITEM_DEFAULT, 9103},
205 {"address", store_str, ITEM(res_store.address), 0, ITEM_REQUIRED, 0},
206 {"sdaddress", store_str, ITEM(res_store.address), 0, 0, 0},
207 {"password", store_password, ITEM(res_store.password), 0, ITEM_REQUIRED, 0},
208 {"sdpassword", store_password, ITEM(res_store.password), 0, 0, 0},
209 {"device", store_device, ITEM(res_store.device), R_DEVICE, ITEM_REQUIRED, 0},
210 {"mediatype", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
211 {"autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
212 {"enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true},
213 {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
214 {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
215 {"tlsenable", store_bool, ITEM(res_store.tls_enable), 0, 0, 0},
216 {"tlsrequire", store_bool, ITEM(res_store.tls_require), 0, 0, 0},
217 {"tlscacertificatefile", store_dir, ITEM(res_store.tls_ca_certfile), 0, 0, 0},
218 {"tlscacertificatedir", store_dir, ITEM(res_store.tls_ca_certdir), 0, 0, 0},
219 {"tlscertificate", store_dir, ITEM(res_store.tls_certfile), 0, 0, 0},
220 {"tlskey", store_dir, ITEM(res_store.tls_keyfile), 0, 0, 0},
221 {NULL, NULL, {0}, 0, 0, 0}
225 * Catalog Resource Directives
227 * name handler value code flags default_value
229 static RES_ITEM cat_items[] = {
230 {"name", store_name, ITEM(res_cat.hdr.name), 0, ITEM_REQUIRED, 0},
231 {"description", store_str, ITEM(res_cat.hdr.desc), 0, 0, 0},
232 {"address", store_str, ITEM(res_cat.db_address), 0, 0, 0},
233 {"dbaddress", store_str, ITEM(res_cat.db_address), 0, 0, 0},
234 {"dbport", store_pint, ITEM(res_cat.db_port), 0, 0, 0},
235 /* keep this password as store_str for the moment */
236 {"password", store_str, ITEM(res_cat.db_password), 0, 0, 0},
237 {"dbpassword", store_str, ITEM(res_cat.db_password), 0, 0, 0},
238 {"user", store_str, ITEM(res_cat.db_user), 0, 0, 0},
239 {"dbname", store_str, ITEM(res_cat.db_name), 0, ITEM_REQUIRED, 0},
240 {"dbsocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0},
241 /* Turned off for the moment */
242 {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
243 {NULL, NULL, {0}, 0, 0, 0}
247 * Job Resource Directives
249 * name handler value code flags default_value
251 RES_ITEM job_items[] = {
252 {"name", store_name, ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
253 {"description", store_str, ITEM(res_job.hdr.desc), 0, 0, 0},
254 {"type", store_jobtype, ITEM(res_job.JobType), 0, ITEM_REQUIRED, 0},
255 {"level", store_level, ITEM(res_job.JobLevel), 0, 0, 0},
256 {"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
257 {"storage", store_alist_res, ITEM(res_job.storage), R_STORAGE, 0, 0},
258 {"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
259 {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
260 {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
261 {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
262 {"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
263 {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
264 {"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
265 {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
266 {"jobtoverify", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
267 {"jobdefs", store_res, ITEM(res_job.jobdefs), R_JOBDEFS, 0, 0},
268 {"run", store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
269 /* Root of where to restore files */
270 {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
271 {"whereuseregexp", store_bool, ITEM(res_job.where_use_regexp), 0, 0, 0},
272 {"stripprefix", store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
273 {"addprefix", store_str, ITEM(res_job.add_prefix), 0, 0, 0},
274 {"addsuffix", store_str, ITEM(res_job.add_suffix), 0, 0, 0},
275 /* Where to find bootstrap during restore */
276 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
277 /* Where to write bootstrap file during backup */
278 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
279 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
280 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
281 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
282 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
283 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
284 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
285 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
286 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
287 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
288 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
289 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
290 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
291 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
292 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
293 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
294 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
295 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
296 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
297 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
298 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
299 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
300 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
301 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
302 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
303 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
304 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
305 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
306 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
307 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
308 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
309 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
310 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
311 {NULL, NULL, {0}, 0, 0, 0}
316 * name handler value code flags default_value
318 static RES_ITEM fs_items[] = {
319 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
320 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
321 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
322 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
323 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
324 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
325 {NULL, NULL, {0}, 0, 0, 0}
328 /* Schedule -- see run_conf.c */
331 * name handler value code flags default_value
333 static RES_ITEM sch_items[] = {
334 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
335 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
336 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
337 {NULL, NULL, {0}, 0, 0, 0}
342 * name handler value code flags default_value
344 static RES_ITEM pool_items[] = {
345 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
346 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
347 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
348 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
349 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
350 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
351 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
352 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
353 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
354 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
355 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
356 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
357 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
358 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
359 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
360 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
361 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
362 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
363 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
364 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
365 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
366 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
367 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
368 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
369 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
370 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
371 {NULL, NULL, {0}, 0, 0, 0}
376 * name handler value code flags default_value
378 static RES_ITEM counter_items[] = {
379 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
380 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
381 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
382 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
383 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
384 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
385 {NULL, NULL, {0}, 0, 0, 0}
389 /* Message resource */
390 extern RES_ITEM msgs_items[];
393 * This is the master resource definition.
394 * It must have one item for each of the resources.
396 * NOTE!!! keep it in the same order as the R_codes
397 * or eliminate all resources[rindex].name
399 * name items rcode res_head
401 RES_TABLE resources[] = {
402 {"director", dir_items, R_DIRECTOR},
403 {"client", cli_items, R_CLIENT},
404 {"job", job_items, R_JOB},
405 {"storage", store_items, R_STORAGE},
406 {"catalog", cat_items, R_CATALOG},
407 {"schedule", sch_items, R_SCHEDULE},
408 {"fileset", fs_items, R_FILESET},
409 {"pool", pool_items, R_POOL},
410 {"messages", msgs_items, R_MSGS},
411 {"counter", counter_items, R_COUNTER},
412 {"console", con_items, R_CONSOLE},
413 {"jobdefs", job_items, R_JOBDEFS},
414 {"device", NULL, R_DEVICE}, /* info obtained from SD */
419 /* Keywords (RHS) permitted in Job Level records
421 * level_name level job_type
423 struct s_jl joblevels[] = {
424 {"Full", L_FULL, JT_BACKUP},
425 {"Base", L_BASE, JT_BACKUP},
426 {"Incremental", L_INCREMENTAL, JT_BACKUP},
427 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
428 {"Since", L_SINCE, JT_BACKUP},
429 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
430 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
431 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
432 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
433 {"Data", L_VERIFY_DATA, JT_VERIFY},
434 {" ", L_NONE, JT_ADMIN},
435 {" ", L_NONE, JT_RESTORE},
439 /* Keywords (RHS) permitted in Job type records
443 struct s_jt jobtypes[] = {
444 {"backup", JT_BACKUP},
446 {"verify", JT_VERIFY},
447 {"restore", JT_RESTORE},
448 {"migrate", JT_MIGRATE},
453 /* Keywords (RHS) permitted in Selection type records
457 struct s_jt migtypes[] = {
458 {"smallestvolume", MT_SMALLEST_VOL},
459 {"oldestvolume", MT_OLDEST_VOL},
460 {"pooloccupancy", MT_POOL_OCCUPANCY},
461 {"pooltime", MT_POOL_TIME},
462 {"client", MT_CLIENT},
463 {"volume", MT_VOLUME},
465 {"sqlquery", MT_SQLQUERY},
471 /* Options permitted in Restore replace= */
472 struct s_kw ReplaceOptions[] = {
473 {"always", REPLACE_ALWAYS},
474 {"ifnewer", REPLACE_IFNEWER},
475 {"ifolder", REPLACE_IFOLDER},
476 {"never", REPLACE_NEVER},
480 const char *level_to_str(int level)
483 static char level_no[30];
484 const char *str = level_no;
486 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
487 for (i=0; joblevels[i].level_name; i++) {
488 if (level == joblevels[i].level) {
489 str = joblevels[i].level_name;
496 /* Dump contents of resource */
497 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
499 URES *res = (URES *)reshdr;
501 char ed1[100], ed2[100], ed3[100];
505 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
508 if (type < 0) { /* no recursion */
514 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
515 reshdr->name, res->res_dir.MaxConcurrentJobs,
516 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
517 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
518 if (res->res_dir.query_file) {
519 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
521 if (res->res_dir.messages) {
522 sendit(sock, _(" --> "));
523 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
527 sendit(sock, _("Console: name=%s SSL=%d\n"),
528 res->res_con.hdr.name, res->res_con.tls_enable);
531 if (res->res_counter.WrapCounter) {
532 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
533 res->res_counter.hdr.name, res->res_counter.MinValue,
534 res->res_counter.MaxValue, res->res_counter.CurrentValue,
535 res->res_counter.WrapCounter->hdr.name);
537 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
538 res->res_counter.hdr.name, res->res_counter.MinValue,
539 res->res_counter.MaxValue);
541 if (res->res_counter.Catalog) {
542 sendit(sock, _(" --> "));
543 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
548 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
549 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
550 res->res_client.MaxConcurrentJobs);
551 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
552 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
553 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
554 res->res_client.AutoPrune);
555 if (res->res_client.catalog) {
556 sendit(sock, _(" --> "));
557 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
563 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
564 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
565 " poolid=%s volname=%s MediaType=%s\n"),
566 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
567 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
568 dev->offline, dev->autochanger,
569 edit_uint64(dev->PoolId, ed1),
570 dev->VolumeName, dev->MediaType);
573 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
574 " DeviceName=%s MediaType=%s StorageId=%s\n"),
575 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
576 res->res_store.MaxConcurrentJobs,
577 res->res_store.dev_name(),
578 res->res_store.media_type,
579 edit_int64(res->res_store.StorageId, ed1));
582 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
583 " db_user=%s MutliDBConn=%d\n"),
584 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
585 res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
586 res->res_cat.mult_db_connections);
590 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
591 type == R_JOB ? _("Job") : _("JobDefs"),
592 res->res_job.hdr.name, res->res_job.JobType,
593 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
594 res->res_job.enabled);
595 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
596 res->res_job.MaxConcurrentJobs,
597 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
598 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
599 res->res_job.spool_data, res->res_job.write_part_after_job);
600 if (res->res_job.JobType == JT_MIGRATE) {
601 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
603 if (res->res_job.client) {
604 sendit(sock, _(" --> "));
605 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
607 if (res->res_job.fileset) {
608 sendit(sock, _(" --> "));
609 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
611 if (res->res_job.schedule) {
612 sendit(sock, _(" --> "));
613 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
615 if (res->res_job.RestoreWhere) {
616 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
618 if (res->res_job.where_use_regexp) {
619 sendit(sock, _(" --> RWhere=%u\n"), res->res_job.where_use_regexp);
621 if (res->res_job.RestoreBootstrap) {
622 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
624 if (res->res_job.WriteBootstrap) {
625 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
627 if (res->res_job.storage) {
629 foreach_alist(store, res->res_job.storage) {
630 sendit(sock, _(" --> "));
631 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
634 if (res->res_job.RunScripts) {
636 foreach_alist(script, res->res_job.RunScripts) {
637 sendit(sock, _(" --> RunScript\n"));
638 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
639 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
640 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
641 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
642 sendit(sock, _(" --> AbortJobOnError=%u\n"), script->abort_on_error);
643 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
646 if (res->res_job.pool) {
647 sendit(sock, _(" --> "));
648 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
650 if (res->res_job.full_pool) {
651 sendit(sock, _(" --> "));
652 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
654 if (res->res_job.inc_pool) {
655 sendit(sock, _(" --> "));
656 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
658 if (res->res_job.diff_pool) {
659 sendit(sock, _(" --> "));
660 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
662 if (res->res_job.verify_job) {
663 sendit(sock, _(" --> "));
664 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
666 if (res->res_job.run_cmds) {
668 foreach_alist(runcmd, res->res_job.run_cmds) {
669 sendit(sock, _(" --> Run=%s\n"), runcmd);
672 if (res->res_job.selection_pattern) {
673 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
675 if (res->res_job.messages) {
676 sendit(sock, _(" --> "));
677 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
683 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
684 for (i=0; i<res->res_fs.num_includes; i++) {
685 INCEXE *incexe = res->res_fs.include_items[i];
686 for (j=0; j<incexe->num_opts; j++) {
687 FOPTS *fo = incexe->opts_list[j];
688 sendit(sock, " O %s\n", fo->opts);
690 bool enhanced_wild = false;
691 for (k=0; fo->opts[k]!='\0'; k++) {
692 if (fo->opts[k]=='W') {
693 enhanced_wild = true;
698 for (k=0; k<fo->regex.size(); k++) {
699 sendit(sock, " R %s\n", fo->regex.get(k));
701 for (k=0; k<fo->regexdir.size(); k++) {
702 sendit(sock, " RD %s\n", fo->regexdir.get(k));
704 for (k=0; k<fo->regexfile.size(); k++) {
705 sendit(sock, " RF %s\n", fo->regexfile.get(k));
707 for (k=0; k<fo->wild.size(); k++) {
708 sendit(sock, " W %s\n", fo->wild.get(k));
710 for (k=0; k<fo->wilddir.size(); k++) {
711 sendit(sock, " WD %s\n", fo->wilddir.get(k));
713 for (k=0; k<fo->wildfile.size(); k++) {
714 sendit(sock, " WF %s\n", fo->wildfile.get(k));
716 for (k=0; k<fo->wildbase.size(); k++) {
717 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
719 for (k=0; k<fo->base.size(); k++) {
720 sendit(sock, " B %s\n", fo->base.get(k));
722 for (k=0; k<fo->fstype.size(); k++) {
723 sendit(sock, " X %s\n", fo->fstype.get(k));
725 for (k=0; k<fo->drivetype.size(); k++) {
726 sendit(sock, " XD %s\n", fo->drivetype.get(k));
729 sendit(sock, " D %s\n", fo->reader);
732 sendit(sock, " T %s\n", fo->writer);
734 sendit(sock, " N\n");
736 for (j=0; j<incexe->name_list.size(); j++) {
737 sendit(sock, " I %s\n", incexe->name_list.get(j));
739 if (incexe->name_list.size()) {
740 sendit(sock, " N\n");
744 for (i=0; i<res->res_fs.num_excludes; i++) {
745 INCEXE *incexe = res->res_fs.exclude_items[i];
746 for (j=0; j<incexe->name_list.size(); j++) {
747 sendit(sock, " E %s\n", incexe->name_list.get(j));
749 if (incexe->name_list.size()) {
750 sendit(sock, " N\n");
756 if (res->res_sch.run) {
758 RUN *run = res->res_sch.run;
759 char buf[1000], num[30];
760 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
765 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
766 bstrncpy(buf, _(" hour="), sizeof(buf));
767 for (i=0; i<24; i++) {
768 if (bit_is_set(i, run->hour)) {
769 bsnprintf(num, sizeof(num), "%d ", i);
770 bstrncat(buf, num, sizeof(buf));
773 bstrncat(buf, "\n", sizeof(buf));
775 bstrncpy(buf, _(" mday="), sizeof(buf));
776 for (i=0; i<31; i++) {
777 if (bit_is_set(i, run->mday)) {
778 bsnprintf(num, sizeof(num), "%d ", i);
779 bstrncat(buf, num, sizeof(buf));
782 bstrncat(buf, "\n", sizeof(buf));
784 bstrncpy(buf, _(" month="), sizeof(buf));
785 for (i=0; i<12; i++) {
786 if (bit_is_set(i, run->month)) {
787 bsnprintf(num, sizeof(num), "%d ", i);
788 bstrncat(buf, num, sizeof(buf));
791 bstrncat(buf, "\n", sizeof(buf));
793 bstrncpy(buf, _(" wday="), sizeof(buf));
794 for (i=0; i<7; i++) {
795 if (bit_is_set(i, run->wday)) {
796 bsnprintf(num, sizeof(num), "%d ", i);
797 bstrncat(buf, num, sizeof(buf));
800 bstrncat(buf, "\n", sizeof(buf));
802 bstrncpy(buf, _(" wom="), sizeof(buf));
803 for (i=0; i<5; i++) {
804 if (bit_is_set(i, run->wom)) {
805 bsnprintf(num, sizeof(num), "%d ", i);
806 bstrncat(buf, num, sizeof(buf));
809 bstrncat(buf, "\n", sizeof(buf));
811 bstrncpy(buf, _(" woy="), sizeof(buf));
812 for (i=0; i<54; i++) {
813 if (bit_is_set(i, run->woy)) {
814 bsnprintf(num, sizeof(num), "%d ", i);
815 bstrncat(buf, num, sizeof(buf));
818 bstrncat(buf, "\n", sizeof(buf));
820 sendit(sock, _(" mins=%d\n"), run->minute);
822 sendit(sock, _(" --> "));
823 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
826 sendit(sock, _(" --> "));
827 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
830 sendit(sock, _(" --> "));
831 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
833 /* If another Run record is chained in, go print it */
839 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
843 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
844 res->res_pool.pool_type);
845 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
846 res->res_pool.use_catalog, res->res_pool.use_volume_once,
847 res->res_pool.catalog_files);
848 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
849 res->res_pool.max_volumes, res->res_pool.AutoPrune,
850 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
851 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
852 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
853 res->res_pool.Recycle,
854 NPRT(res->res_pool.label_format));
855 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
856 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
857 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
858 res->res_pool.recycle_oldest_volume,
859 res->res_pool.purge_oldest_volume);
860 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
861 res->res_pool.MaxVolJobs,
862 res->res_pool.MaxVolFiles,
863 edit_uint64(res->res_pool.MaxVolFiles, ed1));
864 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
865 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
866 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
867 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
868 if (res->res_pool.NextPool) {
869 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
871 if (res->res_pool.RecyclePool) {
872 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
874 if (res->res_pool.storage) {
876 foreach_alist(store, res->res_pool.storage) {
877 sendit(sock, _(" --> "));
878 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
883 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
884 if (res->res_msgs.mail_cmd)
885 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
886 if (res->res_msgs.operator_cmd)
887 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
890 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
893 if (recurse && res->res_dir.hdr.next) {
894 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
899 * Free all the members of an INCEXE structure
901 static void free_incexe(INCEXE *incexe)
903 incexe->name_list.destroy();
904 for (int i=0; i<incexe->num_opts; i++) {
905 FOPTS *fopt = incexe->opts_list[i];
906 fopt->regex.destroy();
907 fopt->regexdir.destroy();
908 fopt->regexfile.destroy();
909 fopt->wild.destroy();
910 fopt->wilddir.destroy();
911 fopt->wildfile.destroy();
912 fopt->wildbase.destroy();
913 fopt->base.destroy();
914 fopt->fstype.destroy();
915 fopt->drivetype.destroy();
924 if (incexe->opts_list) {
925 free(incexe->opts_list);
931 * Free memory of resource -- called when daemon terminates.
932 * NB, we don't need to worry about freeing any references
933 * to other resources as they will be freed when that
934 * resource chain is traversed. Mainly we worry about freeing
935 * allocated strings (names).
937 void free_resource(RES *sres, int type)
940 RES *nres; /* next resource if linked */
941 URES *res = (URES *)sres;
946 /* common stuff -- free the resource name and description */
947 nres = (RES *)res->res_dir.hdr.next;
948 if (res->res_dir.hdr.name) {
949 free(res->res_dir.hdr.name);
951 if (res->res_dir.hdr.desc) {
952 free(res->res_dir.hdr.desc);
957 if (res->res_dir.working_directory) {
958 free(res->res_dir.working_directory);
960 if (res->res_dir.scripts_directory) {
961 free((char *)res->res_dir.scripts_directory);
963 if (res->res_dir.pid_directory) {
964 free(res->res_dir.pid_directory);
966 if (res->res_dir.subsys_directory) {
967 free(res->res_dir.subsys_directory);
969 if (res->res_dir.password) {
970 free(res->res_dir.password);
972 if (res->res_dir.query_file) {
973 free(res->res_dir.query_file);
975 if (res->res_dir.DIRaddrs) {
976 free_addresses(res->res_dir.DIRaddrs);
978 if (res->res_dir.tls_ctx) {
979 free_tls_context(res->res_dir.tls_ctx);
981 if (res->res_dir.tls_ca_certfile) {
982 free(res->res_dir.tls_ca_certfile);
984 if (res->res_dir.tls_ca_certdir) {
985 free(res->res_dir.tls_ca_certdir);
987 if (res->res_dir.tls_certfile) {
988 free(res->res_dir.tls_certfile);
990 if (res->res_dir.tls_keyfile) {
991 free(res->res_dir.tls_keyfile);
993 if (res->res_dir.tls_dhfile) {
994 free(res->res_dir.tls_dhfile);
996 if (res->res_dir.tls_allowed_cns) {
997 delete res->res_dir.tls_allowed_cns;
1004 if (res->res_con.password) {
1005 free(res->res_con.password);
1007 if (res->res_con.tls_ctx) {
1008 free_tls_context(res->res_con.tls_ctx);
1010 if (res->res_con.tls_ca_certfile) {
1011 free(res->res_con.tls_ca_certfile);
1013 if (res->res_con.tls_ca_certdir) {
1014 free(res->res_con.tls_ca_certdir);
1016 if (res->res_con.tls_certfile) {
1017 free(res->res_con.tls_certfile);
1019 if (res->res_con.tls_keyfile) {
1020 free(res->res_con.tls_keyfile);
1022 if (res->res_con.tls_dhfile) {
1023 free(res->res_con.tls_dhfile);
1025 if (res->res_con.tls_allowed_cns) {
1026 delete res->res_con.tls_allowed_cns;
1028 for (int i=0; i<Num_ACL; i++) {
1029 if (res->res_con.ACL_lists[i]) {
1030 delete res->res_con.ACL_lists[i];
1031 res->res_con.ACL_lists[i] = NULL;
1036 if (res->res_client.address) {
1037 free(res->res_client.address);
1039 if (res->res_client.password) {
1040 free(res->res_client.password);
1042 if (res->res_client.tls_ctx) {
1043 free_tls_context(res->res_client.tls_ctx);
1045 if (res->res_client.tls_ca_certfile) {
1046 free(res->res_client.tls_ca_certfile);
1048 if (res->res_client.tls_ca_certdir) {
1049 free(res->res_client.tls_ca_certdir);
1051 if (res->res_client.tls_certfile) {
1052 free(res->res_client.tls_certfile);
1054 if (res->res_client.tls_keyfile) {
1055 free(res->res_client.tls_keyfile);
1057 if (res->res_client.tls_allowed_cns) {
1058 delete res->res_client.tls_allowed_cns;
1062 if (res->res_store.address) {
1063 free(res->res_store.address);
1065 if (res->res_store.password) {
1066 free(res->res_store.password);
1068 if (res->res_store.media_type) {
1069 free(res->res_store.media_type);
1071 if (res->res_store.device) {
1072 delete res->res_store.device;
1074 if (res->res_store.tls_ctx) {
1075 free_tls_context(res->res_store.tls_ctx);
1077 if (res->res_store.tls_ca_certfile) {
1078 free(res->res_store.tls_ca_certfile);
1080 if (res->res_store.tls_ca_certdir) {
1081 free(res->res_store.tls_ca_certdir);
1083 if (res->res_store.tls_certfile) {
1084 free(res->res_store.tls_certfile);
1086 if (res->res_store.tls_keyfile) {
1087 free(res->res_store.tls_keyfile);
1091 if (res->res_cat.db_address) {
1092 free(res->res_cat.db_address);
1094 if (res->res_cat.db_socket) {
1095 free(res->res_cat.db_socket);
1097 if (res->res_cat.db_user) {
1098 free(res->res_cat.db_user);
1100 if (res->res_cat.db_name) {
1101 free(res->res_cat.db_name);
1103 if (res->res_cat.db_password) {
1104 free(res->res_cat.db_password);
1108 if ((num=res->res_fs.num_includes)) {
1109 while (--num >= 0) {
1110 free_incexe(res->res_fs.include_items[num]);
1112 free(res->res_fs.include_items);
1114 res->res_fs.num_includes = 0;
1115 if ((num=res->res_fs.num_excludes)) {
1116 while (--num >= 0) {
1117 free_incexe(res->res_fs.exclude_items[num]);
1119 free(res->res_fs.exclude_items);
1121 res->res_fs.num_excludes = 0;
1124 if (res->res_pool.pool_type) {
1125 free(res->res_pool.pool_type);
1127 if (res->res_pool.label_format) {
1128 free(res->res_pool.label_format);
1130 if (res->res_pool.cleaning_prefix) {
1131 free(res->res_pool.cleaning_prefix);
1133 if (res->res_pool.storage) {
1134 delete res->res_pool.storage;
1138 if (res->res_sch.run) {
1140 nrun = res->res_sch.run;
1150 if (res->res_job.RestoreWhere) {
1151 free(res->res_job.RestoreWhere);
1153 if (res->res_job.strip_prefix) {
1154 free(res->res_job.strip_prefix);
1156 if (res->res_job.add_prefix) {
1157 free(res->res_job.add_prefix);
1159 if (res->res_job.add_suffix) {
1160 free(res->res_job.add_suffix);
1162 if (res->res_job.RestoreBootstrap) {
1163 free(res->res_job.RestoreBootstrap);
1165 if (res->res_job.WriteBootstrap) {
1166 free(res->res_job.WriteBootstrap);
1168 if (res->res_job.selection_pattern) {
1169 free(res->res_job.selection_pattern);
1171 if (res->res_job.run_cmds) {
1172 delete res->res_job.run_cmds;
1174 if (res->res_job.storage) {
1175 delete res->res_job.storage;
1177 if (res->res_job.RunScripts) {
1178 free_runscripts(res->res_job.RunScripts);
1179 delete res->res_job.RunScripts;
1183 if (res->res_msgs.mail_cmd) {
1184 free(res->res_msgs.mail_cmd);
1186 if (res->res_msgs.operator_cmd) {
1187 free(res->res_msgs.operator_cmd);
1189 free_msgs_res((MSGS *)res); /* free message resource */
1193 printf(_("Unknown resource type %d in free_resource.\n"), type);
1195 /* Common stuff again -- free the resource, recurse to next one */
1200 free_resource(nres, type);
1205 * Save the new resource by chaining it into the head list for
1206 * the resource. If this is pass 2, we update any resource
1207 * pointers because they may not have been defined until
1210 void save_resource(int type, RES_ITEM *items, int pass)
1213 int rindex = type - r_first;
1217 /* Check Job requirements after applying JobDefs */
1218 if (type != R_JOB && type != R_JOBDEFS) {
1220 * Ensure that all required items are present
1222 for (i=0; items[i].name; i++) {
1223 if (items[i].flags & ITEM_REQUIRED) {
1224 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1225 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1226 items[i].name, resources[rindex]);
1229 /* If this triggers, take a look at lib/parse_conf.h */
1230 if (i >= MAX_RES_ITEMS) {
1231 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1234 } else if (type == R_JOB) {
1236 * Ensure that the name item is present
1238 if (items[0].flags & ITEM_REQUIRED) {
1239 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1240 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1241 items[0].name, resources[rindex]);
1247 * During pass 2 in each "store" routine, we looked up pointers
1248 * to all the resources referrenced in the current resource, now we
1249 * must copy their addresses from the static record to the allocated
1254 /* Resources not containing a resource */
1262 * Resources containing another resource or alist. First
1263 * look up the resource which contains another resource. It
1264 * was written during pass 1. Then stuff in the pointers to
1265 * the resources it contains, which were inserted this pass.
1266 * Finally, it will all be stored back.
1269 /* Find resource saved in pass 1 */
1270 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1271 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1273 /* Explicitly copy resource pointers from this pass (res_all) */
1274 res->res_pool.NextPool = res_all.res_pool.NextPool;
1275 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1276 res->res_pool.storage = res_all.res_pool.storage;
1279 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1280 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1282 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1285 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1286 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1288 res->res_dir.messages = res_all.res_dir.messages;
1289 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1292 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1293 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1294 res_all.res_dir.hdr.name);
1296 /* we must explicitly copy the device alist pointer */
1297 res->res_store.device = res_all.res_store.device;
1301 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1302 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1303 res_all.res_dir.hdr.name);
1305 res->res_job.messages = res_all.res_job.messages;
1306 res->res_job.schedule = res_all.res_job.schedule;
1307 res->res_job.client = res_all.res_job.client;
1308 res->res_job.fileset = res_all.res_job.fileset;
1309 res->res_job.storage = res_all.res_job.storage;
1310 res->res_job.pool = res_all.res_job.pool;
1311 res->res_job.full_pool = res_all.res_job.full_pool;
1312 res->res_job.inc_pool = res_all.res_job.inc_pool;
1313 res->res_job.diff_pool = res_all.res_job.diff_pool;
1314 res->res_job.verify_job = res_all.res_job.verify_job;
1315 res->res_job.jobdefs = res_all.res_job.jobdefs;
1316 res->res_job.run_cmds = res_all.res_job.run_cmds;
1317 res->res_job.RunScripts = res_all.res_job.RunScripts;
1318 if (res->res_job.strip_prefix ||
1319 res->res_job.add_suffix ||
1320 res->res_job.add_prefix)
1322 if (res->res_job.RestoreWhere) {
1323 free(res->res_job.RestoreWhere);
1325 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1326 res->res_job.add_prefix,
1327 res->res_job.add_suffix);
1328 res->res_job.RestoreWhere = (char *) bmalloc (len * sizeof(char));
1329 bregexp_build_where(res->res_job.RestoreWhere, len,
1330 res->res_job.strip_prefix,
1331 res->res_job.add_prefix,
1332 res->res_job.add_suffix);
1333 res->res_job.where_use_regexp = true;
1335 /* TODO: test bregexp */
1339 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1340 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1342 res->res_counter.Catalog = res_all.res_counter.Catalog;
1343 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1347 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1348 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1350 res->res_client.catalog = res_all.res_client.catalog;
1351 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1355 * Schedule is a bit different in that it contains a RUN record
1356 * chain which isn't a "named" resource. This chain was linked
1357 * in by run_conf.c during pass 2, so here we jam the pointer
1358 * into the Schedule resource.
1360 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1361 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1363 res->res_sch.run = res_all.res_sch.run;
1366 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1370 /* Note, the resource name was already saved during pass 1,
1371 * so here, we can just release it.
1373 if (res_all.res_dir.hdr.name) {
1374 free(res_all.res_dir.hdr.name);
1375 res_all.res_dir.hdr.name = NULL;
1377 if (res_all.res_dir.hdr.desc) {
1378 free(res_all.res_dir.hdr.desc);
1379 res_all.res_dir.hdr.desc = NULL;
1385 * The following code is only executed during pass 1
1389 size = sizeof(DIRRES);
1392 size = sizeof(CONRES);
1395 size =sizeof(CLIENT);
1398 size = sizeof(STORE);
1408 size = sizeof(FILESET);
1411 size = sizeof(SCHED);
1414 size = sizeof(POOL);
1417 size = sizeof(MSGS);
1420 size = sizeof(COUNTER);
1426 printf(_("Unknown resource type %d in save_resource.\n"), type);
1432 res = (URES *)malloc(size);
1433 memcpy(res, &res_all, size);
1434 if (!res_head[rindex]) {
1435 res_head[rindex] = (RES *)res; /* store first entry */
1436 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1437 res->res_dir.hdr.name, rindex);
1440 if (res->res_dir.hdr.name == NULL) {
1441 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1444 /* Add new res to end of chain */
1445 for (last=next=res_head[rindex]; next; next=next->next) {
1447 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1448 Emsg2(M_ERROR_TERM, 0,
1449 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1450 resources[rindex].name, res->res_dir.hdr.name);
1453 last->next = (RES *)res;
1454 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1455 res->res_dir.hdr.name, rindex, pass);
1461 * Store Device. Note, the resource is created upon the
1462 * first reference. The details of the resource are obtained
1463 * later from the SD.
1465 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1469 int rindex = R_DEVICE - r_first;
1470 int size = sizeof(DEVICE);
1474 token = lex_get_token(lc, T_NAME);
1475 if (!res_head[rindex]) {
1476 res = (URES *)malloc(size);
1477 memset(res, 0, size);
1478 res->res_dev.hdr.name = bstrdup(lc->str);
1479 res_head[rindex] = (RES *)res; /* store first entry */
1480 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1481 res->res_dir.hdr.name, rindex);
1484 /* See if it is already defined */
1485 for (next=res_head[rindex]; next->next; next=next->next) {
1486 if (strcmp(next->name, lc->str) == 0) {
1492 res = (URES *)malloc(size);
1493 memset(res, 0, size);
1494 res->res_dev.hdr.name = bstrdup(lc->str);
1495 next->next = (RES *)res;
1496 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1497 res->res_dir.hdr.name, rindex, pass);
1502 set_bit(index, res_all.hdr.item_present);
1504 store_alist_res(lc, item, index, pass);
1509 * Store JobType (backup, verify, restore)
1512 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1516 token = lex_get_token(lc, T_NAME);
1517 /* Store the type both pass 1 and pass 2 */
1518 for (i=0; migtypes[i].type_name; i++) {
1519 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1520 *(int *)(item->value) = migtypes[i].job_type;
1526 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1529 set_bit(index, res_all.hdr.item_present);
1535 * Store JobType (backup, verify, restore)
1538 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1542 token = lex_get_token(lc, T_NAME);
1543 /* Store the type both pass 1 and pass 2 */
1544 for (i=0; jobtypes[i].type_name; i++) {
1545 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1546 *(int *)(item->value) = jobtypes[i].job_type;
1552 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1555 set_bit(index, res_all.hdr.item_present);
1559 * Store Job Level (Full, Incremental, ...)
1562 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1566 token = lex_get_token(lc, T_NAME);
1567 /* Store the level pass 2 so that type is defined */
1568 for (i=0; joblevels[i].level_name; i++) {
1569 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1570 *(int *)(item->value) = joblevels[i].level;
1576 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1579 set_bit(index, res_all.hdr.item_present);
1583 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1586 token = lex_get_token(lc, T_NAME);
1587 /* Scan Replacement options */
1588 for (i=0; ReplaceOptions[i].name; i++) {
1589 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1590 *(int *)(item->value) = ReplaceOptions[i].token;
1596 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1599 set_bit(index, res_all.hdr.item_present);
1603 * Store ACL (access control list)
1606 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1611 token = lex_get_token(lc, T_STRING);
1613 if (((alist **)item->value)[item->code] == NULL) {
1614 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1615 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1617 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1618 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1620 token = lex_get_token(lc, T_ALL);
1621 if (token == T_COMMA) {
1622 continue; /* get another ACL */
1626 set_bit(index, res_all.hdr.item_present);
1629 /* We build RunScripts items here */
1630 static RUNSCRIPT res_runscript;
1632 /* Store a runscript->when in a bit field */
1633 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1635 lex_get_token(lc, T_NAME);
1637 if (strcasecmp(lc->str, "before") == 0) {
1638 *(int *)(item->value) = SCRIPT_Before ;
1639 } else if (strcasecmp(lc->str, "after") == 0) {
1640 *(int *)(item->value) = SCRIPT_After;
1641 } else if (strcasecmp(lc->str, "always") == 0) {
1642 *(int *)(item->value) = SCRIPT_Any;
1644 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1649 /* Store a runscript->target
1652 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1654 lex_get_token(lc, T_STRING);
1657 if (strcmp(lc->str, "%c") == 0) {
1658 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1659 } else if (strcasecmp(lc->str, "yes") == 0) {
1660 ((RUNSCRIPT*) item->value)->set_target("%c");
1661 } else if (strcasecmp(lc->str, "no") == 0) {
1662 ((RUNSCRIPT*) item->value)->set_target("");
1664 RES *res = GetResWithName(R_CLIENT, lc->str);
1666 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1667 lc->str, lc->line_no, lc->line);
1670 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1676 /* Store a runscript->command in a bit field
1679 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1681 lex_get_token(lc, T_STRING);
1684 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1689 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1691 lex_get_token(lc, T_STRING);
1692 alist **runscripts = (alist **)(item->value) ;
1695 RUNSCRIPT *script = new_runscript();
1697 script->set_command(lc->str);
1699 /* TODO: remove all script->old_proto with bacula 1.42 */
1701 if (strcmp(item->name, "runbeforejob") == 0) {
1702 script->when = SCRIPT_Before;
1703 script->abort_on_error = true;
1704 script->set_target("");
1706 } else if (strcmp(item->name, "runafterjob") == 0) {
1707 script->when = SCRIPT_After;
1708 script->on_success = true;
1709 script->on_failure = false;
1710 script->set_target("");
1712 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1713 script->old_proto = true;
1714 script->when = SCRIPT_After;
1715 script->set_target("%c");
1716 script->on_success = true;
1717 script->on_failure = false;
1719 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1720 script->old_proto = true;
1721 script->when = SCRIPT_Before;
1722 script->set_target("%c");
1723 script->abort_on_error = true;
1725 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1726 script->when = SCRIPT_After;
1727 script->on_failure = true;
1728 script->on_success = false;
1729 script->set_target("");
1732 if (*runscripts == NULL) {
1733 *runscripts = New(alist(10, not_owned_by_alist));
1736 (*runscripts)->append(script);
1743 /* Store a bool in a bit field without modifing res_all.hdr
1744 * We can also add an option to store_bool to skip res_all.hdr
1746 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1748 lex_get_token(lc, T_NAME);
1749 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1750 *(bool *)(item->value) = true;
1751 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1752 *(bool *)(item->value) = false;
1754 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1760 * new RunScript items
1761 * name handler value code flags default_value
1763 static RES_ITEM runscript_items[] = {
1764 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1765 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1766 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1767 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1768 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.abort_on_error},0, 0, 0},
1769 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1770 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1771 {NULL, NULL, {0}, 0, 0, 0}
1775 * Store RunScript info
1777 * Note, when this routine is called, we are inside a Job
1778 * resource. We treat the RunScript like a sort of
1779 * mini-resource within the Job resource.
1781 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1784 alist **runscripts = (alist **)(item->value) ;
1786 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1788 res_runscript.reset_default(); /* setting on_success, on_failure, abort_on_error */
1790 token = lex_get_token(lc, T_SKIP_EOL);
1792 if (token != T_BOB) {
1793 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1796 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1797 if (token == T_EOB) {
1800 if (token != T_IDENTIFIER) {
1801 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1803 for (i=0; runscript_items[i].name; i++) {
1804 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1805 token = lex_get_token(lc, T_SKIP_EOL);
1806 if (token != T_EQUALS) {
1807 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1810 /* Call item handler */
1811 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1818 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1823 if (res_runscript.command == NULL) {
1824 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1825 "command", "runscript");
1828 /* run on client by default */
1829 if (res_runscript.target == NULL) {
1830 res_runscript.set_target("%c");
1833 RUNSCRIPT *script = new_runscript();
1834 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1836 if (*runscripts == NULL) {
1837 *runscripts = New(alist(10, not_owned_by_alist));
1840 (*runscripts)->append(script);
1845 set_bit(index, res_all.hdr.item_present);