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