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