2 Bacula® - The Network Backup Solution
4 Copyright (C) 2000-2008 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 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_device(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 {"plugindirectory", store_dir, ITEM(res_dir.plugin_directory), 0, 0, 0},
118 {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
119 {"piddirectory",store_dir, ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
120 {"subsysdirectory", store_dir, ITEM(res_dir.subsys_directory), 0, 0, 0},
121 {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
122 {"password", store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
123 {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
124 {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
125 {"heartbeatinterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 0},
126 {"tlsauthenticate", store_bool, ITEM(res_dir.tls_authenticate), 0, 0, 0},
127 {"tlsenable", store_bool, ITEM(res_dir.tls_enable), 0, 0, 0},
128 {"tlsrequire", store_bool, ITEM(res_dir.tls_require), 0, 0, 0},
129 {"tlsverifypeer", store_bool, ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
130 {"tlscacertificatefile", store_dir, ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
131 {"tlscacertificatedir", store_dir, ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
132 {"tlscertificate", store_dir, ITEM(res_dir.tls_certfile), 0, 0, 0},
133 {"tlskey", store_dir, ITEM(res_dir.tls_keyfile), 0, 0, 0},
134 {"tlsdhfile", store_dir, ITEM(res_dir.tls_dhfile), 0, 0, 0},
135 {"tlsallowedcn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
136 {"statisticsretention", store_time, ITEM(res_dir.stats_retention), 0, ITEM_DEFAULT, 60*60*24*31*12*5},
137 {NULL, NULL, {0}, 0, 0, 0}
143 * name handler value code flags default_value
145 static RES_ITEM con_items[] = {
146 {"name", store_name, ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
147 {"description", store_str, ITEM(res_con.hdr.desc), 0, 0, 0},
148 {"password", store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
149 {"jobacl", store_acl, ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
150 {"clientacl", store_acl, ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
151 {"storageacl", store_acl, ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
152 {"scheduleacl", store_acl, ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
153 {"runacl", store_acl, ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
154 {"poolacl", store_acl, ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
155 {"commandacl", store_acl, ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
156 {"filesetacl", store_acl, ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
157 {"catalogacl", store_acl, ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
158 {"whereacl", store_acl, ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
159 {"pluginoptionsacl", store_acl, ITEM(res_con.ACL_lists), PluginOptions_ACL, 0, 0},
160 {"tlsauthenticate", store_bool, ITEM(res_con.tls_authenticate), 0, 0, 0},
161 {"tlsenable", store_bool, ITEM(res_con.tls_enable), 0, 0, 0},
162 {"tlsrequire", store_bool, ITEM(res_con.tls_require), 0, 0, 0},
163 {"tlsverifypeer", store_bool, ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
164 {"tlscacertificatefile", store_dir, ITEM(res_con.tls_ca_certfile), 0, 0, 0},
165 {"tlscacertificatedir", store_dir, ITEM(res_con.tls_ca_certdir), 0, 0, 0},
166 {"tlscertificate", store_dir, ITEM(res_con.tls_certfile), 0, 0, 0},
167 {"tlskey", store_dir, ITEM(res_con.tls_keyfile), 0, 0, 0},
168 {"tlsdhfile", store_dir, ITEM(res_con.tls_dhfile), 0, 0, 0},
169 {"tlsallowedcn", store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
170 {NULL, NULL, {0}, 0, 0, 0}
175 * Client or File daemon resource
177 * name handler value code flags default_value
180 static RES_ITEM cli_items[] = {
181 {"name", store_name, ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
182 {"description", store_str, ITEM(res_client.hdr.desc), 0, 0, 0},
183 {"address", store_str, ITEM(res_client.address), 0, ITEM_REQUIRED, 0},
184 {"fdaddress", store_str, ITEM(res_client.address), 0, 0, 0},
185 {"fdport", store_pint, ITEM(res_client.FDport), 0, ITEM_DEFAULT, 9102},
186 {"password", store_password, ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
187 {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
188 {"catalog", store_res, ITEM(res_client.catalog), R_CATALOG, ITEM_REQUIRED, 0},
189 {"fileretention", store_time, ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
190 {"jobretention", store_time, ITEM(res_client.JobRetention), 0, ITEM_DEFAULT, 60*60*24*180},
191 {"heartbeatinterval", store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 0},
192 {"autoprune", store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
193 {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
194 {"tlsauthenticate", store_bool, ITEM(res_client.tls_authenticate), 0, 0, 0},
195 {"tlsenable", store_bool, ITEM(res_client.tls_enable), 0, 0, 0},
196 {"tlsrequire", store_bool, ITEM(res_client.tls_require), 0, 0, 0},
197 {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0},
198 {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0},
199 {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0},
200 {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0},
201 {"tlsallowedcn", store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
202 {NULL, NULL, {0}, 0, 0, 0}
205 /* Storage daemon resource
207 * name handler value code flags default_value
209 static RES_ITEM store_items[] = {
210 {"name", store_name, ITEM(res_store.hdr.name), 0, ITEM_REQUIRED, 0},
211 {"description", store_str, ITEM(res_store.hdr.desc), 0, 0, 0},
212 {"sdport", store_pint, ITEM(res_store.SDport), 0, ITEM_DEFAULT, 9103},
213 {"address", store_str, ITEM(res_store.address), 0, ITEM_REQUIRED, 0},
214 {"sdaddress", store_str, ITEM(res_store.address), 0, 0, 0},
215 {"password", store_password, ITEM(res_store.password), 0, ITEM_REQUIRED, 0},
216 {"sdpassword", store_password, ITEM(res_store.password), 0, 0, 0},
217 {"device", store_device, ITEM(res_store.device), R_DEVICE, ITEM_REQUIRED, 0},
218 {"mediatype", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
219 {"autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
220 {"enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true},
221 {"heartbeatinterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 0},
222 {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
223 {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
224 {"tlsauthenticate", store_bool, ITEM(res_store.tls_authenticate), 0, 0, 0},
225 {"tlsenable", store_bool, ITEM(res_store.tls_enable), 0, 0, 0},
226 {"tlsrequire", store_bool, ITEM(res_store.tls_require), 0, 0, 0},
227 {"tlscacertificatefile", store_dir, ITEM(res_store.tls_ca_certfile), 0, 0, 0},
228 {"tlscacertificatedir", store_dir, ITEM(res_store.tls_ca_certdir), 0, 0, 0},
229 {"tlscertificate", store_dir, ITEM(res_store.tls_certfile), 0, 0, 0},
230 {"tlskey", store_dir, ITEM(res_store.tls_keyfile), 0, 0, 0},
231 {NULL, NULL, {0}, 0, 0, 0}
235 * Catalog Resource Directives
237 * name handler value code flags default_value
239 static RES_ITEM cat_items[] = {
240 {"name", store_name, ITEM(res_cat.hdr.name), 0, ITEM_REQUIRED, 0},
241 {"description", store_str, ITEM(res_cat.hdr.desc), 0, 0, 0},
242 {"address", store_str, ITEM(res_cat.db_address), 0, 0, 0},
243 {"dbaddress", store_str, ITEM(res_cat.db_address), 0, 0, 0},
244 {"dbport", store_pint, ITEM(res_cat.db_port), 0, 0, 0},
245 /* keep this password as store_str for the moment */
246 {"password", store_str, ITEM(res_cat.db_password), 0, 0, 0},
247 {"dbpassword", store_str, ITEM(res_cat.db_password), 0, 0, 0},
248 {"user", store_str, ITEM(res_cat.db_user), 0, 0, 0},
249 {"dbname", store_str, ITEM(res_cat.db_name), 0, ITEM_REQUIRED, 0},
250 {"dbdriver", store_str, ITEM(res_cat.db_driver), 0, 0, 0},
251 {"dbsocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0},
252 /* Turned off for the moment */
253 {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
254 {NULL, NULL, {0}, 0, 0, 0}
258 * Job Resource Directives
260 * name handler value code flags default_value
262 RES_ITEM job_items[] = {
263 {"name", store_name, ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
264 {"description", store_str, ITEM(res_job.hdr.desc), 0, 0, 0},
265 {"type", store_jobtype, ITEM(res_job.JobType), 0, ITEM_REQUIRED, 0},
266 {"level", store_level, ITEM(res_job.JobLevel), 0, 0, 0},
267 {"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
268 {"storage", store_alist_res, ITEM(res_job.storage), R_STORAGE, 0, 0},
269 {"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
270 {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
271 {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
272 {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
273 {"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
274 {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
275 {"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
276 {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
277 {"jobtoverify", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
278 {"jobdefs", store_res, ITEM(res_job.jobdefs), R_JOBDEFS, 0, 0},
279 {"run", store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
280 /* Root of where to restore files */
281 {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
282 {"regexwhere", store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
283 {"stripprefix", store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
284 {"addprefix", store_str, ITEM(res_job.add_prefix), 0, 0, 0},
285 {"addsuffix", store_str, ITEM(res_job.add_suffix), 0, 0, 0},
286 /* Where to find bootstrap during restore */
287 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
288 /* Where to write bootstrap file during backup */
289 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
290 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
291 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
292 {"maxrunschedtime", store_time, ITEM(res_job.MaxRunSchedTime), 0, 0, 0},
293 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
294 /* xxxMaxWaitTime are deprecated */
295 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxRunTime), 0, 0, 0},
296 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxRunTime), 0, 0, 0},
297 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxRunTime), 0, 0, 0},
298 {"fullmaxruntime", store_time, ITEM(res_job.FullMaxRunTime), 0, 0, 0},
299 {"incrementalmaxruntime", store_time, ITEM(res_job.IncMaxRunTime), 0, 0, 0},
300 {"differentialmaxruntime", store_time, ITEM(res_job.DiffMaxRunTime), 0, 0, 0},
301 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
302 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
303 {"maxfullinterval", store_time, ITEM(res_job.MaxFullInterval), 0, 0, 0},
304 {"maxdiffinterval", store_time, ITEM(res_job.MaxDiffInterval), 0, 0, 0},
305 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
306 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
307 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
308 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
309 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
310 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
311 {"optimizejobscheduling",store_bool, ITEM(res_job.OptimizeJobScheduling), 0, ITEM_DEFAULT, false},
312 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
313 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
314 {"spoolsize", store_size, ITEM(res_job.spool_size), 0, 0, 0},
315 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
316 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
317 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
318 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
319 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
320 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
321 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
322 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
323 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
324 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
325 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
326 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
327 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
328 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
329 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
330 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
331 {"usestatistics", store_bool, ITEM(res_job.stats_enabled), 0, 0, 0},
332 {"accurate", store_bool, ITEM(res_job.accurate), 0,0,0},
333 {"allowduplicatejobs", store_bool, ITEM(res_job.AllowDuplicateJobs), 0, ITEM_DEFAULT, false},
334 {"allowhigherduplicates", store_bool, ITEM(res_job.AllowHigherDuplicates), 0, ITEM_DEFAULT, true},
335 {"cancelqueuedduplicates", store_bool, ITEM(res_job.CancelQueuedDuplicates), 0, ITEM_DEFAULT, true},
336 {"cancelrunningduplicates", store_bool, ITEM(res_job.CancelRunningDuplicates), 0, ITEM_DEFAULT, false},
337 {"pluginoptions", store_str, ITEM(res_job.PluginOptions), 0, 0, 0},
338 {NULL, NULL, {0}, 0, 0, 0}
343 * name handler value code flags default_value
345 static RES_ITEM fs_items[] = {
346 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
347 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
348 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
349 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
350 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
351 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
352 {NULL, NULL, {0}, 0, 0, 0}
355 /* Schedule -- see run_conf.c */
358 * name handler value code flags default_value
360 static RES_ITEM sch_items[] = {
361 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
362 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
363 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
364 {NULL, NULL, {0}, 0, 0, 0}
369 * name handler value code flags default_value
371 static RES_ITEM pool_items[] = {
372 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
373 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
374 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
375 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
376 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
377 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
378 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
379 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
380 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
381 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
382 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
383 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
384 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
385 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
386 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
387 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
388 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
389 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
390 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
391 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
392 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
393 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
394 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
395 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
396 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
397 {"recyclepool", store_res, ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
398 {"copypool", store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
399 {"catalog", store_res, ITEM(res_pool.catalog), R_CATALOG, 0, 0},
400 {NULL, NULL, {0}, 0, 0, 0}
405 * name handler value code flags default_value
407 static RES_ITEM counter_items[] = {
408 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
409 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
410 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
411 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
412 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
413 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
414 {NULL, NULL, {0}, 0, 0, 0}
418 /* Message resource */
419 extern RES_ITEM msgs_items[];
422 * This is the master resource definition.
423 * It must have one item for each of the resources.
425 * NOTE!!! keep it in the same order as the R_codes
426 * or eliminate all resources[rindex].name
428 * name items rcode res_head
430 RES_TABLE resources[] = {
431 {"director", dir_items, R_DIRECTOR},
432 {"client", cli_items, R_CLIENT},
433 {"job", job_items, R_JOB},
434 {"storage", store_items, R_STORAGE},
435 {"catalog", cat_items, R_CATALOG},
436 {"schedule", sch_items, R_SCHEDULE},
437 {"fileset", fs_items, R_FILESET},
438 {"pool", pool_items, R_POOL},
439 {"messages", msgs_items, R_MSGS},
440 {"counter", counter_items, R_COUNTER},
441 {"console", con_items, R_CONSOLE},
442 {"jobdefs", job_items, R_JOBDEFS},
443 {"device", NULL, R_DEVICE}, /* info obtained from SD */
448 /* Keywords (RHS) permitted in Job Level records
450 * level_name level job_type
452 struct s_jl joblevels[] = {
453 {"Full", L_FULL, JT_BACKUP},
454 {"Base", L_BASE, JT_BACKUP},
455 {"Incremental", L_INCREMENTAL, JT_BACKUP},
456 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
457 {"Since", L_SINCE, JT_BACKUP},
458 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
459 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
460 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
461 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
462 {"Data", L_VERIFY_DATA, JT_VERIFY},
463 {" ", L_NONE, JT_ADMIN},
464 {" ", L_NONE, JT_RESTORE},
468 /* Keywords (RHS) permitted in Job type records
472 struct s_jt jobtypes[] = {
473 {"backup", JT_BACKUP},
475 {"verify", JT_VERIFY},
476 {"restore", JT_RESTORE},
477 {"migrate", JT_MIGRATE},
483 /* Keywords (RHS) permitted in Selection type records
487 struct s_jt migtypes[] = {
488 {"smallestvolume", MT_SMALLEST_VOL},
489 {"oldestvolume", MT_OLDEST_VOL},
490 {"pooloccupancy", MT_POOL_OCCUPANCY},
491 {"pooltime", MT_POOL_TIME},
492 {"client", MT_CLIENT},
493 {"volume", MT_VOLUME},
495 {"sqlquery", MT_SQLQUERY},
501 /* Options permitted in Restore replace= */
502 struct s_kw ReplaceOptions[] = {
503 {"always", REPLACE_ALWAYS},
504 {"ifnewer", REPLACE_IFNEWER},
505 {"ifolder", REPLACE_IFOLDER},
506 {"never", REPLACE_NEVER},
510 const char *level_to_str(int level)
513 static char level_no[30];
514 const char *str = level_no;
516 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
517 for (i=0; joblevels[i].level_name; i++) {
518 if (level == joblevels[i].level) {
519 str = joblevels[i].level_name;
526 /* Dump contents of resource */
527 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
529 URES *res = (URES *)reshdr;
531 char ed1[100], ed2[100], ed3[100];
535 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
538 if (type < 0) { /* no recursion */
544 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
545 reshdr->name, res->res_dir.MaxConcurrentJobs,
546 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
547 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
548 if (res->res_dir.query_file) {
549 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
551 if (res->res_dir.messages) {
552 sendit(sock, _(" --> "));
553 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
557 sendit(sock, _("Console: name=%s SSL=%d\n"),
558 res->res_con.hdr.name, res->res_con.tls_enable);
561 if (res->res_counter.WrapCounter) {
562 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
563 res->res_counter.hdr.name, res->res_counter.MinValue,
564 res->res_counter.MaxValue, res->res_counter.CurrentValue,
565 res->res_counter.WrapCounter->hdr.name);
567 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
568 res->res_counter.hdr.name, res->res_counter.MinValue,
569 res->res_counter.MaxValue);
571 if (res->res_counter.Catalog) {
572 sendit(sock, _(" --> "));
573 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
578 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
579 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
580 res->res_client.MaxConcurrentJobs);
581 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
582 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
583 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
584 res->res_client.AutoPrune);
585 if (res->res_client.catalog) {
586 sendit(sock, _(" --> "));
587 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
594 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
595 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
596 " poolid=%s volname=%s MediaType=%s\n"),
597 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
598 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
599 dev->offline, dev->autochanger,
600 edit_uint64(dev->PoolId, ed1),
601 dev->VolumeName, dev->MediaType);
605 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
606 " DeviceName=%s MediaType=%s StorageId=%s\n"),
607 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
608 res->res_store.MaxConcurrentJobs,
609 res->res_store.dev_name(),
610 res->res_store.media_type,
611 edit_int64(res->res_store.StorageId, ed1));
615 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
616 " db_driver=%s db_user=%s MutliDBConn=%d\n"),
617 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
618 res->res_cat.db_port, res->res_cat.db_name,
619 NPRT(res->res_cat.db_driver), NPRT(res->res_cat.db_user),
620 res->res_cat.mult_db_connections);
625 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
626 type == R_JOB ? _("Job") : _("JobDefs"),
627 res->res_job.hdr.name, res->res_job.JobType,
628 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
629 res->res_job.enabled);
630 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
631 res->res_job.MaxConcurrentJobs,
632 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
633 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
634 res->res_job.spool_data, res->res_job.write_part_after_job);
635 if (res->res_job.spool_size) {
636 sendit(sock, _(" SpoolSize=%s\n"), edit_uint64(res->res_job.spool_size, ed1));
638 if (res->res_job.stats_enabled) {
639 sendit(sock, _(" StatsEnabled=%d\n"), res->res_job.stats_enabled);
641 if (res->res_job.JobType == JT_BACKUP) {
642 sendit(sock, _(" Accurate=%d\n"), res->res_job.accurate);
644 if (res->res_job.JobType == JT_MIGRATE || res->res_job.JobType == JT_COPY) {
645 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
647 if (res->res_job.client) {
648 sendit(sock, _(" --> "));
649 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
651 if (res->res_job.fileset) {
652 sendit(sock, _(" --> "));
653 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
655 if (res->res_job.schedule) {
656 sendit(sock, _(" --> "));
657 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
659 if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
660 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
662 if (res->res_job.RegexWhere) {
663 sendit(sock, _(" --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
665 if (res->res_job.RestoreBootstrap) {
666 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
668 if (res->res_job.WriteBootstrap) {
669 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
671 if (res->res_job.PluginOptions) {
672 sendit(sock, _(" --> PluginOptions=%s\n"), NPRT(res->res_job.PluginOptions));
674 if (res->res_job.MaxRunTime) {
675 sendit(sock, _(" --> MaxRunTime=%u\n"), res->res_job.MaxRunTime);
677 if (res->res_job.MaxWaitTime) {
678 sendit(sock, _(" --> MaxWaitTime=%u\n"), res->res_job.MaxWaitTime);
680 if (res->res_job.MaxStartDelay) {
681 sendit(sock, _(" --> MaxStartDelay=%u\n"), res->res_job.MaxStartDelay);
683 if (res->res_job.storage) {
685 foreach_alist(store, res->res_job.storage) {
686 sendit(sock, _(" --> "));
687 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
690 if (res->res_job.RunScripts) {
692 foreach_alist(script, res->res_job.RunScripts) {
693 sendit(sock, _(" --> RunScript\n"));
694 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
695 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
696 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
697 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
698 sendit(sock, _(" --> FailJobOnError=%u\n"), script->fail_on_error);
699 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
702 if (res->res_job.pool) {
703 sendit(sock, _(" --> "));
704 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
706 if (res->res_job.full_pool) {
707 sendit(sock, _(" --> "));
708 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
710 if (res->res_job.inc_pool) {
711 sendit(sock, _(" --> "));
712 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
714 if (res->res_job.diff_pool) {
715 sendit(sock, _(" --> "));
716 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
718 if (res->res_job.verify_job) {
719 sendit(sock, _(" --> "));
720 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
722 if (res->res_job.run_cmds) {
724 foreach_alist(runcmd, res->res_job.run_cmds) {
725 sendit(sock, _(" --> Run=%s\n"), runcmd);
728 if (res->res_job.selection_pattern) {
729 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
731 if (res->res_job.messages) {
732 sendit(sock, _(" --> "));
733 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
740 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
741 for (i=0; i<res->res_fs.num_includes; i++) {
742 INCEXE *incexe = res->res_fs.include_items[i];
743 for (j=0; j<incexe->num_opts; j++) {
744 FOPTS *fo = incexe->opts_list[j];
745 sendit(sock, " O %s\n", fo->opts);
747 bool enhanced_wild = false;
748 for (k=0; fo->opts[k]!='\0'; k++) {
749 if (fo->opts[k]=='W') {
750 enhanced_wild = true;
755 for (k=0; k<fo->regex.size(); k++) {
756 sendit(sock, " R %s\n", fo->regex.get(k));
758 for (k=0; k<fo->regexdir.size(); k++) {
759 sendit(sock, " RD %s\n", fo->regexdir.get(k));
761 for (k=0; k<fo->regexfile.size(); k++) {
762 sendit(sock, " RF %s\n", fo->regexfile.get(k));
764 for (k=0; k<fo->wild.size(); k++) {
765 sendit(sock, " W %s\n", fo->wild.get(k));
767 for (k=0; k<fo->wilddir.size(); k++) {
768 sendit(sock, " WD %s\n", fo->wilddir.get(k));
770 for (k=0; k<fo->wildfile.size(); k++) {
771 sendit(sock, " WF %s\n", fo->wildfile.get(k));
773 for (k=0; k<fo->wildbase.size(); k++) {
774 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
776 for (k=0; k<fo->base.size(); k++) {
777 sendit(sock, " B %s\n", fo->base.get(k));
779 for (k=0; k<fo->fstype.size(); k++) {
780 sendit(sock, " X %s\n", fo->fstype.get(k));
782 for (k=0; k<fo->drivetype.size(); k++) {
783 sendit(sock, " XD %s\n", fo->drivetype.get(k));
786 sendit(sock, " G %s\n", fo->plugin);
789 sendit(sock, " D %s\n", fo->reader);
792 sendit(sock, " T %s\n", fo->writer);
794 sendit(sock, " N\n");
796 for (j=0; j<incexe->name_list.size(); j++) {
797 sendit(sock, " I %s\n", incexe->name_list.get(j));
799 if (incexe->name_list.size()) {
800 sendit(sock, " N\n");
802 for (j=0; j<incexe->plugin_list.size(); j++) {
803 sendit(sock, " P %s\n", incexe->plugin_list.get(j));
805 if (incexe->plugin_list.size()) {
806 sendit(sock, " N\n");
811 for (i=0; i<res->res_fs.num_excludes; i++) {
812 INCEXE *incexe = res->res_fs.exclude_items[i];
813 for (j=0; j<incexe->name_list.size(); j++) {
814 sendit(sock, " E %s\n", incexe->name_list.get(j));
816 if (incexe->name_list.size()) {
817 sendit(sock, " N\n");
824 if (res->res_sch.run) {
826 RUN *run = res->res_sch.run;
827 char buf[1000], num[30];
828 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
833 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
834 bstrncpy(buf, _(" hour="), sizeof(buf));
835 for (i=0; i<24; i++) {
836 if (bit_is_set(i, run->hour)) {
837 bsnprintf(num, sizeof(num), "%d ", i);
838 bstrncat(buf, num, sizeof(buf));
841 bstrncat(buf, "\n", sizeof(buf));
843 bstrncpy(buf, _(" mday="), sizeof(buf));
844 for (i=0; i<31; i++) {
845 if (bit_is_set(i, run->mday)) {
846 bsnprintf(num, sizeof(num), "%d ", i);
847 bstrncat(buf, num, sizeof(buf));
850 bstrncat(buf, "\n", sizeof(buf));
852 bstrncpy(buf, _(" month="), sizeof(buf));
853 for (i=0; i<12; i++) {
854 if (bit_is_set(i, run->month)) {
855 bsnprintf(num, sizeof(num), "%d ", i);
856 bstrncat(buf, num, sizeof(buf));
859 bstrncat(buf, "\n", sizeof(buf));
861 bstrncpy(buf, _(" wday="), sizeof(buf));
862 for (i=0; i<7; i++) {
863 if (bit_is_set(i, run->wday)) {
864 bsnprintf(num, sizeof(num), "%d ", i);
865 bstrncat(buf, num, sizeof(buf));
868 bstrncat(buf, "\n", sizeof(buf));
870 bstrncpy(buf, _(" wom="), sizeof(buf));
871 for (i=0; i<5; i++) {
872 if (bit_is_set(i, run->wom)) {
873 bsnprintf(num, sizeof(num), "%d ", i);
874 bstrncat(buf, num, sizeof(buf));
877 bstrncat(buf, "\n", sizeof(buf));
879 bstrncpy(buf, _(" woy="), sizeof(buf));
880 for (i=0; i<54; i++) {
881 if (bit_is_set(i, run->woy)) {
882 bsnprintf(num, sizeof(num), "%d ", i);
883 bstrncat(buf, num, sizeof(buf));
886 bstrncat(buf, "\n", sizeof(buf));
888 sendit(sock, _(" mins=%d\n"), run->minute);
890 sendit(sock, _(" --> "));
891 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
894 sendit(sock, _(" --> "));
895 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
898 sendit(sock, _(" --> "));
899 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
901 /* If another Run record is chained in, go print it */
907 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
912 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
913 res->res_pool.pool_type);
914 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
915 res->res_pool.use_catalog, res->res_pool.use_volume_once,
916 res->res_pool.catalog_files);
917 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
918 res->res_pool.max_volumes, res->res_pool.AutoPrune,
919 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
920 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
921 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
922 res->res_pool.Recycle,
923 NPRT(res->res_pool.label_format));
924 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
925 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
926 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d\n"),
927 res->res_pool.recycle_oldest_volume,
928 res->res_pool.purge_oldest_volume);
929 sendit(sock, _(" MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
930 res->res_pool.MaxVolJobs,
931 res->res_pool.MaxVolFiles,
932 edit_uint64(res->res_pool.MaxVolFiles, ed1));
933 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
934 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
935 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
936 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
937 if (res->res_pool.NextPool) {
938 sendit(sock, _(" NextPool=%s\n"), res->res_pool.NextPool->name());
940 if (res->res_pool.RecyclePool) {
941 sendit(sock, _(" RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
943 if (res->res_pool.catalog) {
944 sendit(sock, _(" Catalog=%s\n"), res->res_pool.catalog->name());
946 if (res->res_pool.storage) {
948 foreach_alist(store, res->res_pool.storage) {
949 sendit(sock, _(" --> "));
950 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
953 if (res->res_pool.CopyPool) {
955 foreach_alist(copy, res->res_pool.CopyPool) {
956 sendit(sock, _(" --> "));
957 dump_resource(-R_POOL, (RES *)copy, sendit, sock);
964 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
965 if (res->res_msgs.mail_cmd)
966 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
967 if (res->res_msgs.operator_cmd)
968 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
972 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
975 if (recurse && res->res_dir.hdr.next) {
976 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
981 * Free all the members of an INCEXE structure
983 static void free_incexe(INCEXE *incexe)
985 incexe->name_list.destroy();
986 incexe->plugin_list.destroy();
987 for (int i=0; i<incexe->num_opts; i++) {
988 FOPTS *fopt = incexe->opts_list[i];
989 fopt->regex.destroy();
990 fopt->regexdir.destroy();
991 fopt->regexfile.destroy();
992 fopt->wild.destroy();
993 fopt->wilddir.destroy();
994 fopt->wildfile.destroy();
995 fopt->wildbase.destroy();
996 fopt->base.destroy();
997 fopt->fstype.destroy();
998 fopt->drivetype.destroy();
1010 if (incexe->opts_list) {
1011 free(incexe->opts_list);
1017 * Free memory of resource -- called when daemon terminates.
1018 * NB, we don't need to worry about freeing any references
1019 * to other resources as they will be freed when that
1020 * resource chain is traversed. Mainly we worry about freeing
1021 * allocated strings (names).
1023 void free_resource(RES *sres, int type)
1026 RES *nres; /* next resource if linked */
1027 URES *res = (URES *)sres;
1032 /* common stuff -- free the resource name and description */
1033 nres = (RES *)res->res_dir.hdr.next;
1034 if (res->res_dir.hdr.name) {
1035 free(res->res_dir.hdr.name);
1037 if (res->res_dir.hdr.desc) {
1038 free(res->res_dir.hdr.desc);
1043 if (res->res_dir.working_directory) {
1044 free(res->res_dir.working_directory);
1046 if (res->res_dir.scripts_directory) {
1047 free((char *)res->res_dir.scripts_directory);
1049 if (res->res_dir.plugin_directory) {
1050 free((char *)res->res_dir.plugin_directory);
1052 if (res->res_dir.pid_directory) {
1053 free(res->res_dir.pid_directory);
1055 if (res->res_dir.subsys_directory) {
1056 free(res->res_dir.subsys_directory);
1058 if (res->res_dir.password) {
1059 free(res->res_dir.password);
1061 if (res->res_dir.query_file) {
1062 free(res->res_dir.query_file);
1064 if (res->res_dir.DIRaddrs) {
1065 free_addresses(res->res_dir.DIRaddrs);
1067 if (res->res_dir.tls_ctx) {
1068 free_tls_context(res->res_dir.tls_ctx);
1070 if (res->res_dir.tls_ca_certfile) {
1071 free(res->res_dir.tls_ca_certfile);
1073 if (res->res_dir.tls_ca_certdir) {
1074 free(res->res_dir.tls_ca_certdir);
1076 if (res->res_dir.tls_certfile) {
1077 free(res->res_dir.tls_certfile);
1079 if (res->res_dir.tls_keyfile) {
1080 free(res->res_dir.tls_keyfile);
1082 if (res->res_dir.tls_dhfile) {
1083 free(res->res_dir.tls_dhfile);
1085 if (res->res_dir.tls_allowed_cns) {
1086 delete res->res_dir.tls_allowed_cns;
1093 if (res->res_con.password) {
1094 free(res->res_con.password);
1096 if (res->res_con.tls_ctx) {
1097 free_tls_context(res->res_con.tls_ctx);
1099 if (res->res_con.tls_ca_certfile) {
1100 free(res->res_con.tls_ca_certfile);
1102 if (res->res_con.tls_ca_certdir) {
1103 free(res->res_con.tls_ca_certdir);
1105 if (res->res_con.tls_certfile) {
1106 free(res->res_con.tls_certfile);
1108 if (res->res_con.tls_keyfile) {
1109 free(res->res_con.tls_keyfile);
1111 if (res->res_con.tls_dhfile) {
1112 free(res->res_con.tls_dhfile);
1114 if (res->res_con.tls_allowed_cns) {
1115 delete res->res_con.tls_allowed_cns;
1117 for (int i=0; i<Num_ACL; i++) {
1118 if (res->res_con.ACL_lists[i]) {
1119 delete res->res_con.ACL_lists[i];
1120 res->res_con.ACL_lists[i] = NULL;
1125 if (res->res_client.address) {
1126 free(res->res_client.address);
1128 if (res->res_client.password) {
1129 free(res->res_client.password);
1131 if (res->res_client.tls_ctx) {
1132 free_tls_context(res->res_client.tls_ctx);
1134 if (res->res_client.tls_ca_certfile) {
1135 free(res->res_client.tls_ca_certfile);
1137 if (res->res_client.tls_ca_certdir) {
1138 free(res->res_client.tls_ca_certdir);
1140 if (res->res_client.tls_certfile) {
1141 free(res->res_client.tls_certfile);
1143 if (res->res_client.tls_keyfile) {
1144 free(res->res_client.tls_keyfile);
1146 if (res->res_client.tls_allowed_cns) {
1147 delete res->res_client.tls_allowed_cns;
1151 if (res->res_store.address) {
1152 free(res->res_store.address);
1154 if (res->res_store.password) {
1155 free(res->res_store.password);
1157 if (res->res_store.media_type) {
1158 free(res->res_store.media_type);
1160 if (res->res_store.device) {
1161 delete res->res_store.device;
1163 if (res->res_store.tls_ctx) {
1164 free_tls_context(res->res_store.tls_ctx);
1166 if (res->res_store.tls_ca_certfile) {
1167 free(res->res_store.tls_ca_certfile);
1169 if (res->res_store.tls_ca_certdir) {
1170 free(res->res_store.tls_ca_certdir);
1172 if (res->res_store.tls_certfile) {
1173 free(res->res_store.tls_certfile);
1175 if (res->res_store.tls_keyfile) {
1176 free(res->res_store.tls_keyfile);
1180 if (res->res_cat.db_address) {
1181 free(res->res_cat.db_address);
1183 if (res->res_cat.db_socket) {
1184 free(res->res_cat.db_socket);
1186 if (res->res_cat.db_user) {
1187 free(res->res_cat.db_user);
1189 if (res->res_cat.db_name) {
1190 free(res->res_cat.db_name);
1192 if (res->res_cat.db_driver) {
1193 free(res->res_cat.db_driver);
1195 if (res->res_cat.db_password) {
1196 free(res->res_cat.db_password);
1200 if ((num=res->res_fs.num_includes)) {
1201 while (--num >= 0) {
1202 free_incexe(res->res_fs.include_items[num]);
1204 free(res->res_fs.include_items);
1206 res->res_fs.num_includes = 0;
1207 if ((num=res->res_fs.num_excludes)) {
1208 while (--num >= 0) {
1209 free_incexe(res->res_fs.exclude_items[num]);
1211 free(res->res_fs.exclude_items);
1213 res->res_fs.num_excludes = 0;
1216 if (res->res_pool.pool_type) {
1217 free(res->res_pool.pool_type);
1219 if (res->res_pool.label_format) {
1220 free(res->res_pool.label_format);
1222 if (res->res_pool.cleaning_prefix) {
1223 free(res->res_pool.cleaning_prefix);
1225 if (res->res_pool.storage) {
1226 delete res->res_pool.storage;
1230 if (res->res_sch.run) {
1232 nrun = res->res_sch.run;
1242 if (res->res_job.RestoreWhere) {
1243 free(res->res_job.RestoreWhere);
1245 if (res->res_job.RegexWhere) {
1246 free(res->res_job.RegexWhere);
1248 if (res->res_job.strip_prefix) {
1249 free(res->res_job.strip_prefix);
1251 if (res->res_job.add_prefix) {
1252 free(res->res_job.add_prefix);
1254 if (res->res_job.add_suffix) {
1255 free(res->res_job.add_suffix);
1257 if (res->res_job.RestoreBootstrap) {
1258 free(res->res_job.RestoreBootstrap);
1260 if (res->res_job.WriteBootstrap) {
1261 free(res->res_job.WriteBootstrap);
1263 if (res->res_job.PluginOptions) {
1264 free(res->res_job.PluginOptions);
1266 if (res->res_job.selection_pattern) {
1267 free(res->res_job.selection_pattern);
1269 if (res->res_job.run_cmds) {
1270 delete res->res_job.run_cmds;
1272 if (res->res_job.storage) {
1273 delete res->res_job.storage;
1275 if (res->res_job.RunScripts) {
1276 free_runscripts(res->res_job.RunScripts);
1277 delete res->res_job.RunScripts;
1281 if (res->res_msgs.mail_cmd) {
1282 free(res->res_msgs.mail_cmd);
1284 if (res->res_msgs.operator_cmd) {
1285 free(res->res_msgs.operator_cmd);
1287 free_msgs_res((MSGS *)res); /* free message resource */
1291 printf(_("Unknown resource type %d in free_resource.\n"), type);
1293 /* Common stuff again -- free the resource, recurse to next one */
1298 free_resource(nres, type);
1303 * Save the new resource by chaining it into the head list for
1304 * the resource. If this is pass 2, we update any resource
1305 * pointers because they may not have been defined until
1308 void save_resource(int type, RES_ITEM *items, int pass)
1311 int rindex = type - r_first;
1315 /* Check Job requirements after applying JobDefs */
1316 if (type != R_JOB && type != R_JOBDEFS) {
1318 * Ensure that all required items are present
1320 for (i=0; items[i].name; i++) {
1321 if (items[i].flags & ITEM_REQUIRED) {
1322 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1323 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1324 items[i].name, resources[rindex]);
1327 /* If this triggers, take a look at lib/parse_conf.h */
1328 if (i >= MAX_RES_ITEMS) {
1329 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1332 } else if (type == R_JOB) {
1334 * Ensure that the name item is present
1336 if (items[0].flags & ITEM_REQUIRED) {
1337 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1338 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1339 items[0].name, resources[rindex]);
1345 * During pass 2 in each "store" routine, we looked up pointers
1346 * to all the resources referrenced in the current resource, now we
1347 * must copy their addresses from the static record to the allocated
1352 /* Resources not containing a resource */
1360 * Resources containing another resource or alist. First
1361 * look up the resource which contains another resource. It
1362 * was written during pass 1. Then stuff in the pointers to
1363 * the resources it contains, which were inserted this pass.
1364 * Finally, it will all be stored back.
1367 /* Find resource saved in pass 1 */
1368 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1369 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1371 /* Explicitly copy resource pointers from this pass (res_all) */
1372 res->res_pool.NextPool = res_all.res_pool.NextPool;
1373 res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1374 res->res_pool.storage = res_all.res_pool.storage;
1375 res->res_pool.catalog = res_all.res_pool.catalog;
1378 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1379 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1381 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1384 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1385 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1387 res->res_dir.messages = res_all.res_dir.messages;
1388 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1391 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1392 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1393 res_all.res_dir.hdr.name);
1395 /* we must explicitly copy the device alist pointer */
1396 res->res_store.device = res_all.res_store.device;
1400 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1401 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1402 res_all.res_dir.hdr.name);
1404 res->res_job.messages = res_all.res_job.messages;
1405 res->res_job.schedule = res_all.res_job.schedule;
1406 res->res_job.client = res_all.res_job.client;
1407 res->res_job.fileset = res_all.res_job.fileset;
1408 res->res_job.storage = res_all.res_job.storage;
1409 res->res_job.pool = res_all.res_job.pool;
1410 res->res_job.full_pool = res_all.res_job.full_pool;
1411 res->res_job.inc_pool = res_all.res_job.inc_pool;
1412 res->res_job.diff_pool = res_all.res_job.diff_pool;
1413 res->res_job.verify_job = res_all.res_job.verify_job;
1414 res->res_job.jobdefs = res_all.res_job.jobdefs;
1415 res->res_job.run_cmds = res_all.res_job.run_cmds;
1416 res->res_job.RunScripts = res_all.res_job.RunScripts;
1418 /* TODO: JobDefs where/regexwhere doesn't work well (but this
1419 * is not very useful)
1420 * We have to set_bit(index, res_all.hdr.item_present);
1421 * or something like that
1424 /* we take RegexWhere before all other options */
1425 if (!res->res_job.RegexWhere
1427 (res->res_job.strip_prefix ||
1428 res->res_job.add_suffix ||
1429 res->res_job.add_prefix))
1431 int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1432 res->res_job.add_prefix,
1433 res->res_job.add_suffix);
1434 res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1435 bregexp_build_where(res->res_job.RegexWhere, len,
1436 res->res_job.strip_prefix,
1437 res->res_job.add_prefix,
1438 res->res_job.add_suffix);
1439 /* TODO: test bregexp */
1442 if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1443 free(res->res_job.RestoreWhere);
1444 res->res_job.RestoreWhere = NULL;
1449 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1450 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1452 res->res_counter.Catalog = res_all.res_counter.Catalog;
1453 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1457 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1458 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1460 res->res_client.catalog = res_all.res_client.catalog;
1461 res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1465 * Schedule is a bit different in that it contains a RUN record
1466 * chain which isn't a "named" resource. This chain was linked
1467 * in by run_conf.c during pass 2, so here we jam the pointer
1468 * into the Schedule resource.
1470 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1471 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1473 res->res_sch.run = res_all.res_sch.run;
1476 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1480 /* Note, the resource name was already saved during pass 1,
1481 * so here, we can just release it.
1483 if (res_all.res_dir.hdr.name) {
1484 free(res_all.res_dir.hdr.name);
1485 res_all.res_dir.hdr.name = NULL;
1487 if (res_all.res_dir.hdr.desc) {
1488 free(res_all.res_dir.hdr.desc);
1489 res_all.res_dir.hdr.desc = NULL;
1495 * The following code is only executed during pass 1
1499 size = sizeof(DIRRES);
1502 size = sizeof(CONRES);
1505 size =sizeof(CLIENT);
1508 size = sizeof(STORE);
1518 size = sizeof(FILESET);
1521 size = sizeof(SCHED);
1524 size = sizeof(POOL);
1527 size = sizeof(MSGS);
1530 size = sizeof(COUNTER);
1536 printf(_("Unknown resource type %d in save_resource.\n"), type);
1542 res = (URES *)malloc(size);
1543 memcpy(res, &res_all, size);
1544 if (!res_head[rindex]) {
1545 res_head[rindex] = (RES *)res; /* store first entry */
1546 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1547 res->res_dir.hdr.name, rindex);
1550 if (res->res_dir.hdr.name == NULL) {
1551 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1554 /* Add new res to end of chain */
1555 for (last=next=res_head[rindex]; next; next=next->next) {
1557 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1558 Emsg2(M_ERROR_TERM, 0,
1559 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1560 resources[rindex].name, res->res_dir.hdr.name);
1563 last->next = (RES *)res;
1564 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1565 res->res_dir.hdr.name, rindex, pass);
1571 * Store Device. Note, the resource is created upon the
1572 * first reference. The details of the resource are obtained
1573 * later from the SD.
1575 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1579 int rindex = R_DEVICE - r_first;
1580 int size = sizeof(DEVICE);
1584 token = lex_get_token(lc, T_NAME);
1585 if (!res_head[rindex]) {
1586 res = (URES *)malloc(size);
1587 memset(res, 0, size);
1588 res->res_dev.hdr.name = bstrdup(lc->str);
1589 res_head[rindex] = (RES *)res; /* store first entry */
1590 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1591 res->res_dir.hdr.name, rindex);
1594 /* See if it is already defined */
1595 for (next=res_head[rindex]; next->next; next=next->next) {
1596 if (strcmp(next->name, lc->str) == 0) {
1602 res = (URES *)malloc(size);
1603 memset(res, 0, size);
1604 res->res_dev.hdr.name = bstrdup(lc->str);
1605 next->next = (RES *)res;
1606 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1607 res->res_dir.hdr.name, rindex, pass);
1612 set_bit(index, res_all.hdr.item_present);
1614 store_alist_res(lc, item, index, pass);
1619 * Store Migration/Copy type
1622 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1626 token = lex_get_token(lc, T_NAME);
1627 /* Store the type both pass 1 and pass 2 */
1628 for (i=0; migtypes[i].type_name; i++) {
1629 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1630 *(int *)(item->value) = migtypes[i].job_type;
1636 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1639 set_bit(index, res_all.hdr.item_present);
1645 * Store JobType (backup, verify, restore)
1648 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1652 token = lex_get_token(lc, T_NAME);
1653 /* Store the type both pass 1 and pass 2 */
1654 for (i=0; jobtypes[i].type_name; i++) {
1655 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1656 *(int *)(item->value) = jobtypes[i].job_type;
1662 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1665 set_bit(index, res_all.hdr.item_present);
1669 * Store Job Level (Full, Incremental, ...)
1672 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1676 token = lex_get_token(lc, T_NAME);
1677 /* Store the level pass 2 so that type is defined */
1678 for (i=0; joblevels[i].level_name; i++) {
1679 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1680 *(int *)(item->value) = joblevels[i].level;
1686 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1689 set_bit(index, res_all.hdr.item_present);
1693 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1696 token = lex_get_token(lc, T_NAME);
1697 /* Scan Replacement options */
1698 for (i=0; ReplaceOptions[i].name; i++) {
1699 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1700 *(int *)(item->value) = ReplaceOptions[i].token;
1706 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1709 set_bit(index, res_all.hdr.item_present);
1713 * Store ACL (access control list)
1716 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1721 token = lex_get_token(lc, T_STRING);
1723 if (((alist **)item->value)[item->code] == NULL) {
1724 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1725 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1727 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1728 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1730 token = lex_get_token(lc, T_ALL);
1731 if (token == T_COMMA) {
1732 continue; /* get another ACL */
1736 set_bit(index, res_all.hdr.item_present);
1739 /* We build RunScripts items here */
1740 static RUNSCRIPT res_runscript;
1742 /* Store a runscript->when in a bit field */
1743 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1745 lex_get_token(lc, T_NAME);
1747 if (strcasecmp(lc->str, "before") == 0) {
1748 *(int *)(item->value) = SCRIPT_Before ;
1749 } else if (strcasecmp(lc->str, "after") == 0) {
1750 *(int *)(item->value) = SCRIPT_After;
1751 } else if (strcasecmp(lc->str, "aftervss") == 0) {
1752 *(int *)(item->value) = SCRIPT_AfterVSS;
1753 } else if (strcasecmp(lc->str, "always") == 0) {
1754 *(int *)(item->value) = SCRIPT_Any;
1756 scan_err2(lc, _("Expect %s, got: %s"), "Before, After, AfterVSS or Always", lc->str);
1761 /* Store a runscript->target
1764 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1766 lex_get_token(lc, T_STRING);
1769 if (strcmp(lc->str, "%c") == 0) {
1770 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1771 } else if (strcasecmp(lc->str, "yes") == 0) {
1772 ((RUNSCRIPT*) item->value)->set_target("%c");
1773 } else if (strcasecmp(lc->str, "no") == 0) {
1774 ((RUNSCRIPT*) item->value)->set_target("");
1776 RES *res = GetResWithName(R_CLIENT, lc->str);
1778 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1779 lc->str, lc->line_no, lc->line);
1782 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1789 * Store a runscript->command as a string and runscript->cmd_type as a pointer
1791 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1793 lex_get_token(lc, T_STRING);
1796 Dmsg2(1, "runscript cmd=%s type=%c\n", lc->str, item->code);
1797 POOLMEM *c = get_pool_memory(PM_FNAME);
1798 /* Each runscript command takes 2 entries in commands list */
1799 pm_strcpy(c, lc->str);
1800 ((RUNSCRIPT*) item->value)->commands->prepend(c); /* command line */
1801 ((RUNSCRIPT*) item->value)->commands->prepend((void *)item->code); /* command type */
1806 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1808 lex_get_token(lc, T_STRING);
1809 alist **runscripts = (alist **)(item->value) ;
1812 RUNSCRIPT *script = new_runscript();
1813 script->set_job_code_callback(job_code_callback_filesetname);
1815 script->set_command(lc->str);
1817 /* TODO: remove all script->old_proto with bacula 1.42 */
1819 if (strcmp(item->name, "runbeforejob") == 0) {
1820 script->when = SCRIPT_Before;
1821 script->fail_on_error = true;
1822 script->set_target("");
1824 } else if (strcmp(item->name, "runafterjob") == 0) {
1825 script->when = SCRIPT_After;
1826 script->on_success = true;
1827 script->on_failure = false;
1828 script->set_target("");
1830 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1831 script->old_proto = true;
1832 script->when = SCRIPT_After;
1833 script->set_target("%c");
1834 script->on_success = true;
1835 script->on_failure = false;
1837 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1838 script->old_proto = true;
1839 script->when = SCRIPT_Before;
1840 script->set_target("%c");
1841 script->fail_on_error = true;
1843 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1844 script->when = SCRIPT_After;
1845 script->on_failure = true;
1846 script->on_success = false;
1847 script->set_target("");
1850 if (*runscripts == NULL) {
1851 *runscripts = New(alist(10, not_owned_by_alist));
1854 (*runscripts)->append(script);
1861 /* Store a bool in a bit field without modifing res_all.hdr
1862 * We can also add an option to store_bool to skip res_all.hdr
1864 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1866 lex_get_token(lc, T_NAME);
1867 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1868 *(bool *)(item->value) = true;
1869 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1870 *(bool *)(item->value) = false;
1872 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1878 * new RunScript items
1879 * name handler value code flags default_value
1881 static RES_ITEM runscript_items[] = {
1882 {"command", store_runscript_cmd, {(char **)&res_runscript}, SHELL_CMD, 0, 0},
1883 {"console", store_runscript_cmd, {(char **)&res_runscript}, CONSOLE_CMD, 0, 0},
1884 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1885 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1886 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1887 {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1888 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1889 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1890 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1891 {NULL, NULL, {0}, 0, 0, 0}
1895 * Store RunScript info
1897 * Note, when this routine is called, we are inside a Job
1898 * resource. We treat the RunScript like a sort of
1899 * mini-resource within the Job resource.
1901 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1905 alist **runscripts = (alist **)(item->value) ;
1907 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1909 token = lex_get_token(lc, T_SKIP_EOL);
1911 if (token != T_BOB) {
1912 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1914 /* setting on_success, on_failure, fail_on_error */
1915 res_runscript.reset_default();
1918 res_runscript.commands = New(alist(10, not_owned_by_alist));
1921 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1922 if (token == T_EOB) {
1925 if (token != T_IDENTIFIER) {
1926 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1928 for (i=0; runscript_items[i].name; i++) {
1929 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1930 token = lex_get_token(lc, T_SKIP_EOL);
1931 if (token != T_EQUALS) {
1932 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1935 /* Call item handler */
1936 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1943 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1948 /* run on client by default */
1949 if (res_runscript.target == NULL) {
1950 res_runscript.set_target("%c");
1952 if (*runscripts == NULL) {
1953 *runscripts = New(alist(10, not_owned_by_alist));
1956 * commands list contains 2 values per command
1957 * - POOLMEM command string (ex: /bin/true)
1958 * - int command type (ex: SHELL_CMD)
1960 res_runscript.set_job_code_callback(job_code_callback_filesetname);
1961 while ((c=(char*)res_runscript.commands->pop()) != NULL) {
1962 t = (long)res_runscript.commands->pop();
1963 RUNSCRIPT *script = new_runscript();
1964 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1965 script->command = c;
1966 script->cmd_type = t;
1967 /* target is taken from res_runscript, each runscript object have
1970 script->target = NULL;
1971 script->set_target(res_runscript.target);
1973 (*runscripts)->append(script);
1976 delete res_runscript.commands;
1977 /* setting on_success, on_failure... cleanup target field */
1978 res_runscript.reset_default(true);
1982 set_bit(index, res_all.hdr.item_present);
1985 /* callback function for edit_job_codes */
1986 extern "C" char *job_code_callback_filesetname(JCR *jcr, const char* param)
1988 if (param[0] == 'f') {
1989 return jcr->fileset->name();