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 {"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 {"whereuseregexp", store_bool, ITEM(res_job.where_use_regexp), 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 {NULL, NULL, {0}, 0, 0, 0}
379 * name handler value code flags default_value
381 static RES_ITEM counter_items[] = {
382 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
383 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
384 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
385 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
386 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
387 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
388 {NULL, NULL, {0}, 0, 0, 0}
392 /* Message resource */
393 extern RES_ITEM msgs_items[];
396 * This is the master resource definition.
397 * It must have one item for each of the resources.
399 * NOTE!!! keep it in the same order as the R_codes
400 * or eliminate all resources[rindex].name
402 * name items rcode res_head
404 RES_TABLE resources[] = {
405 {"director", dir_items, R_DIRECTOR},
406 {"client", cli_items, R_CLIENT},
407 {"job", job_items, R_JOB},
408 {"storage", store_items, R_STORAGE},
409 {"catalog", cat_items, R_CATALOG},
410 {"schedule", sch_items, R_SCHEDULE},
411 {"fileset", fs_items, R_FILESET},
412 {"pool", pool_items, R_POOL},
413 {"messages", msgs_items, R_MSGS},
414 {"counter", counter_items, R_COUNTER},
415 {"console", con_items, R_CONSOLE},
416 {"jobdefs", job_items, R_JOBDEFS},
417 {"device", NULL, R_DEVICE}, /* info obtained from SD */
422 /* Keywords (RHS) permitted in Job Level records
424 * level_name level job_type
426 struct s_jl joblevels[] = {
427 {"Full", L_FULL, JT_BACKUP},
428 {"Base", L_BASE, JT_BACKUP},
429 {"Incremental", L_INCREMENTAL, JT_BACKUP},
430 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
431 {"Since", L_SINCE, JT_BACKUP},
432 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
433 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
434 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
435 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
436 {"Data", L_VERIFY_DATA, JT_VERIFY},
437 {" ", L_NONE, JT_ADMIN},
438 {" ", L_NONE, JT_RESTORE},
442 /* Keywords (RHS) permitted in Job type records
446 struct s_jt jobtypes[] = {
447 {"backup", JT_BACKUP},
449 {"verify", JT_VERIFY},
450 {"restore", JT_RESTORE},
451 {"migrate", JT_MIGRATE},
456 /* Keywords (RHS) permitted in Selection type records
460 struct s_jt migtypes[] = {
461 {"smallestvolume", MT_SMALLEST_VOL},
462 {"oldestvolume", MT_OLDEST_VOL},
463 {"pooloccupancy", MT_POOL_OCCUPANCY},
464 {"pooltime", MT_POOL_TIME},
465 {"client", MT_CLIENT},
466 {"volume", MT_VOLUME},
468 {"sqlquery", MT_SQLQUERY},
474 /* Options permitted in Restore replace= */
475 struct s_kw ReplaceOptions[] = {
476 {"always", REPLACE_ALWAYS},
477 {"ifnewer", REPLACE_IFNEWER},
478 {"ifolder", REPLACE_IFOLDER},
479 {"never", REPLACE_NEVER},
483 const char *level_to_str(int level)
486 static char level_no[30];
487 const char *str = level_no;
489 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
490 for (i=0; joblevels[i].level_name; i++) {
491 if (level == joblevels[i].level) {
492 str = joblevels[i].level_name;
499 /* Dump contents of resource */
500 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
502 URES *res = (URES *)reshdr;
504 char ed1[100], ed2[100], ed3[100];
508 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
511 if (type < 0) { /* no recursion */
517 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
518 reshdr->name, res->res_dir.MaxConcurrentJobs,
519 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
520 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
521 if (res->res_dir.query_file) {
522 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
524 if (res->res_dir.messages) {
525 sendit(sock, _(" --> "));
526 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
530 sendit(sock, _("Console: name=%s SSL=%d\n"),
531 res->res_con.hdr.name, res->res_con.tls_enable);
534 if (res->res_counter.WrapCounter) {
535 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
536 res->res_counter.hdr.name, res->res_counter.MinValue,
537 res->res_counter.MaxValue, res->res_counter.CurrentValue,
538 res->res_counter.WrapCounter->hdr.name);
540 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
541 res->res_counter.hdr.name, res->res_counter.MinValue,
542 res->res_counter.MaxValue);
544 if (res->res_counter.Catalog) {
545 sendit(sock, _(" --> "));
546 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
551 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
552 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
553 res->res_client.MaxConcurrentJobs);
554 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
555 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
556 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
557 res->res_client.AutoPrune);
558 if (res->res_client.catalog) {
559 sendit(sock, _(" --> "));
560 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
566 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
567 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
568 " poolid=%s volname=%s MediaType=%s\n"),
569 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
570 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
571 dev->offline, dev->autochanger,
572 edit_uint64(dev->PoolId, ed1),
573 dev->VolumeName, dev->MediaType);
576 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
577 " DeviceName=%s MediaType=%s StorageId=%s\n"),
578 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
579 res->res_store.MaxConcurrentJobs,
580 res->res_store.dev_name(),
581 res->res_store.media_type,
582 edit_int64(res->res_store.StorageId, ed1));
585 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
586 " db_user=%s MutliDBConn=%d\n"),
587 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
588 res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
589 res->res_cat.mult_db_connections);
593 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
594 type == R_JOB ? _("Job") : _("JobDefs"),
595 res->res_job.hdr.name, res->res_job.JobType,
596 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
597 res->res_job.enabled);
598 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
599 res->res_job.MaxConcurrentJobs,
600 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
601 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
602 res->res_job.spool_data, res->res_job.write_part_after_job);
603 if (res->res_job.JobType == JT_MIGRATE) {
604 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
606 if (res->res_job.client) {
607 sendit(sock, _(" --> "));
608 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
610 if (res->res_job.fileset) {
611 sendit(sock, _(" --> "));
612 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
614 if (res->res_job.schedule) {
615 sendit(sock, _(" --> "));
616 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
618 if (res->res_job.RestoreWhere) {
619 if (res->res_job.where_use_regexp) {
620 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RestoreWhere));
622 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
625 if (res->res_job.RestoreBootstrap) {
626 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
628 if (res->res_job.WriteBootstrap) {
629 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
631 if (res->res_job.storage) {
633 foreach_alist(store, res->res_job.storage) {
634 sendit(sock, _(" --> "));
635 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
638 if (res->res_job.RunScripts) {
640 foreach_alist(script, res->res_job.RunScripts) {
641 sendit(sock, _(" --> RunScript\n"));
642 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
643 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
644 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
645 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
646 sendit(sock, _(" --> AbortJobOnError=%u\n"), script->abort_on_error);
647 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
650 if (res->res_job.pool) {
651 sendit(sock, _(" --> "));
652 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
654 if (res->res_job.full_pool) {
655 sendit(sock, _(" --> "));
656 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
658 if (res->res_job.inc_pool) {
659 sendit(sock, _(" --> "));
660 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
662 if (res->res_job.diff_pool) {
663 sendit(sock, _(" --> "));
664 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
666 if (res->res_job.verify_job) {
667 sendit(sock, _(" --> "));
668 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
670 if (res->res_job.run_cmds) {
672 foreach_alist(runcmd, res->res_job.run_cmds) {
673 sendit(sock, _(" --> Run=%s\n"), runcmd);
676 if (res->res_job.selection_pattern) {
677 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
679 if (res->res_job.messages) {
680 sendit(sock, _(" --> "));
681 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
687 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
688 for (i=0; i<res->res_fs.num_includes; i++) {
689 INCEXE *incexe = res->res_fs.include_items[i];
690 for (j=0; j<incexe->num_opts; j++) {
691 FOPTS *fo = incexe->opts_list[j];
692 sendit(sock, " O %s\n", fo->opts);
694 bool enhanced_wild = false;
695 for (k=0; fo->opts[k]!='\0'; k++) {
696 if (fo->opts[k]=='W') {
697 enhanced_wild = true;
702 for (k=0; k<fo->regex.size(); k++) {
703 sendit(sock, " R %s\n", fo->regex.get(k));
705 for (k=0; k<fo->regexdir.size(); k++) {
706 sendit(sock, " RD %s\n", fo->regexdir.get(k));
708 for (k=0; k<fo->regexfile.size(); k++) {
709 sendit(sock, " RF %s\n", fo->regexfile.get(k));
711 for (k=0; k<fo->wild.size(); k++) {
712 sendit(sock, " W %s\n", fo->wild.get(k));
714 for (k=0; k<fo->wilddir.size(); k++) {
715 sendit(sock, " WD %s\n", fo->wilddir.get(k));
717 for (k=0; k<fo->wildfile.size(); k++) {
718 sendit(sock, " WF %s\n", fo->wildfile.get(k));
720 for (k=0; k<fo->wildbase.size(); k++) {
721 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
723 for (k=0; k<fo->base.size(); k++) {
724 sendit(sock, " B %s\n", fo->base.get(k));
726 for (k=0; k<fo->fstype.size(); k++) {
727 sendit(sock, " X %s\n", fo->fstype.get(k));
729 for (k=0; k<fo->drivetype.size(); k++) {
730 sendit(sock, " XD %s\n", fo->drivetype.get(k));
733 sendit(sock, " D %s\n", fo->reader);
736 sendit(sock, " T %s\n", fo->writer);
738 sendit(sock, " N\n");
740 for (j=0; j<incexe->name_list.size(); j++) {
741 sendit(sock, " I %s\n", incexe->name_list.get(j));
743 if (incexe->name_list.size()) {
744 sendit(sock, " N\n");
748 for (i=0; i<res->res_fs.num_excludes; i++) {
749 INCEXE *incexe = res->res_fs.exclude_items[i];
750 for (j=0; j<incexe->name_list.size(); j++) {
751 sendit(sock, " E %s\n", incexe->name_list.get(j));
753 if (incexe->name_list.size()) {
754 sendit(sock, " N\n");
760 if (res->res_sch.run) {
762 RUN *run = res->res_sch.run;
763 char buf[1000], num[30];
764 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
769 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
770 bstrncpy(buf, _(" hour="), sizeof(buf));
771 for (i=0; i<24; i++) {
772 if (bit_is_set(i, run->hour)) {
773 bsnprintf(num, sizeof(num), "%d ", i);
774 bstrncat(buf, num, sizeof(buf));
777 bstrncat(buf, "\n", sizeof(buf));
779 bstrncpy(buf, _(" mday="), sizeof(buf));
780 for (i=0; i<31; i++) {
781 if (bit_is_set(i, run->mday)) {
782 bsnprintf(num, sizeof(num), "%d ", i);
783 bstrncat(buf, num, sizeof(buf));
786 bstrncat(buf, "\n", sizeof(buf));
788 bstrncpy(buf, _(" month="), sizeof(buf));
789 for (i=0; i<12; i++) {
790 if (bit_is_set(i, run->month)) {
791 bsnprintf(num, sizeof(num), "%d ", i);
792 bstrncat(buf, num, sizeof(buf));
795 bstrncat(buf, "\n", sizeof(buf));
797 bstrncpy(buf, _(" wday="), sizeof(buf));
798 for (i=0; i<7; i++) {
799 if (bit_is_set(i, run->wday)) {
800 bsnprintf(num, sizeof(num), "%d ", i);
801 bstrncat(buf, num, sizeof(buf));
804 bstrncat(buf, "\n", sizeof(buf));
806 bstrncpy(buf, _(" wom="), sizeof(buf));
807 for (i=0; i<5; i++) {
808 if (bit_is_set(i, run->wom)) {
809 bsnprintf(num, sizeof(num), "%d ", i);
810 bstrncat(buf, num, sizeof(buf));
813 bstrncat(buf, "\n", sizeof(buf));
815 bstrncpy(buf, _(" woy="), sizeof(buf));
816 for (i=0; i<54; i++) {
817 if (bit_is_set(i, run->woy)) {
818 bsnprintf(num, sizeof(num), "%d ", i);
819 bstrncat(buf, num, sizeof(buf));
822 bstrncat(buf, "\n", sizeof(buf));
824 sendit(sock, _(" mins=%d\n"), run->minute);
826 sendit(sock, _(" --> "));
827 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
830 sendit(sock, _(" --> "));
831 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
834 sendit(sock, _(" --> "));
835 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
837 /* If another Run record is chained in, go print it */
843 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
847 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
848 res->res_pool.pool_type);
849 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
850 res->res_pool.use_catalog, res->res_pool.use_volume_once,
851 res->res_pool.catalog_files);
852 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
853 res->res_pool.max_volumes, res->res_pool.AutoPrune,
854 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
855 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
856 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
857 res->res_pool.Recycle,
858 NPRT(res->res_pool.label_format));
859 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
860 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
861 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
862 res->res_pool.recycle_oldest_volume,
863 res->res_pool.purge_oldest_volume);
864 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
865 res->res_pool.MaxVolJobs,
866 res->res_pool.MaxVolFiles,
867 edit_uint64(res->res_pool.MaxVolFiles, ed1));
868 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
869 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
870 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
871 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
872 if (res->res_pool.NextPool) {
873 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
875 if (res->res_pool.RecyclePool) {
876 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
878 if (res->res_pool.storage) {
880 foreach_alist(store, res->res_pool.storage) {
881 sendit(sock, _(" --> "));
882 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
887 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
888 if (res->res_msgs.mail_cmd)
889 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
890 if (res->res_msgs.operator_cmd)
891 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
894 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
897 if (recurse && res->res_dir.hdr.next) {
898 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
903 * Free all the members of an INCEXE structure
905 static void free_incexe(INCEXE *incexe)
907 incexe->name_list.destroy();
908 for (int i=0; i<incexe->num_opts; i++) {
909 FOPTS *fopt = incexe->opts_list[i];
910 fopt->regex.destroy();
911 fopt->regexdir.destroy();
912 fopt->regexfile.destroy();
913 fopt->wild.destroy();
914 fopt->wilddir.destroy();
915 fopt->wildfile.destroy();
916 fopt->wildbase.destroy();
917 fopt->base.destroy();
918 fopt->fstype.destroy();
919 fopt->drivetype.destroy();
928 if (incexe->opts_list) {
929 free(incexe->opts_list);
935 * Free memory of resource -- called when daemon terminates.
936 * NB, we don't need to worry about freeing any references
937 * to other resources as they will be freed when that
938 * resource chain is traversed. Mainly we worry about freeing
939 * allocated strings (names).
941 void free_resource(RES *sres, int type)
944 RES *nres; /* next resource if linked */
945 URES *res = (URES *)sres;
950 /* common stuff -- free the resource name and description */
951 nres = (RES *)res->res_dir.hdr.next;
952 if (res->res_dir.hdr.name) {
953 free(res->res_dir.hdr.name);
955 if (res->res_dir.hdr.desc) {
956 free(res->res_dir.hdr.desc);
961 if (res->res_dir.working_directory) {
962 free(res->res_dir.working_directory);
964 if (res->res_dir.scripts_directory) {
965 free((char *)res->res_dir.scripts_directory);
967 if (res->res_dir.pid_directory) {
968 free(res->res_dir.pid_directory);
970 if (res->res_dir.subsys_directory) {
971 free(res->res_dir.subsys_directory);
973 if (res->res_dir.password) {
974 free(res->res_dir.password);
976 if (res->res_dir.query_file) {
977 free(res->res_dir.query_file);
979 if (res->res_dir.DIRaddrs) {
980 free_addresses(res->res_dir.DIRaddrs);
982 if (res->res_dir.tls_ctx) {
983 free_tls_context(res->res_dir.tls_ctx);
985 if (res->res_dir.tls_ca_certfile) {
986 free(res->res_dir.tls_ca_certfile);
988 if (res->res_dir.tls_ca_certdir) {
989 free(res->res_dir.tls_ca_certdir);
991 if (res->res_dir.tls_certfile) {
992 free(res->res_dir.tls_certfile);
994 if (res->res_dir.tls_keyfile) {
995 free(res->res_dir.tls_keyfile);
997 if (res->res_dir.tls_dhfile) {
998 free(res->res_dir.tls_dhfile);
1000 if (res->res_dir.tls_allowed_cns) {
1001 delete res->res_dir.tls_allowed_cns;
1008 if (res->res_con.password) {
1009 free(res->res_con.password);
1011 if (res->res_con.tls_ctx) {
1012 free_tls_context(res->res_con.tls_ctx);
1014 if (res->res_con.tls_ca_certfile) {
1015 free(res->res_con.tls_ca_certfile);
1017 if (res->res_con.tls_ca_certdir) {
1018 free(res->res_con.tls_ca_certdir);
1020 if (res->res_con.tls_certfile) {
1021 free(res->res_con.tls_certfile);
1023 if (res->res_con.tls_keyfile) {
1024 free(res->res_con.tls_keyfile);
1026 if (res->res_con.tls_dhfile) {
1027 free(res->res_con.tls_dhfile);
1029 if (res->res_con.tls_allowed_cns) {
1030 delete res->res_con.tls_allowed_cns;
1032 for (int i=0; i<Num_ACL; i++) {
1033 if (res->res_con.ACL_lists[i]) {
1034 delete res->res_con.ACL_lists[i];
1035 res->res_con.ACL_lists[i] = NULL;
1040 if (res->res_client.address) {
1041 free(res->res_client.address);
1043 if (res->res_client.password) {
1044 free(res->res_client.password);
1046 if (res->res_client.tls_ctx) {
1047 free_tls_context(res->res_client.tls_ctx);
1049 if (res->res_client.tls_ca_certfile) {
1050 free(res->res_client.tls_ca_certfile);
1052 if (res->res_client.tls_ca_certdir) {
1053 free(res->res_client.tls_ca_certdir);
1055 if (res->res_client.tls_certfile) {
1056 free(res->res_client.tls_certfile);
1058 if (res->res_client.tls_keyfile) {
1059 free(res->res_client.tls_keyfile);
1061 if (res->res_client.tls_allowed_cns) {
1062 delete res->res_client.tls_allowed_cns;
1066 if (res->res_store.address) {
1067 free(res->res_store.address);
1069 if (res->res_store.password) {
1070 free(res->res_store.password);
1072 if (res->res_store.media_type) {
1073 free(res->res_store.media_type);
1075 if (res->res_store.device) {
1076 delete res->res_store.device;
1078 if (res->res_store.tls_ctx) {
1079 free_tls_context(res->res_store.tls_ctx);
1081 if (res->res_store.tls_ca_certfile) {
1082 free(res->res_store.tls_ca_certfile);
1084 if (res->res_store.tls_ca_certdir) {
1085 free(res->res_store.tls_ca_certdir);
1087 if (res->res_store.tls_certfile) {
1088 free(res->res_store.tls_certfile);
1090 if (res->res_store.tls_keyfile) {
1091 free(res->res_store.tls_keyfile);
1095 if (res->res_cat.db_address) {
1096 free(res->res_cat.db_address);
1098 if (res->res_cat.db_socket) {
1099 free(res->res_cat.db_socket);
1101 if (res->res_cat.db_user) {
1102 free(res->res_cat.db_user);
1104 if (res->res_cat.db_name) {
1105 free(res->res_cat.db_name);
1107 if (res->res_cat.db_password) {
1108 free(res->res_cat.db_password);
1112 if ((num=res->res_fs.num_includes)) {
1113 while (--num >= 0) {
1114 free_incexe(res->res_fs.include_items[num]);
1116 free(res->res_fs.include_items);
1118 res->res_fs.num_includes = 0;
1119 if ((num=res->res_fs.num_excludes)) {
1120 while (--num >= 0) {
1121 free_incexe(res->res_fs.exclude_items[num]);
1123 free(res->res_fs.exclude_items);
1125 res->res_fs.num_excludes = 0;
1128 if (res->res_pool.pool_type) {
1129 free(res->res_pool.pool_type);
1131 if (res->res_pool.label_format) {
1132 free(res->res_pool.label_format);
1134 if (res->res_pool.cleaning_prefix) {
1135 free(res->res_pool.cleaning_prefix);
1137 if (res->res_pool.storage) {
1138 delete res->res_pool.storage;
1142 if (res->res_sch.run) {
1144 nrun = res->res_sch.run;
1154 if (res->res_job.RestoreWhere) {
1155 free(res->res_job.RestoreWhere);
1157 if (res->res_job.strip_prefix) {
1158 free(res->res_job.strip_prefix);
1160 if (res->res_job.add_prefix) {
1161 free(res->res_job.add_prefix);
1163 if (res->res_job.add_suffix) {
1164 free(res->res_job.add_suffix);
1166 if (res->res_job.RestoreBootstrap) {
1167 free(res->res_job.RestoreBootstrap);
1169 if (res->res_job.WriteBootstrap) {
1170 free(res->res_job.WriteBootstrap);
1172 if (res->res_job.selection_pattern) {
1173 free(res->res_job.selection_pattern);
1175 if (res->res_job.run_cmds) {
1176 delete res->res_job.run_cmds;
1178 if (res->res_job.storage) {
1179 delete res->res_job.storage;
1181 if (res->res_job.RunScripts) {
1182 free_runscripts(res->res_job.RunScripts);
1183 delete res->res_job.RunScripts;
1187 if (res->res_msgs.mail_cmd) {
1188 free(res->res_msgs.mail_cmd);
1190 if (res->res_msgs.operator_cmd) {
1191 free(res->res_msgs.operator_cmd);
1193 free_msgs_res((MSGS *)res); /* free message resource */
1197 printf(_("Unknown resource type %d in free_resource.\n"), type);
1199 /* Common stuff again -- free the resource, recurse to next one */
1204 free_resource(nres, type);
1209 * Save the new resource by chaining it into the head list for
1210 * the resource. If this is pass 2, we update any resource
1211 * pointers because they may not have been defined until
1214 void save_resource(int type, RES_ITEM *items, int pass)
1217 int rindex = type - r_first;
1221 /* Check Job requirements after applying JobDefs */
1222 if (type != R_JOB && type != R_JOBDEFS) {
1224 * Ensure that all required items are present
1226 for (i=0; items[i].name; i++) {
1227 if (items[i].flags & ITEM_REQUIRED) {
1228 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1229 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1230 items[i].name, resources[rindex]);
1233 /* If this triggers, take a look at lib/parse_conf.h */
1234 if (i >= MAX_RES_ITEMS) {
1235 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1238 } else if (type == R_JOB) {
1240 * Ensure that the name item is present
1242 if (items[0].flags & ITEM_REQUIRED) {
1243 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1244 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1245 items[0].name, resources[rindex]);
1251 * During pass 2 in each "store" routine, we looked up pointers
1252 * to all the resources referrenced in the current resource, now we
1253 * must copy their addresses from the static record to the allocated
1258 /* Resources not containing a resource */
1266 * Resources containing another resource or alist. First
1267 * look up the resource which contains another resource. It
1268 * was written during pass 1. Then stuff in the pointers to
1269 * the resources it contains, which were inserted this pass.
1270 * Finally, it will all be stored back.
1273 /* Find resource saved in pass 1 */
1274 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1275 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1277 /* Explicitly copy resource pointers from this pass (res_all) */
1278 res->res_pool.NextPool = res_all.res_pool.NextPool;
1279 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1280 res->res_pool.storage = res_all.res_pool.storage;
1283 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1284 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1286 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1289 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1290 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1292 res->res_dir.messages = res_all.res_dir.messages;
1293 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1296 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1297 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1298 res_all.res_dir.hdr.name);
1300 /* we must explicitly copy the device alist pointer */
1301 res->res_store.device = res_all.res_store.device;
1305 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1306 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1307 res_all.res_dir.hdr.name);
1309 res->res_job.messages = res_all.res_job.messages;
1310 res->res_job.schedule = res_all.res_job.schedule;
1311 res->res_job.client = res_all.res_job.client;
1312 res->res_job.fileset = res_all.res_job.fileset;
1313 res->res_job.storage = res_all.res_job.storage;
1314 res->res_job.pool = res_all.res_job.pool;
1315 res->res_job.full_pool = res_all.res_job.full_pool;
1316 res->res_job.inc_pool = res_all.res_job.inc_pool;
1317 res->res_job.diff_pool = res_all.res_job.diff_pool;
1318 res->res_job.verify_job = res_all.res_job.verify_job;
1319 res->res_job.jobdefs = res_all.res_job.jobdefs;
1320 res->res_job.run_cmds = res_all.res_job.run_cmds;
1321 res->res_job.RunScripts = res_all.res_job.RunScripts;
1322 if (res->res_job.strip_prefix ||
1323 res->res_job.add_suffix ||
1324 res->res_job.add_prefix)
1326 if (res->res_job.RestoreWhere) {
1327 free(res->res_job.RestoreWhere);
1329 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1330 res->res_job.add_prefix,
1331 res->res_job.add_suffix);
1332 res->res_job.RestoreWhere = (char *) bmalloc (len * sizeof(char));
1333 bregexp_build_where(res->res_job.RestoreWhere, len,
1334 res->res_job.strip_prefix,
1335 res->res_job.add_prefix,
1336 res->res_job.add_suffix);
1337 res->res_job.where_use_regexp = true;
1339 /* TODO: test bregexp */
1343 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1344 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1346 res->res_counter.Catalog = res_all.res_counter.Catalog;
1347 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1351 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1352 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1354 res->res_client.catalog = res_all.res_client.catalog;
1355 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1359 * Schedule is a bit different in that it contains a RUN record
1360 * chain which isn't a "named" resource. This chain was linked
1361 * in by run_conf.c during pass 2, so here we jam the pointer
1362 * into the Schedule resource.
1364 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1365 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1367 res->res_sch.run = res_all.res_sch.run;
1370 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1374 /* Note, the resource name was already saved during pass 1,
1375 * so here, we can just release it.
1377 if (res_all.res_dir.hdr.name) {
1378 free(res_all.res_dir.hdr.name);
1379 res_all.res_dir.hdr.name = NULL;
1381 if (res_all.res_dir.hdr.desc) {
1382 free(res_all.res_dir.hdr.desc);
1383 res_all.res_dir.hdr.desc = NULL;
1389 * The following code is only executed during pass 1
1393 size = sizeof(DIRRES);
1396 size = sizeof(CONRES);
1399 size =sizeof(CLIENT);
1402 size = sizeof(STORE);
1412 size = sizeof(FILESET);
1415 size = sizeof(SCHED);
1418 size = sizeof(POOL);
1421 size = sizeof(MSGS);
1424 size = sizeof(COUNTER);
1430 printf(_("Unknown resource type %d in save_resource.\n"), type);
1436 res = (URES *)malloc(size);
1437 memcpy(res, &res_all, size);
1438 if (!res_head[rindex]) {
1439 res_head[rindex] = (RES *)res; /* store first entry */
1440 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1441 res->res_dir.hdr.name, rindex);
1444 if (res->res_dir.hdr.name == NULL) {
1445 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1448 /* Add new res to end of chain */
1449 for (last=next=res_head[rindex]; next; next=next->next) {
1451 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1452 Emsg2(M_ERROR_TERM, 0,
1453 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1454 resources[rindex].name, res->res_dir.hdr.name);
1457 last->next = (RES *)res;
1458 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1459 res->res_dir.hdr.name, rindex, pass);
1465 * Store Device. Note, the resource is created upon the
1466 * first reference. The details of the resource are obtained
1467 * later from the SD.
1469 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1473 int rindex = R_DEVICE - r_first;
1474 int size = sizeof(DEVICE);
1478 token = lex_get_token(lc, T_NAME);
1479 if (!res_head[rindex]) {
1480 res = (URES *)malloc(size);
1481 memset(res, 0, size);
1482 res->res_dev.hdr.name = bstrdup(lc->str);
1483 res_head[rindex] = (RES *)res; /* store first entry */
1484 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1485 res->res_dir.hdr.name, rindex);
1488 /* See if it is already defined */
1489 for (next=res_head[rindex]; next->next; next=next->next) {
1490 if (strcmp(next->name, lc->str) == 0) {
1496 res = (URES *)malloc(size);
1497 memset(res, 0, size);
1498 res->res_dev.hdr.name = bstrdup(lc->str);
1499 next->next = (RES *)res;
1500 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1501 res->res_dir.hdr.name, rindex, pass);
1506 set_bit(index, res_all.hdr.item_present);
1508 store_alist_res(lc, item, index, pass);
1513 * Store JobType (backup, verify, restore)
1516 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1520 token = lex_get_token(lc, T_NAME);
1521 /* Store the type both pass 1 and pass 2 */
1522 for (i=0; migtypes[i].type_name; i++) {
1523 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1524 *(int *)(item->value) = migtypes[i].job_type;
1530 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1533 set_bit(index, res_all.hdr.item_present);
1539 * Store JobType (backup, verify, restore)
1542 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1546 token = lex_get_token(lc, T_NAME);
1547 /* Store the type both pass 1 and pass 2 */
1548 for (i=0; jobtypes[i].type_name; i++) {
1549 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1550 *(int *)(item->value) = jobtypes[i].job_type;
1556 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1559 set_bit(index, res_all.hdr.item_present);
1563 * Store Job Level (Full, Incremental, ...)
1566 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1570 token = lex_get_token(lc, T_NAME);
1571 /* Store the level pass 2 so that type is defined */
1572 for (i=0; joblevels[i].level_name; i++) {
1573 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1574 *(int *)(item->value) = joblevels[i].level;
1580 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1583 set_bit(index, res_all.hdr.item_present);
1587 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1590 token = lex_get_token(lc, T_NAME);
1591 /* Scan Replacement options */
1592 for (i=0; ReplaceOptions[i].name; i++) {
1593 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1594 *(int *)(item->value) = ReplaceOptions[i].token;
1600 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1603 set_bit(index, res_all.hdr.item_present);
1607 * Store ACL (access control list)
1610 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1615 token = lex_get_token(lc, T_STRING);
1617 if (((alist **)item->value)[item->code] == NULL) {
1618 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1619 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1621 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1622 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1624 token = lex_get_token(lc, T_ALL);
1625 if (token == T_COMMA) {
1626 continue; /* get another ACL */
1630 set_bit(index, res_all.hdr.item_present);
1633 /* We build RunScripts items here */
1634 static RUNSCRIPT res_runscript;
1636 /* Store a runscript->when in a bit field */
1637 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1639 lex_get_token(lc, T_NAME);
1641 if (strcasecmp(lc->str, "before") == 0) {
1642 *(int *)(item->value) = SCRIPT_Before ;
1643 } else if (strcasecmp(lc->str, "after") == 0) {
1644 *(int *)(item->value) = SCRIPT_After;
1645 } else if (strcasecmp(lc->str, "always") == 0) {
1646 *(int *)(item->value) = SCRIPT_Any;
1648 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1653 /* Store a runscript->target
1656 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1658 lex_get_token(lc, T_STRING);
1661 if (strcmp(lc->str, "%c") == 0) {
1662 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1663 } else if (strcasecmp(lc->str, "yes") == 0) {
1664 ((RUNSCRIPT*) item->value)->set_target("%c");
1665 } else if (strcasecmp(lc->str, "no") == 0) {
1666 ((RUNSCRIPT*) item->value)->set_target("");
1668 RES *res = GetResWithName(R_CLIENT, lc->str);
1670 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1671 lc->str, lc->line_no, lc->line);
1674 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1680 /* Store a runscript->command in a bit field
1683 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1685 lex_get_token(lc, T_STRING);
1688 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1693 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1695 lex_get_token(lc, T_STRING);
1696 alist **runscripts = (alist **)(item->value) ;
1699 RUNSCRIPT *script = new_runscript();
1701 script->set_command(lc->str);
1703 /* TODO: remove all script->old_proto with bacula 1.42 */
1705 if (strcmp(item->name, "runbeforejob") == 0) {
1706 script->when = SCRIPT_Before;
1707 script->abort_on_error = true;
1708 script->set_target("");
1710 } else if (strcmp(item->name, "runafterjob") == 0) {
1711 script->when = SCRIPT_After;
1712 script->on_success = true;
1713 script->on_failure = false;
1714 script->set_target("");
1716 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1717 script->old_proto = true;
1718 script->when = SCRIPT_After;
1719 script->set_target("%c");
1720 script->on_success = true;
1721 script->on_failure = false;
1723 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1724 script->old_proto = true;
1725 script->when = SCRIPT_Before;
1726 script->set_target("%c");
1727 script->abort_on_error = true;
1729 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1730 script->when = SCRIPT_After;
1731 script->on_failure = true;
1732 script->on_success = false;
1733 script->set_target("");
1736 if (*runscripts == NULL) {
1737 *runscripts = New(alist(10, not_owned_by_alist));
1740 (*runscripts)->append(script);
1747 /* Store a bool in a bit field without modifing res_all.hdr
1748 * We can also add an option to store_bool to skip res_all.hdr
1750 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1752 lex_get_token(lc, T_NAME);
1753 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1754 *(bool *)(item->value) = true;
1755 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1756 *(bool *)(item->value) = false;
1758 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1764 * new RunScript items
1765 * name handler value code flags default_value
1767 static RES_ITEM runscript_items[] = {
1768 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1769 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1770 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1771 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1772 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.abort_on_error},0, 0, 0},
1773 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1774 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1775 {NULL, NULL, {0}, 0, 0, 0}
1779 * Store RunScript info
1781 * Note, when this routine is called, we are inside a Job
1782 * resource. We treat the RunScript like a sort of
1783 * mini-resource within the Job resource.
1785 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1788 alist **runscripts = (alist **)(item->value) ;
1790 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1792 res_runscript.reset_default(); /* setting on_success, on_failure, abort_on_error */
1794 token = lex_get_token(lc, T_SKIP_EOL);
1796 if (token != T_BOB) {
1797 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1800 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1801 if (token == T_EOB) {
1804 if (token != T_IDENTIFIER) {
1805 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1807 for (i=0; runscript_items[i].name; i++) {
1808 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1809 token = lex_get_token(lc, T_SKIP_EOL);
1810 if (token != T_EQUALS) {
1811 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1814 /* Call item handler */
1815 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1822 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1827 if (res_runscript.command == NULL) {
1828 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1829 "command", "runscript");
1832 /* run on client by default */
1833 if (res_runscript.target == NULL) {
1834 res_runscript.set_target("%c");
1837 RUNSCRIPT *script = new_runscript();
1838 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1840 if (*runscripts == NULL) {
1841 *runscripts = New(alist(10, not_owned_by_alist));
1844 (*runscripts)->append(script);
1849 set_bit(index, res_all.hdr.item_present);