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