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