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