]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Tweak copyrights again
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2000-2011 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    {"purgemigrationjob",  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    {"rescheduleinterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
330    {"rescheduletimes",    store_pint32, ITEM(res_job.RescheduleTimes), 0, 0, 5},
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.catalog) {
610          sendit(sock, _("  --> "));
611          dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
612       }
613       break;
614
615    case R_DEVICE:
616       dev = &res->res_dev;
617       char ed1[50];
618       sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
619 "      reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
620 "      poolid=%s volname=%s MediaType=%s\n"),
621          dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
622          dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
623          dev->offline, dev->autochanger,
624          edit_uint64(dev->PoolId, ed1),
625          dev->VolumeName, dev->MediaType);
626       break;
627
628    case R_STORAGE:
629       sendit(sock, _("Storage: name=%s address=%s SDport=%d MaxJobs=%u\n"
630 "      DeviceName=%s MediaType=%s StorageId=%s\n"),
631          res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
632          res->res_store.MaxConcurrentJobs,
633          res->res_store.dev_name(),
634          res->res_store.media_type,
635          edit_int64(res->res_store.StorageId, ed1));
636       break;
637
638    case R_CATALOG:
639       sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
640 "      db_driver=%s db_user=%s MutliDBConn=%d\n"),
641          res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
642          res->res_cat.db_port, res->res_cat.db_name, 
643          NPRT(res->res_cat.db_driver), NPRT(res->res_cat.db_user),
644          res->res_cat.mult_db_connections);
645       break;
646
647    case R_JOB:
648    case R_JOBDEFS:
649       sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
650          type == R_JOB ? _("Job") : _("JobDefs"),
651          res->res_job.hdr.name, res->res_job.JobType,
652          level_to_str(res->res_job.JobLevel), res->res_job.Priority,
653          res->res_job.enabled);
654       sendit(sock, _("     MaxJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
655          res->res_job.MaxConcurrentJobs, 
656          res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
657          edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
658          res->res_job.spool_data, res->res_job.write_part_after_job);
659       if (res->res_job.spool_size) {
660          sendit(sock, _("     SpoolSize=%s\n"),        edit_uint64(res->res_job.spool_size, ed1));
661       }
662       if (res->res_job.JobType == JT_BACKUP) {
663          sendit(sock, _("     Accurate=%d\n"), res->res_job.accurate);
664       }
665       if (res->res_job.JobType == JT_MIGRATE || res->res_job.JobType == JT_COPY) {
666          sendit(sock, _("     SelectionType=%d\n"), res->res_job.selection_type);
667       }
668       if (res->res_job.client) {
669          sendit(sock, _("  --> "));
670          dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
671       }
672       if (res->res_job.fileset) {
673          sendit(sock, _("  --> "));
674          dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
675       }
676       if (res->res_job.schedule) {
677          sendit(sock, _("  --> "));
678          dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
679       }
680       if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
681            sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
682       }
683       if (res->res_job.RegexWhere) {
684            sendit(sock, _("  --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
685       }
686       if (res->res_job.RestoreBootstrap) {
687          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
688       }
689       if (res->res_job.WriteBootstrap) {
690          sendit(sock, _("  --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
691       }
692       if (res->res_job.PluginOptions) {
693          sendit(sock, _("  --> PluginOptions=%s\n"), NPRT(res->res_job.PluginOptions));
694       }
695       if (res->res_job.MaxRunTime) {
696          sendit(sock, _("  --> MaxRunTime=%u\n"), res->res_job.MaxRunTime);
697       }
698       if (res->res_job.MaxWaitTime) {
699          sendit(sock, _("  --> MaxWaitTime=%u\n"), res->res_job.MaxWaitTime);
700       }
701       if (res->res_job.MaxStartDelay) {
702          sendit(sock, _("  --> MaxStartDelay=%u\n"), res->res_job.MaxStartDelay);
703       }
704       if (res->res_job.MaxRunSchedTime) {
705          sendit(sock, _("  --> MaxRunSchedTime=%u\n"), res->res_job.MaxRunSchedTime);
706       }
707       if (res->res_job.storage) {
708          STORE *store;
709          foreach_alist(store, res->res_job.storage) {
710             sendit(sock, _("  --> "));
711             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
712          }
713       }
714       if (res->res_job.base) {
715          JOB *job;
716          foreach_alist(job, res->res_job.base) {
717             sendit(sock, _("  --> Base %s\n"), job->name());
718          }
719       }
720       if (res->res_job.RunScripts) {
721         RUNSCRIPT *script;
722         foreach_alist(script, res->res_job.RunScripts) {
723            sendit(sock, _(" --> RunScript\n"));
724            sendit(sock, _("  --> Command=%s\n"), NPRT(script->command));
725            sendit(sock, _("  --> Target=%s\n"),  NPRT(script->target));
726            sendit(sock, _("  --> RunOnSuccess=%u\n"),  script->on_success);
727            sendit(sock, _("  --> RunOnFailure=%u\n"),  script->on_failure);
728            sendit(sock, _("  --> FailJobOnError=%u\n"),  script->fail_on_error);
729            sendit(sock, _("  --> RunWhen=%u\n"),  script->when);
730         }
731       }
732       if (res->res_job.pool) {
733          sendit(sock, _("  --> "));
734          dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
735       }
736       if (res->res_job.full_pool) {
737          sendit(sock, _("  --> "));
738          dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
739       }
740       if (res->res_job.inc_pool) {
741          sendit(sock, _("  --> "));
742          dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
743       }
744       if (res->res_job.diff_pool) {
745          sendit(sock, _("  --> "));
746          dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
747       }
748       if (res->res_job.verify_job) {
749          sendit(sock, _("  --> "));
750          dump_resource(-type, (RES *)res->res_job.verify_job, sendit, sock);
751       }
752       if (res->res_job.run_cmds) {
753          char *runcmd;
754          foreach_alist(runcmd, res->res_job.run_cmds) {
755             sendit(sock, _("  --> Run=%s\n"), runcmd);
756          }
757       }
758       if (res->res_job.selection_pattern) {
759          sendit(sock, _("  --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
760       }
761       if (res->res_job.messages) {
762          sendit(sock, _("  --> "));
763          dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
764       }
765       break;
766
767    case R_FILESET:
768    {
769       int i, j, k;
770       sendit(sock, _("FileSet: name=%s\n"), res->res_fs.hdr.name);
771       for (i=0; i<res->res_fs.num_includes; i++) {
772          INCEXE *incexe = res->res_fs.include_items[i];
773          for (j=0; j<incexe->num_opts; j++) {
774             FOPTS *fo = incexe->opts_list[j];
775             sendit(sock, "      O %s\n", fo->opts);
776
777             bool enhanced_wild = false;
778             for (k=0; fo->opts[k]!='\0'; k++) {
779                if (fo->opts[k]=='W') {
780                   enhanced_wild = true;
781                   break;
782                }
783             }
784
785             for (k=0; k<fo->regex.size(); k++) {
786                sendit(sock, "      R %s\n", fo->regex.get(k));
787             }
788             for (k=0; k<fo->regexdir.size(); k++) {
789                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
790             }
791             for (k=0; k<fo->regexfile.size(); k++) {
792                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
793             }
794             for (k=0; k<fo->wild.size(); k++) {
795                sendit(sock, "      W %s\n", fo->wild.get(k));
796             }
797             for (k=0; k<fo->wilddir.size(); k++) {
798                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
799             }
800             for (k=0; k<fo->wildfile.size(); k++) {
801                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
802             }
803             for (k=0; k<fo->wildbase.size(); k++) {
804                sendit(sock, "      W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
805             }
806             for (k=0; k<fo->base.size(); k++) {
807                sendit(sock, "      B %s\n", fo->base.get(k));
808             }
809             for (k=0; k<fo->fstype.size(); k++) {
810                sendit(sock, "      X %s\n", fo->fstype.get(k));
811             }
812             for (k=0; k<fo->drivetype.size(); k++) {
813                sendit(sock, "      XD %s\n", fo->drivetype.get(k));
814             }
815             if (fo->plugin) {
816                sendit(sock, "      G %s\n", fo->plugin);
817             }
818             if (fo->reader) {
819                sendit(sock, "      D %s\n", fo->reader);
820             }
821             if (fo->writer) {
822                sendit(sock, "      T %s\n", fo->writer);
823             }
824             sendit(sock, "      N\n");
825          }
826          if (incexe->ignoredir) {
827             sendit(sock, "      Z %s\n", incexe->ignoredir);
828          }
829          for (j=0; j<incexe->name_list.size(); j++) {
830             sendit(sock, "      I %s\n", incexe->name_list.get(j));
831          }
832          if (incexe->name_list.size()) {
833             sendit(sock, "      N\n");
834          }
835          for (j=0; j<incexe->plugin_list.size(); j++) {
836             sendit(sock, "      P %s\n", incexe->plugin_list.get(j));
837          }
838          if (incexe->plugin_list.size()) {
839             sendit(sock, "      N\n");
840          }
841
842       }
843
844       for (i=0; i<res->res_fs.num_excludes; i++) {
845          INCEXE *incexe = res->res_fs.exclude_items[i];
846          for (j=0; j<incexe->name_list.size(); j++) {
847             sendit(sock, "      E %s\n", incexe->name_list.get(j));
848          }
849          if (incexe->name_list.size()) {
850             sendit(sock, "      N\n");
851          }
852       }
853       break;
854    }
855
856    case R_SCHEDULE:
857       if (res->res_sch.run) {
858          int i;
859          RUN *run = res->res_sch.run;
860          char buf[1000], num[30];
861          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
862          if (!run) {
863             break;
864          }
865 next_run:
866          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
867          bstrncpy(buf, _("      hour="), sizeof(buf));
868          for (i=0; i<24; i++) {
869             if (bit_is_set(i, run->hour)) {
870                bsnprintf(num, sizeof(num), "%d ", i);
871                bstrncat(buf, num, sizeof(buf));
872             }
873          }
874          bstrncat(buf, "\n", sizeof(buf));
875          sendit(sock, buf);
876          bstrncpy(buf, _("      mday="), sizeof(buf));
877          for (i=0; i<31; i++) {
878             if (bit_is_set(i, run->mday)) {
879                bsnprintf(num, sizeof(num), "%d ", i);
880                bstrncat(buf, num, sizeof(buf));
881             }
882          }
883          bstrncat(buf, "\n", sizeof(buf));
884          sendit(sock, buf);
885          bstrncpy(buf, _("      month="), sizeof(buf));
886          for (i=0; i<12; i++) {
887             if (bit_is_set(i, run->month)) {
888                bsnprintf(num, sizeof(num), "%d ", i);
889                bstrncat(buf, num, sizeof(buf));
890             }
891          }
892          bstrncat(buf, "\n", sizeof(buf));
893          sendit(sock, buf);
894          bstrncpy(buf, _("      wday="), sizeof(buf));
895          for (i=0; i<7; i++) {
896             if (bit_is_set(i, run->wday)) {
897                bsnprintf(num, sizeof(num), "%d ", i);
898                bstrncat(buf, num, sizeof(buf));
899             }
900          }
901          bstrncat(buf, "\n", sizeof(buf));
902          sendit(sock, buf);
903          bstrncpy(buf, _("      wom="), sizeof(buf));
904          for (i=0; i<5; i++) {
905             if (bit_is_set(i, run->wom)) {
906                bsnprintf(num, sizeof(num), "%d ", i);
907                bstrncat(buf, num, sizeof(buf));
908             }
909          }
910          bstrncat(buf, "\n", sizeof(buf));
911          sendit(sock, buf);
912          bstrncpy(buf, _("      woy="), sizeof(buf));
913          for (i=0; i<54; i++) {
914             if (bit_is_set(i, run->woy)) {
915                bsnprintf(num, sizeof(num), "%d ", i);
916                bstrncat(buf, num, sizeof(buf));
917             }
918          }
919          bstrncat(buf, "\n", sizeof(buf));
920          sendit(sock, buf);
921          sendit(sock, _("      mins=%d\n"), run->minute);
922          if (run->pool) {
923             sendit(sock, _("     --> "));
924             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
925          }
926          if (run->storage) {
927             sendit(sock, _("     --> "));
928             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
929          }
930          if (run->msgs) {
931             sendit(sock, _("     --> "));
932             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
933          }
934          /* If another Run record is chained in, go print it */
935          if (run->next) {
936             run = run->next;
937             goto next_run;
938          }
939       } else {
940          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
941       }
942       break;
943
944    case R_POOL:
945       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
946               res->res_pool.pool_type);
947       sendit(sock, _("      use_cat=%d use_once=%d cat_files=%d\n"),
948               res->res_pool.use_catalog, res->res_pool.use_volume_once,
949               res->res_pool.catalog_files);
950       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
951               res->res_pool.max_volumes, res->res_pool.AutoPrune,
952               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
953       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
954               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
955               res->res_pool.Recycle,
956               NPRT(res->res_pool.label_format));
957       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
958               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
959       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d ActionOnPurge=%d\n"), 
960               res->res_pool.recycle_oldest_volume,
961               res->res_pool.purge_oldest_volume,
962               res->res_pool.action_on_purge);
963       sendit(sock, _("      MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
964               res->res_pool.MaxVolJobs, 
965               res->res_pool.MaxVolFiles,
966               edit_uint64(res->res_pool.MaxVolBytes, ed1));
967       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
968               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
969               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
970               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
971       sendit(sock, _("      JobRetention=%s FileRetention=%s\n"),
972          edit_utime(res->res_pool.JobRetention, ed1, sizeof(ed1)),
973          edit_utime(res->res_pool.FileRetention, ed2, sizeof(ed2)));
974       if (res->res_pool.NextPool) {
975          sendit(sock, _("      NextPool=%s\n"), res->res_pool.NextPool->name());
976       }
977       if (res->res_pool.RecyclePool) {
978          sendit(sock, _("      RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
979       }
980       if (res->res_pool.ScratchPool) {
981          sendit(sock, _("      ScratchPool=%s\n"), res->res_pool.ScratchPool->name());
982       }
983       if (res->res_pool.catalog) {
984          sendit(sock, _("      Catalog=%s\n"), res->res_pool.catalog->name());
985       }
986       if (res->res_pool.storage) {
987          STORE *store;
988          foreach_alist(store, res->res_pool.storage) {
989             sendit(sock, _("  --> "));
990             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
991          }
992       }
993       if (res->res_pool.CopyPool) {
994          POOL *copy;
995          foreach_alist(copy, res->res_pool.CopyPool) {
996             sendit(sock, _("  --> "));
997             dump_resource(-R_POOL, (RES *)copy, sendit, sock);
998          }
999       }
1000
1001       break;
1002
1003    case R_MSGS:
1004       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
1005       if (res->res_msgs.mail_cmd)
1006          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
1007       if (res->res_msgs.operator_cmd)
1008          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
1009       break;
1010
1011    default:
1012       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
1013       break;
1014    }
1015    if (recurse && res->res_dir.hdr.next) {
1016       dump_resource(type, res->res_dir.hdr.next, sendit, sock);
1017    }
1018 }
1019
1020 /*
1021  * Free all the members of an INCEXE structure
1022  */
1023 static void free_incexe(INCEXE *incexe)
1024 {
1025    incexe->name_list.destroy();
1026    incexe->plugin_list.destroy();
1027    for (int i=0; i<incexe->num_opts; i++) {
1028       FOPTS *fopt = incexe->opts_list[i];
1029       fopt->regex.destroy();
1030       fopt->regexdir.destroy();
1031       fopt->regexfile.destroy();
1032       fopt->wild.destroy();
1033       fopt->wilddir.destroy();
1034       fopt->wildfile.destroy();
1035       fopt->wildbase.destroy();
1036       fopt->base.destroy();
1037       fopt->fstype.destroy();
1038       fopt->drivetype.destroy();
1039       if (fopt->plugin) {
1040          free(fopt->plugin);
1041       }
1042       if (fopt->reader) {
1043          free(fopt->reader);
1044       }
1045       if (fopt->writer) {
1046          free(fopt->writer);
1047       }
1048       free(fopt);
1049    }
1050    if (incexe->opts_list) {
1051       free(incexe->opts_list);
1052    }
1053    if (incexe->ignoredir) {
1054       free(incexe->ignoredir);
1055    }
1056    free(incexe);
1057 }
1058
1059 /*
1060  * Free memory of resource -- called when daemon terminates.
1061  * NB, we don't need to worry about freeing any references
1062  * to other resources as they will be freed when that
1063  * resource chain is traversed.  Mainly we worry about freeing
1064  * allocated strings (names).
1065  */
1066 void free_resource(RES *sres, int type)
1067 {
1068    int num;
1069    RES *nres;                         /* next resource if linked */
1070    URES *res = (URES *)sres;
1071
1072    if (res == NULL)
1073       return;
1074
1075    /* common stuff -- free the resource name and description */
1076    nres = (RES *)res->res_dir.hdr.next;
1077    if (res->res_dir.hdr.name) {
1078       free(res->res_dir.hdr.name);
1079    }
1080    if (res->res_dir.hdr.desc) {
1081       free(res->res_dir.hdr.desc);
1082    }
1083
1084    switch (type) {
1085    case R_DIRECTOR:
1086       if (res->res_dir.working_directory) {
1087          free(res->res_dir.working_directory);
1088       }
1089       if (res->res_dir.scripts_directory) {
1090          free((char *)res->res_dir.scripts_directory);
1091       }
1092       if (res->res_dir.plugin_directory) {
1093          free((char *)res->res_dir.plugin_directory);
1094       }
1095       if (res->res_dir.pid_directory) {
1096          free(res->res_dir.pid_directory);
1097       }
1098       if (res->res_dir.subsys_directory) {
1099          free(res->res_dir.subsys_directory);
1100       }
1101       if (res->res_dir.password) {
1102          free(res->res_dir.password);
1103       }
1104       if (res->res_dir.query_file) {
1105          free(res->res_dir.query_file);
1106       }
1107       if (res->res_dir.DIRaddrs) {
1108          free_addresses(res->res_dir.DIRaddrs);
1109       }
1110       if (res->res_dir.DIRsrc_addr) {
1111          free_addresses(res->res_dir.DIRsrc_addr);
1112       }
1113       if (res->res_dir.tls_ctx) { 
1114          free_tls_context(res->res_dir.tls_ctx);
1115       }
1116       if (res->res_dir.tls_ca_certfile) {
1117          free(res->res_dir.tls_ca_certfile);
1118       }
1119       if (res->res_dir.tls_ca_certdir) {
1120          free(res->res_dir.tls_ca_certdir);
1121       }
1122       if (res->res_dir.tls_certfile) {
1123          free(res->res_dir.tls_certfile);
1124       }
1125       if (res->res_dir.tls_keyfile) {
1126          free(res->res_dir.tls_keyfile);
1127       }
1128       if (res->res_dir.tls_dhfile) {
1129          free(res->res_dir.tls_dhfile);
1130       }
1131       if (res->res_dir.tls_allowed_cns) {
1132          delete res->res_dir.tls_allowed_cns;
1133       }
1134       if (res->res_dir.verid) {
1135          free(res->res_dir.verid);
1136       }
1137       break;
1138    case R_DEVICE:
1139    case R_COUNTER:
1140        break;
1141    case R_CONSOLE:
1142       if (res->res_con.password) {
1143          free(res->res_con.password);
1144       }
1145       if (res->res_con.tls_ctx) { 
1146          free_tls_context(res->res_con.tls_ctx);
1147       }
1148       if (res->res_con.tls_ca_certfile) {
1149          free(res->res_con.tls_ca_certfile);
1150       }
1151       if (res->res_con.tls_ca_certdir) {
1152          free(res->res_con.tls_ca_certdir);
1153       }
1154       if (res->res_con.tls_certfile) {
1155          free(res->res_con.tls_certfile);
1156       }
1157       if (res->res_con.tls_keyfile) {
1158          free(res->res_con.tls_keyfile);
1159       }
1160       if (res->res_con.tls_dhfile) {
1161          free(res->res_con.tls_dhfile);
1162       }
1163       if (res->res_con.tls_allowed_cns) {
1164          delete res->res_con.tls_allowed_cns;
1165       }
1166       for (int i=0; i<Num_ACL; i++) {
1167          if (res->res_con.ACL_lists[i]) {
1168             delete res->res_con.ACL_lists[i];
1169             res->res_con.ACL_lists[i] = NULL;
1170          }
1171       }
1172       break;
1173    case R_CLIENT:
1174       if (res->res_client.address) {
1175          free(res->res_client.address);
1176       }
1177       if (res->res_client.password) {
1178          free(res->res_client.password);
1179       }
1180       if (res->res_client.tls_ctx) { 
1181          free_tls_context(res->res_client.tls_ctx);
1182       }
1183       if (res->res_client.tls_ca_certfile) {
1184          free(res->res_client.tls_ca_certfile);
1185       }
1186       if (res->res_client.tls_ca_certdir) {
1187          free(res->res_client.tls_ca_certdir);
1188       }
1189       if (res->res_client.tls_certfile) {
1190          free(res->res_client.tls_certfile);
1191       }
1192       if (res->res_client.tls_keyfile) {
1193          free(res->res_client.tls_keyfile);
1194       }
1195       if (res->res_client.tls_allowed_cns) {
1196          delete res->res_client.tls_allowed_cns;
1197       }
1198       break;
1199    case R_STORAGE:
1200       if (res->res_store.address) {
1201          free(res->res_store.address);
1202       }
1203       if (res->res_store.password) {
1204          free(res->res_store.password);
1205       }
1206       if (res->res_store.media_type) {
1207          free(res->res_store.media_type);
1208       }
1209       if (res->res_store.device) {
1210          delete res->res_store.device;
1211       }
1212       if (res->res_store.tls_ctx) { 
1213          free_tls_context(res->res_store.tls_ctx);
1214       }
1215       if (res->res_store.tls_ca_certfile) {
1216          free(res->res_store.tls_ca_certfile);
1217       }
1218       if (res->res_store.tls_ca_certdir) {
1219          free(res->res_store.tls_ca_certdir);
1220       }
1221       if (res->res_store.tls_certfile) {
1222          free(res->res_store.tls_certfile);
1223       }
1224       if (res->res_store.tls_keyfile) {
1225          free(res->res_store.tls_keyfile);
1226       }
1227       break;
1228    case R_CATALOG:
1229       if (res->res_cat.db_address) {
1230          free(res->res_cat.db_address);
1231       }
1232       if (res->res_cat.db_socket) {
1233          free(res->res_cat.db_socket);
1234       }
1235       if (res->res_cat.db_user) {
1236          free(res->res_cat.db_user);
1237       }
1238       if (res->res_cat.db_name) {
1239          free(res->res_cat.db_name);
1240       }
1241       if (res->res_cat.db_driver) {
1242          free(res->res_cat.db_driver);
1243       }
1244       if (res->res_cat.db_password) {
1245          free(res->res_cat.db_password);
1246       }
1247       break;
1248    case R_FILESET:
1249       if ((num=res->res_fs.num_includes)) {
1250          while (--num >= 0) {
1251             free_incexe(res->res_fs.include_items[num]);
1252          }
1253          free(res->res_fs.include_items);
1254       }
1255       res->res_fs.num_includes = 0;
1256       if ((num=res->res_fs.num_excludes)) {
1257          while (--num >= 0) {
1258             free_incexe(res->res_fs.exclude_items[num]);
1259          }
1260          free(res->res_fs.exclude_items);
1261       }
1262       res->res_fs.num_excludes = 0;
1263       break;
1264    case R_POOL:
1265       if (res->res_pool.pool_type) {
1266          free(res->res_pool.pool_type);
1267       }
1268       if (res->res_pool.label_format) {
1269          free(res->res_pool.label_format);
1270       }
1271       if (res->res_pool.cleaning_prefix) {
1272          free(res->res_pool.cleaning_prefix);
1273       }
1274       if (res->res_pool.storage) {
1275          delete res->res_pool.storage;
1276       }
1277       break;
1278    case R_SCHEDULE:
1279       if (res->res_sch.run) {
1280          RUN *nrun, *next;
1281          nrun = res->res_sch.run;
1282          while (nrun) {
1283             next = nrun->next;
1284             free(nrun);
1285             nrun = next;
1286          }
1287       }
1288       break;
1289    case R_JOB:
1290    case R_JOBDEFS:
1291       if (res->res_job.RestoreWhere) {
1292          free(res->res_job.RestoreWhere);
1293       }
1294       if (res->res_job.RegexWhere) {
1295          free(res->res_job.RegexWhere);
1296       }
1297       if (res->res_job.strip_prefix) {
1298          free(res->res_job.strip_prefix);
1299       }
1300       if (res->res_job.add_prefix) {
1301          free(res->res_job.add_prefix);
1302       }
1303       if (res->res_job.add_suffix) {
1304          free(res->res_job.add_suffix);
1305       }
1306       if (res->res_job.RestoreBootstrap) {
1307          free(res->res_job.RestoreBootstrap);
1308       }
1309       if (res->res_job.WriteBootstrap) {
1310          free(res->res_job.WriteBootstrap);
1311       }
1312       if (res->res_job.PluginOptions) {
1313          free(res->res_job.PluginOptions);
1314       }
1315       if (res->res_job.selection_pattern) {
1316          free(res->res_job.selection_pattern);
1317       }
1318       if (res->res_job.run_cmds) {
1319          delete res->res_job.run_cmds;
1320       }
1321       if (res->res_job.storage) {
1322          delete res->res_job.storage;
1323       }
1324       if (res->res_job.base) {
1325          delete res->res_job.base;
1326       }
1327       if (res->res_job.RunScripts) {
1328          free_runscripts(res->res_job.RunScripts);
1329          delete res->res_job.RunScripts;
1330       }
1331       break;
1332    case R_MSGS:
1333       if (res->res_msgs.mail_cmd) {
1334          free(res->res_msgs.mail_cmd);
1335       }
1336       if (res->res_msgs.operator_cmd) {
1337          free(res->res_msgs.operator_cmd);
1338       }
1339       free_msgs_res((MSGS *)res);  /* free message resource */
1340       res = NULL;
1341       break;
1342    default:
1343       printf(_("Unknown resource type %d in free_resource.\n"), type);
1344    }
1345    /* Common stuff again -- free the resource, recurse to next one */
1346    if (res) {
1347       free(res);
1348    }
1349    if (nres) {
1350       free_resource(nres, type);
1351    }
1352 }
1353
1354 /*
1355  * Save the new resource by chaining it into the head list for
1356  * the resource. If this is pass 2, we update any resource
1357  * pointers because they may not have been defined until
1358  * later in pass 1.
1359  */
1360 void save_resource(int type, RES_ITEM *items, int pass)
1361 {
1362    URES *res;
1363    int rindex = type - r_first;
1364    int i, size = 0;
1365    bool error = false;
1366
1367    /* Check Job requirements after applying JobDefs */
1368    if (type != R_JOB && type != R_JOBDEFS) {
1369       /*
1370        * Ensure that all required items are present
1371        */
1372       for (i=0; items[i].name; i++) {
1373          if (items[i].flags & ITEM_REQUIRED) {
1374             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1375                 Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1376                     items[i].name, resources[rindex]);
1377             }
1378          }
1379          /* If this triggers, take a look at lib/parse_conf.h */
1380          if (i >= MAX_RES_ITEMS) {
1381             Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"), resources[rindex]);
1382          }
1383       }
1384    } else if (type == R_JOB) {
1385       /*
1386        * Ensure that the name item is present
1387        */
1388       if (items[0].flags & ITEM_REQUIRED) {
1389          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1390              Emsg2(M_ERROR_TERM, 0, _("%s item is required in %s resource, but not found.\n"),
1391                    items[0].name, resources[rindex]);
1392          }
1393       }
1394    }
1395
1396    /*
1397     * During pass 2 in each "store" routine, we looked up pointers
1398     * to all the resources referrenced in the current resource, now we
1399     * must copy their addresses from the static record to the allocated
1400     * record.
1401     */
1402    if (pass == 2) {
1403       switch (type) {
1404       /* Resources not containing a resource */
1405       case R_CATALOG:
1406       case R_MSGS:
1407       case R_FILESET:
1408       case R_DEVICE:
1409          break;
1410
1411       /*
1412        * Resources containing another resource or alist. First
1413        *  look up the resource which contains another resource. It
1414        *  was written during pass 1.  Then stuff in the pointers to
1415        *  the resources it contains, which were inserted this pass.
1416        *  Finally, it will all be stored back.
1417        */
1418       case R_POOL:
1419          /* Find resource saved in pass 1 */
1420          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1421             Emsg1(M_ERROR_TERM, 0, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1422          }
1423          /* Explicitly copy resource pointers from this pass (res_all) */
1424          res->res_pool.NextPool = res_all.res_pool.NextPool;
1425          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1426          res->res_pool.ScratchPool = res_all.res_pool.ScratchPool;
1427          res->res_pool.storage    = res_all.res_pool.storage;
1428          res->res_pool.catalog    = res_all.res_pool.catalog;
1429          break;
1430       case R_CONSOLE:
1431          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1432             Emsg1(M_ERROR_TERM, 0, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1433          }
1434          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1435          break;
1436       case R_DIRECTOR:
1437          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1438             Emsg1(M_ERROR_TERM, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1439          }
1440          res->res_dir.messages = res_all.res_dir.messages;
1441          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1442          break;
1443       case R_STORAGE:
1444          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1445             Emsg1(M_ERROR_TERM, 0, _("Cannot find Storage resource %s\n"),
1446                   res_all.res_dir.hdr.name);
1447          }
1448          /* we must explicitly copy the device alist pointer */
1449          res->res_store.device   = res_all.res_store.device;
1450          break;
1451       case R_JOB:
1452       case R_JOBDEFS:
1453          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1454             Emsg1(M_ERROR_TERM, 0, _("Cannot find Job resource %s\n"),
1455                   res_all.res_dir.hdr.name);
1456          }
1457          res->res_job.messages   = res_all.res_job.messages;
1458          res->res_job.schedule   = res_all.res_job.schedule;
1459          res->res_job.client     = res_all.res_job.client;
1460          res->res_job.fileset    = res_all.res_job.fileset;
1461          res->res_job.storage    = res_all.res_job.storage;
1462          res->res_job.base       = res_all.res_job.base;
1463          res->res_job.pool       = res_all.res_job.pool;
1464          res->res_job.full_pool  = res_all.res_job.full_pool;
1465          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1466          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1467          res->res_job.verify_job = res_all.res_job.verify_job;
1468          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1469          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1470          res->res_job.RunScripts = res_all.res_job.RunScripts;
1471
1472          /* TODO: JobDefs where/regexwhere doesn't work well (but this
1473           * is not very useful) 
1474           * We have to set_bit(index, res_all.hdr.item_present);
1475           * or something like that
1476           */
1477
1478          /* we take RegexWhere before all other options */
1479          if (!res->res_job.RegexWhere 
1480              &&
1481              (res->res_job.strip_prefix ||
1482               res->res_job.add_suffix   ||
1483               res->res_job.add_prefix))
1484          {
1485             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1486                                                    res->res_job.add_prefix,
1487                                                    res->res_job.add_suffix);
1488             res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1489             bregexp_build_where(res->res_job.RegexWhere, len,
1490                                 res->res_job.strip_prefix,
1491                                 res->res_job.add_prefix,
1492                                 res->res_job.add_suffix);
1493             /* TODO: test bregexp */
1494          }
1495
1496          if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1497             free(res->res_job.RestoreWhere);
1498             res->res_job.RestoreWhere = NULL;
1499          }
1500
1501          break;
1502       case R_COUNTER:
1503          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1504             Emsg1(M_ERROR_TERM, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1505          }
1506          res->res_counter.Catalog = res_all.res_counter.Catalog;
1507          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1508          break;
1509
1510       case R_CLIENT:
1511          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.hdr.name)) == NULL) {
1512             Emsg1(M_ERROR_TERM, 0, _("Cannot find Client resource %s\n"), res_all.res_client.hdr.name);
1513          }
1514          res->res_client.catalog = res_all.res_client.catalog;
1515          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1516          break;
1517       case R_SCHEDULE:
1518          /*
1519           * Schedule is a bit different in that it contains a RUN record
1520           * chain which isn't a "named" resource. This chain was linked
1521           * in by run_conf.c during pass 2, so here we jam the pointer
1522           * into the Schedule resource.
1523           */
1524          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.hdr.name)) == NULL) {
1525             Emsg1(M_ERROR_TERM, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.hdr.name);
1526          }
1527          res->res_sch.run = res_all.res_sch.run;
1528          break;
1529       default:
1530          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1531          error = true;
1532          break;
1533       }
1534       /* Note, the resource name was already saved during pass 1,
1535        * so here, we can just release it.
1536        */
1537       if (res_all.res_dir.hdr.name) {
1538          free(res_all.res_dir.hdr.name);
1539          res_all.res_dir.hdr.name = NULL;
1540       }
1541       if (res_all.res_dir.hdr.desc) {
1542          free(res_all.res_dir.hdr.desc);
1543          res_all.res_dir.hdr.desc = NULL;
1544       }
1545       return;
1546    }
1547
1548    /*
1549     * The following code is only executed during pass 1
1550     */
1551    switch (type) {
1552    case R_DIRECTOR:
1553       size = sizeof(DIRRES);
1554       break;
1555    case R_CONSOLE:
1556       size = sizeof(CONRES);
1557       break;
1558    case R_CLIENT:
1559       size =sizeof(CLIENT);
1560       break;
1561    case R_STORAGE:
1562       size = sizeof(STORE);
1563       break;
1564    case R_CATALOG:
1565       size = sizeof(CAT);
1566       break;
1567    case R_JOB:
1568    case R_JOBDEFS:
1569       size = sizeof(JOB);
1570       break;
1571    case R_FILESET:
1572       size = sizeof(FILESET);
1573       break;
1574    case R_SCHEDULE:
1575       size = sizeof(SCHED);
1576       break;
1577    case R_POOL:
1578       size = sizeof(POOL);
1579       break;
1580    case R_MSGS:
1581       size = sizeof(MSGS);
1582       break;
1583    case R_COUNTER:
1584       size = sizeof(COUNTER);
1585       break;
1586    case R_DEVICE:
1587       error = true;
1588       break;
1589    default:
1590       printf(_("Unknown resource type %d in save_resource.\n"), type);
1591       error = true; 
1592       break;
1593    }
1594    /* Common */
1595    if (!error) {
1596       res = (URES *)malloc(size);
1597       memcpy(res, &res_all, size);
1598       if (!res_head[rindex]) {
1599          res_head[rindex] = (RES *)res; /* store first entry */
1600          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(type),
1601                res->res_dir.hdr.name, rindex);
1602       } else {
1603          RES *next, *last;
1604          if (res->res_dir.hdr.name == NULL) {
1605             Emsg1(M_ERROR_TERM, 0, _("Name item is required in %s resource, but not found.\n"),
1606                   resources[rindex]);
1607          }   
1608          /* Add new res to end of chain */
1609          for (last=next=res_head[rindex]; next; next=next->next) {
1610             last = next;
1611             if (strcmp(next->name, res->res_dir.hdr.name) == 0) {
1612                Emsg2(M_ERROR_TERM, 0,
1613                   _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
1614                   resources[rindex].name, res->res_dir.hdr.name);
1615             }
1616          }
1617          last->next = (RES *)res;
1618          Dmsg4(900, _("Inserting %s res: %s index=%d pass=%d\n"), res_to_str(type),
1619                res->res_dir.hdr.name, rindex, pass);
1620       }
1621    }
1622 }
1623
1624 static void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass)
1625 {
1626    uint32_t *destination = (uint32_t*)item->value;
1627    lex_get_token(lc, T_NAME);
1628    if (strcasecmp(lc->str, "truncate") == 0) {
1629       *destination = (*destination) | ON_PURGE_TRUNCATE;
1630    } else {
1631       scan_err2(lc, _("Expected one of: %s, got: %s"), "Truncate", lc->str);
1632       return;
1633    }
1634    scan_to_eol(lc);
1635    set_bit(index, res_all.hdr.item_present);
1636 }
1637
1638 /*
1639  * Store Device. Note, the resource is created upon the
1640  *  first reference. The details of the resource are obtained
1641  *  later from the SD.
1642  */
1643 static void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
1644 {
1645    URES *res;
1646    int rindex = R_DEVICE - r_first;
1647    int size = sizeof(DEVICE);
1648    bool found = false;
1649
1650    if (pass == 1) {
1651       lex_get_token(lc, T_NAME);
1652       if (!res_head[rindex]) {
1653          res = (URES *)malloc(size);
1654          memset(res, 0, size);
1655          res->res_dev.hdr.name = bstrdup(lc->str);
1656          res_head[rindex] = (RES *)res; /* store first entry */
1657          Dmsg3(900, "Inserting first %s res: %s index=%d\n", res_to_str(R_DEVICE),
1658                res->res_dir.hdr.name, rindex);
1659       } else {
1660          RES *next;
1661          /* See if it is already defined */
1662          for (next=res_head[rindex]; next->next; next=next->next) {
1663             if (strcmp(next->name, lc->str) == 0) {
1664                found = true;
1665                break;
1666             }
1667          }
1668          if (!found) {
1669             res = (URES *)malloc(size);
1670             memset(res, 0, size);
1671             res->res_dev.hdr.name = bstrdup(lc->str);
1672             next->next = (RES *)res;
1673             Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", res_to_str(R_DEVICE),
1674                res->res_dir.hdr.name, rindex, pass);
1675          }
1676       }
1677
1678       scan_to_eol(lc);
1679       set_bit(index, res_all.hdr.item_present);
1680    } else {
1681       store_alist_res(lc, item, index, pass);
1682    }
1683 }
1684
1685 /*
1686  * Store Migration/Copy type
1687  *
1688  */
1689 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
1690 {
1691    int i;
1692
1693    lex_get_token(lc, T_NAME);
1694    /* Store the type both pass 1 and pass 2 */
1695    for (i=0; migtypes[i].type_name; i++) {
1696       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
1697          *(uint32_t *)(item->value) = migtypes[i].job_type;
1698          i = 0;
1699          break;
1700       }
1701    }
1702    if (i != 0) {
1703       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
1704    }
1705    scan_to_eol(lc);
1706    set_bit(index, res_all.hdr.item_present);
1707 }
1708
1709
1710
1711 /*
1712  * Store JobType (backup, verify, restore)
1713  *
1714  */
1715 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
1716 {
1717    int i;
1718
1719    lex_get_token(lc, T_NAME);
1720    /* Store the type both pass 1 and pass 2 */
1721    for (i=0; jobtypes[i].type_name; i++) {
1722       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
1723          *(uint32_t *)(item->value) = jobtypes[i].job_type;
1724          i = 0;
1725          break;
1726       }
1727    }
1728    if (i != 0) {
1729       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
1730    }
1731    scan_to_eol(lc);
1732    set_bit(index, res_all.hdr.item_present);
1733 }
1734
1735 /*
1736  * Store Job Level (Full, Incremental, ...)
1737  *
1738  */
1739 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
1740 {
1741    int i;
1742
1743    lex_get_token(lc, T_NAME);
1744    /* Store the level pass 2 so that type is defined */
1745    for (i=0; joblevels[i].level_name; i++) {
1746       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
1747          *(uint32_t *)(item->value) = joblevels[i].level;
1748          i = 0;
1749          break;
1750       }
1751    }
1752    if (i != 0) {
1753       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
1754    }
1755    scan_to_eol(lc);
1756    set_bit(index, res_all.hdr.item_present);
1757 }
1758
1759
1760 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
1761 {
1762    int i;
1763    lex_get_token(lc, T_NAME);
1764    /* Scan Replacement options */
1765    for (i=0; ReplaceOptions[i].name; i++) {
1766       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
1767          *(uint32_t *)(item->value) = ReplaceOptions[i].token;
1768          i = 0;
1769          break;
1770       }
1771    }
1772    if (i != 0) {
1773       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
1774    }
1775    scan_to_eol(lc);
1776    set_bit(index, res_all.hdr.item_present);
1777 }
1778
1779 /*
1780  * Store ACL (access control list)
1781  *
1782  */
1783 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
1784 {
1785    int token;
1786
1787    for (;;) {
1788       lex_get_token(lc, T_STRING);
1789       if (pass == 1) {
1790          if (((alist **)item->value)[item->code] == NULL) {
1791             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
1792             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
1793          }
1794          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
1795          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
1796       }
1797       token = lex_get_token(lc, T_ALL);
1798       if (token == T_COMMA) {
1799          continue;                    /* get another ACL */
1800       }
1801       break;
1802    }
1803    set_bit(index, res_all.hdr.item_present);
1804 }
1805
1806 /* We build RunScripts items here */
1807 static RUNSCRIPT res_runscript;
1808
1809 /* Store a runscript->when in a bit field */
1810 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
1811 {
1812    lex_get_token(lc, T_NAME);
1813
1814    if (strcasecmp(lc->str, "before") == 0) {
1815       *(uint32_t *)(item->value) = SCRIPT_Before ;
1816    } else if (strcasecmp(lc->str, "after") == 0) {
1817       *(uint32_t *)(item->value) = SCRIPT_After;
1818    } else if (strcasecmp(lc->str, "aftervss") == 0) {
1819       *(uint32_t *)(item->value) = SCRIPT_AfterVSS;
1820    } else if (strcasecmp(lc->str, "always") == 0) {
1821       *(uint32_t *)(item->value) = SCRIPT_Any;
1822    } else {
1823       scan_err2(lc, _("Expect %s, got: %s"), "Before, After, AfterVSS or Always", lc->str);
1824    }
1825    scan_to_eol(lc);
1826 }
1827
1828 /* Store a runscript->target
1829  * 
1830  */
1831 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
1832 {
1833    lex_get_token(lc, T_STRING);
1834
1835    if (pass == 2) {
1836       if (strcmp(lc->str, "%c") == 0) {
1837          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1838       } else if (strcasecmp(lc->str, "yes") == 0) {
1839          ((RUNSCRIPT*) item->value)->set_target("%c");
1840       } else if (strcasecmp(lc->str, "no") == 0) {
1841          ((RUNSCRIPT*) item->value)->set_target("");
1842       } else {
1843          RES *res = GetResWithName(R_CLIENT, lc->str);
1844          if (res == NULL) {
1845             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
1846                       lc->str, lc->line_no, lc->line);
1847          }
1848
1849          ((RUNSCRIPT*) item->value)->set_target(lc->str);
1850       }
1851    }
1852    scan_to_eol(lc);
1853 }
1854
1855 /*
1856  * Store a runscript->command as a string and runscript->cmd_type as a pointer
1857  */
1858 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
1859 {
1860    lex_get_token(lc, T_STRING);
1861
1862    if (pass == 2) {
1863       Dmsg2(1, "runscript cmd=%s type=%c\n", lc->str, item->code);
1864       POOLMEM *c = get_pool_memory(PM_FNAME);
1865       /* Each runscript command takes 2 entries in commands list */
1866       pm_strcpy(c, lc->str);
1867       ((RUNSCRIPT*) item->value)->commands->prepend(c); /* command line */
1868       ((RUNSCRIPT*) item->value)->commands->prepend((void *)(intptr_t)item->code); /* command type */
1869    }
1870    scan_to_eol(lc);
1871 }
1872
1873 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1874 {
1875    lex_get_token(lc, T_STRING);
1876    alist **runscripts = (alist **)(item->value) ;
1877
1878    if (pass == 2) {
1879       RUNSCRIPT *script = new_runscript();
1880       script->set_job_code_callback(job_code_callback_director);
1881
1882       script->set_command(lc->str);
1883
1884       /* TODO: remove all script->old_proto with bacula 1.42 */
1885
1886       if (strcmp(item->name, "runbeforejob") == 0) {
1887          script->when = SCRIPT_Before;
1888          script->fail_on_error = true;
1889          script->set_target("");
1890
1891       } else if (strcmp(item->name, "runafterjob") == 0) {
1892          script->when = SCRIPT_After;
1893          script->on_success = true;
1894          script->on_failure = false;
1895          script->set_target("");
1896          
1897       } else if (strcmp(item->name, "clientrunafterjob") == 0) {
1898          script->old_proto = true;
1899          script->when = SCRIPT_After;
1900          script->set_target("%c");
1901          script->on_success = true;
1902          script->on_failure = false;
1903
1904       } else if (strcmp(item->name, "clientrunbeforejob") == 0) {
1905          script->old_proto = true;
1906          script->when = SCRIPT_Before;
1907          script->set_target("%c");
1908          script->fail_on_error = true;
1909
1910       } else if (strcmp(item->name, "runafterfailedjob") == 0) {
1911          script->when = SCRIPT_After;
1912          script->on_failure = true;
1913          script->on_success = false;
1914          script->set_target("");
1915       }
1916
1917       if (*runscripts == NULL) {
1918         *runscripts = New(alist(10, not_owned_by_alist));
1919       }
1920       
1921       (*runscripts)->append(script);
1922       script->debug();
1923    }
1924
1925    scan_to_eol(lc);
1926 }
1927
1928 /* Store a bool in a bit field without modifing res_all.hdr 
1929  * We can also add an option to store_bool to skip res_all.hdr
1930  */
1931 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
1932 {
1933    lex_get_token(lc, T_NAME);
1934    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
1935       *(bool *)(item->value) = true;
1936    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
1937       *(bool *)(item->value) = false;
1938    } else {
1939       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
1940    }
1941    scan_to_eol(lc);
1942 }
1943
1944 /*
1945  * new RunScript items
1946  *   name     handler     value               code flags default_value
1947  */
1948 static RES_ITEM runscript_items[] = {
1949  {"command",        store_runscript_cmd,  {(char **)&res_runscript},     SHELL_CMD, 0, 0}, 
1950  {"console",        store_runscript_cmd,  {(char **)&res_runscript},     CONSOLE_CMD, 0, 0}, 
1951  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, 
1952  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
1953  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
1954  {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1955  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
1956  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
1957  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
1958  {NULL, NULL, {0}, 0, 0, 0}
1959 };
1960
1961 /*
1962  * Store RunScript info
1963  *
1964  *  Note, when this routine is called, we are inside a Job
1965  *  resource.  We treat the RunScript like a sort of
1966  *  mini-resource within the Job resource.
1967  */
1968 static void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
1969 {
1970    char *c;
1971    int token, i, t;
1972    alist **runscripts = (alist **)(item->value) ;
1973
1974    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
1975
1976    token = lex_get_token(lc, T_SKIP_EOL);
1977    
1978    if (token != T_BOB) {
1979       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
1980    }
1981    /* setting on_success, on_failure, fail_on_error */
1982    res_runscript.reset_default();   
1983
1984    if (pass == 2) {
1985       res_runscript.commands = New(alist(10, not_owned_by_alist));
1986    }
1987
1988    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
1989       if (token == T_EOB) {
1990         break;
1991       }
1992       if (token != T_IDENTIFIER) {
1993         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
1994       }
1995       for (i=0; runscript_items[i].name; i++) {
1996         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
1997            token = lex_get_token(lc, T_SKIP_EOL);
1998            if (token != T_EQUALS) {
1999               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
2000            }
2001            
2002            /* Call item handler */
2003            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
2004            i = -1;
2005            break;
2006         }
2007       }
2008       
2009       if (i >=0) {
2010         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
2011       }
2012    }
2013
2014    if (pass == 2) {
2015       /* run on client by default */
2016       if (res_runscript.target == NULL) {
2017          res_runscript.set_target("%c");
2018       }
2019       if (*runscripts == NULL) {
2020          *runscripts = New(alist(10, not_owned_by_alist));
2021       }
2022       /*
2023        * commands list contains 2 values per command
2024        *  - POOLMEM command string (ex: /bin/true) 
2025        *  - int command type (ex: SHELL_CMD)
2026        */
2027       res_runscript.set_job_code_callback(job_code_callback_director);
2028       while ((c=(char*)res_runscript.commands->pop()) != NULL) {
2029          t = (intptr_t)res_runscript.commands->pop();
2030          RUNSCRIPT *script = new_runscript();
2031          memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
2032          script->command = c;
2033          script->cmd_type = t;
2034          /* target is taken from res_runscript, each runscript object have
2035           * a copy 
2036           */
2037          script->target = NULL;
2038          script->set_target(res_runscript.target);
2039
2040          (*runscripts)->append(script);
2041          script->debug();
2042       }
2043       delete res_runscript.commands;
2044       /* setting on_success, on_failure... cleanup target field */
2045       res_runscript.reset_default(true); 
2046    }
2047
2048    scan_to_eol(lc);
2049    set_bit(index, res_all.hdr.item_present);
2050 }
2051
2052 /* callback function for edit_job_codes */
2053 /* See ../lib/util.c, function edit_job_codes, for more remaining codes */
2054 extern "C" char *job_code_callback_director(JCR *jcr, const char* param)
2055 {
2056    static char yes[] = "yes";
2057    static char no[] = "no";
2058    switch (param[0]) {
2059       case 'f':
2060          if (jcr->fileset) {
2061             return jcr->fileset->name();
2062          }
2063          break;
2064       case 'h':
2065          if (jcr->client) {
2066             return jcr->client->address;
2067          }
2068          break;
2069       case 'p':
2070          if (jcr->pool) {
2071             return jcr->pool->name();
2072          }
2073          break;
2074       case 'w':
2075          if (jcr->wstore) {
2076             return jcr->wstore->name();
2077          }
2078          break;
2079       case 'x':
2080          return jcr->spool_data ? yes : no;
2081          break;
2082       case 'D':
2083          return my_name;
2084          break;
2085    }
2086    return NULL;
2087 }
2088
2089 bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code)
2090 {
2091    config->init(configfile, NULL, exit_code, (void *)&res_all, res_all_size,
2092       r_first, r_last, resources, res_head);
2093    return config->parse_config();
2094 }