]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
ebl Make RunScript work as 1.143
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
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).
5  *
6  *   Note, the configuration file parser consists of three parts
7  *
8  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
9  *
10  *   2. The generic config  scanner in lib/parse_config.c and
11  *      lib/parse_config.h.
12  *      These files contain the parser code, some utility
13  *      routines, and the common store routines (name, int,
14  *      string).
15  *
16  *   3. The daemon specific file, which contains the Resource
17  *      definitions as well as any specific store routines
18  *      for the resource records.
19  *
20  *     Kern Sibbald, January MM
21  *
22  *     Version $Id$
23  */
24 /*
25    Copyright (C) 2000-2006 Kern Sibbald
26
27    This program is free software; you can redistribute it and/or
28    modify it under the terms of the GNU General Public License
29    version 2 as amended with additional clauses defined in the
30    file LICENSE in the main source directory.
31
32    This program is distributed in the hope that it will be useful,
33    but WITHOUT ANY WARRANTY; without even the implied warranty of
34    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
35    the file LICENSE for additional details.
36
37  */
38
39 #include "bacula.h"
40 #include "dird.h"
41
42 /* Define the first and last resource ID record
43  * types. Note, these should be unique for each
44  * daemon though not a requirement.
45  */
46 int r_first = R_FIRST;
47 int r_last  = R_LAST;
48 static RES *sres_head[R_LAST - R_FIRST + 1];
49 RES **res_head = sres_head;
50
51 /* Imported subroutines */
52 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
53 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
54 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
55
56
57 /* Forward referenced subroutines */
58
59 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
60 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
61 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
62 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
63 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
64 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
65 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
66 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
67 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
68 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
69
70 /* We build the current resource here as we are
71  * scanning the resource configuration definition,
72  * then move it to allocated memory when the resource
73  * scan is complete.
74  */
75 #if defined(_MSC_VER)
76 extern "C" { // work around visual compiler mangling variables
77     URES res_all;
78 }
79 #else
80 URES res_all;
81 #endif
82 int  res_all_size = sizeof(res_all);
83
84
85 /* Definition of records permitted within each
86  * resource with the routine to process the record
87  * information.  NOTE! quoted names must be in lower case.
88  */
89 /*
90  *    Director Resource
91  *
92  *   name          handler     value                 code flags    default_value
93  */
94 static RES_ITEM dir_items[] = {
95    {"name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
96    {"description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
97    {"messages",    store_res,      ITEM(res_dir.messages), R_MSGS, 0, 0},
98    {"dirport",     store_addresses_port,    ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
99    {"diraddress",  store_addresses_address, ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
100    {"diraddresses",store_addresses,         ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
101    {"queryfile",   store_dir,      ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
102    {"workingdirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
103    {"scriptsdirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
104    {"piddirectory",store_dir,     ITEM(res_dir.pid_directory), 0, ITEM_REQUIRED, 0},
105    {"subsysdirectory", store_dir,  ITEM(res_dir.subsys_directory), 0, 0, 0},
106    {"maximumconcurrentjobs", store_pint, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
107    {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
108    {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
109    {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
110    {"tlsenable",            store_bool,      ITEM(res_dir.tls_enable), 0, 0, 0},
111    {"tlsrequire",           store_bool,      ITEM(res_dir.tls_require), 0, 0, 0},
112    {"tlsverifypeer",        store_bool,      ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
113    {"tlscacertificatefile", store_dir,       ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
114    {"tlscacertificatedir",  store_dir,       ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
115    {"tlscertificate",       store_dir,       ITEM(res_dir.tls_certfile), 0, 0, 0},
116    {"tlskey",               store_dir,       ITEM(res_dir.tls_keyfile), 0, 0, 0},
117    {"tlsdhfile",            store_dir,       ITEM(res_dir.tls_dhfile), 0, 0, 0},
118    {"tlsallowedcn",         store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
119    {NULL, NULL, {0}, 0, 0, 0}
120 };
121
122 /*
123  *    Console Resource
124  *
125  *   name          handler     value                 code flags    default_value
126  */
127 static RES_ITEM con_items[] = {
128    {"name",        store_name,     ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
129    {"description", store_str,      ITEM(res_con.hdr.desc), 0, 0, 0},
130    {"password",    store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
131    {"jobacl",      store_acl,      ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
132    {"clientacl",   store_acl,      ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
133    {"storageacl",  store_acl,      ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
134    {"scheduleacl", store_acl,      ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
135    {"runacl",      store_acl,      ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
136    {"poolacl",     store_acl,      ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
137    {"commandacl",  store_acl,      ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
138    {"filesetacl",  store_acl,      ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
139    {"catalogacl",  store_acl,      ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
140    {"tlsenable",            store_bool,      ITEM(res_con.tls_enable), 0, 0, 0},
141    {"tlsrequire",           store_bool,      ITEM(res_con.tls_require), 0, 0, 0},
142    {"tlsverifypeer",        store_bool,      ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
143    {"tlscacertificatefile", store_dir,       ITEM(res_con.tls_ca_certfile), 0, 0, 0},
144    {"tlscacertificatedir",  store_dir,       ITEM(res_con.tls_ca_certdir), 0, 0, 0},
145    {"tlscertificate",       store_dir,       ITEM(res_con.tls_certfile), 0, 0, 0},
146    {"tlskey",               store_dir,       ITEM(res_con.tls_keyfile), 0, 0, 0},
147    {"tlsdhfile",            store_dir,       ITEM(res_con.tls_dhfile), 0, 0, 0},
148    {"tlsallowedcn",         store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
149    {NULL, NULL, {0}, 0, 0, 0}
150 };
151
152
153 /*
154  *    Client or File daemon resource
155  *
156  *   name          handler     value                 code flags    default_value
157  */
158
159 static RES_ITEM cli_items[] = {
160    {"name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
161    {"description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
162    {"address",  store_str,        ITEM(res_client.address),  0, ITEM_REQUIRED, 0},
163    {"fdaddress",  store_str,      ITEM(res_client.address),  0, 0, 0},
164    {"fdport",   store_pint,       ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
165    {"password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
166    {"fdpassword", store_password,   ITEM(res_client.password), 0, 0, 0},
167    {"catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, ITEM_REQUIRED, 0},
168    {"fileretention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
169    {"jobretention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*180},
170    {"autoprune", store_bool,      ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
171    {"maximumconcurrentjobs", store_pint, ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
172    {"tlsenable",            store_bool,      ITEM(res_client.tls_enable), 0, 0, 0},
173    {"tlsrequire",           store_bool,      ITEM(res_client.tls_require), 0, 0, 0},
174    {"tlscacertificatefile", store_dir,       ITEM(res_client.tls_ca_certfile), 0, 0, 0},
175    {"tlscacertificatedir",  store_dir,       ITEM(res_client.tls_ca_certdir), 0, 0, 0},
176    {"tlscertificate",       store_dir,       ITEM(res_client.tls_certfile), 0, 0, 0},
177    {"tlskey",               store_dir,       ITEM(res_client.tls_keyfile), 0, 0, 0},
178    {NULL, NULL, {0}, 0, 0, 0}
179 };
180
181 /* Storage daemon resource
182  *
183  *   name          handler     value                 code flags    default_value
184  */
185 static RES_ITEM store_items[] = {
186    {"name",        store_name,     ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
187    {"description", store_str,      ITEM(res_store.hdr.desc),   0, 0, 0},
188    {"sdport",      store_pint,     ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
189    {"address",     store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
190    {"sdaddress",   store_str,      ITEM(res_store.address),    0, 0, 0},
191    {"password",    store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 0},
192    {"sdpassword",  store_password, ITEM(res_store.password),   0, 0, 0},
193    {"device",      store_device,   ITEM(res_store.device),     R_DEVICE, ITEM_REQUIRED, 0},
194    {"mediatype",   store_strname,  ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
195    {"autochanger", store_bool,     ITEM(res_store.autochanger), 0, ITEM_DEFAULT, 0},
196    {"enabled",     store_bool,     ITEM(res_store.enabled),     0, ITEM_DEFAULT, true},
197    {"maximumconcurrentjobs", store_pint, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
198    {"sddport", store_pint, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
199    {"tlsenable",            store_bool,      ITEM(res_store.tls_enable), 0, 0, 0},
200    {"tlsrequire",           store_bool,      ITEM(res_store.tls_require), 0, 0, 0},
201    {"tlscacertificatefile", store_dir,       ITEM(res_store.tls_ca_certfile), 0, 0, 0},
202    {"tlscacertificatedir",  store_dir,       ITEM(res_store.tls_ca_certdir), 0, 0, 0},
203    {"tlscertificate",       store_dir,       ITEM(res_store.tls_certfile), 0, 0, 0},
204    {"tlskey",               store_dir,       ITEM(res_store.tls_keyfile), 0, 0, 0},
205    {NULL, NULL, {0}, 0, 0, 0}
206 };
207
208 /*
209  *    Catalog Resource Directives
210  *
211  *   name          handler     value                 code flags    default_value
212  */
213 static RES_ITEM cat_items[] = {
214    {"name",     store_name,     ITEM(res_cat.hdr.name),    0, ITEM_REQUIRED, 0},
215    {"description", store_str,   ITEM(res_cat.hdr.desc),    0, 0, 0},
216    {"address",  store_str,      ITEM(res_cat.db_address),  0, 0, 0},
217    {"dbaddress", store_str,     ITEM(res_cat.db_address),  0, 0, 0},
218    {"dbport",   store_pint,     ITEM(res_cat.db_port),      0, 0, 0},
219    /* keep this password as store_str for the moment */
220    {"password", store_str,      ITEM(res_cat.db_password), 0, 0, 0},
221    {"dbpassword", store_str,    ITEM(res_cat.db_password), 0, 0, 0},
222    {"user",     store_str,      ITEM(res_cat.db_user),     0, 0, 0},
223    {"dbname",   store_str,      ITEM(res_cat.db_name),     0, ITEM_REQUIRED, 0},
224    {"dbsocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0},
225    /* Turned off for the moment */
226    {"multipleconnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
227    {NULL, NULL, {0}, 0, 0, 0}
228 };
229
230 /*
231  *    Job Resource Directives
232  *
233  *   name          handler     value                 code flags    default_value
234  */
235 RES_ITEM job_items[] = {
236    {"name",      store_name,    ITEM(res_job.hdr.name), 0, ITEM_REQUIRED, 0},
237    {"description", store_str,   ITEM(res_job.hdr.desc), 0, 0, 0},
238    {"type",      store_jobtype, ITEM(res_job.JobType),  0, ITEM_REQUIRED, 0},
239    {"level",     store_level,   ITEM(res_job.JobLevel),    0, 0, 0},
240    {"messages",  store_res,     ITEM(res_job.messages), R_MSGS, ITEM_REQUIRED, 0},
241    {"storage",   store_alist_res, ITEM(res_job.storage),  R_STORAGE, 0, 0},
242    {"pool",      store_res,     ITEM(res_job.pool),     R_POOL, ITEM_REQUIRED, 0},
243    {"fullbackuppool",  store_res, ITEM(res_job.full_pool),   R_POOL, 0, 0},
244    {"incrementalbackuppool",  store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
245    {"differentialbackuppool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
246    {"client",    store_res,     ITEM(res_job.client),   R_CLIENT, ITEM_REQUIRED, 0},
247    {"fileset",   store_res,     ITEM(res_job.fileset),  R_FILESET, ITEM_REQUIRED, 0},
248    {"schedule",  store_res,     ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
249    {"verifyjob", store_res,     ITEM(res_job.verify_job), R_JOB, 0, 0},
250    {"jobtoverify", store_res,   ITEM(res_job.verify_job), R_JOB, 0, 0},
251    {"jobdefs",   store_res,     ITEM(res_job.jobdefs),    R_JOBDEFS, 0, 0},
252    {"nextpool",  store_res,     ITEM(res_job.next_pool),  R_POOL, 0, 0},
253    {"run",       store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
254    /* Root of where to restore files */
255    {"where",    store_dir,      ITEM(res_job.RestoreWhere), 0, 0, 0},
256    /* Where to find bootstrap during restore */
257    {"bootstrap",store_dir,      ITEM(res_job.RestoreBootstrap), 0, 0, 0},
258    /* Where to write bootstrap file during backup */
259    {"writebootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
260    {"writeverifylist",store_dir, ITEM(res_job.WriteVerifyList), 0, 0, 0},
261    {"replace",  store_replace,  ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
262    {"maxruntime",   store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
263    {"fullmaxwaittime",  store_time, ITEM(res_job.FullMaxWaitTime), 0, 0, 0},
264    {"incrementalmaxwaittime",  store_time, ITEM(res_job.IncMaxWaitTime), 0, 0, 0},
265    {"differentialmaxwaittime",  store_time, ITEM(res_job.DiffMaxWaitTime), 0, 0, 0},
266    {"maxwaittime",  store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
267    {"maxstartdelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
268    {"jobretention", store_time, ITEM(res_job.JobRetention),  0, 0, 0},
269    {"prefixlinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
270    {"prunejobs",   store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
271    {"prunefiles",  store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
272    {"prunevolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
273    {"enabled",     store_bool, ITEM(res_job.enabled), 0, ITEM_DEFAULT, true},
274    {"spoolattributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, false},
275    {"spooldata",   store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
276    {"rerunfailedlevels",   store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
277    {"prefermountedvolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
278    {"runbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
279    {"runafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
280    {"runafterfailedjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
281    {"clientrunbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
282    {"clientrunafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
283    {"maximumconcurrentjobs", store_pint, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
284    {"rescheduleonerror", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
285    {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
286    {"rescheduletimes", store_pint, ITEM(res_job.RescheduleTimes), 0, 0, 0},
287    {"priority",   store_pint, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
288    {"writepartafterjob",   store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, false},
289    {"selectionpattern", store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
290    {"selectiontype", store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
291    {"runscript", store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
292    {NULL, NULL, {0}, 0, 0, 0}
293 };
294
295 /* FileSet resource
296  *
297  *   name          handler     value                 code flags    default_value
298  */
299 static RES_ITEM fs_items[] = {
300    {"name",        store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
301    {"description", store_str,  ITEM(res_fs.hdr.desc), 0, 0, 0},
302    {"include",     store_inc,  {0},                   0, ITEM_NO_EQUALS, 0},
303    {"exclude",     store_inc,  {0},                   1, ITEM_NO_EQUALS, 0},
304    {"ignorefilesetchanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
305    {"enablevss",   store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, false},
306    {NULL,          NULL,       {0},                  0, 0, 0}
307 };
308
309 /* Schedule -- see run_conf.c */
310 /* Schedule
311  *
312  *   name          handler     value                 code flags    default_value
313  */
314 static RES_ITEM sch_items[] = {
315    {"name",     store_name,  ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
316    {"description", store_str, ITEM(res_sch.hdr.desc), 0, 0, 0},
317    {"run",      store_run,   ITEM(res_sch.run),      0, 0, 0},
318    {NULL, NULL, {0}, 0, 0, 0}
319 };
320
321 /* Pool resource
322  *
323  *   name             handler     value                        code flags default_value
324  */
325 static RES_ITEM pool_items[] = {
326    {"name",            store_name,    ITEM(res_pool.hdr.name),      0, ITEM_REQUIRED, 0},
327    {"description",     store_str,     ITEM(res_pool.hdr.desc),      0, 0,     0},
328    {"pooltype",        store_strname, ITEM(res_pool.pool_type),     0, ITEM_REQUIRED, 0},
329    {"labelformat",     store_strname, ITEM(res_pool.label_format),  0, 0,     0},
330    {"labeltype",       store_label,   ITEM(res_pool.LabelType),     0, 0,     0},     
331    {"cleaningprefix",  store_strname, ITEM(res_pool.cleaning_prefix), 0, 0,   0},
332    {"usecatalog",      store_bool,    ITEM(res_pool.use_catalog),    0, ITEM_DEFAULT, true},
333    {"usevolumeonce",   store_bool,    ITEM(res_pool.use_volume_once), 0, 0,   0},
334    {"purgeoldestvolume", store_bool,  ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
335    {"recycleoldestvolume", store_bool,  ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
336    {"recyclecurrentvolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
337    {"maximumvolumes",  store_pint,    ITEM(res_pool.max_volumes),   0, 0,        0},
338    {"maximumvolumejobs", store_pint,  ITEM(res_pool.MaxVolJobs),    0, 0,       0},
339    {"maximumvolumefiles", store_pint, ITEM(res_pool.MaxVolFiles),   0, 0,       0},
340    {"maximumvolumebytes", store_size, ITEM(res_pool.MaxVolBytes),   0, 0,       0},
341    {"catalogfiles",    store_bool,    ITEM(res_pool.catalog_files),  0, ITEM_DEFAULT, true},
342    {"volumeretention", store_time,    ITEM(res_pool.VolRetention),   0, ITEM_DEFAULT, 60*60*24*365},
343    {"volumeuseduration", store_time,  ITEM(res_pool.VolUseDuration), 0, 0, 0},
344    {"migrationtime",  store_time,     ITEM(res_pool.MigrationTime), 0, 0, 0},
345    {"migrationhighbytes", store_size, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
346    {"migrationlowbytes", store_size,  ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
347    {"nextpool",      store_res,       ITEM(res_pool.NextPool), R_POOL, 0, 0},
348    {"storage",       store_alist_res, ITEM(res_pool.storage),  R_STORAGE, 0, 0},
349    {"autoprune",       store_bool,    ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
350    {"recycle",         store_bool,    ITEM(res_pool.Recycle),   0, ITEM_DEFAULT, true},
351    {NULL, NULL, {0}, 0, 0, 0}
352 };
353
354 /*
355  * Counter Resource
356  *   name             handler     value                        code flags default_value
357  */
358 static RES_ITEM counter_items[] = {
359    {"name",            store_name,    ITEM(res_counter.hdr.name),        0, ITEM_REQUIRED, 0},
360    {"description",     store_str,     ITEM(res_counter.hdr.desc),        0, 0,     0},
361    {"minimum",         store_int,     ITEM(res_counter.MinValue),        0, ITEM_DEFAULT, 0},
362    {"maximum",         store_pint,    ITEM(res_counter.MaxValue),        0, ITEM_DEFAULT, INT32_MAX},
363    {"wrapcounter",     store_res,     ITEM(res_counter.WrapCounter),     R_COUNTER, 0, 0},
364    {"catalog",         store_res,     ITEM(res_counter.Catalog),         R_CATALOG, 0, 0},
365    {NULL, NULL, {0}, 0, 0, 0}
366 };
367
368
369 /* Message resource */
370 extern RES_ITEM msgs_items[];
371
372 /*
373  * This is the master resource definition.
374  * It must have one item for each of the resources.
375  *
376  *  NOTE!!! keep it in the same order as the R_codes
377  *    or eliminate all resources[rindex].name
378  *
379  *  name             items        rcode        res_head
380  */
381 RES_TABLE resources[] = {
382    {"director",      dir_items,   R_DIRECTOR},
383    {"client",        cli_items,   R_CLIENT},
384    {"job",           job_items,   R_JOB},
385    {"storage",       store_items, R_STORAGE},
386    {"catalog",       cat_items,   R_CATALOG},
387    {"schedule",      sch_items,   R_SCHEDULE},
388    {"fileset",       fs_items,    R_FILESET},
389    {"pool",          pool_items,  R_POOL},
390    {"messages",      msgs_items,  R_MSGS},
391    {"counter",       counter_items, R_COUNTER},
392    {"console",       con_items,   R_CONSOLE},
393    {"jobdefs",       job_items,   R_JOBDEFS},
394    {"device",        NULL,        R_DEVICE},  /* info obtained from SD */
395    {NULL,            NULL,        0}
396 };
397
398
399 /* Keywords (RHS) permitted in Job Level records
400  *
401  *   level_name      level              job_type
402  */
403 struct s_jl joblevels[] = {
404    {"Full",          L_FULL,            JT_BACKUP},
405    {"Base",          L_BASE,            JT_BACKUP},
406    {"Incremental",   L_INCREMENTAL,     JT_BACKUP},
407    {"Differential",  L_DIFFERENTIAL,    JT_BACKUP},
408    {"Since",         L_SINCE,           JT_BACKUP},
409    {"Catalog",       L_VERIFY_CATALOG,  JT_VERIFY},
410    {"InitCatalog",   L_VERIFY_INIT,     JT_VERIFY},
411    {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG,   JT_VERIFY},
412    {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG,   JT_VERIFY},
413    {"Data",          L_VERIFY_DATA,     JT_VERIFY},
414    {" ",             L_NONE,            JT_ADMIN},
415    {" ",             L_NONE,            JT_RESTORE},
416    {NULL,            0,                          0}
417 };
418
419 /* Keywords (RHS) permitted in Job type records
420  *
421  *   type_name       job_type
422  */
423 struct s_jt jobtypes[] = {
424    {"backup",        JT_BACKUP},
425    {"admin",         JT_ADMIN},
426    {"verify",        JT_VERIFY},
427    {"restore",       JT_RESTORE},
428    {"migrate",       JT_MIGRATE},
429    {NULL,            0}
430 };
431
432
433 /* Keywords (RHS) permitted in Selection type records
434  *
435  *   type_name       job_type
436  */
437 struct s_jt migtypes[] = {
438    {"smallestvolume",   MT_SMALLEST_VOL},
439    {"oldestvolume",     MT_OLDEST_VOL},
440    {"pooloccupancy",    MT_POOL_OCCUPANCY},
441    {"pooltime",         MT_POOL_TIME},
442    {"client",           MT_CLIENT},
443    {"volume",           MT_VOLUME},
444    {"job",              MT_JOB},
445    {"sqlquery",         MT_SQLQUERY},
446    {NULL,            0}
447 };
448
449
450
451 /* Options permitted in Restore replace= */
452 struct s_kw ReplaceOptions[] = {
453    {"always",         REPLACE_ALWAYS},
454    {"ifnewer",        REPLACE_IFNEWER},
455    {"ifolder",        REPLACE_IFOLDER},
456    {"never",          REPLACE_NEVER},
457    {NULL,               0}
458 };
459
460 const char *level_to_str(int level)
461 {
462    int i;
463    static char level_no[30];
464    const char *str = level_no;
465
466    bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level);    /* default if not found */
467    for (i=0; joblevels[i].level_name; i++) {
468       if (level == joblevels[i].level) {
469          str = joblevels[i].level_name;
470          break;
471       }
472    }
473    return str;
474 }
475
476 /* Dump contents of resource */
477 void dump_resource(int type, RES *reshdr, void sendit(void *sock, const char *fmt, ...), void *sock)
478 {
479    URES *res = (URES *)reshdr;
480    bool recurse = true;
481    char ed1[100], ed2[100], ed3[100];
482    DEVICE *dev;
483
484    if (res == NULL) {
485       sendit(sock, _("No %s resource defined\n"), res_to_str(type));
486       return;
487    }
488    if (type < 0) {                    /* no recursion */
489       type = - type;
490       recurse = false;
491    }
492    switch (type) {
493    case R_DIRECTOR:
494       sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
495          reshdr->name, res->res_dir.MaxConcurrentJobs,
496          edit_uint64(res->res_dir.FDConnectTimeout, ed1),
497          edit_uint64(res->res_dir.SDConnectTimeout, ed2));
498       if (res->res_dir.query_file) {
499          sendit(sock, _("   query_file=%s\n"), res->res_dir.query_file);
500       }
501       if (res->res_dir.messages) {
502          sendit(sock, _("  --> "));
503          dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
504       }
505       break;
506    case R_CONSOLE:
507       sendit(sock, _("Console: name=%s SSL=%d\n"),
508          res->res_con.hdr.name, res->res_con.tls_enable);
509       break;
510    case R_COUNTER:
511       if (res->res_counter.WrapCounter) {
512          sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
513             res->res_counter.hdr.name, res->res_counter.MinValue,
514             res->res_counter.MaxValue, res->res_counter.CurrentValue,
515             res->res_counter.WrapCounter->hdr.name);
516       } else {
517          sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
518             res->res_counter.hdr.name, res->res_counter.MinValue,
519             res->res_counter.MaxValue);
520       }
521       if (res->res_counter.Catalog) {
522          sendit(sock, _("  --> "));
523          dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
524       }
525       break;
526
527    case R_CLIENT:
528       sendit(sock, _("Client: name=%s address=%s FDport=%d MaxJobs=%u\n"),
529          res->res_client.hdr.name, res->res_client.address, res->res_client.FDport,
530          res->res_client.MaxConcurrentJobs);
531       sendit(sock, _("      JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
532          edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
533          edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
534          res->res_client.AutoPrune);
535       if (res->res_client.catalog) {
536          sendit(sock, _("  --> "));
537          dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
538       }
539       break;
540    case R_DEVICE:
541       dev = &res->res_dev;
542       char ed1[50];
543       sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
544 "      reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
545 "      poolid=%s volname=%s MediaType=%s\n"),
546          dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
547          dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
548          dev->offline, dev->autochanger,
549          edit_uint64(dev->PoolId, ed1),
550          dev->VolumeName, dev->MediaType);
551       break;
552    case R_STORAGE:
553       sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
554 "      DeviceName=%s MediaType=%s StorageId=%s\n"),
555          res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
556          res->res_store.MaxConcurrentJobs,
557          res->res_store.dev_name(),
558          res->res_store.media_type,
559          edit_int64(res->res_store.StorageId, ed1));
560       break;
561    case R_CATALOG:
562       sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
563 "      db_user=%s MutliDBConn=%d\n"),
564          res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
565          res->res_cat.db_port, res->res_cat.db_name, NPRT(res->res_cat.db_user),
566          res->res_cat.mult_db_connections);
567       break;
568    case R_JOB:
569    case R_JOBDEFS:
570       sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
571          type == R_JOB ? _("Job") : _("JobDefs"),
572          res->res_job.hdr.name, res->res_job.JobType,
573          level_to_str(res->res_job.JobLevel), res->res_job.Priority,
574          res->res_job.enabled);
575       sendit(sock, _("     MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
576          res->res_job.MaxConcurrentJobs, 
577          res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
578          edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
579          res->res_job.spool_data, res->res_job.write_part_after_job);
580       if (res->res_job.JobType == JT_MIGRATE) {
581          sendit(sock, _("     SelectionType=%d\n"), res->res_job.selection_type);
582       }
583       if (res->res_job.client) {
584          sendit(sock, _("  --> "));
585          dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
586       }
587       if (res->res_job.fileset) {
588          sendit(sock, _("  --> "));
589          dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
590       }
591       if (res->res_job.schedule) {
592          sendit(sock, _("  --> "));
593          dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
594       }
595       if (res->res_job.RestoreWhere) {
596          sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
597       }
598       if (res->res_job.RestoreBootstrap) {
599          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
600       }
601       if (res->res_job.WriteBootstrap) {
602          sendit(sock, _("  --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
603       }
604       if (res->res_job.storage) {
605          STORE *store;
606          foreach_alist(store, res->res_job.storage) {
607             sendit(sock, _("  --> "));
608             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
609          }
610       }
611       if (res->res_job.RunScripts) {
612         RUNSCRIPT *script;
613         foreach_alist(script, res->res_job.RunScripts) {
614            sendit(sock, _(" --> RunScript\n"));
615            sendit(sock, _("  --> Command=%s\n"), NPRT(script->command));
616            sendit(sock, _("  --> Target=%s\n"),  NPRT(script->target));
617            sendit(sock, _("  --> RunOnSuccess=%u\n"),  script->on_success);
618            sendit(sock, _("  --> RunOnFailure=%u\n"),  script->on_failure);
619            sendit(sock, _("  --> AbortJobOnError=%u\n"),  script->abort_on_error);
620            sendit(sock, _("  --> RunWhen=%u\n"),  script->when);
621         }
622       }
623       if (res->res_job.pool) {
624          sendit(sock, _("  --> "));
625          dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
626       }
627       if (res->res_job.full_pool) {
628          sendit(sock, _("  --> "));
629          dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
630       }
631       if (res->res_job.inc_pool) {
632          sendit(sock, _("  --> "));
633          dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
634       }
635       if (res->res_job.diff_pool) {
636          sendit(sock, _("  --> "));
637          dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
638       }
639       if (res->res_job.verify_job) {
640          sendit(sock, _("  --> "));
641          dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
642       }
643       if (res->res_job.run_cmds) {
644          char *runcmd;
645          foreach_alist(runcmd, res->res_job.run_cmds) {
646             sendit(sock, _("  --> Run=%s\n"), runcmd);
647          }
648       }
649       if (res->res_job.selection_pattern) {
650          sendit(sock, _("  --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
651       }
652       if (res->res_job.messages) {
653          sendit(sock, _("  --> "));
654          dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
655       }
656       break;
657    case R_FILESET:
658    {
659       int i, j, k;
660       sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
661       for (i=0; i<res->res_fs.num_includes; i++) {
662          INCEXE *incexe = res->res_fs.include_items[i];
663          for (j=0; j<incexe->num_opts; j++) {
664             FOPTS *fo = incexe->opts_list[j];
665             sendit(sock, "      O %s\n", fo->opts);
666
667             bool enhanced_wild = false;
668             for (k=0; fo->opts[k]!='\0'; k++) {
669                if (fo->opts[k]=='W') {
670                   enhanced_wild = true;
671                   break;
672                }
673             }
674
675             for (k=0; k<fo->regex.size(); k++) {
676                sendit(sock, "      R %s\n", fo->regex.get(k));
677             }
678             for (k=0; k<fo->regexdir.size(); k++) {
679                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
680             }
681             for (k=0; k<fo->regexfile.size(); k++) {
682                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
683             }
684             for (k=0; k<fo->wild.size(); k++) {
685                sendit(sock, "      W %s\n", fo->wild.get(k));
686             }
687             for (k=0; k<fo->wilddir.size(); k++) {
688                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
689             }
690             for (k=0; k<fo->wildfile.size(); k++) {
691                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
692             }
693             for (k=0; k<fo->wildbase.size(); k++) {
694                sendit(sock, "      W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
695             }
696             for (k=0; k<fo->base.size(); k++) {
697                sendit(sock, "      B %s\n", fo->base.get(k));
698             }
699             for (k=0; k<fo->fstype.size(); k++) {
700                sendit(sock, "      X %s\n", fo->fstype.get(k));
701             }
702             for (k=0; k<fo->drivetype.size(); k++) {
703                sendit(sock, "      XD %s\n", fo->drivetype.get(k));
704             }
705             if (fo->reader) {
706                sendit(sock, "      D %s\n", fo->reader);
707             }
708             if (fo->writer) {
709                sendit(sock, "      T %s\n", fo->writer);
710             }
711             sendit(sock, "      N\n");
712          }
713          for (j=0; j<incexe->name_list.size(); j++) {
714             sendit(sock, "      I %s\n", incexe->name_list.get(j));
715          }
716          if (incexe->name_list.size()) {
717             sendit(sock, "      N\n");
718          }
719       }
720
721       for (i=0; i<res->res_fs.num_excludes; i++) {
722          INCEXE *incexe = res->res_fs.exclude_items[i];
723          for (j=0; j<incexe->name_list.size(); j++) {
724             sendit(sock, "      E %s\n", incexe->name_list.get(j));
725          }
726          if (incexe->name_list.size()) {
727             sendit(sock, "      N\n");
728          }
729       }
730       break;
731    }
732    case R_SCHEDULE:
733       if (res->res_sch.run) {
734          int i;
735          RUN *run = res->res_sch.run;
736          char buf[1000], num[30];
737          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
738          if (!run) {
739             break;
740          }
741 next_run:
742          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
743          bstrncpy(buf, _("      hour="), sizeof(buf));
744          for (i=0; i<24; i++) {
745             if (bit_is_set(i, run->hour)) {
746                bsnprintf(num, sizeof(num), "%d ", i);
747                bstrncat(buf, num, sizeof(buf));
748             }
749          }
750          bstrncat(buf, "\n", sizeof(buf));
751          sendit(sock, buf);
752          bstrncpy(buf, _("      mday="), sizeof(buf));
753          for (i=0; i<31; i++) {
754             if (bit_is_set(i, run->mday)) {
755                bsnprintf(num, sizeof(num), "%d ", i);
756                bstrncat(buf, num, sizeof(buf));
757             }
758          }
759          bstrncat(buf, "\n", sizeof(buf));
760          sendit(sock, buf);
761          bstrncpy(buf, _("      month="), sizeof(buf));
762          for (i=0; i<12; i++) {
763             if (bit_is_set(i, run->month)) {
764                bsnprintf(num, sizeof(num), "%d ", i);
765                bstrncat(buf, num, sizeof(buf));
766             }
767          }
768          bstrncat(buf, "\n", sizeof(buf));
769          sendit(sock, buf);
770          bstrncpy(buf, _("      wday="), sizeof(buf));
771          for (i=0; i<7; i++) {
772             if (bit_is_set(i, run->wday)) {
773                bsnprintf(num, sizeof(num), "%d ", i);
774                bstrncat(buf, num, sizeof(buf));
775             }
776          }
777          bstrncat(buf, "\n", sizeof(buf));
778          sendit(sock, buf);
779          bstrncpy(buf, _("      wom="), sizeof(buf));
780          for (i=0; i<5; i++) {
781             if (bit_is_set(i, run->wom)) {
782                bsnprintf(num, sizeof(num), "%d ", i);
783                bstrncat(buf, num, sizeof(buf));
784             }
785          }
786          bstrncat(buf, "\n", sizeof(buf));
787          sendit(sock, buf);
788          bstrncpy(buf, _("      woy="), sizeof(buf));
789          for (i=0; i<54; i++) {
790             if (bit_is_set(i, run->woy)) {
791                bsnprintf(num, sizeof(num), "%d ", i);
792                bstrncat(buf, num, sizeof(buf));
793             }
794          }
795          bstrncat(buf, "\n", sizeof(buf));
796          sendit(sock, buf);
797          sendit(sock, _("      mins=%d\n"), run->minute);
798          if (run->pool) {
799             sendit(sock, _("     --> "));
800             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
801          }
802          if (run->storage) {
803             sendit(sock, _("     --> "));
804             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
805          }
806          if (run->msgs) {
807             sendit(sock, _("     --> "));
808             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
809          }
810          /* If another Run record is chained in, go print it */
811          if (run->next) {
812             run = run->next;
813             goto next_run;
814          }
815       } else {
816          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
817       }
818       break;
819    case R_POOL:
820       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
821               res->res_pool.pool_type);
822       sendit(sock, _("      use_cat=%d use_once=%d cat_files=%d\n"),
823               res->res_pool.use_catalog, res->res_pool.use_volume_once,
824               res->res_pool.catalog_files);
825       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
826               res->res_pool.max_volumes, res->res_pool.AutoPrune,
827               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
828       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
829               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
830               res->res_pool.Recycle,
831               NPRT(res->res_pool.label_format));
832       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
833               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
834       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d MaxVolJobs=%d MaxVolFiles=%d\n"),
835               res->res_pool.recycle_oldest_volume,
836               res->res_pool.purge_oldest_volume,
837               res->res_pool.MaxVolJobs, res->res_pool.MaxVolFiles);
838       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
839               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
840               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
841               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
842       if (res->res_pool.NextPool) {
843          sendit(sock, _("  --> "));
844          dump_resource(-R_POOL, (RES *)res->res_pool.NextPool, sendit, sock);
845       }
846       if (res->res_pool.storage) {
847          STORE *store;
848          foreach_alist(store, res->res_pool.storage) {
849             sendit(sock, _("  --> "));
850             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
851          }
852       }
853       break;
854    case R_MSGS:
855       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
856       if (res->res_msgs.mail_cmd)
857          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
858       if (res->res_msgs.operator_cmd)
859          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
860       break;
861    default:
862       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
863       break;
864    }
865    if (recurse && res->res_dir.hdr.next) {
866       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
867    }
868 }
869
870 /*
871  * Free all the members of an INCEXE structure
872  */
873 static void free_incexe(INCEXE *incexe)
874 {
875    incexe->name_list.destroy();
876    for (int i=0; i<incexe->num_opts; i++) {
877       FOPTS *fopt = incexe->opts_list[i];
878       fopt->regex.destroy();
879       fopt->regexdir.destroy();
880       fopt->regexfile.destroy();
881       fopt->wild.destroy();
882       fopt->wilddir.destroy();
883       fopt->wildfile.destroy();
884       fopt->wildbase.destroy();
885       fopt->base.destroy();
886       fopt->fstype.destroy();
887       fopt->drivetype.destroy();
888       if (fopt->reader) {
889          free(fopt->reader);
890       }
891       if (fopt->writer) {
892          free(fopt->writer);
893       }
894       free(fopt);
895    }
896    if (incexe->opts_list) {
897       free(incexe->opts_list);
898    }
899    free(incexe);
900 }
901
902 /*
903  * Free memory of resource -- called when daemon terminates.
904  * NB, we don't need to worry about freeing any references
905  * to other resources as they will be freed when that
906  * resource chain is traversed.  Mainly we worry about freeing
907  * allocated strings (names).
908  */
909 void free_resource(RES *sres, int type)
910 {
911    int num;
912    RES *nres;                         /* next resource if linked */
913    URES *res = (URES *)sres;
914
915    if (res == NULL)
916       return;
917
918    /* common stuff -- free the resource name and description */
919    nres = (RES *)res->res_dir.hdr.next;
920    if (res->res_dir.hdr.name) {
921       free(res->res_dir.hdr.name);
922    }
923    if (res->res_dir.hdr.desc) {
924       free(res->res_dir.hdr.desc);
925    }
926
927    switch (type) {
928    case R_DIRECTOR:
929       if (res->res_dir.working_directory) {
930          free(res->res_dir.working_directory);
931       }
932       if (res->res_dir.scripts_directory) {
933          free((char *)res->res_dir.scripts_directory);
934       }
935       if (res->res_dir.pid_directory) {
936          free(res->res_dir.pid_directory);
937       }
938       if (res->res_dir.subsys_directory) {
939          free(res->res_dir.subsys_directory);
940       }
941       if (res->res_dir.password) {
942          free(res->res_dir.password);
943       }
944       if (res->res_dir.query_file) {
945          free(res->res_dir.query_file);
946       }
947       if (res->res_dir.DIRaddrs) {
948          free_addresses(res->res_dir.DIRaddrs);
949       }
950       if (res->res_dir.tls_ctx) { 
951          free_tls_context(res->res_dir.tls_ctx);
952       }
953       if (res->res_dir.tls_ca_certfile) {
954          free(res->res_dir.tls_ca_certfile);
955       }
956       if (res->res_dir.tls_ca_certdir) {
957          free(res->res_dir.tls_ca_certdir);
958       }
959       if (res->res_dir.tls_certfile) {
960          free(res->res_dir.tls_certfile);
961       }
962       if (res->res_dir.tls_keyfile) {
963          free(res->res_dir.tls_keyfile);
964       }
965       if (res->res_dir.tls_dhfile) {
966          free(res->res_dir.tls_dhfile);
967       }
968       if (res->res_dir.tls_allowed_cns) {
969          delete res->res_dir.tls_allowed_cns;
970       }
971       break;
972    case R_DEVICE:
973    case R_COUNTER:
974        break;
975    case R_CONSOLE:
976       if (res->res_con.password) {
977          free(res->res_con.password);
978       }
979       if (res->res_con.tls_ctx) { 
980          free_tls_context(res->res_con.tls_ctx);
981       }
982       if (res->res_con.tls_ca_certfile) {
983          free(res->res_con.tls_ca_certfile);
984       }
985       if (res->res_con.tls_ca_certdir) {
986          free(res->res_con.tls_ca_certdir);
987       }
988       if (res->res_con.tls_certfile) {
989          free(res->res_con.tls_certfile);
990       }
991       if (res->res_con.tls_keyfile) {
992          free(res->res_con.tls_keyfile);
993       }
994       if (res->res_con.tls_dhfile) {
995          free(res->res_con.tls_dhfile);
996       }
997       if (res->res_con.tls_allowed_cns) {
998          delete res->res_con.tls_allowed_cns;
999       }
1000       for (int i=0; i<Num_ACL; i++) {
1001          if (res->res_con.ACL_lists[i]) {
1002             delete res->res_con.ACL_lists[i];
1003             res->res_con.ACL_lists[i] = NULL;
1004          }
1005       }
1006       break;
1007    case R_CLIENT:
1008       if (res->res_client.address) {
1009          free(res->res_client.address);
1010       }
1011       if (res->res_client.password) {
1012          free(res->res_client.password);
1013       }
1014       if (res->res_client.tls_ctx) { 
1015          free_tls_context(res->res_client.tls_ctx);
1016       }
1017       if (res->res_client.tls_ca_certfile) {
1018          free(res->res_client.tls_ca_certfile);
1019       }
1020       if (res->res_client.tls_ca_certdir) {
1021          free(res->res_client.tls_ca_certdir);
1022       }
1023       if (res->res_client.tls_certfile) {
1024          free(res->res_client.tls_certfile);
1025       }
1026       if (res->res_client.tls_keyfile) {
1027          free(res->res_client.tls_keyfile);
1028       }
1029       break;
1030    case R_STORAGE:
1031       if (res->res_store.address) {
1032          free(res->res_store.address);
1033       }
1034       if (res->res_store.password) {
1035          free(res->res_store.password);
1036       }
1037       if (res->res_store.media_type) {
1038          free(res->res_store.media_type);
1039       }
1040       if (res->res_store.device) {
1041          delete res->res_store.device;
1042       }
1043       if (res->res_store.tls_ctx) { 
1044          free_tls_context(res->res_store.tls_ctx);
1045       }
1046       if (res->res_store.tls_ca_certfile) {
1047          free(res->res_store.tls_ca_certfile);
1048       }
1049       if (res->res_store.tls_ca_certdir) {
1050          free(res->res_store.tls_ca_certdir);
1051       }
1052       if (res->res_store.tls_certfile) {
1053          free(res->res_store.tls_certfile);
1054       }
1055       if (res->res_store.tls_keyfile) {
1056          free(res->res_store.tls_keyfile);
1057       }
1058       break;
1059    case R_CATALOG:
1060       if (res->res_cat.db_address) {
1061          free(res->res_cat.db_address);
1062       }
1063       if (res->res_cat.db_socket) {
1064          free(res->res_cat.db_socket);
1065       }
1066       if (res->res_cat.db_user) {
1067          free(res->res_cat.db_user);
1068       }
1069       if (res->res_cat.db_name) {
1070          free(res->res_cat.db_name);
1071       }
1072       if (res->res_cat.db_password) {
1073          free(res->res_cat.db_password);
1074       }
1075       break;
1076    case R_FILESET:
1077       if ((num=res->res_fs.num_includes)) {
1078          while (--num >= 0) {
1079             free_incexe(res->res_fs.include_items[num]);
1080          }
1081          free(res->res_fs.include_items);
1082       }
1083       res->res_fs.num_includes = 0;
1084       if ((num=res->res_fs.num_excludes)) {
1085          while (--num >= 0) {
1086             free_incexe(res->res_fs.exclude_items[num]);
1087          }
1088          free(res->res_fs.exclude_items);
1089       }
1090       res->res_fs.num_excludes = 0;
1091       break;
1092    case R_POOL:
1093       if (res->res_pool.pool_type) {
1094          free(res->res_pool.pool_type);
1095       }
1096       if (res->res_pool.label_format) {
1097          free(res->res_pool.label_format);
1098       }
1099       if (res->res_pool.cleaning_prefix) {
1100          free(res->res_pool.cleaning_prefix);
1101       }
1102       if (res->res_pool.storage) {
1103          delete res->res_pool.storage;
1104       }
1105       break;
1106    case R_SCHEDULE:
1107       if (res->res_sch.run) {
1108          RUN *nrun, *next;
1109          nrun = res->res_sch.run;
1110          while (nrun) {
1111             next = nrun->next;
1112             free(nrun);
1113             nrun = next;
1114          }
1115       }
1116       break;
1117    case R_JOB:
1118    case R_JOBDEFS:
1119       if (res->res_job.RestoreWhere) {
1120          free(res->res_job.RestoreWhere);
1121       }
1122       if (res->res_job.RestoreBootstrap) {
1123          free(res->res_job.RestoreBootstrap);
1124       }
1125       if (res->res_job.WriteBootstrap) {
1126          free(res->res_job.WriteBootstrap);
1127       }
1128       if (res->res_job.selection_pattern) {
1129          free(res->res_job.selection_pattern);
1130       }
1131       if (res->res_job.run_cmds) {
1132          delete res->res_job.run_cmds;
1133       }
1134       if (res->res_job.storage) {
1135          delete res->res_job.storage;
1136       }
1137       if (res->res_job.RunScripts) {
1138          free_runscripts(res->res_job.RunScripts);
1139          delete res->res_job.RunScripts;
1140       }
1141       break;
1142    case R_MSGS:
1143       if (res->res_msgs.mail_cmd) {
1144          free(res->res_msgs.mail_cmd);
1145       }
1146       if (res->res_msgs.operator_cmd) {
1147          free(res->res_msgs.operator_cmd);
1148       }
1149       free_msgs_res((MSGS *)res);  /* free message resource */
1150       res = NULL;
1151       break;
1152    default:
1153       printf(_("Unknown resource type %d in free_resource.\n"), type);
1154    }
1155    /* Common stuff again -- free the resource, recurse to next one */
1156    if (res) {
1157       free(res);
1158    }
1159    if (nres) {
1160       free_resource(nres, type);
1161    }
1162 }
1163
1164 /*
1165  * Save the new resource by chaining it into the head list for
1166  * the resource. If this is pass 2, we update any resource
1167  * pointers because they may not have been defined until
1168  * later in pass 1.
1169  */
1170 void save_resource(int type, RES_ITEM *items, int pass)
1171 {
1172    URES *res;
1173    int rindex = type - r_first;
1174    int i, size = 0;
1175    bool error = false;
1176
1177    /* Check Job requirements after applying JobDefs */
1178    if (type != R_JOB && type != R_JOBDEFS) {
1179       /*
1180        * Ensure that all required items are present
1181        */
1182       for (i=0; items[i].name; i++) {
1183          if (items[i].flags & ITEM_REQUIRED) {
1184             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1185                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1186                     items[i].name, resources[rindex]);
1187             }
1188          }
1189          /* If this triggers, take a look at lib/parse_conf.h */
1190          if (i >= MAX_RES_ITEMS) {
1191             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1192          }
1193       }
1194    } else if (type == R_JOB) {
1195       /*
1196        * Ensure that the name item is present
1197        */
1198       if (items[0].flags & ITEM_REQUIRED) {
1199          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1200              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1201                    items[0].name, resources[rindex]);
1202          }
1203       }
1204    }
1205
1206    /*
1207     * During pass 2 in each "store" routine, we looked up pointers
1208     * to all the resources referrenced in the current resource, now we
1209     * must copy their addresses from the static record to the allocated
1210     * record.
1211     */
1212    if (pass == 2) {
1213       switch (type) {
1214       /* Resources not containing a resource */
1215       case R_CATALOG:
1216       case R_MSGS:
1217       case R_FILESET:
1218       case R_DEVICE:
1219          break;
1220
1221       /*
1222        * Resources containing another resource or alist. First
1223        *  look up the resource which contains another resource. It
1224        *  was written during pass 1.  Then stuff in the pointers to
1225        *  the resources it contains, which were inserted this pass.
1226        *  Finally, it will all be stored back.
1227        */
1228       case R_POOL:
1229          /* Find resource saved in pass 1 */
1230          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1231             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1232          }
1233          /* Explicitly copy resource pointers from this pass (res_all) */
1234          res->res_pool.NextPool = res_all.res_pool.NextPool;
1235          res->res_pool.storage    = res_all.res_pool.storage;
1236          break;
1237       case R_CONSOLE:
1238          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1239             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1240          }
1241          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1242          break;
1243       case R_DIRECTOR:
1244          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1245             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1246          }
1247          res->res_dir.messages = res_all.res_dir.messages;
1248          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1249          break;
1250       case R_STORAGE:
1251          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1252             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1253                   res_all.res_dir.hdr.name);
1254          }
1255          /* we must explicitly copy the device alist pointer */
1256          res->res_store.device   = res_all.res_store.device;
1257          break;
1258       case R_JOB:
1259       case R_JOBDEFS:
1260          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1261             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1262                   res_all.res_dir.hdr.name);
1263          }
1264          res->res_job.messages   = res_all.res_job.messages;
1265          res->res_job.schedule   = res_all.res_job.schedule;
1266          res->res_job.client     = res_all.res_job.client;
1267          res->res_job.fileset    = res_all.res_job.fileset;
1268          res->res_job.storage    = res_all.res_job.storage;
1269          res->res_job.pool       = res_all.res_job.pool;
1270          res->res_job.full_pool  = res_all.res_job.full_pool;
1271          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1272          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1273          res->res_job.verify_job = res_all.res_job.verify_job;
1274          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1275          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1276          res->res_job.RunScripts = res_all.res_job.RunScripts;
1277          break;
1278       case R_COUNTER:
1279          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1280             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1281          }
1282          res->res_counter.Catalog = res_all.res_counter.Catalog;
1283          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1284          break;
1285
1286       case R_CLIENT:
1287          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1288             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1289          }
1290          res->res_client.catalog = res_all.res_client.catalog;
1291          break;
1292       case R_SCHEDULE:
1293          /*
1294           * Schedule is a bit different in that it contains a RUN record
1295           * chain which isn't a "named" resource. This chain was linked
1296           * in by run_conf.c during pass 2, so here we jam the pointer
1297           * into the Schedule resource.
1298           */
1299          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1300             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1301          }
1302          res->res_sch.run = res_all.res_sch.run;
1303          break;
1304       default:
1305          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1306          error = true;
1307          break;
1308       }
1309       /* Note, the resource name was already saved during pass 1,
1310        * so here, we can just release it.
1311        */
1312       if (res_all.res_dir.hdr.name) {
1313          free(res_all.res_dir.hdr.name);
1314          res_all.res_dir.hdr.name = NULL;
1315       }
1316       if (res_all.res_dir.hdr.desc) {
1317          free(res_all.res_dir.hdr.desc);
1318          res_all.res_dir.hdr.desc = NULL;
1319       }
1320       return;
1321    }
1322
1323    /*
1324     * The following code is only executed during pass 1
1325     */
1326    switch (type) {
1327    case R_DIRECTOR:
1328       size = sizeof(DIRRES);
1329       break;
1330    case R_CONSOLE:
1331       size = sizeof(CONRES);
1332       break;
1333    case R_CLIENT:
1334       size =sizeof(CLIENT);
1335       break;
1336    case R_STORAGE:
1337       size = sizeof(STORE);
1338       break;
1339    case R_CATALOG:
1340       size = sizeof(CAT);
1341       break;
1342    case R_JOB:
1343    case R_JOBDEFS:
1344       size = sizeof(JOB);
1345       break;
1346    case R_FILESET:
1347       size = sizeof(FILESET);
1348       break;
1349    case R_SCHEDULE:
1350       size = sizeof(SCHED);
1351       break;
1352    case R_POOL:
1353       size = sizeof(POOL);
1354       break;
1355    case R_MSGS:
1356       size = sizeof(MSGS);
1357       break;
1358    case R_COUNTER:
1359       size = sizeof(COUNTER);
1360       break;
1361    case R_DEVICE:
1362       error = true;
1363       break;
1364    default:
1365       printf(_("Unknown resource type %d in save_resrouce.\n"), type);
1366       error = true; 
1367       break;
1368    }
1369    /* Common */
1370    if (!error) {
1371       res = (URES *)malloc(size);
1372       memcpy(res, &res_all, size);
1373       if (!res_head[rindex]) {
1374          res_head[rindex] = (RES *)res; /* store first entry */
1375          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1376                res->res_dir.hdr.name, rindex);
1377       } else {
1378          RES *next;
1379          if (res->res_dir.hdr.name == NULL) {
1380             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1381                   resources[rindex]);
1382          }   
1383          /* Add new res to end of chain */
1384          for (next=res_head[rindex]; next->next; next=next->next) {
1385             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1386                Emsg2(M_ERROR_TERM, 0,
1387                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1388                   resources[rindex].name, res->res_dir.hdr.name);
1389             }
1390          }
1391          next->next = (RES *)res;
1392          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1393                res->res_dir.hdr.name, rindex, pass);
1394       }
1395    }
1396 }
1397
1398 /*
1399  * Store Device. Note, the resource is created upon the
1400  *  first reference. The details of the resource are obtained
1401  *  later from the SD.
1402  */
1403 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1404 {
1405    int token;
1406    URES *res;
1407    int rindex = R_DEVICE - r_first;
1408    int size = sizeof(DEVICE);
1409    bool found = false;
1410
1411    if (pass == 1) {
1412       token = lex_get_token(lc, T_NAME);
1413       if (!res_head[rindex]) {
1414          res = (URES *)malloc(size);
1415          memset(res, 0, size);
1416          res->res_dev.hdr.name = bstrdup(lc->str);
1417          res_head[rindex] = (RES *)res; /* store first entry */
1418          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1419                res->res_dir.hdr.name, rindex);
1420       } else {
1421          RES *next;
1422          /* See if it is already defined */
1423          for (next=res_head[rindex]; next->next; next=next->next) {
1424             if (strcmp(next->name, lc->str) == 0) {
1425                found = true;
1426                break;
1427             }
1428          }
1429          if (!found) {
1430             res = (URES *)malloc(size);
1431             memset(res, 0, size);
1432             res->res_dev.hdr.name = bstrdup(lc->str);
1433             next->next = (RES *)res;
1434             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1435                res->res_dir.hdr.name, rindex, pass);
1436          }
1437       }
1438
1439       scan_to_eol(lc);
1440       set_bit(index, res_all.hdr.item_present);
1441    } else {
1442       store_alist_res(lc, item, index, pass);
1443    }
1444 }
1445
1446 /*
1447  * Store JobType (backup, verify, restore)
1448  *
1449  */
1450 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1451 {
1452    int token, i;
1453
1454    token = lex_get_token(lc, T_NAME);
1455    /* Store the type both pass 1 and pass 2 */
1456    for (i=0; migtypes[i].type_name; i++) {
1457       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1458          *(int *)(item->value) = migtypes[i].job_type;
1459          i = 0;
1460          break;
1461       }
1462    }
1463    if (i != 0) {
1464       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1465    }
1466    scan_to_eol(lc);
1467    set_bit(index, res_all.hdr.item_present);
1468 }
1469
1470
1471
1472 /*
1473  * Store JobType (backup, verify, restore)
1474  *
1475  */
1476 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1477 {
1478    int token, i;
1479
1480    token = lex_get_token(lc, T_NAME);
1481    /* Store the type both pass 1 and pass 2 */
1482    for (i=0; jobtypes[i].type_name; i++) {
1483       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1484          *(int *)(item->value) = jobtypes[i].job_type;
1485          i = 0;
1486          break;
1487       }
1488    }
1489    if (i != 0) {
1490       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1491    }
1492    scan_to_eol(lc);
1493    set_bit(index, res_all.hdr.item_present);
1494 }
1495
1496 /*
1497  * Store Job Level (Full, Incremental, ...)
1498  *
1499  */
1500 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1501 {
1502    int token, i;
1503
1504    token = lex_get_token(lc, T_NAME);
1505    /* Store the level pass 2 so that type is defined */
1506    for (i=0; joblevels[i].level_name; i++) {
1507       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1508          *(int *)(item->value) = joblevels[i].level;
1509          i = 0;
1510          break;
1511       }
1512    }
1513    if (i != 0) {
1514       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1515    }
1516    scan_to_eol(lc);
1517    set_bit(index, res_all.hdr.item_present);
1518 }
1519
1520
1521 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1522 {
1523    int token, i;
1524    token = lex_get_token(lc, T_NAME);
1525    /* Scan Replacement options */
1526    for (i=0; ReplaceOptions[i].name; i++) {
1527       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1528          *(int *)(item->value) = ReplaceOptions[i].token;
1529          i = 0;
1530          break;
1531       }
1532    }
1533    if (i != 0) {
1534       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1535    }
1536    scan_to_eol(lc);
1537    set_bit(index, res_all.hdr.item_present);
1538 }
1539
1540 /*
1541  * Store ACL (access control list)
1542  *
1543  */
1544 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1545 {
1546    int token;
1547
1548    for (;;) {
1549       token = lex_get_token(lc, T_NAME);
1550       if (pass == 1) {
1551          if (((alist **)item->value)[item->code] == NULL) {
1552             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1553             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1554          }
1555          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1556          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1557       }
1558       token = lex_get_token(lc, T_ALL);
1559       if (token == T_COMMA) {
1560          continue;                    /* get another ACL */
1561       }
1562       break;
1563    }
1564    set_bit(index, res_all.hdr.item_present);
1565 }
1566
1567 /* We build RunScripts items here */
1568 static RUNSCRIPT res_runscript;
1569
1570 /* Store a runscript->when in a bit field */
1571 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1572 {
1573    lex_get_token(lc, T_NAME);
1574
1575    if (strcasecmp(lc->str, "before") == 0) {
1576       *(int *)(item->value) = SCRIPT_Before ;
1577    } else if (strcasecmp(lc->str, "after") == 0) {
1578       *(int *)(item->value) = SCRIPT_After;
1579    } else if (strcasecmp(lc->str, "always") == 0) {
1580       *(int *)(item->value) = SCRIPT_Any;
1581    } else {
1582       scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1583    }
1584    scan_to_eol(lc);
1585 }
1586
1587 /* Store a runscript->target
1588  * 
1589  */
1590 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1591 {
1592    lex_get_token(lc, T_STRING);
1593
1594    if (pass == 2) {
1595       if (strcmp(lc->str, "%c") == 0) {
1596          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1597       } else if (strcmp(lc->str, "yes") == 0) {
1598          ((RUNSCRIPT*) item->value)->set_target("%c");
1599       } else if (strcmp(lc->str, "no") == 0) {
1600          /* store nothing, run on director */
1601       } else {
1602          RES *res = GetResWithName(R_CLIENT, lc->str);
1603          if (res == NULL) {
1604             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1605                       lc->str, lc->line_no, lc->line);
1606          }
1607
1608          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1609       }
1610    }
1611    scan_to_eol(lc);
1612 }
1613
1614 /* Store a runscript->command in a bit field
1615  * 
1616  */
1617 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1618 {
1619    lex_get_token(lc, T_STRING);
1620
1621    if (pass == 2) {
1622       ((RUNSCRIPT*) item->value)->set_command(lc->str);
1623    }
1624    scan_to_eol(lc);
1625 }
1626
1627 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1628 {
1629    lex_get_token(lc, T_STRING);
1630    alist **runscripts = (alist **)(item->value) ;
1631
1632    if (pass == 2) {
1633       RUNSCRIPT *script = new_runscript();
1634
1635       script->set_command(lc->str);
1636
1637       /* TODO: remove all script->old_proto with bacula 1.42 */
1638
1639       if (strcmp(item->name, "runbeforejob") == 0) {
1640          script->when = SCRIPT_Before;
1641          script->abort_on_error = true;
1642
1643       } else if (strcmp(item->name, "runafterjob") == 0) {
1644          script->when = SCRIPT_After;
1645          script->on_success = true;
1646          script->on_failure = false;
1647          
1648       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1649          script->old_proto = true;
1650          script->when = SCRIPT_After;
1651          script->set_target("%c");
1652          script->on_success = true;
1653          script->on_failure = false;
1654
1655       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1656          script->old_proto = true;
1657          script->when = SCRIPT_Before;
1658          script->set_target("%c");
1659          script->abort_on_error = true;
1660
1661       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1662          script->when = SCRIPT_After;
1663          script->on_failure = true;
1664          script->on_success = false;
1665       }
1666
1667       if (*runscripts == NULL) {
1668         *runscripts = New(alist(10, not_owned_by_alist));
1669       }
1670       
1671       (*runscripts)->append(script);
1672       script->debug();
1673    }
1674
1675    scan_to_eol(lc);
1676 }
1677
1678 /*
1679  * new RunScript items
1680  *   name             handler         value                                code flags default_value
1681  */
1682 static RES_ITEM runscript_items[] = {
1683    {"command", store_runscript_cmd,    (char **)&res_runscript,           0,  ITEM_REQUIRED, 0}, 
1684    {"target", store_runscript_target,  (char **)&res_runscript,            0,  0, 0}, 
1685    {"runsonsuccess",    store_bool,    (char **)&res_runscript.on_success, 0,  0, 0},
1686    {"runsonfailure",    store_bool,    (char **)&res_runscript.on_failure, 0,  0, 0},
1687    {"abortjobonerror", store_bool,     (char **)&res_runscript.abort_on_error, 0, 0,   0},
1688    {"runswhen", store_runscript_when,  (char **)&res_runscript.when,       0,  0, 0},
1689    {"runsonclient", store_runscript_target,  (char **)&res_runscript,       0,  0, 0}, /* TODO */
1690    {NULL, NULL, {0}, 0, 0, 0}
1691 };
1692
1693 /*
1694  * Store RunScript info
1695  *
1696  *  Note, when this routine is called, we are inside a Job
1697  *  resource.  We treat the RunScript like a sort of
1698  *  mini-resource within the Job resource.
1699  */
1700 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1701 {
1702    int token, i;
1703    alist **runscripts = (alist **)(item->value) ;
1704
1705    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1706
1707    res_runscript.reset_default();      /* setting on_success, on_failure, abort_on_error */
1708    
1709    token = lex_get_token(lc, T_SKIP_EOL);
1710    
1711    if (token != T_BOB) {
1712       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1713    }
1714    
1715    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1716       if (token == T_EOB) {
1717         break;
1718       }
1719       if (token != T_IDENTIFIER) {
1720         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1721       }
1722       for (i=0; runscript_items[i].name; i++) {
1723         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1724            token = lex_get_token(lc, T_SKIP_EOL);
1725            if (token != T_EQUALS) {
1726               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1727            }
1728            
1729            /* Call item handler */
1730            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1731            i = -1;
1732            break;
1733         }
1734       }
1735       
1736       if (i >=0) {
1737         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1738       }
1739    }
1740
1741    if (pass == 2) {
1742       if (res_runscript.command == NULL) {
1743          scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1744                    "command", "runscript");
1745       }
1746
1747       /* run on client by default */
1748       if (res_runscript.target == NULL) {
1749          res_runscript.set_target("%c");
1750       }
1751
1752       RUNSCRIPT *script = new_runscript();
1753       memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1754       
1755       if (*runscripts == NULL) {
1756         *runscripts = New(alist(10, not_owned_by_alist));
1757       }
1758       
1759       (*runscripts)->append(script);
1760       script->debug();
1761    }
1762
1763    scan_to_eol(lc);
1764    set_bit(index, res_all.hdr.item_present);
1765 }