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