]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Turn off debug code in jobq.c
[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             for (k=0; k<fo->regex.size(); k++) {
667                sendit(sock, "      R %s\n", fo->regex.get(k));
668             }
669             for (k=0; k<fo->regexdir.size(); k++) {
670                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
671             }
672             for (k=0; k<fo->regexfile.size(); k++) {
673                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
674             }
675             for (k=0; k<fo->wild.size(); k++) {
676                sendit(sock, "      W %s\n", fo->wild.get(k));
677             }
678             for (k=0; k<fo->wilddir.size(); k++) {
679                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
680             }
681             for (k=0; k<fo->wildfile.size(); k++) {
682                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
683             }
684             for (k=0; k<fo->base.size(); k++) {
685                sendit(sock, "      B %s\n", fo->base.get(k));
686             }
687             for (k=0; k<fo->fstype.size(); k++) {
688                sendit(sock, "      X %s\n", fo->fstype.get(k));
689             }
690             if (fo->reader) {
691                sendit(sock, "      D %s\n", fo->reader);
692             }
693             if (fo->writer) {
694                sendit(sock, "      T %s\n", fo->writer);
695             }
696             sendit(sock, "      N\n");
697          }
698          for (j=0; j<incexe->name_list.size(); j++) {
699             sendit(sock, "      I %s\n", incexe->name_list.get(j));
700          }
701          if (incexe->name_list.size()) {
702             sendit(sock, "      N\n");
703          }
704       }
705
706       for (i=0; i<res->res_fs.num_excludes; i++) {
707          INCEXE *incexe = res->res_fs.exclude_items[i];
708          for (j=0; j<incexe->name_list.size(); j++) {
709             sendit(sock, "      E %s\n", incexe->name_list.get(j));
710          }
711          if (incexe->name_list.size()) {
712             sendit(sock, "      N\n");
713          }
714       }
715       break;
716    }
717    case R_SCHEDULE:
718       if (res->res_sch.run) {
719          int i;
720          RUN *run = res->res_sch.run;
721          char buf[1000], num[30];
722          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
723          if (!run) {
724             break;
725          }
726 next_run:
727          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
728          bstrncpy(buf, _("      hour="), sizeof(buf));
729          for (i=0; i<24; i++) {
730             if (bit_is_set(i, run->hour)) {
731                bsnprintf(num, sizeof(num), "%d ", i);
732                bstrncat(buf, num, sizeof(buf));
733             }
734          }
735          bstrncat(buf, "\n", sizeof(buf));
736          sendit(sock, buf);
737          bstrncpy(buf, _("      mday="), sizeof(buf));
738          for (i=0; i<31; i++) {
739             if (bit_is_set(i, run->mday)) {
740                bsnprintf(num, sizeof(num), "%d ", i);
741                bstrncat(buf, num, sizeof(buf));
742             }
743          }
744          bstrncat(buf, "\n", sizeof(buf));
745          sendit(sock, buf);
746          bstrncpy(buf, _("      month="), sizeof(buf));
747          for (i=0; i<12; i++) {
748             if (bit_is_set(i, run->month)) {
749                bsnprintf(num, sizeof(num), "%d ", i);
750                bstrncat(buf, num, sizeof(buf));
751             }
752          }
753          bstrncat(buf, "\n", sizeof(buf));
754          sendit(sock, buf);
755          bstrncpy(buf, _("      wday="), sizeof(buf));
756          for (i=0; i<7; i++) {
757             if (bit_is_set(i, run->wday)) {
758                bsnprintf(num, sizeof(num), "%d ", i);
759                bstrncat(buf, num, sizeof(buf));
760             }
761          }
762          bstrncat(buf, "\n", sizeof(buf));
763          sendit(sock, buf);
764          bstrncpy(buf, _("      wom="), sizeof(buf));
765          for (i=0; i<5; i++) {
766             if (bit_is_set(i, run->wom)) {
767                bsnprintf(num, sizeof(num), "%d ", i);
768                bstrncat(buf, num, sizeof(buf));
769             }
770          }
771          bstrncat(buf, "\n", sizeof(buf));
772          sendit(sock, buf);
773          bstrncpy(buf, _("      woy="), sizeof(buf));
774          for (i=0; i<54; i++) {
775             if (bit_is_set(i, run->woy)) {
776                bsnprintf(num, sizeof(num), "%d ", i);
777                bstrncat(buf, num, sizeof(buf));
778             }
779          }
780          bstrncat(buf, "\n", sizeof(buf));
781          sendit(sock, buf);
782          sendit(sock, _("      mins=%d\n"), run->minute);
783          if (run->pool) {
784             sendit(sock, _("     --> "));
785             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
786          }
787          if (run->storage) {
788             sendit(sock, _("     --> "));
789             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
790          }
791          if (run->msgs) {
792             sendit(sock, _("     --> "));
793             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
794          }
795          /* If another Run record is chained in, go print it */
796          if (run->next) {
797             run = run->next;
798             goto next_run;
799          }
800       } else {
801          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
802       }
803       break;
804    case R_POOL:
805       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
806               res->res_pool.pool_type);
807       sendit(sock, _("      use_cat=%d use_once=%d cat_files=%d\n"),
808               res->res_pool.use_catalog, res->res_pool.use_volume_once,
809               res->res_pool.catalog_files);
810       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
811               res->res_pool.max_volumes, res->res_pool.AutoPrune,
812               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
813       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
814               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
815               res->res_pool.Recycle,
816               NPRT(res->res_pool.label_format));
817       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
818               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
819       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d MaxVolJobs=%d MaxVolFiles=%d\n"),
820               res->res_pool.recycle_oldest_volume,
821               res->res_pool.purge_oldest_volume,
822               res->res_pool.MaxVolJobs, res->res_pool.MaxVolFiles);
823       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
824               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
825               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
826               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
827       if (res->res_pool.NextPool) {
828          sendit(sock, _("  --> "));
829          dump_resource(-R_POOL, (RES *)res->res_pool.NextPool, sendit, sock);
830       }
831       if (res->res_pool.storage) {
832          STORE *store;
833          foreach_alist(store, res->res_pool.storage) {
834             sendit(sock, _("  --> "));
835             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
836          }
837       }
838       break;
839    case R_MSGS:
840       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
841       if (res->res_msgs.mail_cmd)
842          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
843       if (res->res_msgs.operator_cmd)
844          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
845       break;
846    default:
847       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
848       break;
849    }
850    if (recurse && res->res_dir.hdr.next) {
851       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
852    }
853 }
854
855 /*
856  * Free all the members of an INCEXE structure
857  */
858 static void free_incexe(INCEXE *incexe)
859 {
860    incexe->name_list.destroy();
861    for (int i=0; i<incexe->num_opts; i++) {
862       FOPTS *fopt = incexe->opts_list[i];
863       fopt->regex.destroy();
864       fopt->regexdir.destroy();
865       fopt->regexfile.destroy();
866       fopt->wild.destroy();
867       fopt->wilddir.destroy();
868       fopt->wildfile.destroy();
869       fopt->base.destroy();
870       fopt->fstype.destroy();
871       if (fopt->reader) {
872          free(fopt->reader);
873       }
874       if (fopt->writer) {
875          free(fopt->writer);
876       }
877       free(fopt);
878    }
879    if (incexe->opts_list) {
880       free(incexe->opts_list);
881    }
882    free(incexe);
883 }
884
885 /*
886  * Free memory of resource -- called when daemon terminates.
887  * NB, we don't need to worry about freeing any references
888  * to other resources as they will be freed when that
889  * resource chain is traversed.  Mainly we worry about freeing
890  * allocated strings (names).
891  */
892 void free_resource(RES *sres, int type)
893 {
894    int num;
895    RES *nres;                         /* next resource if linked */
896    URES *res = (URES *)sres;
897
898    if (res == NULL)
899       return;
900
901    /* common stuff -- free the resource name and description */
902    nres = (RES *)res->res_dir.hdr.next;
903    if (res->res_dir.hdr.name) {
904       free(res->res_dir.hdr.name);
905    }
906    if (res->res_dir.hdr.desc) {
907       free(res->res_dir.hdr.desc);
908    }
909
910    switch (type) {
911    case R_DIRECTOR:
912       if (res->res_dir.working_directory) {
913          free(res->res_dir.working_directory);
914       }
915       if (res->res_dir.scripts_directory) {
916          free((char *)res->res_dir.scripts_directory);
917       }
918       if (res->res_dir.pid_directory) {
919          free(res->res_dir.pid_directory);
920       }
921       if (res->res_dir.subsys_directory) {
922          free(res->res_dir.subsys_directory);
923       }
924       if (res->res_dir.password) {
925          free(res->res_dir.password);
926       }
927       if (res->res_dir.query_file) {
928          free(res->res_dir.query_file);
929       }
930       if (res->res_dir.DIRaddrs) {
931          free_addresses(res->res_dir.DIRaddrs);
932       }
933       if (res->res_dir.tls_ctx) { 
934          free_tls_context(res->res_dir.tls_ctx);
935       }
936       if (res->res_dir.tls_ca_certfile) {
937          free(res->res_dir.tls_ca_certfile);
938       }
939       if (res->res_dir.tls_ca_certdir) {
940          free(res->res_dir.tls_ca_certdir);
941       }
942       if (res->res_dir.tls_certfile) {
943          free(res->res_dir.tls_certfile);
944       }
945       if (res->res_dir.tls_keyfile) {
946          free(res->res_dir.tls_keyfile);
947       }
948       if (res->res_dir.tls_dhfile) {
949          free(res->res_dir.tls_dhfile);
950       }
951       if (res->res_dir.tls_allowed_cns) {
952          delete res->res_dir.tls_allowed_cns;
953       }
954       break;
955    case R_DEVICE:
956    case R_COUNTER:
957        break;
958    case R_CONSOLE:
959       if (res->res_con.password) {
960          free(res->res_con.password);
961       }
962       if (res->res_con.tls_ctx) { 
963          free_tls_context(res->res_con.tls_ctx);
964       }
965       if (res->res_con.tls_ca_certfile) {
966          free(res->res_con.tls_ca_certfile);
967       }
968       if (res->res_con.tls_ca_certdir) {
969          free(res->res_con.tls_ca_certdir);
970       }
971       if (res->res_con.tls_certfile) {
972          free(res->res_con.tls_certfile);
973       }
974       if (res->res_con.tls_keyfile) {
975          free(res->res_con.tls_keyfile);
976       }
977       if (res->res_con.tls_dhfile) {
978          free(res->res_con.tls_dhfile);
979       }
980       if (res->res_con.tls_allowed_cns) {
981          delete res->res_con.tls_allowed_cns;
982       }
983       for (int i=0; i<Num_ACL; i++) {
984          if (res->res_con.ACL_lists[i]) {
985             delete res->res_con.ACL_lists[i];
986             res->res_con.ACL_lists[i] = NULL;
987          }
988       }
989       break;
990    case R_CLIENT:
991       if (res->res_client.address) {
992          free(res->res_client.address);
993       }
994       if (res->res_client.password) {
995          free(res->res_client.password);
996       }
997       if (res->res_client.tls_ctx) { 
998          free_tls_context(res->res_client.tls_ctx);
999       }
1000       if (res->res_client.tls_ca_certfile) {
1001          free(res->res_client.tls_ca_certfile);
1002       }
1003       if (res->res_client.tls_ca_certdir) {
1004          free(res->res_client.tls_ca_certdir);
1005       }
1006       if (res->res_client.tls_certfile) {
1007          free(res->res_client.tls_certfile);
1008       }
1009       if (res->res_client.tls_keyfile) {
1010          free(res->res_client.tls_keyfile);
1011       }
1012       break;
1013    case R_STORAGE:
1014       if (res->res_store.address) {
1015          free(res->res_store.address);
1016       }
1017       if (res->res_store.password) {
1018          free(res->res_store.password);
1019       }
1020       if (res->res_store.media_type) {
1021          free(res->res_store.media_type);
1022       }
1023       if (res->res_store.device) {
1024          delete res->res_store.device;
1025       }
1026       if (res->res_store.tls_ctx) { 
1027          free_tls_context(res->res_store.tls_ctx);
1028       }
1029       if (res->res_store.tls_ca_certfile) {
1030          free(res->res_store.tls_ca_certfile);
1031       }
1032       if (res->res_store.tls_ca_certdir) {
1033          free(res->res_store.tls_ca_certdir);
1034       }
1035       if (res->res_store.tls_certfile) {
1036          free(res->res_store.tls_certfile);
1037       }
1038       if (res->res_store.tls_keyfile) {
1039          free(res->res_store.tls_keyfile);
1040       }
1041       break;
1042    case R_CATALOG:
1043       if (res->res_cat.db_address) {
1044          free(res->res_cat.db_address);
1045       }
1046       if (res->res_cat.db_socket) {
1047          free(res->res_cat.db_socket);
1048       }
1049       if (res->res_cat.db_user) {
1050          free(res->res_cat.db_user);
1051       }
1052       if (res->res_cat.db_name) {
1053          free(res->res_cat.db_name);
1054       }
1055       if (res->res_cat.db_password) {
1056          free(res->res_cat.db_password);
1057       }
1058       break;
1059    case R_FILESET:
1060       if ((num=res->res_fs.num_includes)) {
1061          while (--num >= 0) {
1062             free_incexe(res->res_fs.include_items[num]);
1063          }
1064          free(res->res_fs.include_items);
1065       }
1066       res->res_fs.num_includes = 0;
1067       if ((num=res->res_fs.num_excludes)) {
1068          while (--num >= 0) {
1069             free_incexe(res->res_fs.exclude_items[num]);
1070          }
1071          free(res->res_fs.exclude_items);
1072       }
1073       res->res_fs.num_excludes = 0;
1074       break;
1075    case R_POOL:
1076       if (res->res_pool.pool_type) {
1077          free(res->res_pool.pool_type);
1078       }
1079       if (res->res_pool.label_format) {
1080          free(res->res_pool.label_format);
1081       }
1082       if (res->res_pool.cleaning_prefix) {
1083          free(res->res_pool.cleaning_prefix);
1084       }
1085       if (res->res_pool.storage) {
1086          delete res->res_pool.storage;
1087       }
1088       break;
1089    case R_SCHEDULE:
1090       if (res->res_sch.run) {
1091          RUN *nrun, *next;
1092          nrun = res->res_sch.run;
1093          while (nrun) {
1094             next = nrun->next;
1095             free(nrun);
1096             nrun = next;
1097          }
1098       }
1099       break;
1100    case R_JOB:
1101    case R_JOBDEFS:
1102       if (res->res_job.RestoreWhere) {
1103          free(res->res_job.RestoreWhere);
1104       }
1105       if (res->res_job.RestoreBootstrap) {
1106          free(res->res_job.RestoreBootstrap);
1107       }
1108       if (res->res_job.WriteBootstrap) {
1109          free(res->res_job.WriteBootstrap);
1110       }
1111       if (res->res_job.selection_pattern) {
1112          free(res->res_job.selection_pattern);
1113       }
1114       if (res->res_job.run_cmds) {
1115          delete res->res_job.run_cmds;
1116       }
1117       if (res->res_job.storage) {
1118          delete res->res_job.storage;
1119       }
1120       if (res->res_job.RunScripts) {
1121          free_runscripts(res->res_job.RunScripts);
1122          delete res->res_job.RunScripts;
1123       }
1124       break;
1125    case R_MSGS:
1126       if (res->res_msgs.mail_cmd) {
1127          free(res->res_msgs.mail_cmd);
1128       }
1129       if (res->res_msgs.operator_cmd) {
1130          free(res->res_msgs.operator_cmd);
1131       }
1132       free_msgs_res((MSGS *)res);  /* free message resource */
1133       res = NULL;
1134       break;
1135    default:
1136       printf(_("Unknown resource type %d in free_resource.\n"), type);
1137    }
1138    /* Common stuff again -- free the resource, recurse to next one */
1139    if (res) {
1140       free(res);
1141    }
1142    if (nres) {
1143       free_resource(nres, type);
1144    }
1145 }
1146
1147 /*
1148  * Save the new resource by chaining it into the head list for
1149  * the resource. If this is pass 2, we update any resource
1150  * pointers because they may not have been defined until
1151  * later in pass 1.
1152  */
1153 void save_resource(int type, RES_ITEM *items, int pass)
1154 {
1155    URES *res;
1156    int rindex = type - r_first;
1157    int i, size = 0;
1158    bool error = false;
1159
1160    /* Check Job requirements after applying JobDefs */
1161    if (type != R_JOB && type != R_JOBDEFS) {
1162       /*
1163        * Ensure that all required items are present
1164        */
1165       for (i=0; items[i].name; i++) {
1166          if (items[i].flags & ITEM_REQUIRED) {
1167             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1168                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1169                     items[i].name, resources[rindex]);
1170             }
1171          }
1172          /* If this triggers, take a look at lib/parse_conf.h */
1173          if (i >= MAX_RES_ITEMS) {
1174             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1175          }
1176       }
1177    } else if (type == R_JOB) {
1178       /*
1179        * Ensure that the name item is present
1180        */
1181       if (items[0].flags & ITEM_REQUIRED) {
1182          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1183              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1184                    items[0].name, resources[rindex]);
1185          }
1186       }
1187    }
1188
1189    /*
1190     * During pass 2 in each "store" routine, we looked up pointers
1191     * to all the resources referrenced in the current resource, now we
1192     * must copy their addresses from the static record to the allocated
1193     * record.
1194     */
1195    if (pass == 2) {
1196       switch (type) {
1197       /* Resources not containing a resource */
1198       case R_CATALOG:
1199       case R_MSGS:
1200       case R_FILESET:
1201       case R_DEVICE:
1202          break;
1203
1204       /*
1205        * Resources containing another resource or alist. First
1206        *  look up the resource which contains another resource. It
1207        *  was written during pass 1.  Then stuff in the pointers to
1208        *  the resources it contains, which were inserted this pass.
1209        *  Finally, it will all be stored back.
1210        */
1211       case R_POOL:
1212          /* Find resource saved in pass 1 */
1213          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1214             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1215          }
1216          /* Explicitly copy resource pointers from this pass (res_all) */
1217          res->res_pool.NextPool = res_all.res_pool.NextPool;
1218          res->res_pool.storage    = res_all.res_pool.storage;
1219          break;
1220       case R_CONSOLE:
1221          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1222             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1223          }
1224          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1225          break;
1226       case R_DIRECTOR:
1227          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1228             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1229          }
1230          res->res_dir.messages = res_all.res_dir.messages;
1231          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1232          break;
1233       case R_STORAGE:
1234          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1235             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1236                   res_all.res_dir.hdr.name);
1237          }
1238          /* we must explicitly copy the device alist pointer */
1239          res->res_store.device   = res_all.res_store.device;
1240          break;
1241       case R_JOB:
1242       case R_JOBDEFS:
1243          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1244             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1245                   res_all.res_dir.hdr.name);
1246          }
1247          res->res_job.messages   = res_all.res_job.messages;
1248          res->res_job.schedule   = res_all.res_job.schedule;
1249          res->res_job.client     = res_all.res_job.client;
1250          res->res_job.fileset    = res_all.res_job.fileset;
1251          res->res_job.storage    = res_all.res_job.storage;
1252          res->res_job.pool       = res_all.res_job.pool;
1253          res->res_job.full_pool  = res_all.res_job.full_pool;
1254          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1255          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1256          res->res_job.verify_job = res_all.res_job.verify_job;
1257          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1258          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1259          res->res_job.RunScripts = res_all.res_job.RunScripts;
1260          break;
1261       case R_COUNTER:
1262          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1263             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1264          }
1265          res->res_counter.Catalog = res_all.res_counter.Catalog;
1266          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1267          break;
1268
1269       case R_CLIENT:
1270          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1271             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1272          }
1273          res->res_client.catalog = res_all.res_client.catalog;
1274          break;
1275       case R_SCHEDULE:
1276          /*
1277           * Schedule is a bit different in that it contains a RUN record
1278           * chain which isn't a "named" resource. This chain was linked
1279           * in by run_conf.c during pass 2, so here we jam the pointer
1280           * into the Schedule resource.
1281           */
1282          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1283             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1284          }
1285          res->res_sch.run = res_all.res_sch.run;
1286          break;
1287       default:
1288          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1289          error = true;
1290          break;
1291       }
1292       /* Note, the resource name was already saved during pass 1,
1293        * so here, we can just release it.
1294        */
1295       if (res_all.res_dir.hdr.name) {
1296          free(res_all.res_dir.hdr.name);
1297          res_all.res_dir.hdr.name = NULL;
1298       }
1299       if (res_all.res_dir.hdr.desc) {
1300          free(res_all.res_dir.hdr.desc);
1301          res_all.res_dir.hdr.desc = NULL;
1302       }
1303       return;
1304    }
1305
1306    /*
1307     * The following code is only executed during pass 1
1308     */
1309    switch (type) {
1310    case R_DIRECTOR:
1311       size = sizeof(DIRRES);
1312       break;
1313    case R_CONSOLE:
1314       size = sizeof(CONRES);
1315       break;
1316    case R_CLIENT:
1317       size =sizeof(CLIENT);
1318       break;
1319    case R_STORAGE:
1320       size = sizeof(STORE);
1321       break;
1322    case R_CATALOG:
1323       size = sizeof(CAT);
1324       break;
1325    case R_JOB:
1326    case R_JOBDEFS:
1327       size = sizeof(JOB);
1328       break;
1329    case R_FILESET:
1330       size = sizeof(FILESET);
1331       break;
1332    case R_SCHEDULE:
1333       size = sizeof(SCHED);
1334       break;
1335    case R_POOL:
1336       size = sizeof(POOL);
1337       break;
1338    case R_MSGS:
1339       size = sizeof(MSGS);
1340       break;
1341    case R_COUNTER:
1342       size = sizeof(COUNTER);
1343       break;
1344    case R_DEVICE:
1345       error = true;
1346       break;
1347    default:
1348       printf(_("Unknown resource type %d in save_resrouce.\n"), type);
1349       error = true; 
1350       break;
1351    }
1352    /* Common */
1353    if (!error) {
1354       res = (URES *)malloc(size);
1355       memcpy(res, &res_all, size);
1356       if (!res_head[rindex]) {
1357          res_head[rindex] = (RES *)res; /* store first entry */
1358          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1359                res->res_dir.hdr.name, rindex);
1360       } else {
1361          RES *next;
1362          if (res->res_dir.hdr.name == NULL) {
1363             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1364                   resources[rindex]);
1365          }   
1366          /* Add new res to end of chain */
1367          for (next=res_head[rindex]; next->next; next=next->next) {
1368             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1369                Emsg2(M_ERROR_TERM, 0,
1370                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1371                   resources[rindex].name, res->res_dir.hdr.name);
1372             }
1373          }
1374          next->next = (RES *)res;
1375          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1376                res->res_dir.hdr.name, rindex, pass);
1377       }
1378    }
1379 }
1380
1381 /*
1382  * Store Device. Note, the resource is created upon the
1383  *  first reference. The details of the resource are obtained
1384  *  later from the SD.
1385  */
1386 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1387 {
1388    int token;
1389    URES *res;
1390    int rindex = R_DEVICE - r_first;
1391    int size = sizeof(DEVICE);
1392    bool found = false;
1393
1394    if (pass == 1) {
1395       token = lex_get_token(lc, T_NAME);
1396       if (!res_head[rindex]) {
1397          res = (URES *)malloc(size);
1398          memset(res, 0, size);
1399          res->res_dev.hdr.name = bstrdup(lc->str);
1400          res_head[rindex] = (RES *)res; /* store first entry */
1401          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1402                res->res_dir.hdr.name, rindex);
1403       } else {
1404          RES *next;
1405          /* See if it is already defined */
1406          for (next=res_head[rindex]; next->next; next=next->next) {
1407             if (strcmp(next->name, lc->str) == 0) {
1408                found = true;
1409                break;
1410             }
1411          }
1412          if (!found) {
1413             res = (URES *)malloc(size);
1414             memset(res, 0, size);
1415             res->res_dev.hdr.name = bstrdup(lc->str);
1416             next->next = (RES *)res;
1417             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1418                res->res_dir.hdr.name, rindex, pass);
1419          }
1420       }
1421
1422       scan_to_eol(lc);
1423       set_bit(index, res_all.hdr.item_present);
1424    } else {
1425       store_alist_res(lc, item, index, pass);
1426    }
1427 }
1428
1429 /*
1430  * Store JobType (backup, verify, restore)
1431  *
1432  */
1433 static void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1434 {
1435    int token, i;
1436
1437    token = lex_get_token(lc, T_NAME);
1438    /* Store the type both pass 1 and pass 2 */
1439    for (i=0; migtypes[i].type_name; i++) {
1440       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1441          *(int *)(item->value) = migtypes[i].job_type;
1442          i = 0;
1443          break;
1444       }
1445    }
1446    if (i != 0) {
1447       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1448    }
1449    scan_to_eol(lc);
1450    set_bit(index, res_all.hdr.item_present);
1451 }
1452
1453
1454
1455 /*
1456  * Store JobType (backup, verify, restore)
1457  *
1458  */
1459 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1460 {
1461    int token, i;
1462
1463    token = lex_get_token(lc, T_NAME);
1464    /* Store the type both pass 1 and pass 2 */
1465    for (i=0; jobtypes[i].type_name; i++) {
1466       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1467          *(int *)(item->value) = jobtypes[i].job_type;
1468          i = 0;
1469          break;
1470       }
1471    }
1472    if (i != 0) {
1473       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1474    }
1475    scan_to_eol(lc);
1476    set_bit(index, res_all.hdr.item_present);
1477 }
1478
1479 /*
1480  * Store Job Level (Full, Incremental, ...)
1481  *
1482  */
1483 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1484 {
1485    int token, i;
1486
1487    token = lex_get_token(lc, T_NAME);
1488    /* Store the level pass 2 so that type is defined */
1489    for (i=0; joblevels[i].level_name; i++) {
1490       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1491          *(int *)(item->value) = joblevels[i].level;
1492          i = 0;
1493          break;
1494       }
1495    }
1496    if (i != 0) {
1497       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1498    }
1499    scan_to_eol(lc);
1500    set_bit(index, res_all.hdr.item_present);
1501 }
1502
1503
1504 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1505 {
1506    int token, i;
1507    token = lex_get_token(lc, T_NAME);
1508    /* Scan Replacement options */
1509    for (i=0; ReplaceOptions[i].name; i++) {
1510       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1511          *(int *)(item->value) = ReplaceOptions[i].token;
1512          i = 0;
1513          break;
1514       }
1515    }
1516    if (i != 0) {
1517       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1518    }
1519    scan_to_eol(lc);
1520    set_bit(index, res_all.hdr.item_present);
1521 }
1522
1523 /*
1524  * Store ACL (access control list)
1525  *
1526  */
1527 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1528 {
1529    int token;
1530
1531    for (;;) {
1532       token = lex_get_token(lc, T_NAME);
1533       if (pass == 1) {
1534          if (((alist **)item->value)[item->code] == NULL) {
1535             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1536             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1537          }
1538          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1539          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1540       }
1541       token = lex_get_token(lc, T_ALL);
1542       if (token == T_COMMA) {
1543          continue;                    /* get another ACL */
1544       }
1545       break;
1546    }
1547    set_bit(index, res_all.hdr.item_present);
1548 }
1549
1550
1551 /* Store a runscript->when in a bit field */
1552 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1553 {
1554    lex_get_token(lc, T_NAME);
1555
1556    if (strcasecmp(lc->str, "before") == 0) {
1557       *(int *)(item->value) = SCRIPT_Before ;
1558    } else if (strcasecmp(lc->str, "after") == 0) {
1559       *(int *)(item->value) = SCRIPT_After;
1560    } else if (strcasecmp(lc->str, "always") == 0) {
1561       *(int *)(item->value) = SCRIPT_Any;
1562    } else {
1563       scan_err2(lc, _("Expect %s, got: %s"), "Before, After or Always", lc->str);
1564    }
1565    scan_to_eol(lc);
1566 }
1567
1568 /* Store a runscript->target
1569  * 
1570  */
1571 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1572 {
1573    lex_get_token(lc, T_STRING);
1574
1575    if (pass == 2) {
1576       if (strcmp(lc->str, "%c") == 0) {
1577          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1578       } else if (strcmp(lc->str, "yes") == 0) {
1579          ((RUNSCRIPT*) item->value)->set_target("%c");
1580       } else if (strcmp(lc->str, "no") == 0) {
1581          /* store nothing, run on director */
1582       } else {
1583          RES *res = GetResWithName(R_CLIENT, lc->str);
1584          if (res == NULL) {
1585             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1586                       lc->str, lc->line_no, lc->line);
1587          }
1588
1589          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1590       }
1591    }
1592    scan_to_eol(lc);
1593 }
1594
1595 /* Store a runscript->command in a bit field
1596  * 
1597  */
1598 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1599 {
1600    lex_get_token(lc, T_STRING);
1601
1602    if (pass == 2) {
1603       ((RUNSCRIPT*) item->value)->set_command(lc->str);
1604    }
1605    scan_to_eol(lc);
1606 }
1607
1608 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1609 {
1610    lex_get_token(lc, T_STRING);
1611    alist **runscripts = (alist **)(item->value) ;
1612
1613    if (pass == 2) {
1614       RUNSCRIPT *script = new_runscript();
1615
1616       script->set_command(lc->str);
1617
1618       if (strcmp(item->name, "runbeforejob") == 0) {
1619          script->when = SCRIPT_Before;
1620          script->abort_on_error = true;
1621
1622       } else if (strcmp(item->name, "runafterjob") == 0) {
1623          script->when = SCRIPT_After;
1624          script->on_success = true;
1625          script->on_failure = false;
1626          
1627       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1628          script->when = SCRIPT_After;
1629          script->set_target("%c");
1630          script->on_success = true;
1631          script->on_failure = false;
1632
1633       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1634          script->when = SCRIPT_Before;
1635          script->set_target("%c");
1636          script->abort_on_error = true;
1637
1638       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1639          script->when = SCRIPT_After;
1640          script->on_failure = true;
1641          script->on_success = false;
1642       }
1643
1644       if (*runscripts == NULL) {
1645         *runscripts = New(alist(10, not_owned_by_alist));
1646       }
1647       
1648       (*runscripts)->append(script);
1649       script->debug();
1650    }
1651
1652    scan_to_eol(lc);
1653 }
1654
1655 static RUNSCRIPT  res_runscript;
1656
1657 /*
1658  * new RunScript items
1659  *   name             handler         value                                code flags default_value
1660  */
1661 static RES_ITEM runscript_items[] = {
1662    {"command", store_runscript_cmd,   ITEM(res_runscript),           0,  ITEM_REQUIRED, 0}, 
1663    {"target", store_runscript_target, ITEM(res_runscript),            0,  0, 0}, 
1664    {"runsonsuccess",    store_bool,   ITEM(res_runscript.on_success), 0,  0, 0},
1665    {"runsonfailure",    store_bool,   ITEM(res_runscript.on_failure), 0,  0, 0},
1666    {"abortjobonerror", store_bool,    ITEM(res_runscript.abort_on_error), 0, 0,   0},
1667    {"runswhen", store_runscript_when, ITEM(res_runscript.when),       0,  0, 0},
1668    {"runsonclient", store_runscript_target, ITEM(res_runscript),       0,  0, 0}, /* TODO */
1669    {NULL, NULL, {0}, 0, 0, 0}
1670 };
1671
1672 /*
1673  * Store RunScript info
1674  *
1675  *  Note, when this routine is called, we are inside a Job
1676  *  resource.  We treat the RunScript like a sort of
1677  *  mini-resource within the Job resource.
1678  */
1679 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1680 {
1681    int token, i;
1682    alist **runscripts = (alist **)(item->value) ;
1683
1684    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1685
1686    res_runscript.reset_default();      /* setting on_success, on_failure, abort_on_error */
1687    
1688    token = lex_get_token(lc, T_SKIP_EOL);
1689    
1690    if (token != T_BOB) {
1691       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1692    }
1693    
1694    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1695       if (token == T_EOB) {
1696         break;
1697       }
1698       if (token != T_IDENTIFIER) {
1699         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1700       }
1701       for (i=0; runscript_items[i].name; i++) {
1702         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1703            token = lex_get_token(lc, T_SKIP_EOL);
1704            if (token != T_EQUALS) {
1705               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
1706            }
1707            
1708            /* Call item handler */
1709            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
1710            i = -1;
1711            break;
1712         }
1713       }
1714       
1715       if (i >=0) {
1716         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
1717       }
1718    }
1719
1720    if (pass == 2) {
1721       if (res_runscript.command == NULL) {
1722          scan_err2(lc, _("%s item is required in %s resource, but not found.\n"),
1723                    "command", "runscript");
1724       }
1725
1726       /* run on client by default */
1727       if (res_runscript.target == NULL) {
1728          res_runscript.set_target("%c");
1729       }
1730
1731       RUNSCRIPT *script = new_runscript();
1732       memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
1733       
1734       if (*runscripts == NULL) {
1735         *runscripts = New(alist(10, not_owned_by_alist));
1736       }
1737       
1738       (*runscripts)->append(script);
1739       script->debug();
1740    }
1741
1742    scan_to_eol(lc);
1743    set_bit(index, res_all.hdr.item_present);
1744 }