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 {"regexwhere", store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
275 {"stripprefix", store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
276 {"addprefix", store_str, ITEM(res_job.add_prefix), 0, 0, 0},
277 {"addsuffix", store_str, ITEM(res_job.add_suffix), 0, 0, 0},
278 /* Where to find bootstrap during restore */
279 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
280 /* Where to write bootstrap file during backup */
281 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
282 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
283 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
284 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
285 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
286 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
287 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
288 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
289 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
290 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
291 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
292 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
293 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
294 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
295 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
296 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
297 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
298 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
299 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
300 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
301 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
302 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
303 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
304 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
305 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
306 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
307 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
308 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
309 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
310 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
311 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
312 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
313 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
314 {NULL, NULL, {0}, 0, 0, 0}
319 * name handler value code flags default_value
321 static RES_ITEM fs_items[] = {
322 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
323 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
324 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
325 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
326 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
327 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
328 {NULL, NULL, {0}, 0, 0, 0}
331 /* Schedule -- see run_conf.c */
334 * name handler value code flags default_value
336 static RES_ITEM sch_items[] = {
337 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
338 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
339 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
340 {NULL, NULL, {0}, 0, 0, 0}
345 * name handler value code flags default_value
347 static RES_ITEM pool_items[] = {
348 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
349 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
350 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
351 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
352 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
353 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
354 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
355 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
356 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
357 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
358 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
359 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
360 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
361 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
362 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
363 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
364 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
365 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
366 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
367 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
368 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
369 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
370 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
371 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
372 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
373 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
374 {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.RegexWhere) {
1158 free(res->res_job.RegexWhere);
1160 if (res->res_job.strip_prefix) {
1161 free(res->res_job.strip_prefix);
1163 if (res->res_job.add_prefix) {
1164 free(res->res_job.add_prefix);
1166 if (res->res_job.add_suffix) {
1167 free(res->res_job.add_suffix);
1169 if (res->res_job.RestoreBootstrap) {
1170 free(res->res_job.RestoreBootstrap);
1172 if (res->res_job.WriteBootstrap) {
1173 free(res->res_job.WriteBootstrap);
1175 if (res->res_job.selection_pattern) {
1176 free(res->res_job.selection_pattern);
1178 if (res->res_job.run_cmds) {
1179 delete res->res_job.run_cmds;
1181 if (res->res_job.storage) {
1182 delete res->res_job.storage;
1184 if (res->res_job.RunScripts) {
1185 free_runscripts(res->res_job.RunScripts);
1186 delete res->res_job.RunScripts;
1190 if (res->res_msgs.mail_cmd) {
1191 free(res->res_msgs.mail_cmd);
1193 if (res->res_msgs.operator_cmd) {
1194 free(res->res_msgs.operator_cmd);
1196 free_msgs_res((MSGS *)res); /* free message resource */
1200 printf(_("Unknown resource type %d in free_resource.\n"), type);
1202 /* Common stuff again -- free the resource, recurse to next one */
1207 free_resource(nres, type);
1212 * Save the new resource by chaining it into the head list for
1213 * the resource. If this is pass 2, we update any resource
1214 * pointers because they may not have been defined until
1217 void save_resource(int type, RES_ITEM *items, int pass)
1220 int rindex = type - r_first;
1224 /* Check Job requirements after applying JobDefs */
1225 if (type != R_JOB && type != R_JOBDEFS) {
1227 * Ensure that all required items are present
1229 for (i=0; items[i].name; i++) {
1230 if (items[i].flags & ITEM_REQUIRED) {
1231 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1232 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1233 items[i].name, resources[rindex]);
1236 /* If this triggers, take a look at lib/parse_conf.h */
1237 if (i >= MAX_RES_ITEMS) {
1238 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1241 } else if (type == R_JOB) {
1243 * Ensure that the name item is present
1245 if (items[0].flags & ITEM_REQUIRED) {
1246 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1247 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1248 items[0].name, resources[rindex]);
1254 * During pass 2 in each "store" routine, we looked up pointers
1255 * to all the resources referrenced in the current resource, now we
1256 * must copy their addresses from the static record to the allocated
1261 /* Resources not containing a resource */
1269 * Resources containing another resource or alist. First
1270 * look up the resource which contains another resource. It
1271 * was written during pass 1. Then stuff in the pointers to
1272 * the resources it contains, which were inserted this pass.
1273 * Finally, it will all be stored back.
1276 /* Find resource saved in pass 1 */
1277 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1278 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1280 /* Explicitly copy resource pointers from this pass (res_all) */
1281 res->res_pool.NextPool = res_all.res_pool.NextPool;
1282 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1283 res->res_pool.storage = res_all.res_pool.storage;
1286 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1287 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1289 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1292 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1293 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1295 res->res_dir.messages = res_all.res_dir.messages;
1296 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1299 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1300 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1301 res_all.res_dir.hdr.name);
1303 /* we must explicitly copy the device alist pointer */
1304 res->res_store.device = res_all.res_store.device;
1308 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1309 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1310 res_all.res_dir.hdr.name);
1312 res->res_job.messages = res_all.res_job.messages;
1313 res->res_job.schedule = res_all.res_job.schedule;
1314 res->res_job.client = res_all.res_job.client;
1315 res->res_job.fileset = res_all.res_job.fileset;
1316 res->res_job.storage = res_all.res_job.storage;
1317 res->res_job.pool = res_all.res_job.pool;
1318 res->res_job.full_pool = res_all.res_job.full_pool;
1319 res->res_job.inc_pool = res_all.res_job.inc_pool;
1320 res->res_job.diff_pool = res_all.res_job.diff_pool;
1321 res->res_job.verify_job = res_all.res_job.verify_job;
1322 res->res_job.jobdefs = res_all.res_job.jobdefs;
1323 res->res_job.run_cmds = res_all.res_job.run_cmds;
1324 res->res_job.RunScripts = res_all.res_job.RunScripts;
1326 if (res->res_job.strip_prefix ||
1327 res->res_job.add_suffix ||
1328 res->res_job.add_prefix)
1330 if (res->res_job.RestoreWhere) {
1331 free(res->res_job.RestoreWhere);
1333 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1334 res->res_job.add_prefix,
1335 res->res_job.add_suffix);
1336 res->res_job.RestoreWhere = (char *) bmalloc (len * sizeof(char));
1337 bregexp_build_where(res->res_job.RestoreWhere, len,
1338 res->res_job.strip_prefix,
1339 res->res_job.add_prefix,
1340 res->res_job.add_suffix);
1341 res->res_job.where_use_regexp = true;
1343 /* TODO: test bregexp */
1346 if (res->res_job.RegexWhere) {
1347 if (res->res_job.RestoreWhere) {
1348 free(res->res_job.RestoreWhere);
1350 res->res_job.RestoreWhere = bstrdup(res->res_job.RegexWhere);
1351 res->res_job.where_use_regexp = true;
1356 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1357 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1359 res->res_counter.Catalog = res_all.res_counter.Catalog;
1360 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1364 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1365 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1367 res->res_client.catalog = res_all.res_client.catalog;
1368 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1372 * Schedule is a bit different in that it contains a RUN record
1373 * chain which isn't a "named" resource. This chain was linked
1374 * in by run_conf.c during pass 2, so here we jam the pointer
1375 * into the Schedule resource.
1377 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1378 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1380 res->res_sch.run = res_all.res_sch.run;
1383 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1387 /* Note, the resource name was already saved during pass 1,
1388 * so here, we can just release it.
1390 if (res_all.res_dir.hdr.name) {
1391 free(res_all.res_dir.hdr.name);
1392 res_all.res_dir.hdr.name = NULL;
1394 if (res_all.res_dir.hdr.desc) {
1395 free(res_all.res_dir.hdr.desc);
1396 res_all.res_dir.hdr.desc = NULL;
1402 * The following code is only executed during pass 1
1406 size = sizeof(DIRRES);
1409 size = sizeof(CONRES);
1412 size =sizeof(CLIENT);
1415 size = sizeof(STORE);
1425 size = sizeof(FILESET);
1428 size = sizeof(SCHED);
1431 size = sizeof(POOL);
1434 size = sizeof(MSGS);
1437 size = sizeof(COUNTER);
1443 printf(_("Unknown resource type %d in save_resource.\n"), type);
1449 res = (URES *)malloc(size);
1450 memcpy(res, &res_all, size);
1451 if (!res_head[rindex]) {
1452 res_head[rindex] = (RES *)res; /* store first entry */
1453 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1454 res->res_dir.hdr.name, rindex);
1457 if (res->res_dir.hdr.name == NULL) {
1458 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1461 /* Add new res to end of chain */
1462 for (last=next=res_head[rindex]; next; next=next->next) {
1464 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1465 Emsg2(M_ERROR_TERM, 0,
1466 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1467 resources[rindex].name, res->res_dir.hdr.name);
1470 last->next = (RES *)res;
1471 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1472 res->res_dir.hdr.name, rindex, pass);
1478 * Store Device. Note, the resource is created upon the
1479 * first reference. The details of the resource are obtained
1480 * later from the SD.
1482 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1486 int rindex = R_DEVICE - r_first;
1487 int size = sizeof(DEVICE);
1491 token = lex_get_token(lc, T_NAME);
1492 if (!res_head[rindex]) {
1493 res = (URES *)malloc(size);
1494 memset(res, 0, size);
1495 res->res_dev.hdr.name = bstrdup(lc->str);
1496 res_head[rindex] = (RES *)res; /* store first entry */
1497 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1498 res->res_dir.hdr.name, rindex);
1501 /* See if it is already defined */
1502 for (next=res_head[rindex]; next->next; next=next->next) {
1503 if (strcmp(next->name, lc->str) == 0) {
1509 res = (URES *)malloc(size);
1510 memset(res, 0, size);
1511 res->res_dev.hdr.name = bstrdup(lc->str);
1512 next->next = (RES *)res;
1513 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1514 res->res_dir.hdr.name, rindex, pass);
1519 set_bit(index, res_all.hdr.item_present);
1521 store_alist_res(lc, item, index, pass);
1526 * Store JobType (backup, verify, restore)
1529 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1533 token = lex_get_token(lc, T_NAME);
1534 /* Store the type both pass 1 and pass 2 */
1535 for (i=0; migtypes[i].type_name; i++) {
1536 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1537 *(int *)(item->value) = migtypes[i].job_type;
1543 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1546 set_bit(index, res_all.hdr.item_present);
1552 * Store JobType (backup, verify, restore)
1555 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1559 token = lex_get_token(lc, T_NAME);
1560 /* Store the type both pass 1 and pass 2 */
1561 for (i=0; jobtypes[i].type_name; i++) {
1562 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1563 *(int *)(item->value) = jobtypes[i].job_type;
1569 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1572 set_bit(index, res_all.hdr.item_present);
1576 * Store Job Level (Full, Incremental, ...)
1579 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1583 token = lex_get_token(lc, T_NAME);
1584 /* Store the level pass 2 so that type is defined */
1585 for (i=0; joblevels[i].level_name; i++) {
1586 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1587 *(int *)(item->value) = joblevels[i].level;
1593 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1596 set_bit(index, res_all.hdr.item_present);
1600 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1603 token = lex_get_token(lc, T_NAME);
1604 /* Scan Replacement options */
1605 for (i=0; ReplaceOptions[i].name; i++) {
1606 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1607 *(int *)(item->value) = ReplaceOptions[i].token;
1613 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1616 set_bit(index, res_all.hdr.item_present);
1620 * Store ACL (access control list)
1623 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1628 token = lex_get_token(lc, T_STRING);
1630 if (((alist **)item->value)[item->code] == NULL) {
1631 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1632 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1634 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1635 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1637 token = lex_get_token(lc, T_ALL);
1638 if (token == T_COMMA) {
1639 continue; /* get another ACL */
1643 set_bit(index, res_all.hdr.item_present);
1646 /* We build RunScripts items here */
1647 static RUNSCRIPT res_runscript;
1649 /* Store a runscript->when in a bit field */
1650 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1652 lex_get_token(lc, T_NAME);
1654 if (strcasecmp(lc->str, "before") == 0) {
1655 *(int *)(item->value) = SCRIPT_Before ;
1656 } else if (strcasecmp(lc->str, "after") == 0) {
1657 *(int *)(item->value) = SCRIPT_After;
1658 } else if (strcasecmp(lc->str, "always") == 0) {
1659 *(int *)(item->value) = SCRIPT_Any;
1661 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1666 /* Store a runscript->target
1669 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1671 lex_get_token(lc, T_STRING);
1674 if (strcmp(lc->str, "%c") == 0) {
1675 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1676 } else if (strcasecmp(lc->str, "yes") == 0) {
1677 ((RUNSCRIPT*) item->value)->set_target("%c");
1678 } else if (strcasecmp(lc->str, "no") == 0) {
1679 ((RUNSCRIPT*) item->value)->set_target("");
1681 RES *res = GetResWithName(R_CLIENT, lc->str);
1683 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1684 lc->str, lc->line_no, lc->line);
1687 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1693 /* Store a runscript->command in a bit field
1696 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1698 lex_get_token(lc, T_STRING);
1701 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1706 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1708 lex_get_token(lc, T_STRING);
1709 alist **runscripts = (alist **)(item->value) ;
1712 RUNSCRIPT *script = new_runscript();
1714 script->set_command(lc->str);
1716 /* TODO: remove all script->old_proto with bacula 1.42 */
1718 if (strcmp(item->name, "runbeforejob") == 0) {
1719 script->when = SCRIPT_Before;
1720 script->abort_on_error = true;
1721 script->set_target("");
1723 } else if (strcmp(item->name, "runafterjob") == 0) {
1724 script->when = SCRIPT_After;
1725 script->on_success = true;
1726 script->on_failure = false;
1727 script->set_target("");
1729 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1730 script->old_proto = true;
1731 script->when = SCRIPT_After;
1732 script->set_target("%c");
1733 script->on_success = true;
1734 script->on_failure = false;
1736 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1737 script->old_proto = true;
1738 script->when = SCRIPT_Before;
1739 script->set_target("%c");
1740 script->abort_on_error = true;
1742 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1743 script->when = SCRIPT_After;
1744 script->on_failure = true;
1745 script->on_success = false;
1746 script->set_target("");
1749 if (*runscripts == NULL) {
1750 *runscripts = New(alist(10, not_owned_by_alist));
1753 (*runscripts)->append(script);
1760 /* Store a bool in a bit field without modifing res_all.hdr
1761 * We can also add an option to store_bool to skip res_all.hdr
1763 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1765 lex_get_token(lc, T_NAME);
1766 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1767 *(bool *)(item->value) = true;
1768 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1769 *(bool *)(item->value) = false;
1771 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1777 * new RunScript items
1778 * name handler value code flags default_value
1780 static RES_ITEM runscript_items[] = {
1781 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1782 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1783 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1784 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1785 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.abort_on_error},0, 0, 0},
1786 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1787 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1788 {NULL, NULL, {0}, 0, 0, 0}
1792 * Store RunScript info
1794 * Note, when this routine is called, we are inside a Job
1795 * resource. We treat the RunScript like a sort of
1796 * mini-resource within the Job resource.
1798 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1801 alist **runscripts = (alist **)(item->value) ;
1803 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1805 res_runscript.reset_default(); /* setting on_success, on_failure, abort_on_error */
1807 token = lex_get_token(lc, T_SKIP_EOL);
1809 if (token != T_BOB) {
1810 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1813 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1814 if (token == T_EOB) {
1817 if (token != T_IDENTIFIER) {
1818 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1820 for (i=0; runscript_items[i].name; i++) {
1821 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1822 token = lex_get_token(lc, T_SKIP_EOL);
1823 if (token != T_EQUALS) {
1824 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1827 /* Call item handler */
1828 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1835 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1840 if (res_runscript.command == NULL) {
1841 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1842 "command", "runscript");
1845 /* run on client by default */
1846 if (res_runscript.target == NULL) {
1847 res_runscript.set_target("%c");
1850 RUNSCRIPT *script = new_runscript();
1851 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1853 if (*runscripts == NULL) {
1854 *runscripts = New(alist(10, not_owned_by_alist));
1857 (*runscripts)->append(script);
1862 set_bit(index, res_all.hdr.item_present);