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 && !res->res_job.RegexWhere) {
619 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
621 if (res->res_job.RegexWhere) {
622 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
624 if (res->res_job.RestoreBootstrap) {
625 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
627 if (res->res_job.WriteBootstrap) {
628 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
630 if (res->res_job.storage) {
632 foreach_alist(store, res->res_job.storage) {
633 sendit(sock, _(" --> "));
634 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
637 if (res->res_job.RunScripts) {
639 foreach_alist(script, res->res_job.RunScripts) {
640 sendit(sock, _(" --> RunScript\n"));
641 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
642 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
643 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
644 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
645 sendit(sock, _(" --> AbortJobOnError=%u\n"), script->abort_on_error);
646 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
649 if (res->res_job.pool) {
650 sendit(sock, _(" --> "));
651 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
653 if (res->res_job.full_pool) {
654 sendit(sock, _(" --> "));
655 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
657 if (res->res_job.inc_pool) {
658 sendit(sock, _(" --> "));
659 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
661 if (res->res_job.diff_pool) {
662 sendit(sock, _(" --> "));
663 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
665 if (res->res_job.verify_job) {
666 sendit(sock, _(" --> "));
667 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
669 if (res->res_job.run_cmds) {
671 foreach_alist(runcmd, res->res_job.run_cmds) {
672 sendit(sock, _(" --> Run=%s\n"), runcmd);
675 if (res->res_job.selection_pattern) {
676 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
678 if (res->res_job.messages) {
679 sendit(sock, _(" --> "));
680 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
686 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
687 for (i=0; i<res->res_fs.num_includes; i++) {
688 INCEXE *incexe = res->res_fs.include_items[i];
689 for (j=0; j<incexe->num_opts; j++) {
690 FOPTS *fo = incexe->opts_list[j];
691 sendit(sock, " O %s\n", fo->opts);
693 bool enhanced_wild = false;
694 for (k=0; fo->opts[k]!='\0'; k++) {
695 if (fo->opts[k]=='W') {
696 enhanced_wild = true;
701 for (k=0; k<fo->regex.size(); k++) {
702 sendit(sock, " R %s\n", fo->regex.get(k));
704 for (k=0; k<fo->regexdir.size(); k++) {
705 sendit(sock, " RD %s\n", fo->regexdir.get(k));
707 for (k=0; k<fo->regexfile.size(); k++) {
708 sendit(sock, " RF %s\n", fo->regexfile.get(k));
710 for (k=0; k<fo->wild.size(); k++) {
711 sendit(sock, " W %s\n", fo->wild.get(k));
713 for (k=0; k<fo->wilddir.size(); k++) {
714 sendit(sock, " WD %s\n", fo->wilddir.get(k));
716 for (k=0; k<fo->wildfile.size(); k++) {
717 sendit(sock, " WF %s\n", fo->wildfile.get(k));
719 for (k=0; k<fo->wildbase.size(); k++) {
720 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
722 for (k=0; k<fo->base.size(); k++) {
723 sendit(sock, " B %s\n", fo->base.get(k));
725 for (k=0; k<fo->fstype.size(); k++) {
726 sendit(sock, " X %s\n", fo->fstype.get(k));
728 for (k=0; k<fo->drivetype.size(); k++) {
729 sendit(sock, " XD %s\n", fo->drivetype.get(k));
732 sendit(sock, " D %s\n", fo->reader);
735 sendit(sock, " T %s\n", fo->writer);
737 sendit(sock, " N\n");
739 for (j=0; j<incexe->name_list.size(); j++) {
740 sendit(sock, " I %s\n", incexe->name_list.get(j));
742 if (incexe->name_list.size()) {
743 sendit(sock, " N\n");
747 for (i=0; i<res->res_fs.num_excludes; i++) {
748 INCEXE *incexe = res->res_fs.exclude_items[i];
749 for (j=0; j<incexe->name_list.size(); j++) {
750 sendit(sock, " E %s\n", incexe->name_list.get(j));
752 if (incexe->name_list.size()) {
753 sendit(sock, " N\n");
759 if (res->res_sch.run) {
761 RUN *run = res->res_sch.run;
762 char buf[1000], num[30];
763 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
768 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
769 bstrncpy(buf, _(" hour="), sizeof(buf));
770 for (i=0; i<24; i++) {
771 if (bit_is_set(i, run->hour)) {
772 bsnprintf(num, sizeof(num), "%d ", i);
773 bstrncat(buf, num, sizeof(buf));
776 bstrncat(buf, "\n", sizeof(buf));
778 bstrncpy(buf, _(" mday="), sizeof(buf));
779 for (i=0; i<31; i++) {
780 if (bit_is_set(i, run->mday)) {
781 bsnprintf(num, sizeof(num), "%d ", i);
782 bstrncat(buf, num, sizeof(buf));
785 bstrncat(buf, "\n", sizeof(buf));
787 bstrncpy(buf, _(" month="), sizeof(buf));
788 for (i=0; i<12; i++) {
789 if (bit_is_set(i, run->month)) {
790 bsnprintf(num, sizeof(num), "%d ", i);
791 bstrncat(buf, num, sizeof(buf));
794 bstrncat(buf, "\n", sizeof(buf));
796 bstrncpy(buf, _(" wday="), sizeof(buf));
797 for (i=0; i<7; i++) {
798 if (bit_is_set(i, run->wday)) {
799 bsnprintf(num, sizeof(num), "%d ", i);
800 bstrncat(buf, num, sizeof(buf));
803 bstrncat(buf, "\n", sizeof(buf));
805 bstrncpy(buf, _(" wom="), sizeof(buf));
806 for (i=0; i<5; i++) {
807 if (bit_is_set(i, run->wom)) {
808 bsnprintf(num, sizeof(num), "%d ", i);
809 bstrncat(buf, num, sizeof(buf));
812 bstrncat(buf, "\n", sizeof(buf));
814 bstrncpy(buf, _(" woy="), sizeof(buf));
815 for (i=0; i<54; i++) {
816 if (bit_is_set(i, run->woy)) {
817 bsnprintf(num, sizeof(num), "%d ", i);
818 bstrncat(buf, num, sizeof(buf));
821 bstrncat(buf, "\n", sizeof(buf));
823 sendit(sock, _(" mins=%d\n"), run->minute);
825 sendit(sock, _(" --> "));
826 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
829 sendit(sock, _(" --> "));
830 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
833 sendit(sock, _(" --> "));
834 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
836 /* If another Run record is chained in, go print it */
842 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
846 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
847 res->res_pool.pool_type);
848 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
849 res->res_pool.use_catalog, res->res_pool.use_volume_once,
850 res->res_pool.catalog_files);
851 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
852 res->res_pool.max_volumes, res->res_pool.AutoPrune,
853 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
854 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
855 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
856 res->res_pool.Recycle,
857 NPRT(res->res_pool.label_format));
858 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
859 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
860 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
861 res->res_pool.recycle_oldest_volume,
862 res->res_pool.purge_oldest_volume);
863 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
864 res->res_pool.MaxVolJobs,
865 res->res_pool.MaxVolFiles,
866 edit_uint64(res->res_pool.MaxVolFiles, ed1));
867 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
868 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
869 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
870 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
871 if (res->res_pool.NextPool) {
872 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
874 if (res->res_pool.RecyclePool) {
875 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
877 if (res->res_pool.storage) {
879 foreach_alist(store, res->res_pool.storage) {
880 sendit(sock, _(" --> "));
881 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
886 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
887 if (res->res_msgs.mail_cmd)
888 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
889 if (res->res_msgs.operator_cmd)
890 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
893 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
896 if (recurse && res->res_dir.hdr.next) {
897 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
902 * Free all the members of an INCEXE structure
904 static void free_incexe(INCEXE *incexe)
906 incexe->name_list.destroy();
907 for (int i=0; i<incexe->num_opts; i++) {
908 FOPTS *fopt = incexe->opts_list[i];
909 fopt->regex.destroy();
910 fopt->regexdir.destroy();
911 fopt->regexfile.destroy();
912 fopt->wild.destroy();
913 fopt->wilddir.destroy();
914 fopt->wildfile.destroy();
915 fopt->wildbase.destroy();
916 fopt->base.destroy();
917 fopt->fstype.destroy();
918 fopt->drivetype.destroy();
927 if (incexe->opts_list) {
928 free(incexe->opts_list);
934 * Free memory of resource -- called when daemon terminates.
935 * NB, we don't need to worry about freeing any references
936 * to other resources as they will be freed when that
937 * resource chain is traversed. Mainly we worry about freeing
938 * allocated strings (names).
940 void free_resource(RES *sres, int type)
943 RES *nres; /* next resource if linked */
944 URES *res = (URES *)sres;
949 /* common stuff -- free the resource name and description */
950 nres = (RES *)res->res_dir.hdr.next;
951 if (res->res_dir.hdr.name) {
952 free(res->res_dir.hdr.name);
954 if (res->res_dir.hdr.desc) {
955 free(res->res_dir.hdr.desc);
960 if (res->res_dir.working_directory) {
961 free(res->res_dir.working_directory);
963 if (res->res_dir.scripts_directory) {
964 free((char *)res->res_dir.scripts_directory);
966 if (res->res_dir.pid_directory) {
967 free(res->res_dir.pid_directory);
969 if (res->res_dir.subsys_directory) {
970 free(res->res_dir.subsys_directory);
972 if (res->res_dir.password) {
973 free(res->res_dir.password);
975 if (res->res_dir.query_file) {
976 free(res->res_dir.query_file);
978 if (res->res_dir.DIRaddrs) {
979 free_addresses(res->res_dir.DIRaddrs);
981 if (res->res_dir.tls_ctx) {
982 free_tls_context(res->res_dir.tls_ctx);
984 if (res->res_dir.tls_ca_certfile) {
985 free(res->res_dir.tls_ca_certfile);
987 if (res->res_dir.tls_ca_certdir) {
988 free(res->res_dir.tls_ca_certdir);
990 if (res->res_dir.tls_certfile) {
991 free(res->res_dir.tls_certfile);
993 if (res->res_dir.tls_keyfile) {
994 free(res->res_dir.tls_keyfile);
996 if (res->res_dir.tls_dhfile) {
997 free(res->res_dir.tls_dhfile);
999 if (res->res_dir.tls_allowed_cns) {
1000 delete res->res_dir.tls_allowed_cns;
1007 if (res->res_con.password) {
1008 free(res->res_con.password);
1010 if (res->res_con.tls_ctx) {
1011 free_tls_context(res->res_con.tls_ctx);
1013 if (res->res_con.tls_ca_certfile) {
1014 free(res->res_con.tls_ca_certfile);
1016 if (res->res_con.tls_ca_certdir) {
1017 free(res->res_con.tls_ca_certdir);
1019 if (res->res_con.tls_certfile) {
1020 free(res->res_con.tls_certfile);
1022 if (res->res_con.tls_keyfile) {
1023 free(res->res_con.tls_keyfile);
1025 if (res->res_con.tls_dhfile) {
1026 free(res->res_con.tls_dhfile);
1028 if (res->res_con.tls_allowed_cns) {
1029 delete res->res_con.tls_allowed_cns;
1031 for (int i=0; i<Num_ACL; i++) {
1032 if (res->res_con.ACL_lists[i]) {
1033 delete res->res_con.ACL_lists[i];
1034 res->res_con.ACL_lists[i] = NULL;
1039 if (res->res_client.address) {
1040 free(res->res_client.address);
1042 if (res->res_client.password) {
1043 free(res->res_client.password);
1045 if (res->res_client.tls_ctx) {
1046 free_tls_context(res->res_client.tls_ctx);
1048 if (res->res_client.tls_ca_certfile) {
1049 free(res->res_client.tls_ca_certfile);
1051 if (res->res_client.tls_ca_certdir) {
1052 free(res->res_client.tls_ca_certdir);
1054 if (res->res_client.tls_certfile) {
1055 free(res->res_client.tls_certfile);
1057 if (res->res_client.tls_keyfile) {
1058 free(res->res_client.tls_keyfile);
1060 if (res->res_client.tls_allowed_cns) {
1061 delete res->res_client.tls_allowed_cns;
1065 if (res->res_store.address) {
1066 free(res->res_store.address);
1068 if (res->res_store.password) {
1069 free(res->res_store.password);
1071 if (res->res_store.media_type) {
1072 free(res->res_store.media_type);
1074 if (res->res_store.device) {
1075 delete res->res_store.device;
1077 if (res->res_store.tls_ctx) {
1078 free_tls_context(res->res_store.tls_ctx);
1080 if (res->res_store.tls_ca_certfile) {
1081 free(res->res_store.tls_ca_certfile);
1083 if (res->res_store.tls_ca_certdir) {
1084 free(res->res_store.tls_ca_certdir);
1086 if (res->res_store.tls_certfile) {
1087 free(res->res_store.tls_certfile);
1089 if (res->res_store.tls_keyfile) {
1090 free(res->res_store.tls_keyfile);
1094 if (res->res_cat.db_address) {
1095 free(res->res_cat.db_address);
1097 if (res->res_cat.db_socket) {
1098 free(res->res_cat.db_socket);
1100 if (res->res_cat.db_user) {
1101 free(res->res_cat.db_user);
1103 if (res->res_cat.db_name) {
1104 free(res->res_cat.db_name);
1106 if (res->res_cat.db_password) {
1107 free(res->res_cat.db_password);
1111 if ((num=res->res_fs.num_includes)) {
1112 while (--num >= 0) {
1113 free_incexe(res->res_fs.include_items[num]);
1115 free(res->res_fs.include_items);
1117 res->res_fs.num_includes = 0;
1118 if ((num=res->res_fs.num_excludes)) {
1119 while (--num >= 0) {
1120 free_incexe(res->res_fs.exclude_items[num]);
1122 free(res->res_fs.exclude_items);
1124 res->res_fs.num_excludes = 0;
1127 if (res->res_pool.pool_type) {
1128 free(res->res_pool.pool_type);
1130 if (res->res_pool.label_format) {
1131 free(res->res_pool.label_format);
1133 if (res->res_pool.cleaning_prefix) {
1134 free(res->res_pool.cleaning_prefix);
1136 if (res->res_pool.storage) {
1137 delete res->res_pool.storage;
1141 if (res->res_sch.run) {
1143 nrun = res->res_sch.run;
1153 if (res->res_job.RestoreWhere) {
1154 free(res->res_job.RestoreWhere);
1156 if (res->res_job.RegexWhere) {
1157 free(res->res_job.RegexWhere);
1159 if (res->res_job.strip_prefix) {
1160 free(res->res_job.strip_prefix);
1162 if (res->res_job.add_prefix) {
1163 free(res->res_job.add_prefix);
1165 if (res->res_job.add_suffix) {
1166 free(res->res_job.add_suffix);
1168 if (res->res_job.RestoreBootstrap) {
1169 free(res->res_job.RestoreBootstrap);
1171 if (res->res_job.WriteBootstrap) {
1172 free(res->res_job.WriteBootstrap);
1174 if (res->res_job.selection_pattern) {
1175 free(res->res_job.selection_pattern);
1177 if (res->res_job.run_cmds) {
1178 delete res->res_job.run_cmds;
1180 if (res->res_job.storage) {
1181 delete res->res_job.storage;
1183 if (res->res_job.RunScripts) {
1184 free_runscripts(res->res_job.RunScripts);
1185 delete res->res_job.RunScripts;
1189 if (res->res_msgs.mail_cmd) {
1190 free(res->res_msgs.mail_cmd);
1192 if (res->res_msgs.operator_cmd) {
1193 free(res->res_msgs.operator_cmd);
1195 free_msgs_res((MSGS *)res); /* free message resource */
1199 printf(_("Unknown resource type %d in free_resource.\n"), type);
1201 /* Common stuff again -- free the resource, recurse to next one */
1206 free_resource(nres, type);
1211 * Save the new resource by chaining it into the head list for
1212 * the resource. If this is pass 2, we update any resource
1213 * pointers because they may not have been defined until
1216 void save_resource(int type, RES_ITEM *items, int pass)
1219 int rindex = type - r_first;
1223 /* Check Job requirements after applying JobDefs */
1224 if (type != R_JOB && type != R_JOBDEFS) {
1226 * Ensure that all required items are present
1228 for (i=0; items[i].name; i++) {
1229 if (items[i].flags & ITEM_REQUIRED) {
1230 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1231 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1232 items[i].name, resources[rindex]);
1235 /* If this triggers, take a look at lib/parse_conf.h */
1236 if (i >= MAX_RES_ITEMS) {
1237 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1240 } else if (type == R_JOB) {
1242 * Ensure that the name item is present
1244 if (items[0].flags & ITEM_REQUIRED) {
1245 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1246 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1247 items[0].name, resources[rindex]);
1253 * During pass 2 in each "store" routine, we looked up pointers
1254 * to all the resources referrenced in the current resource, now we
1255 * must copy their addresses from the static record to the allocated
1260 /* Resources not containing a resource */
1268 * Resources containing another resource or alist. First
1269 * look up the resource which contains another resource. It
1270 * was written during pass 1. Then stuff in the pointers to
1271 * the resources it contains, which were inserted this pass.
1272 * Finally, it will all be stored back.
1275 /* Find resource saved in pass 1 */
1276 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1277 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1279 /* Explicitly copy resource pointers from this pass (res_all) */
1280 res->res_pool.NextPool = res_all.res_pool.NextPool;
1281 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1282 res->res_pool.storage = res_all.res_pool.storage;
1285 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1286 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1288 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1291 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1292 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1294 res->res_dir.messages = res_all.res_dir.messages;
1295 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1298 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1299 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1300 res_all.res_dir.hdr.name);
1302 /* we must explicitly copy the device alist pointer */
1303 res->res_store.device = res_all.res_store.device;
1307 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1308 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1309 res_all.res_dir.hdr.name);
1311 res->res_job.messages = res_all.res_job.messages;
1312 res->res_job.schedule = res_all.res_job.schedule;
1313 res->res_job.client = res_all.res_job.client;
1314 res->res_job.fileset = res_all.res_job.fileset;
1315 res->res_job.storage = res_all.res_job.storage;
1316 res->res_job.pool = res_all.res_job.pool;
1317 res->res_job.full_pool = res_all.res_job.full_pool;
1318 res->res_job.inc_pool = res_all.res_job.inc_pool;
1319 res->res_job.diff_pool = res_all.res_job.diff_pool;
1320 res->res_job.verify_job = res_all.res_job.verify_job;
1321 res->res_job.jobdefs = res_all.res_job.jobdefs;
1322 res->res_job.run_cmds = res_all.res_job.run_cmds;
1323 res->res_job.RunScripts = res_all.res_job.RunScripts;
1325 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1326 * is not very useful)
1327 * We have to set_bit(index, res_all.hdr.item_present);
1328 * or something like that
1331 /* we take RegexWhere before all other options */
1332 if (!res->res_job.RegexWhere
1334 (res->res_job.strip_prefix ||
1335 res->res_job.add_suffix ||
1336 res->res_job.add_prefix))
1338 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1339 res->res_job.add_prefix,
1340 res->res_job.add_suffix);
1341 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1342 bregexp_build_where(res->res_job.RegexWhere, len,
1343 res->res_job.strip_prefix,
1344 res->res_job.add_prefix,
1345 res->res_job.add_suffix);
1346 /* TODO: test bregexp */
1349 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1350 free(res->res_job.RestoreWhere);
1351 res->res_job.RestoreWhere = NULL;
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);