2 * Main configuration file parser for Bacula Directors,
3 * some parts may be split into separate files such as
4 * the schedule configuration (run_config.c).
6 * Note, the configuration file parser consists of three parts
8 * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
10 * 2. The generic config scanner in lib/parse_config.c and
12 * These files contain the parser code, some utility
13 * routines, and the common store routines (name, int,
16 * 3. The daemon specific file, which contains the Resource
17 * definitions as well as any specific store routines
18 * for the resource records.
20 * Kern Sibbald, January MM
25 Bacula® - The Network Backup Solution
27 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
29 The main author of Bacula is Kern Sibbald, with contributions from
30 many others, a complete list can be found in the file AUTHORS.
31 This program is Free Software; you can redistribute it and/or
32 modify it under the terms of version two of the GNU General Public
33 License as published by the Free Software Foundation plus additions
34 that are listed in the file LICENSE.
36 This program is distributed in the hope that it will be useful, but
37 WITHOUT ANY WARRANTY; without even the implied warranty of
38 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
39 General Public License for more details.
41 You should have received a copy of the GNU General Public License
42 along with this program; if not, write to the Free Software
43 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
46 Bacula® is a registered trademark of John Walker.
47 The licensor of Bacula is the Free Software Foundation Europe
48 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
49 Switzerland, email:ftf@fsfeurope.org.
55 /* Define the first and last resource ID record
56 * types. Note, these should be unique for each
57 * daemon though not a requirement.
59 int r_first = R_FIRST;
61 static RES *sres_head[R_LAST - R_FIRST + 1];
62 RES **res_head = sres_head;
64 /* Imported subroutines */
65 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
66 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
67 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
70 /* Forward referenced subroutines */
72 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
73 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
74 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
75 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
76 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
77 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
79 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
80 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
81 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
83 /* We build the current resource here as we are
84 * scanning the resource configuration definition,
85 * then move it to allocated memory when the resource
89 extern "C" { // work around visual compiler mangling variables
95 int res_all_size = sizeof(res_all);
98 /* Definition of records permitted within each
99 * resource with the routine to process the record
100 * information. NOTE! quoted names must be in lower case.
105 * name handler value code flags default_value
107 static RES_ITEM dir_items[] = {
108 {"name", store_name, ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
109 {"description", store_str, ITEM(res_dir.hdr.desc), 0, 0, 0},
110 {"messages", store_res, ITEM(res_dir.messages), R_MSGS, 0, 0},
111 {"dirport", store_addresses_port, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
112 {"diraddress", store_addresses_address, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
113 {"diraddresses",store_addresses, ITEM(res_dir.DIRaddrs), 0, ITEM_DEFAULT, 9101},
114 {"queryfile", store_dir, ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
115 {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
116 {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
117 {"piddirectory",store_dir, ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
118 {"subsysdirectory", store_dir, ITEM(res_dir.subsys_directory), 0, 0, 0},
119 {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
120 {"password", store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
121 {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
122 {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
123 {"tlsenable", store_bool, ITEM(res_dir.tls_enable), 0, 0, 0},
124 {"tlsrequire", store_bool, ITEM(res_dir.tls_require), 0, 0, 0},
125 {"tlsverifypeer", store_bool, ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
126 {"tlscacertificatefile", store_dir, ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
127 {"tlscacertificatedir", store_dir, ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
128 {"tlscertificate", store_dir, ITEM(res_dir.tls_certfile), 0, 0, 0},
129 {"tlskey", store_dir, ITEM(res_dir.tls_keyfile), 0, 0, 0},
130 {"tlsdhfile", store_dir, ITEM(res_dir.tls_dhfile), 0, 0, 0},
131 {"tlsallowedcn", store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
132 {NULL, NULL, {0}, 0, 0, 0}
138 * name handler value code flags default_value
140 static RES_ITEM con_items[] = {
141 {"name", store_name, ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
142 {"description", store_str, ITEM(res_con.hdr.desc), 0, 0, 0},
143 {"password", store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
144 {"jobacl", store_acl, ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
145 {"clientacl", store_acl, ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
146 {"storageacl", store_acl, ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
147 {"scheduleacl", store_acl, ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
148 {"runacl", store_acl, ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
149 {"poolacl", store_acl, ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
150 {"commandacl", store_acl, ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
151 {"filesetacl", store_acl, ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
152 {"catalogacl", store_acl, ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
153 {"whereacl", store_acl, ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
154 {"tlsenable", store_bool, ITEM(res_con.tls_enable), 0, 0, 0},
155 {"tlsrequire", store_bool, ITEM(res_con.tls_require), 0, 0, 0},
156 {"tlsverifypeer", store_bool, ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
157 {"tlscacertificatefile", store_dir, ITEM(res_con.tls_ca_certfile), 0, 0, 0},
158 {"tlscacertificatedir", store_dir, ITEM(res_con.tls_ca_certdir), 0, 0, 0},
159 {"tlscertificate", store_dir, ITEM(res_con.tls_certfile), 0, 0, 0},
160 {"tlskey", store_dir, ITEM(res_con.tls_keyfile), 0, 0, 0},
161 {"tlsdhfile", store_dir, ITEM(res_con.tls_dhfile), 0, 0, 0},
162 {"tlsallowedcn", store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
163 {NULL, NULL, {0}, 0, 0, 0}
168 * Client or File daemon resource
170 * name handler value code flags default_value
173 static RES_ITEM cli_items[] = {
174 {"name", store_name, ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
175 {"description", store_str, ITEM(res_client.hdr.desc), 0, 0, 0},
176 {"address", store_str, ITEM(res_client.address), 0, ITEM_REQUIRED, 0},
177 {"fdaddress", store_str, ITEM(res_client.address), 0, 0, 0},
178 {"fdport", store_pint, ITEM(res_client.FDport), 0, ITEM_DEFAULT, 9102},
179 {"password", store_password, ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
180 {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
181 {"catalog", store_res, ITEM(res_client.catalog), R_CATALOG, ITEM_REQUIRED, 0},
182 {"fileretention", store_time, ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
183 {"jobretention", store_time, ITEM(res_client.JobRetention), 0, ITEM_DEFAULT, 60*60*24*180},
184 {"autoprune", store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
185 {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
186 {"tlsenable", store_bool, ITEM(res_client.tls_enable), 0, 0, 0},
187 {"tlsrequire", store_bool, ITEM(res_client.tls_require), 0, 0, 0},
188 {"tlscacertificatefile", store_dir, ITEM(res_client.tls_ca_certfile), 0, 0, 0},
189 {"tlscacertificatedir", store_dir, ITEM(res_client.tls_ca_certdir), 0, 0, 0},
190 {"tlscertificate", store_dir, ITEM(res_client.tls_certfile), 0, 0, 0},
191 {"tlskey", store_dir, ITEM(res_client.tls_keyfile), 0, 0, 0},
192 {NULL, NULL, {0}, 0, 0, 0}
195 /* Storage daemon resource
197 * name handler value code flags default_value
199 static RES_ITEM store_items[] = {
200 {"name", store_name, ITEM(res_store.hdr.name), 0, ITEM_REQUIRED, 0},
201 {"description", store_str, ITEM(res_store.hdr.desc), 0, 0, 0},
202 {"sdport", store_pint, ITEM(res_store.SDport), 0, ITEM_DEFAULT, 9103},
203 {"address", store_str, ITEM(res_store.address), 0, ITEM_REQUIRED, 0},
204 {"sdaddress", store_str, ITEM(res_store.address), 0, 0, 0},
205 {"password", store_password, ITEM(res_store.password), 0, ITEM_REQUIRED, 0},
206 {"sdpassword", store_password, ITEM(res_store.password), 0, 0, 0},
207 {"device", store_device, ITEM(res_store.device), R_DEVICE, ITEM_REQUIRED, 0},
208 {"mediatype", store_strname, ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
209 {"autochanger", store_bool, ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
210 {"enabled", store_bool, ITEM(res_store.enabled), 0, ITEM_DEFAULT, true},
211 {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
212 {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
213 {"tlsenable", store_bool, ITEM(res_store.tls_enable), 0, 0, 0},
214 {"tlsrequire", store_bool, ITEM(res_store.tls_require), 0, 0, 0},
215 {"tlscacertificatefile", store_dir, ITEM(res_store.tls_ca_certfile), 0, 0, 0},
216 {"tlscacertificatedir", store_dir, ITEM(res_store.tls_ca_certdir), 0, 0, 0},
217 {"tlscertificate", store_dir, ITEM(res_store.tls_certfile), 0, 0, 0},
218 {"tlskey", store_dir, ITEM(res_store.tls_keyfile), 0, 0, 0},
219 {NULL, NULL, {0}, 0, 0, 0}
223 * Catalog Resource Directives
225 * name handler value code flags default_value
227 static RES_ITEM cat_items[] = {
228 {"name", store_name, ITEM(res_cat.hdr.name), 0, ITEM_REQUIRED, 0},
229 {"description", store_str, ITEM(res_cat.hdr.desc), 0, 0, 0},
230 {"address", store_str, ITEM(res_cat.db_address), 0, 0, 0},
231 {"dbaddress", store_str, ITEM(res_cat.db_address), 0, 0, 0},
232 {"dbport", store_pint, ITEM(res_cat.db_port), 0, 0, 0},
233 /* keep this password as store_str for the moment */
234 {"password", store_str, ITEM(res_cat.db_password), 0, 0, 0},
235 {"dbpassword", store_str, ITEM(res_cat.db_password), 0, 0, 0},
236 {"user", store_str, ITEM(res_cat.db_user), 0, 0, 0},
237 {"dbname", store_str, ITEM(res_cat.db_name), 0, ITEM_REQUIRED, 0},
238 {"dbsocket", store_str, ITEM(res_cat.db_socket), 0, 0, 0},
239 /* Turned off for the moment */
240 {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
241 {NULL, NULL, {0}, 0, 0, 0}
245 * Job Resource Directives
247 * name handler value code flags default_value
249 RES_ITEM job_items[] = {
250 {"name", store_name, ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
251 {"description", store_str, ITEM(res_job.hdr.desc), 0, 0, 0},
252 {"type", store_jobtype, ITEM(res_job.JobType), 0, ITEM_REQUIRED, 0},
253 {"level", store_level, ITEM(res_job.JobLevel), 0, 0, 0},
254 {"messages", store_res, ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
255 {"storage", store_alist_res, ITEM(res_job.storage), R_STORAGE, 0, 0},
256 {"pool", store_res, ITEM(res_job.pool), R_POOL, ITEM_REQUIRED, 0},
257 {"fullbackuppool", store_res, ITEM(res_job.full_pool), R_POOL, 0, 0},
258 {"incrementalbackuppool", store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
259 {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
260 {"client", store_res, ITEM(res_job.client), R_CLIENT, ITEM_REQUIRED, 0},
261 {"fileset", store_res, ITEM(res_job.fileset), R_FILESET, ITEM_REQUIRED, 0},
262 {"schedule", store_res, ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
263 {"verifyjob", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
264 {"jobtoverify", store_res, ITEM(res_job.verify_job), R_JOB, 0, 0},
265 {"jobdefs", store_res, ITEM(res_job.jobdefs), R_JOBDEFS, 0, 0},
266 {"run", store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
267 /* Root of where to restore files */
268 {"where", store_dir, ITEM(res_job.RestoreWhere), 0, 0, 0},
269 /* Where to find bootstrap during restore */
270 {"bootstrap",store_dir, ITEM(res_job.RestoreBootstrap), 0, 0, 0},
271 /* Where to write bootstrap file during backup */
272 {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
273 {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
274 {"replace", store_replace, ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
275 {"maxruntime", store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
276 {"fullmaxwaittime", store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
277 {"incrementalmaxwaittime", store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
278 {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
279 {"maxwaittime", store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
280 {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
281 {"jobretention", store_time, ITEM(res_job.JobRetention), 0, 0, 0},
282 {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
283 {"prunejobs", store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
284 {"prunefiles", store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
285 {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
286 {"enabled", store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
287 {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
288 {"spooldata", store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
289 {"rerunfailedlevels", store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
290 {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
291 {"runbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
292 {"runafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
293 {"runafterfailedjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
294 {"clientrunbeforejob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
295 {"clientrunafterjob", store_short_runscript, ITEM(res_job.RunScripts), 0, 0, 0},
296 {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
297 {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
298 {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
299 {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
300 {"priority", store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
301 {"writepartafterjob", store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
302 {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
303 {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
304 {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
305 {NULL, NULL, {0}, 0, 0, 0}
310 * name handler value code flags default_value
312 static RES_ITEM fs_items[] = {
313 {"name", store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
314 {"description", store_str, ITEM(res_fs.hdr.desc), 0, 0, 0},
315 {"include", store_inc, {0}, 0, ITEM_NO_EQUALS, 0},
316 {"exclude", store_inc, {0}, 1, ITEM_NO_EQUALS, 0},
317 {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
318 {"enablevss", store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
319 {NULL, NULL, {0}, 0, 0, 0}
322 /* Schedule -- see run_conf.c */
325 * name handler value code flags default_value
327 static RES_ITEM sch_items[] = {
328 {"name", store_name, ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
329 {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
330 {"run", store_run, ITEM(res_sch.run), 0, 0, 0},
331 {NULL, NULL, {0}, 0, 0, 0}
336 * name handler value code flags default_value
338 static RES_ITEM pool_items[] = {
339 {"name", store_name, ITEM(res_pool.hdr.name), 0, ITEM_REQUIRED, 0},
340 {"description", store_str, ITEM(res_pool.hdr.desc), 0, 0, 0},
341 {"pooltype", store_strname, ITEM(res_pool.pool_type), 0, ITEM_REQUIRED, 0},
342 {"labelformat", store_strname, ITEM(res_pool.label_format), 0, 0, 0},
343 {"labeltype", store_label, ITEM(res_pool.LabelType), 0, 0, 0},
344 {"cleaningprefix", store_strname, ITEM(res_pool.cleaning_prefix), 0, 0, 0},
345 {"usecatalog", store_bool, ITEM(res_pool.use_catalog), 0, ITEM_DEFAULT, true},
346 {"usevolumeonce", store_bool, ITEM(res_pool.use_volume_once), 0, 0, 0},
347 {"purgeoldestvolume", store_bool, ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
348 {"recycleoldestvolume", store_bool, ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
349 {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
350 {"maximumvolumes", store_pint, ITEM(res_pool.max_volumes), 0, 0, 0},
351 {"maximumvolumejobs", store_pint, ITEM(res_pool.MaxVolJobs), 0, 0, 0},
352 {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles), 0, 0, 0},
353 {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes), 0, 0, 0},
354 {"catalogfiles", store_bool, ITEM(res_pool.catalog_files), 0, ITEM_DEFAULT, true},
355 {"volumeretention", store_time, ITEM(res_pool.VolRetention), 0, ITEM_DEFAULT, 60*60*24*365},
356 {"volumeuseduration", store_time, ITEM(res_pool.VolUseDuration), 0, 0, 0},
357 {"migrationtime", store_time, ITEM(res_pool.MigrationTime), 0, 0, 0},
358 {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
359 {"migrationlowbytes", store_size, ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
360 {"nextpool", store_res, ITEM(res_pool.NextPool), R_POOL, 0, 0},
361 {"storage", store_alist_res, ITEM(res_pool.storage), R_STORAGE, 0, 0},
362 {"autoprune", store_bool, ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
363 {"recycle", store_bool, ITEM(res_pool.Recycle), 0, ITEM_DEFAULT, true},
364 {NULL, NULL, {0}, 0, 0, 0}
369 * name handler value code flags default_value
371 static RES_ITEM counter_items[] = {
372 {"name", store_name, ITEM(res_counter.hdr.name), 0, ITEM_REQUIRED, 0},
373 {"description", store_str, ITEM(res_counter.hdr.desc), 0, 0, 0},
374 {"minimum", store_int, ITEM(res_counter.MinValue), 0, ITEM_DEFAULT, 0},
375 {"maximum", store_pint, ITEM(res_counter.MaxValue), 0, ITEM_DEFAULT, INT32_MAX},
376 {"wrapcounter", store_res, ITEM(res_counter.WrapCounter), R_COUNTER, 0, 0},
377 {"catalog", store_res, ITEM(res_counter.Catalog), R_CATALOG, 0, 0},
378 {NULL, NULL, {0}, 0, 0, 0}
382 /* Message resource */
383 extern RES_ITEM msgs_items[];
386 * This is the master resource definition.
387 * It must have one item for each of the resources.
389 * NOTE!!! keep it in the same order as the R_codes
390 * or eliminate all resources[rindex].name
392 * name items rcode res_head
394 RES_TABLE resources[] = {
395 {"director", dir_items, R_DIRECTOR},
396 {"client", cli_items, R_CLIENT},
397 {"job", job_items, R_JOB},
398 {"storage", store_items, R_STORAGE},
399 {"catalog", cat_items, R_CATALOG},
400 {"schedule", sch_items, R_SCHEDULE},
401 {"fileset", fs_items, R_FILESET},
402 {"pool", pool_items, R_POOL},
403 {"messages", msgs_items, R_MSGS},
404 {"counter", counter_items, R_COUNTER},
405 {"console", con_items, R_CONSOLE},
406 {"jobdefs", job_items, R_JOBDEFS},
407 {"device", NULL, R_DEVICE}, /* info obtained from SD */
412 /* Keywords (RHS) permitted in Job Level records
414 * level_name level job_type
416 struct s_jl joblevels[] = {
417 {"Full", L_FULL, JT_BACKUP},
418 {"Base", L_BASE, JT_BACKUP},
419 {"Incremental", L_INCREMENTAL, JT_BACKUP},
420 {"Differential", L_DIFFERENTIAL, JT_BACKUP},
421 {"Since", L_SINCE, JT_BACKUP},
422 {"Catalog", L_VERIFY_CATALOG, JT_VERIFY},
423 {"InitCatalog", L_VERIFY_INIT, JT_VERIFY},
424 {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY},
425 {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY},
426 {"Data", L_VERIFY_DATA, JT_VERIFY},
427 {" ", L_NONE, JT_ADMIN},
428 {" ", L_NONE, JT_RESTORE},
432 /* Keywords (RHS) permitted in Job type records
436 struct s_jt jobtypes[] = {
437 {"backup", JT_BACKUP},
439 {"verify", JT_VERIFY},
440 {"restore", JT_RESTORE},
441 {"migrate", JT_MIGRATE},
446 /* Keywords (RHS) permitted in Selection type records
450 struct s_jt migtypes[] = {
451 {"smallestvolume", MT_SMALLEST_VOL},
452 {"oldestvolume", MT_OLDEST_VOL},
453 {"pooloccupancy", MT_POOL_OCCUPANCY},
454 {"pooltime", MT_POOL_TIME},
455 {"client", MT_CLIENT},
456 {"volume", MT_VOLUME},
458 {"sqlquery", MT_SQLQUERY},
464 /* Options permitted in Restore replace= */
465 struct s_kw ReplaceOptions[] = {
466 {"always", REPLACE_ALWAYS},
467 {"ifnewer", REPLACE_IFNEWER},
468 {"ifolder", REPLACE_IFOLDER},
469 {"never", REPLACE_NEVER},
473 const char *level_to_str(int level)
476 static char level_no[30];
477 const char *str = level_no;
479 bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
480 for (i=0; joblevels[i].level_name; i++) {
481 if (level == joblevels[i].level) {
482 str = joblevels[i].level_name;
489 /* Dump contents of resource */
490 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
492 URES *res = (URES *)reshdr;
494 char ed1[100], ed2[100], ed3[100];
498 sendit(sock, _("No %s resource defined\n"), res_to_str(type));
501 if (type < 0) { /* no recursion */
507 sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
508 reshdr->name, res->res_dir.MaxConcurrentJobs,
509 edit_uint64(res->res_dir.FDConnectTimeout, ed1),
510 edit_uint64(res->res_dir.SDConnectTimeout, ed2));
511 if (res->res_dir.query_file) {
512 sendit(sock, _(" query_file=%s\n"), res->res_dir.query_file);
514 if (res->res_dir.messages) {
515 sendit(sock, _(" --> "));
516 dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
520 sendit(sock, _("Console: name=%s SSL=%d\n"),
521 res->res_con.hdr.name, res->res_con.tls_enable);
524 if (res->res_counter.WrapCounter) {
525 sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
526 res->res_counter.hdr.name, res->res_counter.MinValue,
527 res->res_counter.MaxValue, res->res_counter.CurrentValue,
528 res->res_counter.WrapCounter->hdr.name);
530 sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
531 res->res_counter.hdr.name, res->res_counter.MinValue,
532 res->res_counter.MaxValue);
534 if (res->res_counter.Catalog) {
535 sendit(sock, _(" --> "));
536 dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
541 sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
542 res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
543 res->res_client.MaxConcurrentJobs);
544 sendit(sock, _(" JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
545 edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
546 edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
547 res->res_client.AutoPrune);
548 if (res->res_client.catalog) {
549 sendit(sock, _(" --> "));
550 dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
556 sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
557 " reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
558 " poolid=%s volname=%s MediaType=%s\n"),
559 dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
560 dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
561 dev->offline, dev->autochanger,
562 edit_uint64(dev->PoolId, ed1),
563 dev->VolumeName, dev->MediaType);
566 sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
567 " DeviceName=%s MediaType=%s StorageId=%s\n"),
568 res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
569 res->res_store.MaxConcurrentJobs,
570 res->res_store.dev_name(),
571 res->res_store.media_type,
572 edit_int64(res->res_store.StorageId, ed1));
575 sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
576 " db_user=%s MutliDBConn=%d\n"),
577 res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
578 res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
579 res->res_cat.mult_db_connections);
583 sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
584 type == R_JOB ? _("Job") : _("JobDefs"),
585 res->res_job.hdr.name, res->res_job.JobType,
586 level_to_str(res->res_job.JobLevel), res->res_job.Priority,
587 res->res_job.enabled);
588 sendit(sock, _(" MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
589 res->res_job.MaxConcurrentJobs,
590 res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
591 edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
592 res->res_job.spool_data, res->res_job.write_part_after_job);
593 if (res->res_job.JobType == JT_MIGRATE) {
594 sendit(sock, _(" SelectionType=%d\n"), res->res_job.selection_type);
596 if (res->res_job.client) {
597 sendit(sock, _(" --> "));
598 dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
600 if (res->res_job.fileset) {
601 sendit(sock, _(" --> "));
602 dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
604 if (res->res_job.schedule) {
605 sendit(sock, _(" --> "));
606 dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
608 if (res->res_job.RestoreWhere) {
609 sendit(sock, _(" --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
611 if (res->res_job.RestoreBootstrap) {
612 sendit(sock, _(" --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
614 if (res->res_job.WriteBootstrap) {
615 sendit(sock, _(" --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
617 if (res->res_job.storage) {
619 foreach_alist(store, res->res_job.storage) {
620 sendit(sock, _(" --> "));
621 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
624 if (res->res_job.RunScripts) {
626 foreach_alist(script, res->res_job.RunScripts) {
627 sendit(sock, _(" --> RunScript\n"));
628 sendit(sock, _(" --> Command=%s\n"), NPRT(script->command));
629 sendit(sock, _(" --> Target=%s\n"), NPRT(script->target));
630 sendit(sock, _(" --> RunOnSuccess=%u\n"), script->on_success);
631 sendit(sock, _(" --> RunOnFailure=%u\n"), script->on_failure);
632 sendit(sock, _(" --> AbortJobOnError=%u\n"), script->abort_on_error);
633 sendit(sock, _(" --> RunWhen=%u\n"), script->when);
636 if (res->res_job.pool) {
637 sendit(sock, _(" --> "));
638 dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
640 if (res->res_job.full_pool) {
641 sendit(sock, _(" --> "));
642 dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
644 if (res->res_job.inc_pool) {
645 sendit(sock, _(" --> "));
646 dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
648 if (res->res_job.diff_pool) {
649 sendit(sock, _(" --> "));
650 dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
652 if (res->res_job.verify_job) {
653 sendit(sock, _(" --> "));
654 dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
656 if (res->res_job.run_cmds) {
658 foreach_alist(runcmd, res->res_job.run_cmds) {
659 sendit(sock, _(" --> Run=%s\n"), runcmd);
662 if (res->res_job.selection_pattern) {
663 sendit(sock, _(" --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
665 if (res->res_job.messages) {
666 sendit(sock, _(" --> "));
667 dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
673 sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
674 for (i=0; i<res->res_fs.num_includes; i++) {
675 INCEXE *incexe = res->res_fs.include_items[i];
676 for (j=0; j<incexe->num_opts; j++) {
677 FOPTS *fo = incexe->opts_list[j];
678 sendit(sock, " O %s\n", fo->opts);
680 bool enhanced_wild = false;
681 for (k=0; fo->opts[k]!='\0'; k++) {
682 if (fo->opts[k]=='W') {
683 enhanced_wild = true;
688 for (k=0; k<fo->regex.size(); k++) {
689 sendit(sock, " R %s\n", fo->regex.get(k));
691 for (k=0; k<fo->regexdir.size(); k++) {
692 sendit(sock, " RD %s\n", fo->regexdir.get(k));
694 for (k=0; k<fo->regexfile.size(); k++) {
695 sendit(sock, " RF %s\n", fo->regexfile.get(k));
697 for (k=0; k<fo->wild.size(); k++) {
698 sendit(sock, " W %s\n", fo->wild.get(k));
700 for (k=0; k<fo->wilddir.size(); k++) {
701 sendit(sock, " WD %s\n", fo->wilddir.get(k));
703 for (k=0; k<fo->wildfile.size(); k++) {
704 sendit(sock, " WF %s\n", fo->wildfile.get(k));
706 for (k=0; k<fo->wildbase.size(); k++) {
707 sendit(sock, " W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
709 for (k=0; k<fo->base.size(); k++) {
710 sendit(sock, " B %s\n", fo->base.get(k));
712 for (k=0; k<fo->fstype.size(); k++) {
713 sendit(sock, " X %s\n", fo->fstype.get(k));
715 for (k=0; k<fo->drivetype.size(); k++) {
716 sendit(sock, " XD %s\n", fo->drivetype.get(k));
719 sendit(sock, " D %s\n", fo->reader);
722 sendit(sock, " T %s\n", fo->writer);
724 sendit(sock, " N\n");
726 for (j=0; j<incexe->name_list.size(); j++) {
727 sendit(sock, " I %s\n", incexe->name_list.get(j));
729 if (incexe->name_list.size()) {
730 sendit(sock, " N\n");
734 for (i=0; i<res->res_fs.num_excludes; i++) {
735 INCEXE *incexe = res->res_fs.exclude_items[i];
736 for (j=0; j<incexe->name_list.size(); j++) {
737 sendit(sock, " E %s\n", incexe->name_list.get(j));
739 if (incexe->name_list.size()) {
740 sendit(sock, " N\n");
746 if (res->res_sch.run) {
748 RUN *run = res->res_sch.run;
749 char buf[1000], num[30];
750 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
755 sendit(sock, _(" --> Run Level=%s\n"), level_to_str(run->level));
756 bstrncpy(buf, _(" hour="), sizeof(buf));
757 for (i=0; i<24; i++) {
758 if (bit_is_set(i, run->hour)) {
759 bsnprintf(num, sizeof(num), "%d ", i);
760 bstrncat(buf, num, sizeof(buf));
763 bstrncat(buf, "\n", sizeof(buf));
765 bstrncpy(buf, _(" mday="), sizeof(buf));
766 for (i=0; i<31; i++) {
767 if (bit_is_set(i, run->mday)) {
768 bsnprintf(num, sizeof(num), "%d ", i);
769 bstrncat(buf, num, sizeof(buf));
772 bstrncat(buf, "\n", sizeof(buf));
774 bstrncpy(buf, _(" month="), sizeof(buf));
775 for (i=0; i<12; i++) {
776 if (bit_is_set(i, run->month)) {
777 bsnprintf(num, sizeof(num), "%d ", i);
778 bstrncat(buf, num, sizeof(buf));
781 bstrncat(buf, "\n", sizeof(buf));
783 bstrncpy(buf, _(" wday="), sizeof(buf));
784 for (i=0; i<7; i++) {
785 if (bit_is_set(i, run->wday)) {
786 bsnprintf(num, sizeof(num), "%d ", i);
787 bstrncat(buf, num, sizeof(buf));
790 bstrncat(buf, "\n", sizeof(buf));
792 bstrncpy(buf, _(" wom="), sizeof(buf));
793 for (i=0; i<5; i++) {
794 if (bit_is_set(i, run->wom)) {
795 bsnprintf(num, sizeof(num), "%d ", i);
796 bstrncat(buf, num, sizeof(buf));
799 bstrncat(buf, "\n", sizeof(buf));
801 bstrncpy(buf, _(" woy="), sizeof(buf));
802 for (i=0; i<54; i++) {
803 if (bit_is_set(i, run->woy)) {
804 bsnprintf(num, sizeof(num), "%d ", i);
805 bstrncat(buf, num, sizeof(buf));
808 bstrncat(buf, "\n", sizeof(buf));
810 sendit(sock, _(" mins=%d\n"), run->minute);
812 sendit(sock, _(" --> "));
813 dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
816 sendit(sock, _(" --> "));
817 dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
820 sendit(sock, _(" --> "));
821 dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
823 /* If another Run record is chained in, go print it */
829 sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
833 sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
834 res->res_pool.pool_type);
835 sendit(sock, _(" use_cat=%d use_once=%d cat_files=%d\n"),
836 res->res_pool.use_catalog, res->res_pool.use_volume_once,
837 res->res_pool.catalog_files);
838 sendit(sock, _(" max_vols=%d auto_prune=%d VolRetention=%s\n"),
839 res->res_pool.max_volumes, res->res_pool.AutoPrune,
840 edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
841 sendit(sock, _(" VolUse=%s recycle=%d LabelFormat=%s\n"),
842 edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
843 res->res_pool.Recycle,
844 NPRT(res->res_pool.label_format));
845 sendit(sock, _(" CleaningPrefix=%s LabelType=%d\n"),
846 NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
847 sendit(sock, _(" RecyleOldest=%d PurgeOldest=%d MaxVolJobs=%d MaxVolFiles=%d\n"),
848 res->res_pool.recycle_oldest_volume,
849 res->res_pool.purge_oldest_volume,
850 res->res_pool.MaxVolJobs, res->res_pool.MaxVolFiles);
851 sendit(sock, _(" MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
852 edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
853 edit_uint64(res->res_pool.MigrationHighBytes, ed2),
854 edit_uint64(res->res_pool.MigrationLowBytes, ed3));
855 if (res->res_pool.NextPool) {
856 sendit(sock, _(" --> "));
857 dump_resource(-R_POOL, (RES *)res->res_pool.NextPool, sendit, sock);
859 if (res->res_pool.storage) {
861 foreach_alist(store, res->res_pool.storage) {
862 sendit(sock, _(" --> "));
863 dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
868 sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
869 if (res->res_msgs.mail_cmd)
870 sendit(sock, _(" mailcmd=%s\n"), res->res_msgs.mail_cmd);
871 if (res->res_msgs.operator_cmd)
872 sendit(sock, _(" opcmd=%s\n"), res->res_msgs.operator_cmd);
875 sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
878 if (recurse && res->res_dir.hdr.next) {
879 dump_resource(type, res->res_dir.hdr.next, sendit, sock);
884 * Free all the members of an INCEXE structure
886 static void free_incexe(INCEXE *incexe)
888 incexe->name_list.destroy();
889 for (int i=0; i<incexe->num_opts; i++) {
890 FOPTS *fopt = incexe->opts_list[i];
891 fopt->regex.destroy();
892 fopt->regexdir.destroy();
893 fopt->regexfile.destroy();
894 fopt->wild.destroy();
895 fopt->wilddir.destroy();
896 fopt->wildfile.destroy();
897 fopt->wildbase.destroy();
898 fopt->base.destroy();
899 fopt->fstype.destroy();
900 fopt->drivetype.destroy();
909 if (incexe->opts_list) {
910 free(incexe->opts_list);
916 * Free memory of resource -- called when daemon terminates.
917 * NB, we don't need to worry about freeing any references
918 * to other resources as they will be freed when that
919 * resource chain is traversed. Mainly we worry about freeing
920 * allocated strings (names).
922 void free_resource(RES *sres, int type)
925 RES *nres; /* next resource if linked */
926 URES *res = (URES *)sres;
931 /* common stuff -- free the resource name and description */
932 nres = (RES *)res->res_dir.hdr.next;
933 if (res->res_dir.hdr.name) {
934 free(res->res_dir.hdr.name);
936 if (res->res_dir.hdr.desc) {
937 free(res->res_dir.hdr.desc);
942 if (res->res_dir.working_directory) {
943 free(res->res_dir.working_directory);
945 if (res->res_dir.scripts_directory) {
946 free((char *)res->res_dir.scripts_directory);
948 if (res->res_dir.pid_directory) {
949 free(res->res_dir.pid_directory);
951 if (res->res_dir.subsys_directory) {
952 free(res->res_dir.subsys_directory);
954 if (res->res_dir.password) {
955 free(res->res_dir.password);
957 if (res->res_dir.query_file) {
958 free(res->res_dir.query_file);
960 if (res->res_dir.DIRaddrs) {
961 free_addresses(res->res_dir.DIRaddrs);
963 if (res->res_dir.tls_ctx) {
964 free_tls_context(res->res_dir.tls_ctx);
966 if (res->res_dir.tls_ca_certfile) {
967 free(res->res_dir.tls_ca_certfile);
969 if (res->res_dir.tls_ca_certdir) {
970 free(res->res_dir.tls_ca_certdir);
972 if (res->res_dir.tls_certfile) {
973 free(res->res_dir.tls_certfile);
975 if (res->res_dir.tls_keyfile) {
976 free(res->res_dir.tls_keyfile);
978 if (res->res_dir.tls_dhfile) {
979 free(res->res_dir.tls_dhfile);
981 if (res->res_dir.tls_allowed_cns) {
982 delete res->res_dir.tls_allowed_cns;
989 if (res->res_con.password) {
990 free(res->res_con.password);
992 if (res->res_con.tls_ctx) {
993 free_tls_context(res->res_con.tls_ctx);
995 if (res->res_con.tls_ca_certfile) {
996 free(res->res_con.tls_ca_certfile);
998 if (res->res_con.tls_ca_certdir) {
999 free(res->res_con.tls_ca_certdir);
1001 if (res->res_con.tls_certfile) {
1002 free(res->res_con.tls_certfile);
1004 if (res->res_con.tls_keyfile) {
1005 free(res->res_con.tls_keyfile);
1007 if (res->res_con.tls_dhfile) {
1008 free(res->res_con.tls_dhfile);
1010 if (res->res_con.tls_allowed_cns) {
1011 delete res->res_con.tls_allowed_cns;
1013 for (int i=0; i<Num_ACL; i++) {
1014 if (res->res_con.ACL_lists[i]) {
1015 delete res->res_con.ACL_lists[i];
1016 res->res_con.ACL_lists[i] = NULL;
1021 if (res->res_client.address) {
1022 free(res->res_client.address);
1024 if (res->res_client.password) {
1025 free(res->res_client.password);
1027 if (res->res_client.tls_ctx) {
1028 free_tls_context(res->res_client.tls_ctx);
1030 if (res->res_client.tls_ca_certfile) {
1031 free(res->res_client.tls_ca_certfile);
1033 if (res->res_client.tls_ca_certdir) {
1034 free(res->res_client.tls_ca_certdir);
1036 if (res->res_client.tls_certfile) {
1037 free(res->res_client.tls_certfile);
1039 if (res->res_client.tls_keyfile) {
1040 free(res->res_client.tls_keyfile);
1044 if (res->res_store.address) {
1045 free(res->res_store.address);
1047 if (res->res_store.password) {
1048 free(res->res_store.password);
1050 if (res->res_store.media_type) {
1051 free(res->res_store.media_type);
1053 if (res->res_store.device) {
1054 delete res->res_store.device;
1056 if (res->res_store.tls_ctx) {
1057 free_tls_context(res->res_store.tls_ctx);
1059 if (res->res_store.tls_ca_certfile) {
1060 free(res->res_store.tls_ca_certfile);
1062 if (res->res_store.tls_ca_certdir) {
1063 free(res->res_store.tls_ca_certdir);
1065 if (res->res_store.tls_certfile) {
1066 free(res->res_store.tls_certfile);
1068 if (res->res_store.tls_keyfile) {
1069 free(res->res_store.tls_keyfile);
1073 if (res->res_cat.db_address) {
1074 free(res->res_cat.db_address);
1076 if (res->res_cat.db_socket) {
1077 free(res->res_cat.db_socket);
1079 if (res->res_cat.db_user) {
1080 free(res->res_cat.db_user);
1082 if (res->res_cat.db_name) {
1083 free(res->res_cat.db_name);
1085 if (res->res_cat.db_password) {
1086 free(res->res_cat.db_password);
1090 if ((num=res->res_fs.num_includes)) {
1091 while (--num >= 0) {
1092 free_incexe(res->res_fs.include_items[num]);
1094 free(res->res_fs.include_items);
1096 res->res_fs.num_includes = 0;
1097 if ((num=res->res_fs.num_excludes)) {
1098 while (--num >= 0) {
1099 free_incexe(res->res_fs.exclude_items[num]);
1101 free(res->res_fs.exclude_items);
1103 res->res_fs.num_excludes = 0;
1106 if (res->res_pool.pool_type) {
1107 free(res->res_pool.pool_type);
1109 if (res->res_pool.label_format) {
1110 free(res->res_pool.label_format);
1112 if (res->res_pool.cleaning_prefix) {
1113 free(res->res_pool.cleaning_prefix);
1115 if (res->res_pool.storage) {
1116 delete res->res_pool.storage;
1120 if (res->res_sch.run) {
1122 nrun = res->res_sch.run;
1132 if (res->res_job.RestoreWhere) {
1133 free(res->res_job.RestoreWhere);
1135 if (res->res_job.RestoreBootstrap) {
1136 free(res->res_job.RestoreBootstrap);
1138 if (res->res_job.WriteBootstrap) {
1139 free(res->res_job.WriteBootstrap);
1141 if (res->res_job.selection_pattern) {
1142 free(res->res_job.selection_pattern);
1144 if (res->res_job.run_cmds) {
1145 delete res->res_job.run_cmds;
1147 if (res->res_job.storage) {
1148 delete res->res_job.storage;
1150 if (res->res_job.RunScripts) {
1151 free_runscripts(res->res_job.RunScripts);
1152 delete res->res_job.RunScripts;
1156 if (res->res_msgs.mail_cmd) {
1157 free(res->res_msgs.mail_cmd);
1159 if (res->res_msgs.operator_cmd) {
1160 free(res->res_msgs.operator_cmd);
1162 free_msgs_res((MSGS *)res); /* free message resource */
1166 printf(_("Unknown resource type %d in free_resource.\n"), type);
1168 /* Common stuff again -- free the resource, recurse to next one */
1173 free_resource(nres, type);
1178 * Save the new resource by chaining it into the head list for
1179 * the resource. If this is pass 2, we update any resource
1180 * pointers because they may not have been defined until
1183 void save_resource(int type, RES_ITEM *items, int pass)
1186 int rindex = type - r_first;
1190 /* Check Job requirements after applying JobDefs */
1191 if (type != R_JOB && type != R_JOBDEFS) {
1193 * Ensure that all required items are present
1195 for (i=0; items[i].name; i++) {
1196 if (items[i].flags & ITEM_REQUIRED) {
1197 if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1198 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1199 items[i].name, resources[rindex]);
1202 /* If this triggers, take a look at lib/parse_conf.h */
1203 if (i >= MAX_RES_ITEMS) {
1204 Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1207 } else if (type == R_JOB) {
1209 * Ensure that the name item is present
1211 if (items[0].flags & ITEM_REQUIRED) {
1212 if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1213 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1214 items[0].name, resources[rindex]);
1220 * During pass 2 in each "store" routine, we looked up pointers
1221 * to all the resources referrenced in the current resource, now we
1222 * must copy their addresses from the static record to the allocated
1227 /* Resources not containing a resource */
1235 * Resources containing another resource or alist. First
1236 * look up the resource which contains another resource. It
1237 * was written during pass 1. Then stuff in the pointers to
1238 * the resources it contains, which were inserted this pass.
1239 * Finally, it will all be stored back.
1242 /* Find resource saved in pass 1 */
1243 if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1244 Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1246 /* Explicitly copy resource pointers from this pass (res_all) */
1247 res->res_pool.NextPool = res_all.res_pool.NextPool;
1248 res->res_pool.storage = res_all.res_pool.storage;
1251 if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1252 Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1254 res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1257 if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1258 Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1260 res->res_dir.messages = res_all.res_dir.messages;
1261 res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1264 if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1265 Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1266 res_all.res_dir.hdr.name);
1268 /* we must explicitly copy the device alist pointer */
1269 res->res_store.device = res_all.res_store.device;
1273 if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1274 Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1275 res_all.res_dir.hdr.name);
1277 res->res_job.messages = res_all.res_job.messages;
1278 res->res_job.schedule = res_all.res_job.schedule;
1279 res->res_job.client = res_all.res_job.client;
1280 res->res_job.fileset = res_all.res_job.fileset;
1281 res->res_job.storage = res_all.res_job.storage;
1282 res->res_job.pool = res_all.res_job.pool;
1283 res->res_job.full_pool = res_all.res_job.full_pool;
1284 res->res_job.inc_pool = res_all.res_job.inc_pool;
1285 res->res_job.diff_pool = res_all.res_job.diff_pool;
1286 res->res_job.verify_job = res_all.res_job.verify_job;
1287 res->res_job.jobdefs = res_all.res_job.jobdefs;
1288 res->res_job.run_cmds = res_all.res_job.run_cmds;
1289 res->res_job.RunScripts = res_all.res_job.RunScripts;
1292 if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1293 Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1295 res->res_counter.Catalog = res_all.res_counter.Catalog;
1296 res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1300 if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1301 Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1303 res->res_client.catalog = res_all.res_client.catalog;
1307 * Schedule is a bit different in that it contains a RUN record
1308 * chain which isn't a "named" resource. This chain was linked
1309 * in by run_conf.c during pass 2, so here we jam the pointer
1310 * into the Schedule resource.
1312 if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1313 Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1315 res->res_sch.run = res_all.res_sch.run;
1318 Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1322 /* Note, the resource name was already saved during pass 1,
1323 * so here, we can just release it.
1325 if (res_all.res_dir.hdr.name) {
1326 free(res_all.res_dir.hdr.name);
1327 res_all.res_dir.hdr.name = NULL;
1329 if (res_all.res_dir.hdr.desc) {
1330 free(res_all.res_dir.hdr.desc);
1331 res_all.res_dir.hdr.desc = NULL;
1337 * The following code is only executed during pass 1
1341 size = sizeof(DIRRES);
1344 size = sizeof(CONRES);
1347 size =sizeof(CLIENT);
1350 size = sizeof(STORE);
1360 size = sizeof(FILESET);
1363 size = sizeof(SCHED);
1366 size = sizeof(POOL);
1369 size = sizeof(MSGS);
1372 size = sizeof(COUNTER);
1378 printf(_("Unknown resource type %d in save_resource.\n"), type);
1384 res = (URES *)malloc(size);
1385 memcpy(res, &res_all, size);
1386 if (!res_head[rindex]) {
1387 res_head[rindex] = (RES *)res; /* store first entry */
1388 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1389 res->res_dir.hdr.name, rindex);
1392 if (res->res_dir.hdr.name == NULL) {
1393 Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1396 /* Add new res to end of chain */
1397 for (last=next=res_head[rindex]; next; next=next->next) {
1399 if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1400 Emsg2(M_ERROR_TERM, 0,
1401 _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1402 resources[rindex].name, res->res_dir.hdr.name);
1405 last->next = (RES *)res;
1406 Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1407 res->res_dir.hdr.name, rindex, pass);
1413 * Store Device. Note, the resource is created upon the
1414 * first reference. The details of the resource are obtained
1415 * later from the SD.
1417 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1421 int rindex = R_DEVICE - r_first;
1422 int size = sizeof(DEVICE);
1426 token = lex_get_token(lc, T_NAME);
1427 if (!res_head[rindex]) {
1428 res = (URES *)malloc(size);
1429 memset(res, 0, size);
1430 res->res_dev.hdr.name = bstrdup(lc->str);
1431 res_head[rindex] = (RES *)res; /* store first entry */
1432 Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1433 res->res_dir.hdr.name, rindex);
1436 /* See if it is already defined */
1437 for (next=res_head[rindex]; next->next; next=next->next) {
1438 if (strcmp(next->name, lc->str) == 0) {
1444 res = (URES *)malloc(size);
1445 memset(res, 0, size);
1446 res->res_dev.hdr.name = bstrdup(lc->str);
1447 next->next = (RES *)res;
1448 Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1449 res->res_dir.hdr.name, rindex, pass);
1454 set_bit(index, res_all.hdr.item_present);
1456 store_alist_res(lc, item, index, pass);
1461 * Store JobType (backup, verify, restore)
1464 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1468 token = lex_get_token(lc, T_NAME);
1469 /* Store the type both pass 1 and pass 2 */
1470 for (i=0; migtypes[i].type_name; i++) {
1471 if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1472 *(int *)(item->value) = migtypes[i].job_type;
1478 scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1481 set_bit(index, res_all.hdr.item_present);
1487 * Store JobType (backup, verify, restore)
1490 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1494 token = lex_get_token(lc, T_NAME);
1495 /* Store the type both pass 1 and pass 2 */
1496 for (i=0; jobtypes[i].type_name; i++) {
1497 if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1498 *(int *)(item->value) = jobtypes[i].job_type;
1504 scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1507 set_bit(index, res_all.hdr.item_present);
1511 * Store Job Level (Full, Incremental, ...)
1514 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1518 token = lex_get_token(lc, T_NAME);
1519 /* Store the level pass 2 so that type is defined */
1520 for (i=0; joblevels[i].level_name; i++) {
1521 if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1522 *(int *)(item->value) = joblevels[i].level;
1528 scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1531 set_bit(index, res_all.hdr.item_present);
1535 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1538 token = lex_get_token(lc, T_NAME);
1539 /* Scan Replacement options */
1540 for (i=0; ReplaceOptions[i].name; i++) {
1541 if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1542 *(int *)(item->value) = ReplaceOptions[i].token;
1548 scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1551 set_bit(index, res_all.hdr.item_present);
1555 * Store ACL (access control list)
1558 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1563 token = lex_get_token(lc, T_STRING);
1565 if (((alist **)item->value)[item->code] == NULL) {
1566 ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1567 Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1569 ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1570 Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1572 token = lex_get_token(lc, T_ALL);
1573 if (token == T_COMMA) {
1574 continue; /* get another ACL */
1578 set_bit(index, res_all.hdr.item_present);
1581 /* We build RunScripts items here */
1582 static RUNSCRIPT res_runscript;
1584 /* Store a runscript->when in a bit field */
1585 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1587 lex_get_token(lc, T_NAME);
1589 if (strcasecmp(lc->str, "before") == 0) {
1590 *(int *)(item->value) = SCRIPT_Before ;
1591 } else if (strcasecmp(lc->str, "after") == 0) {
1592 *(int *)(item->value) = SCRIPT_After;
1593 } else if (strcasecmp(lc->str, "always") == 0) {
1594 *(int *)(item->value) = SCRIPT_Any;
1596 scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1601 /* Store a runscript->target
1604 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1606 lex_get_token(lc, T_STRING);
1609 if (strcmp(lc->str, "%c") == 0) {
1610 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1611 } else if (strcmp(lc->str, "yes") == 0) {
1612 ((RUNSCRIPT*) item->value)->set_target("%c");
1613 } else if (strcmp(lc->str, "no") == 0) {
1614 ((RUNSCRIPT*) item->value)->set_target("");
1616 RES *res = GetResWithName(R_CLIENT, lc->str);
1618 scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1619 lc->str, lc->line_no, lc->line);
1622 ((RUNSCRIPT*) item->value)->set_target(lc->str);
1628 /* Store a runscript->command in a bit field
1631 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1633 lex_get_token(lc, T_STRING);
1636 ((RUNSCRIPT*) item->value)->set_command(lc->str);
1641 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1643 lex_get_token(lc, T_STRING);
1644 alist **runscripts = (alist **)(item->value) ;
1647 RUNSCRIPT *script = new_runscript();
1649 script->set_command(lc->str);
1651 /* TODO: remove all script->old_proto with bacula 1.42 */
1653 if (strcmp(item->name, "runbeforejob") == 0) {
1654 script->when = SCRIPT_Before;
1655 script->abort_on_error = true;
1656 script->set_target("");
1658 } else if (strcmp(item->name, "runafterjob") == 0) {
1659 script->when = SCRIPT_After;
1660 script->on_success = true;
1661 script->on_failure = false;
1662 script->set_target("");
1664 } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1665 script->old_proto = true;
1666 script->when = SCRIPT_After;
1667 script->set_target("%c");
1668 script->on_success = true;
1669 script->on_failure = false;
1671 } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1672 script->old_proto = true;
1673 script->when = SCRIPT_Before;
1674 script->set_target("%c");
1675 script->abort_on_error = true;
1677 } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1678 script->when = SCRIPT_After;
1679 script->on_failure = true;
1680 script->on_success = false;
1681 script->set_target("");
1684 if (*runscripts == NULL) {
1685 *runscripts = New(alist(10, not_owned_by_alist));
1688 (*runscripts)->append(script);
1695 /* Store a bool in a bit field without modifing res_all.hdr
1696 * We can also add an option to store_bool to skip res_all.hdr
1698 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1700 lex_get_token(lc, T_NAME);
1701 if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1702 *(bool *)(item->value) = true;
1703 } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1704 *(bool *)(item->value) = false;
1706 scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1712 * new RunScript items
1713 * name handler value code flags default_value
1715 static RES_ITEM runscript_items[] = {
1716 {"command", store_runscript_cmd, {(char **)&res_runscript}, 0, ITEM_REQUIRED, 0},
1717 {"target", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0},
1718 {"runsonsuccess", store_runscript_bool, {(char **)&res_runscript.on_success},0, 0, 0},
1719 {"runsonfailure", store_runscript_bool, {(char **)&res_runscript.on_failure},0, 0, 0},
1720 {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.abort_on_error},0, 0, 0},
1721 {"runswhen", store_runscript_when, {(char **)&res_runscript.when}, 0, 0, 0},
1722 {"runsonclient", store_runscript_target,{(char **)&res_runscript}, 0, 0, 0}, /* TODO */
1723 {NULL, NULL, {0}, 0, 0, 0}
1727 * Store RunScript info
1729 * Note, when this routine is called, we are inside a Job
1730 * resource. We treat the RunScript like a sort of
1731 * mini-resource within the Job resource.
1733 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1736 alist **runscripts = (alist **)(item->value) ;
1738 Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1740 res_runscript.reset_default(); /* setting on_success, on_failure, abort_on_error */
1742 token = lex_get_token(lc, T_SKIP_EOL);
1744 if (token != T_BOB) {
1745 scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1748 while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1749 if (token == T_EOB) {
1752 if (token != T_IDENTIFIER) {
1753 scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1755 for (i=0; runscript_items[i].name; i++) {
1756 if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1757 token = lex_get_token(lc, T_SKIP_EOL);
1758 if (token != T_EQUALS) {
1759 scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1762 /* Call item handler */
1763 runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1770 scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1775 if (res_runscript.command == NULL) {
1776 scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1777 "command", "runscript");
1780 /* run on client by default */
1781 if (res_runscript.target == NULL) {
1782 res_runscript.set_target("%c");
1785 RUNSCRIPT *script = new_runscript();
1786 memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1788 if (*runscripts == NULL) {
1789 *runscripts = New(alist(10, not_owned_by_alist));
1792 (*runscripts)->append(script);
1797 set_bit(index, res_all.hdr.item_present);