2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation and included
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
29 * Main configuration file parser for Bacula Directors,
30 * some parts may be split into separate files such as
31 * the schedule configuration (run_config.c).
33 * Note, the configuration file parser consists of three parts
35 * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
37 * 2. The generic config scanner in lib/parse_config.c and
39 * These files contain the parser code, some utility
40 * routines, and the common store routines (name, int,
43 * 3. The daemon specific file, which contains the Resource
44 * definitions as well as any specific store routines
45 * for the resource records.
47 * Kern Sibbald, January MM
56 /* Define the first and last resource ID record
57 * types. Note, these should be unique for each
58 * daemon though not a requirement.
60 int r_first = R_FIRST;
62 static RES *sres_head[R_LAST - R_FIRST + 1];
63 RES **res_head = sres_head;
65 /* Imported subroutines */
66 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
67 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
68 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
71 /* Forward referenced subroutines */
73 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
74 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
75 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
76 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
77 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
79 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
80 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
81 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
82 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
84 /* We build the current resource here as we are
85 * scanning the resource configuration definition,
86 * then move it to allocated memory when the resource
90 extern "C" { // work around visual compiler mangling variables
96 int res_all_size = sizeof(res_all);
99 /* Definition of records permitted within each
100 * resource with the routine to process the record
101 * information. NOTE! quoted names must be in lower case.
106 * name handler value code flags default_value
108 static RES_ITEM dir_items[] = {
109 {"name", store_name, ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
110 {"description", store_str, ITEM(res_dir.hdr.desc), 0, 0, 0},
111 {"messages", store_res, ITEM(res_dir.messages), R_MSGS, 0, 0},
112 {"dirport", store_addresses_port, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
113 {"diraddress", store_addresses_address, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
114 {"diraddresses",store_addresses, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
115 {"queryfile", store_dir, ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
116 {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
117 {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
118 {"piddirectory",store_dir, ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
119 {"subsysdirectory", store_dir, ITEM(res_dir.subsys_directory), 0, 0, 0},
120 {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
121 {"password", store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
122 {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
123 {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
124 {"heartbeatinterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 0},
125 {"tlsenable", store_bool, ITEM(res_dir.tls_enable), 0, 0, 0},
126 {"tlsrequire", store_bool, ITEM(res_dir.tls_require), 0, 0, 0},
127 {"tlsverifypeer", store_bool, ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
128 {"tlscacertificatefile", store_dir, ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
129 {"tlscacertificatedir", store_dir, ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
130 {"tlscertificate", store_dir, ITEM(res_dir.tls_certfile), 0, 0, 0},
131 {"tlskey", store_dir, ITEM(res_dir.tls_keyfile), 0, 0, 0},
132 {"tlsdhfile", store_dir, ITEM(res_dir.tls_dhfile), 0, 0, 0},
133 {"tlsallowedcn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
134 {NULL, NULL, {0}, 0, 0, 0}
140 * name handler value code flags default_value
142 static RES_ITEM con_items[] = {
143 {"name", store_name, ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
144 {"description", store_str, ITEM(res_con.hdr.desc), 0, 0, 0},
145 {"password", store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
146 {"jobacl", store_acl, ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
147 {"clientacl", store_acl, ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
148 {"storageacl", store_acl, ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
149 {"scheduleacl", store_acl, ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
150 {"runacl", store_acl, ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
151 {"poolacl", store_acl, ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
152 {"commandacl", store_acl, ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
153 {"filesetacl", store_acl, ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
154 {"catalogacl", store_acl, ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
155 {"whereacl", store_acl, ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
156 {"tlsenable", store_bool, ITEM(res_con.tls_enable), 0, 0, 0},
157 {"tlsrequire", store_bool, ITEM(res_con.tls_require), 0, 0, 0},
158 {"tlsverifypeer", store_bool, ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
159 {"tlscacertificatefile", store_dir, ITEM(res_con.tls_ca_certfile), 0, 0, 0},
160 {"tlscacertificatedir", store_dir, ITEM(res_con.tls_ca_certdir), 0, 0, 0},
161 {"tlscertificate", store_dir, ITEM(res_con.tls_certfile), 0, 0, 0},
162 {"tlskey", store_dir, ITEM(res_con.tls_keyfile), 0, 0, 0},
163 {"tlsdhfile", store_dir, ITEM(res_con.tls_dhfile), 0, 0, 0},
164 {"tlsallowedcn", store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
165 {NULL, NULL, {0}, 0, 0, 0}
170 * Client or File daemon resource
172 * name handler value code flags default_value
175 static RES_ITEM cli_items[] = {
176 {"name", store_name, ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
177 {"description", store_str, ITEM(res_client.hdr.desc), 0, 0, 0},
178 {"address", store_str, ITEM(res_client.address), 0, ITEM_REQUIRED, 0},
179 {"fdaddress", store_str, ITEM(res_client.address), 0, 0, 0},
180 {"fdport", store_pint, ITEM(res_client.FDport), 0, ITEM_DEFAULT, 9102},
181 {"password", store_password, ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
182 {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
183 {"catalog", store_res, ITEM(res_client.catalog), R_CATALOG, ITEM_REQUIRED, 0},
184 {"fileretention", store_time, ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
185 {"jobretention", store_time, ITEM(res_client.JobRetention), 0, ITEM_DEFAULT, 60*60*24*180},
186 {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0},
187 {"autoprune", store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
188 {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
189 {"tlsenable", store_bool, ITEM(res_client.tls_enable), 0, 0, 0},
190 {"tlsrequire", store_bool, ITEM(res_client.tls_require), 0, 0, 0},
191 {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0},
192 {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0},
193 {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0},
194 {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0},
195 {"tlsallowedcn", store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
196 {NULL, NULL, {0}, 0, 0, 0}
199 /* Storage daemon resource
201 * name handler value code flags default_value
203 static RES_ITEM store_items[] = {
204 {"name", store_name, ITEM(res_store.hdr.name), 0, ITEM_REQUIRED, 0},
205 {"description", store_str, ITEM(res_store.hdr.desc), 0, 0, 0},
206 {"sdport", store_pint, ITEM(res_store.SDport), 0, ITEM_DEFAULT, 9103},
207 {"address", store_str, ITEM(res_store.address), 0, ITEM_REQUIRED, 0},
208 {"sdaddress", store_str, ITEM(res_store.address), 0, 0, 0},
209 {"password", store_password, ITEM(res_store.password), 0, ITEM_REQUIRED, 0},
210 {"sdpassword", store_password, ITEM(res_store.password), 0, 0, 0},
211 {"device", store_device, ITEM(res_store.device), R_DEVICE, ITEM_REQUIRED, 0},
212 {"mediatype", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
213 {"autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
214 {"enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true},
215 {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
216 {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
217 {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
218 {"tlsenable", store_bool, ITEM(res_store.tls_enable), 0, 0, 0},
219 {"tlsrequire", store_bool, ITEM(res_store.tls_require), 0, 0, 0},
220 {"tlscacertificatefile", store_dir, ITEM(res_store.tls_ca_certfile), 0, 0, 0},
221 {"tlscacertificatedir", store_dir, ITEM(res_store.tls_ca_certdir), 0, 0, 0},
222 {"tlscertificate", store_dir, ITEM(res_store.tls_certfile), 0, 0, 0},
223 {"tlskey", store_dir, ITEM(res_store.tls_keyfile), 0, 0, 0},
224 {NULL, NULL, {0}, 0, 0, 0}
228 * Catalog Resource Directives
230 * name handler value code flags default_value
232 static RES_ITEM cat_items[] = {
233 {"name", store_name, ITEM(res_cat.hdr.name), 0, ITEM_REQUIRED, 0},
234 {"description", store_str, ITEM(res_cat.hdr.desc), 0, 0, 0},
235 {"address", store_str, ITEM(res_cat.db_address), 0, 0, 0},
236 {"dbaddress", store_str, ITEM(res_cat.db_address), 0, 0, 0},
237 {"dbport", store_pint, ITEM(res_cat.db_port), 0, 0, 0},
238 /* keep this password as store_str for the moment */
239 {"password", store_str, ITEM(res_cat.db_password), 0, 0, 0},
240 {"dbpassword", store_str, ITEM(res_cat.db_password), 0, 0, 0},
241 {"user", store_str, ITEM(res_cat.db_user), 0, 0, 0},
242 {"dbname", store_str, ITEM(res_cat.db_name), 0, ITEM_REQUIRED, 0},
243 {"dbsocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0},
244 /* Turned off for the moment */
245 {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
246 {NULL, NULL, {0}, 0, 0, 0}
250 * Job Resource Directives
252 * name handler value code flags default_value
254 RES_ITEM job_items[] = {
255 {"name", store_name, ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
256 {"description", store_str, ITEM(res_job.hdr.desc), 0, 0, 0},
257 {"type", store_jobtype, ITEM(res_job.JobType), 0, ITEM_REQUIRED, 0},
258 {"level", store_level, ITEM(res_job.JobLevel), 0, 0, 0},
259 {"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
260 {"storage", store_alist_res, ITEM(res_job.storage), R_STORAGE, 0, 0},
261 {"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
262 {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
263 {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
264 {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
265 {"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
266 {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
267 {"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
268 {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
269 {"jobtoverify", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
270 {"jobdefs", store_res, ITEM(res_job.jobdefs), R_JOBDEFS, 0, 0},
271 {"run", store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
272 /* Root of where to restore files */
273 {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
274 {"regexwhere", store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
275 {"stripprefix", store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
276 {"addprefix", store_str, ITEM(res_job.add_prefix), 0, 0, 0},
277 {"addsuffix", store_str, ITEM(res_job.add_suffix), 0, 0, 0},
278 /* Where to find bootstrap during restore */
279 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
280 /* Where to write bootstrap file during backup */
281 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
282 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
283 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
284 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
285 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
286 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
287 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
288 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
289 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
290 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
291 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
292 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
293 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
294 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
295 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
296 {"optimizejobscheduling",store_bool, ITEM(res_job.OptimizeJobScheduling), 0, ITEM_DEFAULT, false},
297 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
298 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
299 {"spoolsize", store_size, ITEM(res_job.spool_size), 0, 0, 0},
300 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
301 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
302 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
303 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
304 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
305 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
306 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
307 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
308 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
309 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
310 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
311 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
312 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
313 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
314 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
315 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
316 {NULL, NULL, {0}, 0, 0, 0}
321 * name handler value code flags default_value
323 static RES_ITEM fs_items[] = {
324 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
325 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
326 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
327 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
328 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
329 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
330 {NULL, NULL, {0}, 0, 0, 0}
333 /* Schedule -- see run_conf.c */
336 * name handler value code flags default_value
338 static RES_ITEM sch_items[] = {
339 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
340 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
341 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
342 {NULL, NULL, {0}, 0, 0, 0}
347 * name handler value code flags default_value
349 static RES_ITEM pool_items[] = {
350 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
351 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
352 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
353 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
354 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
355 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
356 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
357 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
358 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
359 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
360 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
361 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
362 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
363 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
364 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
365 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
366 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
367 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
368 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
369 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
370 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
371 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
372 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
373 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
374 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
375 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
376 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
377 {"catalog", store_res, ITEM(res_pool.Catalog), R_CATALOG, 0, 0},
378 {NULL, NULL, {0}, 0, 0, 0}
383 * name handler value code flags default_value
385 static RES_ITEM counter_items[] = {
386 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
387 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
388 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
389 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
390 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
391 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
392 {NULL, NULL, {0}, 0, 0, 0}
396 /* Message resource */
397 extern RES_ITEM msgs_items[];
400 * This is the master resource definition.
401 * It must have one item for each of the resources.
403 * NOTE!!! keep it in the same order as the R_codes
404 * or eliminate all resources[rindex].name
406 * name items rcode res_head
408 RES_TABLE resources[] = {
409 {"director", dir_items, R_DIRECTOR},
410 {"client", cli_items, R_CLIENT},
411 {"job", job_items, R_JOB},
412 {"storage", store_items, R_STORAGE},
413 {"catalog", cat_items, R_CATALOG},
414 {"schedule", sch_items, R_SCHEDULE},
415 {"fileset", fs_items, R_FILESET},
416 {"pool", pool_items, R_POOL},
417 {"messages", msgs_items, R_MSGS},
418 {"counter", counter_items, R_COUNTER},
419 {"console", con_items, R_CONSOLE},
420 {"jobdefs", job_items, R_JOBDEFS},
421 {"device", NULL, R_DEVICE}, /* info obtained from SD */
426 /* Keywords (RHS) permitted in Job Level records
428 * level_name level job_type
430 struct s_jl joblevels[] = {
431 {"Full", L_FULL, JT_BACKUP},
432 {"Base", L_BASE, JT_BACKUP},
433 {"Incremental", L_INCREMENTAL, JT_BACKUP},
434 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
435 {"Since", L_SINCE, JT_BACKUP},
436 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
437 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
438 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
439 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
440 {"Data", L_VERIFY_DATA, JT_VERIFY},
441 {" ", L_NONE, JT_ADMIN},
442 {" ", L_NONE, JT_RESTORE},
446 /* Keywords (RHS) permitted in Job type records
450 struct s_jt jobtypes[] = {
451 {"backup", JT_BACKUP},
453 {"verify", JT_VERIFY},
454 {"restore", JT_RESTORE},
455 {"migrate", JT_MIGRATE},
460 /* Keywords (RHS) permitted in Selection type records
464 struct s_jt migtypes[] = {
465 {"smallestvolume", MT_SMALLEST_VOL},
466 {"oldestvolume", MT_OLDEST_VOL},
467 {"pooloccupancy", MT_POOL_OCCUPANCY},
468 {"pooltime", MT_POOL_TIME},
469 {"client", MT_CLIENT},
470 {"volume", MT_VOLUME},
472 {"sqlquery", MT_SQLQUERY},
478 /* Options permitted in Restore replace= */
479 struct s_kw ReplaceOptions[] = {
480 {"always", REPLACE_ALWAYS},
481 {"ifnewer", REPLACE_IFNEWER},
482 {"ifolder", REPLACE_IFOLDER},
483 {"never", REPLACE_NEVER},
487 const char *level_to_str(int level)
490 static char level_no[30];
491 const char *str = level_no;
493 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
494 for (i=0; joblevels[i].level_name; i++) {
495 if (level == joblevels[i].level) {
496 str = joblevels[i].level_name;
503 /* Dump contents of resource */
504 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
506 URES *res = (URES *)reshdr;
508 char ed1[100], ed2[100], ed3[100];
512 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
515 if (type < 0) { /* no recursion */
521 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
522 reshdr->name, res->res_dir.MaxConcurrentJobs,
523 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
524 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
525 if (res->res_dir.query_file) {
526 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
528 if (res->res_dir.messages) {
529 sendit(sock, _(" --> "));
530 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
534 sendit(sock, _("Console: name=%s SSL=%d\n"),
535 res->res_con.hdr.name, res->res_con.tls_enable);
538 if (res->res_counter.WrapCounter) {
539 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
540 res->res_counter.hdr.name, res->res_counter.MinValue,
541 res->res_counter.MaxValue, res->res_counter.CurrentValue,
542 res->res_counter.WrapCounter->hdr.name);
544 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
545 res->res_counter.hdr.name, res->res_counter.MinValue,
546 res->res_counter.MaxValue);
548 if (res->res_counter.Catalog) {
549 sendit(sock, _(" --> "));
550 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
555 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
556 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
557 res->res_client.MaxConcurrentJobs);
558 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
559 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
560 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
561 res->res_client.AutoPrune);
562 if (res->res_client.catalog) {
563 sendit(sock, _(" --> "));
564 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
571 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
572 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
573 " poolid=%s volname=%s MediaType=%s\n"),
574 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
575 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
576 dev->offline, dev->autochanger,
577 edit_uint64(dev->PoolId, ed1),
578 dev->VolumeName, dev->MediaType);
582 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
583 " DeviceName=%s MediaType=%s StorageId=%s\n"),
584 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
585 res->res_store.MaxConcurrentJobs,
586 res->res_store.dev_name(),
587 res->res_store.media_type,
588 edit_int64(res->res_store.StorageId, ed1));
592 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
593 " db_user=%s MutliDBConn=%d\n"),
594 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
595 res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
596 res->res_cat.mult_db_connections);
601 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
602 type == R_JOB ? _("Job") : _("JobDefs"),
603 res->res_job.hdr.name, res->res_job.JobType,
604 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
605 res->res_job.enabled);
606 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
607 res->res_job.MaxConcurrentJobs,
608 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
609 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
610 res->res_job.spool_data, res->res_job.write_part_after_job);
611 if (res->res_job.spool_size) {
612 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
614 if (res->res_job.JobType == JT_MIGRATE) {
615 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
617 if (res->res_job.client) {
618 sendit(sock, _(" --> "));
619 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
621 if (res->res_job.fileset) {
622 sendit(sock, _(" --> "));
623 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
625 if (res->res_job.schedule) {
626 sendit(sock, _(" --> "));
627 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
629 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
630 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
632 if (res->res_job.RegexWhere) {
633 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
635 if (res->res_job.RestoreBootstrap) {
636 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
638 if (res->res_job.WriteBootstrap) {
639 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
641 if (res->res_job.storage) {
643 foreach_alist(store, res->res_job.storage) {
644 sendit(sock, _(" --> "));
645 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
648 if (res->res_job.RunScripts) {
650 foreach_alist(script, res->res_job.RunScripts) {
651 sendit(sock, _(" --> RunScript\n"));
652 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
653 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
654 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
655 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
656 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
657 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
660 if (res->res_job.pool) {
661 sendit(sock, _(" --> "));
662 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
664 if (res->res_job.full_pool) {
665 sendit(sock, _(" --> "));
666 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
668 if (res->res_job.inc_pool) {
669 sendit(sock, _(" --> "));
670 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
672 if (res->res_job.diff_pool) {
673 sendit(sock, _(" --> "));
674 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
676 if (res->res_job.verify_job) {
677 sendit(sock, _(" --> "));
678 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
680 if (res->res_job.run_cmds) {
682 foreach_alist(runcmd, res->res_job.run_cmds) {
683 sendit(sock, _(" --> Run=%s\n"), runcmd);
686 if (res->res_job.selection_pattern) {
687 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
689 if (res->res_job.messages) {
690 sendit(sock, _(" --> "));
691 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
698 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
699 for (i=0; i<res->res_fs.num_includes; i++) {
700 INCEXE *incexe = res->res_fs.include_items[i];
701 for (j=0; j<incexe->num_opts; j++) {
702 FOPTS *fo = incexe->opts_list[j];
703 sendit(sock, " O %s\n", fo->opts);
705 bool enhanced_wild = false;
706 for (k=0; fo->opts[k]!='\0'; k++) {
707 if (fo->opts[k]=='W') {
708 enhanced_wild = true;
713 for (k=0; k<fo->regex.size(); k++) {
714 sendit(sock, " R %s\n", fo->regex.get(k));
716 for (k=0; k<fo->regexdir.size(); k++) {
717 sendit(sock, " RD %s\n", fo->regexdir.get(k));
719 for (k=0; k<fo->regexfile.size(); k++) {
720 sendit(sock, " RF %s\n", fo->regexfile.get(k));
722 for (k=0; k<fo->wild.size(); k++) {
723 sendit(sock, " W %s\n", fo->wild.get(k));
725 for (k=0; k<fo->wilddir.size(); k++) {
726 sendit(sock, " WD %s\n", fo->wilddir.get(k));
728 for (k=0; k<fo->wildfile.size(); k++) {
729 sendit(sock, " WF %s\n", fo->wildfile.get(k));
731 for (k=0; k<fo->wildbase.size(); k++) {
732 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
734 for (k=0; k<fo->base.size(); k++) {
735 sendit(sock, " B %s\n", fo->base.get(k));
737 for (k=0; k<fo->fstype.size(); k++) {
738 sendit(sock, " X %s\n", fo->fstype.get(k));
740 for (k=0; k<fo->drivetype.size(); k++) {
741 sendit(sock, " XD %s\n", fo->drivetype.get(k));
744 sendit(sock, " D %s\n", fo->reader);
747 sendit(sock, " T %s\n", fo->writer);
749 sendit(sock, " N\n");
751 for (j=0; j<incexe->name_list.size(); j++) {
752 sendit(sock, " I %s\n", incexe->name_list.get(j));
754 if (incexe->name_list.size()) {
755 sendit(sock, " N\n");
759 for (i=0; i<res->res_fs.num_excludes; i++) {
760 INCEXE *incexe = res->res_fs.exclude_items[i];
761 for (j=0; j<incexe->name_list.size(); j++) {
762 sendit(sock, " E %s\n", incexe->name_list.get(j));
764 if (incexe->name_list.size()) {
765 sendit(sock, " N\n");
772 if (res->res_sch.run) {
774 RUN *run = res->res_sch.run;
775 char buf[1000], num[30];
776 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
781 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
782 bstrncpy(buf, _(" hour="), sizeof(buf));
783 for (i=0; i<24; i++) {
784 if (bit_is_set(i, run->hour)) {
785 bsnprintf(num, sizeof(num), "%d ", i);
786 bstrncat(buf, num, sizeof(buf));
789 bstrncat(buf, "\n", sizeof(buf));
791 bstrncpy(buf, _(" mday="), sizeof(buf));
792 for (i=0; i<31; i++) {
793 if (bit_is_set(i, run->mday)) {
794 bsnprintf(num, sizeof(num), "%d ", i);
795 bstrncat(buf, num, sizeof(buf));
798 bstrncat(buf, "\n", sizeof(buf));
800 bstrncpy(buf, _(" month="), sizeof(buf));
801 for (i=0; i<12; i++) {
802 if (bit_is_set(i, run->month)) {
803 bsnprintf(num, sizeof(num), "%d ", i);
804 bstrncat(buf, num, sizeof(buf));
807 bstrncat(buf, "\n", sizeof(buf));
809 bstrncpy(buf, _(" wday="), sizeof(buf));
810 for (i=0; i<7; i++) {
811 if (bit_is_set(i, run->wday)) {
812 bsnprintf(num, sizeof(num), "%d ", i);
813 bstrncat(buf, num, sizeof(buf));
816 bstrncat(buf, "\n", sizeof(buf));
818 bstrncpy(buf, _(" wom="), sizeof(buf));
819 for (i=0; i<5; i++) {
820 if (bit_is_set(i, run->wom)) {
821 bsnprintf(num, sizeof(num), "%d ", i);
822 bstrncat(buf, num, sizeof(buf));
825 bstrncat(buf, "\n", sizeof(buf));
827 bstrncpy(buf, _(" woy="), sizeof(buf));
828 for (i=0; i<54; i++) {
829 if (bit_is_set(i, run->woy)) {
830 bsnprintf(num, sizeof(num), "%d ", i);
831 bstrncat(buf, num, sizeof(buf));
834 bstrncat(buf, "\n", sizeof(buf));
836 sendit(sock, _(" mins=%d\n"), run->minute);
838 sendit(sock, _(" --> "));
839 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
842 sendit(sock, _(" --> "));
843 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
846 sendit(sock, _(" --> "));
847 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
849 /* If another Run record is chained in, go print it */
855 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
860 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
861 res->res_pool.pool_type);
862 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
863 res->res_pool.use_catalog, res->res_pool.use_volume_once,
864 res->res_pool.catalog_files);
865 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
866 res->res_pool.max_volumes, res->res_pool.AutoPrune,
867 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
868 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
869 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
870 res->res_pool.Recycle,
871 NPRT(res->res_pool.label_format));
872 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
873 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
874 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
875 res->res_pool.recycle_oldest_volume,
876 res->res_pool.purge_oldest_volume);
877 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
878 res->res_pool.MaxVolJobs,
879 res->res_pool.MaxVolFiles,
880 edit_uint64(res->res_pool.MaxVolFiles, ed1));
881 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
882 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
883 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
884 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
885 if (res->res_pool.NextPool) {
886 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
888 if (res->res_pool.RecyclePool) {
889 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
891 if (res->res_pool.Catalog) {
892 sendit(sock, _(" Catalog=%s\n"), res->res_pool.Catalog->name());
894 if (res->res_pool.storage) {
896 foreach_alist(store, res->res_pool.storage) {
897 sendit(sock, _(" --> "));
898 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
901 if (res->res_pool.CopyPool) {
903 foreach_alist(copy, res->res_pool.CopyPool) {
904 sendit(sock, _(" --> "));
905 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
912 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
913 if (res->res_msgs.mail_cmd)
914 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
915 if (res->res_msgs.operator_cmd)
916 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
920 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
923 if (recurse && res->res_dir.hdr.next) {
924 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
929 * Free all the members of an INCEXE structure
931 static void free_incexe(INCEXE *incexe)
933 incexe->name_list.destroy();
934 for (int i=0; i<incexe->num_opts; i++) {
935 FOPTS *fopt = incexe->opts_list[i];
936 fopt->regex.destroy();
937 fopt->regexdir.destroy();
938 fopt->regexfile.destroy();
939 fopt->wild.destroy();
940 fopt->wilddir.destroy();
941 fopt->wildfile.destroy();
942 fopt->wildbase.destroy();
943 fopt->base.destroy();
944 fopt->fstype.destroy();
945 fopt->drivetype.destroy();
954 if (incexe->opts_list) {
955 free(incexe->opts_list);
961 * Free memory of resource -- called when daemon terminates.
962 * NB, we don't need to worry about freeing any references
963 * to other resources as they will be freed when that
964 * resource chain is traversed. Mainly we worry about freeing
965 * allocated strings (names).
967 void free_resource(RES *sres, int type)
970 RES *nres; /* next resource if linked */
971 URES *res = (URES *)sres;
976 /* common stuff -- free the resource name and description */
977 nres = (RES *)res->res_dir.hdr.next;
978 if (res->res_dir.hdr.name) {
979 free(res->res_dir.hdr.name);
981 if (res->res_dir.hdr.desc) {
982 free(res->res_dir.hdr.desc);
987 if (res->res_dir.working_directory) {
988 free(res->res_dir.working_directory);
990 if (res->res_dir.scripts_directory) {
991 free((char *)res->res_dir.scripts_directory);
993 if (res->res_dir.pid_directory) {
994 free(res->res_dir.pid_directory);
996 if (res->res_dir.subsys_directory) {
997 free(res->res_dir.subsys_directory);
999 if (res->res_dir.password) {
1000 free(res->res_dir.password);
1002 if (res->res_dir.query_file) {
1003 free(res->res_dir.query_file);
1005 if (res->res_dir.DIRaddrs) {
1006 free_addresses(res->res_dir.DIRaddrs);
1008 if (res->res_dir.tls_ctx) {
1009 free_tls_context(res->res_dir.tls_ctx);
1011 if (res->res_dir.tls_ca_certfile) {
1012 free(res->res_dir.tls_ca_certfile);
1014 if (res->res_dir.tls_ca_certdir) {
1015 free(res->res_dir.tls_ca_certdir);
1017 if (res->res_dir.tls_certfile) {
1018 free(res->res_dir.tls_certfile);
1020 if (res->res_dir.tls_keyfile) {
1021 free(res->res_dir.tls_keyfile);
1023 if (res->res_dir.tls_dhfile) {
1024 free(res->res_dir.tls_dhfile);
1026 if (res->res_dir.tls_allowed_cns) {
1027 delete res->res_dir.tls_allowed_cns;
1034 if (res->res_con.password) {
1035 free(res->res_con.password);
1037 if (res->res_con.tls_ctx) {
1038 free_tls_context(res->res_con.tls_ctx);
1040 if (res->res_con.tls_ca_certfile) {
1041 free(res->res_con.tls_ca_certfile);
1043 if (res->res_con.tls_ca_certdir) {
1044 free(res->res_con.tls_ca_certdir);
1046 if (res->res_con.tls_certfile) {
1047 free(res->res_con.tls_certfile);
1049 if (res->res_con.tls_keyfile) {
1050 free(res->res_con.tls_keyfile);
1052 if (res->res_con.tls_dhfile) {
1053 free(res->res_con.tls_dhfile);
1055 if (res->res_con.tls_allowed_cns) {
1056 delete res->res_con.tls_allowed_cns;
1058 for (int i=0; i<Num_ACL; i++) {
1059 if (res->res_con.ACL_lists[i]) {
1060 delete res->res_con.ACL_lists[i];
1061 res->res_con.ACL_lists[i] = NULL;
1066 if (res->res_client.address) {
1067 free(res->res_client.address);
1069 if (res->res_client.password) {
1070 free(res->res_client.password);
1072 if (res->res_client.tls_ctx) {
1073 free_tls_context(res->res_client.tls_ctx);
1075 if (res->res_client.tls_ca_certfile) {
1076 free(res->res_client.tls_ca_certfile);
1078 if (res->res_client.tls_ca_certdir) {
1079 free(res->res_client.tls_ca_certdir);
1081 if (res->res_client.tls_certfile) {
1082 free(res->res_client.tls_certfile);
1084 if (res->res_client.tls_keyfile) {
1085 free(res->res_client.tls_keyfile);
1087 if (res->res_client.tls_allowed_cns) {
1088 delete res->res_client.tls_allowed_cns;
1092 if (res->res_store.address) {
1093 free(res->res_store.address);
1095 if (res->res_store.password) {
1096 free(res->res_store.password);
1098 if (res->res_store.media_type) {
1099 free(res->res_store.media_type);
1101 if (res->res_store.device) {
1102 delete res->res_store.device;
1104 if (res->res_store.tls_ctx) {
1105 free_tls_context(res->res_store.tls_ctx);
1107 if (res->res_store.tls_ca_certfile) {
1108 free(res->res_store.tls_ca_certfile);
1110 if (res->res_store.tls_ca_certdir) {
1111 free(res->res_store.tls_ca_certdir);
1113 if (res->res_store.tls_certfile) {
1114 free(res->res_store.tls_certfile);
1116 if (res->res_store.tls_keyfile) {
1117 free(res->res_store.tls_keyfile);
1121 if (res->res_cat.db_address) {
1122 free(res->res_cat.db_address);
1124 if (res->res_cat.db_socket) {
1125 free(res->res_cat.db_socket);
1127 if (res->res_cat.db_user) {
1128 free(res->res_cat.db_user);
1130 if (res->res_cat.db_name) {
1131 free(res->res_cat.db_name);
1133 if (res->res_cat.db_password) {
1134 free(res->res_cat.db_password);
1138 if ((num=res->res_fs.num_includes)) {
1139 while (--num >= 0) {
1140 free_incexe(res->res_fs.include_items[num]);
1142 free(res->res_fs.include_items);
1144 res->res_fs.num_includes = 0;
1145 if ((num=res->res_fs.num_excludes)) {
1146 while (--num >= 0) {
1147 free_incexe(res->res_fs.exclude_items[num]);
1149 free(res->res_fs.exclude_items);
1151 res->res_fs.num_excludes = 0;
1154 if (res->res_pool.pool_type) {
1155 free(res->res_pool.pool_type);
1157 if (res->res_pool.label_format) {
1158 free(res->res_pool.label_format);
1160 if (res->res_pool.cleaning_prefix) {
1161 free(res->res_pool.cleaning_prefix);
1163 if (res->res_pool.storage) {
1164 delete res->res_pool.storage;
1168 if (res->res_sch.run) {
1170 nrun = res->res_sch.run;
1180 if (res->res_job.RestoreWhere) {
1181 free(res->res_job.RestoreWhere);
1183 if (res->res_job.RegexWhere) {
1184 free(res->res_job.RegexWhere);
1186 if (res->res_job.strip_prefix) {
1187 free(res->res_job.strip_prefix);
1189 if (res->res_job.add_prefix) {
1190 free(res->res_job.add_prefix);
1192 if (res->res_job.add_suffix) {
1193 free(res->res_job.add_suffix);
1195 if (res->res_job.RestoreBootstrap) {
1196 free(res->res_job.RestoreBootstrap);
1198 if (res->res_job.WriteBootstrap) {
1199 free(res->res_job.WriteBootstrap);
1201 if (res->res_job.selection_pattern) {
1202 free(res->res_job.selection_pattern);
1204 if (res->res_job.run_cmds) {
1205 delete res->res_job.run_cmds;
1207 if (res->res_job.storage) {
1208 delete res->res_job.storage;
1210 if (res->res_job.RunScripts) {
1211 free_runscripts(res->res_job.RunScripts);
1212 delete res->res_job.RunScripts;
1216 if (res->res_msgs.mail_cmd) {
1217 free(res->res_msgs.mail_cmd);
1219 if (res->res_msgs.operator_cmd) {
1220 free(res->res_msgs.operator_cmd);
1222 free_msgs_res((MSGS *)res); /* free message resource */
1226 printf(_("Unknown resource type %d in free_resource.\n"), type);
1228 /* Common stuff again -- free the resource, recurse to next one */
1233 free_resource(nres, type);
1238 * Save the new resource by chaining it into the head list for
1239 * the resource. If this is pass 2, we update any resource
1240 * pointers because they may not have been defined until
1243 void save_resource(int type, RES_ITEM *items, int pass)
1246 int rindex = type - r_first;
1250 /* Check Job requirements after applying JobDefs */
1251 if (type != R_JOB && type != R_JOBDEFS) {
1253 * Ensure that all required items are present
1255 for (i=0; items[i].name; i++) {
1256 if (items[i].flags & ITEM_REQUIRED) {
1257 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1258 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1259 items[i].name, resources[rindex]);
1262 /* If this triggers, take a look at lib/parse_conf.h */
1263 if (i >= MAX_RES_ITEMS) {
1264 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1267 } else if (type == R_JOB) {
1269 * Ensure that the name item is present
1271 if (items[0].flags & ITEM_REQUIRED) {
1272 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1273 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1274 items[0].name, resources[rindex]);
1280 * During pass 2 in each "store" routine, we looked up pointers
1281 * to all the resources referrenced in the current resource, now we
1282 * must copy their addresses from the static record to the allocated
1287 /* Resources not containing a resource */
1295 * Resources containing another resource or alist. First
1296 * look up the resource which contains another resource. It
1297 * was written during pass 1. Then stuff in the pointers to
1298 * the resources it contains, which were inserted this pass.
1299 * Finally, it will all be stored back.
1302 /* Find resource saved in pass 1 */
1303 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1304 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1306 /* Explicitly copy resource pointers from this pass (res_all) */
1307 res->res_pool.NextPool = res_all.res_pool.NextPool;
1308 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1309 res->res_pool.storage = res_all.res_pool.storage;
1312 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1313 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1315 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1318 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1319 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1321 res->res_dir.messages = res_all.res_dir.messages;
1322 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1325 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1326 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1327 res_all.res_dir.hdr.name);
1329 /* we must explicitly copy the device alist pointer */
1330 res->res_store.device = res_all.res_store.device;
1334 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1335 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1336 res_all.res_dir.hdr.name);
1338 res->res_job.messages = res_all.res_job.messages;
1339 res->res_job.schedule = res_all.res_job.schedule;
1340 res->res_job.client = res_all.res_job.client;
1341 res->res_job.fileset = res_all.res_job.fileset;
1342 res->res_job.storage = res_all.res_job.storage;
1343 res->res_job.pool = res_all.res_job.pool;
1344 res->res_job.full_pool = res_all.res_job.full_pool;
1345 res->res_job.inc_pool = res_all.res_job.inc_pool;
1346 res->res_job.diff_pool = res_all.res_job.diff_pool;
1347 res->res_job.verify_job = res_all.res_job.verify_job;
1348 res->res_job.jobdefs = res_all.res_job.jobdefs;
1349 res->res_job.run_cmds = res_all.res_job.run_cmds;
1350 res->res_job.RunScripts = res_all.res_job.RunScripts;
1352 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1353 * is not very useful)
1354 * We have to set_bit(index, res_all.hdr.item_present);
1355 * or something like that
1358 /* we take RegexWhere before all other options */
1359 if (!res->res_job.RegexWhere
1361 (res->res_job.strip_prefix ||
1362 res->res_job.add_suffix ||
1363 res->res_job.add_prefix))
1365 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1366 res->res_job.add_prefix,
1367 res->res_job.add_suffix);
1368 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1369 bregexp_build_where(res->res_job.RegexWhere, len,
1370 res->res_job.strip_prefix,
1371 res->res_job.add_prefix,
1372 res->res_job.add_suffix);
1373 /* TODO: test bregexp */
1376 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1377 free(res->res_job.RestoreWhere);
1378 res->res_job.RestoreWhere = NULL;
1383 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1384 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1386 res->res_counter.Catalog = res_all.res_counter.Catalog;
1387 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1391 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1392 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1394 res->res_client.catalog = res_all.res_client.catalog;
1395 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1399 * Schedule is a bit different in that it contains a RUN record
1400 * chain which isn't a "named" resource. This chain was linked
1401 * in by run_conf.c during pass 2, so here we jam the pointer
1402 * into the Schedule resource.
1404 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1405 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1407 res->res_sch.run = res_all.res_sch.run;
1410 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1414 /* Note, the resource name was already saved during pass 1,
1415 * so here, we can just release it.
1417 if (res_all.res_dir.hdr.name) {
1418 free(res_all.res_dir.hdr.name);
1419 res_all.res_dir.hdr.name = NULL;
1421 if (res_all.res_dir.hdr.desc) {
1422 free(res_all.res_dir.hdr.desc);
1423 res_all.res_dir.hdr.desc = NULL;
1429 * The following code is only executed during pass 1
1433 size = sizeof(DIRRES);
1436 size = sizeof(CONRES);
1439 size =sizeof(CLIENT);
1442 size = sizeof(STORE);
1452 size = sizeof(FILESET);
1455 size = sizeof(SCHED);
1458 size = sizeof(POOL);
1461 size = sizeof(MSGS);
1464 size = sizeof(COUNTER);
1470 printf(_("Unknown resource type %d in save_resource.\n"), type);
1476 res = (URES *)malloc(size);
1477 memcpy(res, &res_all, size);
1478 if (!res_head[rindex]) {
1479 res_head[rindex] = (RES *)res; /* store first entry */
1480 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1481 res->res_dir.hdr.name, rindex);
1484 if (res->res_dir.hdr.name == NULL) {
1485 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1488 /* Add new res to end of chain */
1489 for (last=next=res_head[rindex]; next; next=next->next) {
1491 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1492 Emsg2(M_ERROR_TERM, 0,
1493 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1494 resources[rindex].name, res->res_dir.hdr.name);
1497 last->next = (RES *)res;
1498 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1499 res->res_dir.hdr.name, rindex, pass);
1505 * Store Device. Note, the resource is created upon the
1506 * first reference. The details of the resource are obtained
1507 * later from the SD.
1509 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1513 int rindex = R_DEVICE - r_first;
1514 int size = sizeof(DEVICE);
1518 token = lex_get_token(lc, T_NAME);
1519 if (!res_head[rindex]) {
1520 res = (URES *)malloc(size);
1521 memset(res, 0, size);
1522 res->res_dev.hdr.name = bstrdup(lc->str);
1523 res_head[rindex] = (RES *)res; /* store first entry */
1524 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1525 res->res_dir.hdr.name, rindex);
1528 /* See if it is already defined */
1529 for (next=res_head[rindex]; next->next; next=next->next) {
1530 if (strcmp(next->name, lc->str) == 0) {
1536 res = (URES *)malloc(size);
1537 memset(res, 0, size);
1538 res->res_dev.hdr.name = bstrdup(lc->str);
1539 next->next = (RES *)res;
1540 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1541 res->res_dir.hdr.name, rindex, pass);
1546 set_bit(index, res_all.hdr.item_present);
1548 store_alist_res(lc, item, index, pass);
1553 * Store JobType (backup, verify, restore)
1556 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1560 token = lex_get_token(lc, T_NAME);
1561 /* Store the type both pass 1 and pass 2 */
1562 for (i=0; migtypes[i].type_name; i++) {
1563 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1564 *(int *)(item->value) = migtypes[i].job_type;
1570 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1573 set_bit(index, res_all.hdr.item_present);
1579 * Store JobType (backup, verify, restore)
1582 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1586 token = lex_get_token(lc, T_NAME);
1587 /* Store the type both pass 1 and pass 2 */
1588 for (i=0; jobtypes[i].type_name; i++) {
1589 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1590 *(int *)(item->value) = jobtypes[i].job_type;
1596 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1599 set_bit(index, res_all.hdr.item_present);
1603 * Store Job Level (Full, Incremental, ...)
1606 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1610 token = lex_get_token(lc, T_NAME);
1611 /* Store the level pass 2 so that type is defined */
1612 for (i=0; joblevels[i].level_name; i++) {
1613 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1614 *(int *)(item->value) = joblevels[i].level;
1620 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1623 set_bit(index, res_all.hdr.item_present);
1627 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1630 token = lex_get_token(lc, T_NAME);
1631 /* Scan Replacement options */
1632 for (i=0; ReplaceOptions[i].name; i++) {
1633 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1634 *(int *)(item->value) = ReplaceOptions[i].token;
1640 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1643 set_bit(index, res_all.hdr.item_present);
1647 * Store ACL (access control list)
1650 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1655 token = lex_get_token(lc, T_STRING);
1657 if (((alist **)item->value)[item->code] == NULL) {
1658 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1659 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1661 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1662 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1664 token = lex_get_token(lc, T_ALL);
1665 if (token == T_COMMA) {
1666 continue; /* get another ACL */
1670 set_bit(index, res_all.hdr.item_present);
1673 /* We build RunScripts items here */
1674 static RUNSCRIPT res_runscript;
1676 /* Store a runscript->when in a bit field */
1677 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1679 lex_get_token(lc, T_NAME);
1681 if (strcasecmp(lc->str, "before") == 0) {
1682 *(int *)(item->value) = SCRIPT_Before ;
1683 } else if (strcasecmp(lc->str, "after") == 0) {
1684 *(int *)(item->value) = SCRIPT_After;
1685 } else if (strcasecmp(lc->str, "always") == 0) {
1686 *(int *)(item->value) = SCRIPT_Any;
1688 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1693 /* Store a runscript->target
1696 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1698 lex_get_token(lc, T_STRING);
1701 if (strcmp(lc->str, "%c") == 0) {
1702 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1703 } else if (strcasecmp(lc->str, "yes") == 0) {
1704 ((RUNSCRIPT*) item->value)->set_target("%c");
1705 } else if (strcasecmp(lc->str, "no") == 0) {
1706 ((RUNSCRIPT*) item->value)->set_target("");
1708 RES *res = GetResWithName(R_CLIENT, lc->str);
1710 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1711 lc->str, lc->line_no, lc->line);
1714 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1720 /* Store a runscript->command in a bit field
1723 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1725 lex_get_token(lc, T_STRING);
1728 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1733 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1735 lex_get_token(lc, T_STRING);
1736 alist **runscripts = (alist **)(item->value) ;
1739 RUNSCRIPT *script = new_runscript();
1741 script->set_command(lc->str);
1743 /* TODO: remove all script->old_proto with bacula 1.42 */
1745 if (strcmp(item->name, "runbeforejob") == 0) {
1746 script->when = SCRIPT_Before;
1747 script->fail_on_error = true;
1748 script->set_target("");
1750 } else if (strcmp(item->name, "runafterjob") == 0) {
1751 script->when = SCRIPT_After;
1752 script->on_success = true;
1753 script->on_failure = false;
1754 script->set_target("");
1756 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1757 script->old_proto = true;
1758 script->when = SCRIPT_After;
1759 script->set_target("%c");
1760 script->on_success = true;
1761 script->on_failure = false;
1763 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1764 script->old_proto = true;
1765 script->when = SCRIPT_Before;
1766 script->set_target("%c");
1767 script->fail_on_error = true;
1769 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1770 script->when = SCRIPT_After;
1771 script->on_failure = true;
1772 script->on_success = false;
1773 script->set_target("");
1776 if (*runscripts == NULL) {
1777 *runscripts = New(alist(10, not_owned_by_alist));
1780 (*runscripts)->append(script);
1787 /* Store a bool in a bit field without modifing res_all.hdr
1788 * We can also add an option to store_bool to skip res_all.hdr
1790 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1792 lex_get_token(lc, T_NAME);
1793 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1794 *(bool *)(item->value) = true;
1795 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1796 *(bool *)(item->value) = false;
1798 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1804 * new RunScript items
1805 * name handler value code flags default_value
1807 static RES_ITEM runscript_items[] = {
1808 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1809 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1810 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1811 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1812 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1813 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1814 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1815 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1816 {NULL, NULL, {0}, 0, 0, 0}
1820 * Store RunScript info
1822 * Note, when this routine is called, we are inside a Job
1823 * resource. We treat the RunScript like a sort of
1824 * mini-resource within the Job resource.
1826 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1829 alist **runscripts = (alist **)(item->value) ;
1831 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1833 res_runscript.reset_default(); /* setting on_success, on_failure, fail_on_error */
1835 token = lex_get_token(lc, T_SKIP_EOL);
1837 if (token != T_BOB) {
1838 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1841 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1842 if (token == T_EOB) {
1845 if (token != T_IDENTIFIER) {
1846 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1848 for (i=0; runscript_items[i].name; i++) {
1849 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1850 token = lex_get_token(lc, T_SKIP_EOL);
1851 if (token != T_EQUALS) {
1852 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1855 /* Call item handler */
1856 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1863 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1868 if (res_runscript.command == NULL) {
1869 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1870 "command", "runscript");
1873 /* run on client by default */
1874 if (res_runscript.target == NULL) {
1875 res_runscript.set_target("%c");
1878 RUNSCRIPT *script = new_runscript();
1879 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1881 if (*runscripts == NULL) {
1882 *runscripts = New(alist(10, not_owned_by_alist));
1885 (*runscripts)->append(script);
1890 set_bit(index, res_all.hdr.item_present);