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