]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/dird/dird_conf.c
Fix #3284 about Client address not reloaded properly
[bacula/bacula] / bacula / src / dird / dird_conf.c
1 /*
2    Bacula(R) - The Network Backup Solution
3
4    Copyright (C) 2000-2017 Kern Sibbald
5
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   Main configuration file parser for Bacula Directors,
21  *    some parts may be split into separate files such as
22  *    the schedule configuration (run_config.c).
23  *
24  *   Note, the configuration file parser consists of three parts
25  *
26  *   1. The generic lexical scanner in lib/lex.c and lib/lex.h
27  *
28  *   2. The generic config  scanner in lib/parse_config.c and
29  *      lib/parse_config.h.
30  *      These files contain the parser code, some utility
31  *      routines, and the common store routines (name, int,
32  *      string).
33  *
34  *   3. The daemon specific file, which contains the Resource
35  *      definitions as well as any specific store routines
36  *      for the resource records.
37  *
38  *     Kern Sibbald, January MM
39  */
40
41
42 #include "bacula.h"
43 #include "dird.h"
44
45 /* Define the first and last resource ID record
46  * types. Note, these should be unique for each
47  * daemon though not a requirement.
48  */
49 int32_t r_first = R_FIRST;
50 int32_t r_last  = R_LAST;
51 RES_HEAD **res_head;
52
53 static pthread_mutex_t globals_mutex = PTHREAD_MUTEX_INITIALIZER;
54 dlist client_globals;
55 dlist job_globals;
56 dlist store_globals;
57 dlist sched_globals;
58
59
60 /* Imported subroutines */
61 extern void store_run(LEX *lc, RES_ITEM *item, int index, int pass);
62 extern void store_finc(LEX *lc, RES_ITEM *item, int index, int pass);
63 extern void store_inc(LEX *lc, RES_ITEM *item, int index, int pass);
64
65
66 /* Forward referenced subroutines */
67
68 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass);
69 void store_level(LEX *lc, RES_ITEM *item, int index, int pass);
70 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass);
71 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass);
72 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass);
73 void store_ac_res(LEX *lc, RES_ITEM *item, int index, int pass);
74 void store_device(LEX *lc, RES_ITEM *item, int index, int pass);
75 void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass);
76 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass);
77 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass);
78 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass);
79
80 /* We build the current resource here as we are
81  * scanning the resource configuration definition,
82  * then move it to allocated memory when the resource
83  * scan is complete.
84  */
85 #if defined(_MSC_VER)
86 extern "C" { // work around visual compiler mangling variables
87    URES res_all;
88 }
89 #else
90 URES res_all;
91 #endif
92 int32_t res_all_size = sizeof(res_all);
93
94 /* Implementation of certain classes */
95
96 void CLIENT::create_client_globals()
97 {
98    globals = (CLIENT_GLOBALS *)malloc(sizeof(CLIENT_GLOBALS));
99    memset(globals, 0, sizeof(CLIENT_GLOBALS));
100    globals->name = bstrdup(name());
101    globals->enabled = -1;       /* Not set */
102    client_globals.append(globals);
103 }
104
105 int32_t CLIENT::getNumConcurrentJobs()
106 {
107    if (!globals) {
108       return 0;
109    }
110    return globals->NumConcurrentJobs;
111 }
112
113 void CLIENT::setNumConcurrentJobs(int32_t num)
114 {
115    P(globals_mutex);
116    if (!globals) {
117       create_client_globals();
118    }
119    globals->NumConcurrentJobs = num;
120    V(globals_mutex);
121    ASSERT(num >= 0);
122    Dmsg2(200, "Set NumConcurrentJobs=%ld for Client %s\n",
123       num, globals->name);
124 }
125
126 char *CLIENT::address()
127 {
128    if (!globals) {
129       return client_address;
130    }
131    if (!globals->SetIPaddress) {
132       return client_address;
133    }
134    /* TODO: Fix concurrency issue between setAddress() and address()
135     * we need to copy the address in an argument, the variable can be
136     * freed at any time.
137     */
138    return globals->SetIPaddress;
139 }
140
141 void CLIENT::setAddress(char *addr)
142 {
143    P(globals_mutex);
144    if (!globals) {
145       create_client_globals();
146    }
147    if (globals->SetIPaddress) {
148       free(globals->SetIPaddress);
149    }
150    globals->SetIPaddress = bstrdup(addr);
151    V(globals_mutex);
152 }
153
154 bool CLIENT::is_enabled()
155 {
156    if (!globals || globals->enabled < 0) {
157       return Enabled;
158    }
159    return globals->enabled;
160 }
161
162 void CLIENT::setEnabled(bool val)
163 {
164    P(globals_mutex);
165    if (!globals) {
166       create_client_globals();
167    }
168    /* TODO: We probably need to set -1 (not set) when we are back to the default value */
169    globals->enabled = val? 1 : 0;
170    V(globals_mutex);
171    Dmsg2(200, "Set Enabled=%d for Client %s\n",
172       val, globals->name);
173 }
174
175 void JOB::create_job_globals()
176 {
177    globals = (JOB_GLOBALS *)malloc(sizeof(JOB_GLOBALS));
178    memset(globals, 0, sizeof(JOB_GLOBALS));
179    globals->name = bstrdup(name());
180    globals->enabled = -1;       /* Not set */
181    job_globals.append(globals);
182 }
183
184 int32_t JOB::getNumConcurrentJobs()
185 {
186    if (!globals) {
187       return 0;
188    }
189    return globals->NumConcurrentJobs;
190 }
191
192 void JOB::setNumConcurrentJobs(int32_t num)
193 {
194    P(globals_mutex);
195    if (!globals) {
196       create_job_globals();
197    }
198    globals->NumConcurrentJobs = num;
199    V(globals_mutex);
200    ASSERT(num >= 0);
201    Dmsg2(200, "Set NumConcurrentJobs=%ld for Job %s\n",
202       num, globals->name);
203 }
204
205 bool JOB::is_enabled()
206 {
207    if (!globals || globals->enabled < 0) {
208       return Enabled;
209    }
210    return globals->enabled;
211 }
212
213 void JOB::setEnabled(bool val)
214 {
215    P(globals_mutex);
216    if (!globals) {
217       create_job_globals();
218    }
219    globals->enabled = val ? 1 : 0;
220    V(globals_mutex);
221    Dmsg2(200, "Set Enabled=%d for Job %s\n",
222       val, globals->name);
223 }
224
225 void STORE::create_store_globals()
226 {
227    globals = (STORE_GLOBALS *)malloc(sizeof(STORE_GLOBALS));
228    memset(globals, 0, sizeof(STORE_GLOBALS));
229    globals->name = bstrdup(name());
230    globals->enabled = -1;       /* Not set */
231    store_globals.append(globals);
232 }
233
234 int32_t STORE::getNumConcurrentReadJobs()
235 {
236    if (!globals) {
237       return 0;
238    }
239    return globals->NumConcurrentReadJobs;
240 }
241
242 void STORE::setNumConcurrentReadJobs(int32_t num)
243 {
244    P(globals_mutex);
245    if (!globals) {
246       create_store_globals();
247    }
248    globals->NumConcurrentReadJobs = num;
249    V(globals_mutex);
250    Dmsg2(200, "Set NumConcurrentReadJobs=%ld for Store %s\n",
251       num, globals->name);
252    ASSERT(num >= 0);
253 }
254
255 int32_t STORE::getNumConcurrentJobs()
256 {
257    if (!globals) {
258       return 0;
259    }
260    return globals->NumConcurrentJobs;
261 }
262
263 void STORE::setNumConcurrentJobs(int32_t num)
264 {
265    P(globals_mutex);
266    if (!globals) {
267       create_store_globals();
268    }
269    globals->NumConcurrentJobs = num;
270    V(globals_mutex);
271    Dmsg2(200, "Set numconcurrentJobs=%ld for Store %s\n",
272       num, globals->name);
273    ASSERT(num >= 0);
274 }
275
276 bool STORE::is_enabled()
277 {
278    if (!globals || globals->enabled < 0) {
279       return Enabled;
280    }
281    return globals->enabled;
282 }
283
284 void STORE::setEnabled(bool val)
285 {
286    P(globals_mutex);
287    if (!globals) {
288       create_store_globals();
289    }
290    globals->enabled = val ? 1 : 0;
291    V(globals_mutex);
292    Dmsg2(200, "Set Enabled=%d for Storage %s\n",
293       val, globals->name);
294 }
295
296 void SCHED::create_sched_globals()
297 {
298    globals = (SCHED_GLOBALS *)malloc(sizeof(CLIENT_GLOBALS));
299    memset(globals, 0, sizeof(SCHED_GLOBALS));
300    globals->name = bstrdup(name());
301    globals->enabled = -1;       /* Not set */
302    sched_globals.append(globals);
303 }
304
305 bool SCHED::is_enabled()
306 {
307    if (!globals || globals->enabled < 0) {
308       return Enabled;
309    }
310    return globals->enabled;
311 }
312
313 void SCHED::setEnabled(bool val)
314 {
315    P(globals_mutex);
316    if (!globals) {
317       create_sched_globals();
318    }
319    globals->enabled = val ? 1 : 0;
320    V(globals_mutex);
321    Dmsg2(200, "Set Enabled=%d for Schedule %s\n",
322       val, globals->name);
323 }
324
325 /*
326  * Definition of records permitted within each
327  * resource with the routine to process the record
328  * information.  NOTE! quoted names must be in lower case.
329  */
330 /*
331  *    Director Resource
332  *
333  *   name          handler     value                 code flags    default_value
334  */
335 static RES_ITEM dir_items[] = {
336    {"Name",        store_name,     ITEM(res_dir.hdr.name), 0, ITEM_REQUIRED, 0},
337    {"Description", store_str,      ITEM(res_dir.hdr.desc), 0, 0, 0},
338    {"Messages",    store_res,      ITEM(res_dir.messages), R_MSGS, 0, 0},
339    {"DirPort",     store_addresses_port,    ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
340    {"DirAddress",  store_addresses_address, ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
341    {"DirAddresses",store_addresses,         ITEM(res_dir.DIRaddrs),  0, ITEM_DEFAULT, 9101},
342    {"DirSourceAddress",store_addresses_address, ITEM(res_dir.DIRsrc_addr),  0, ITEM_DEFAULT, 0},
343    {"QueryFile",   store_dir,      ITEM(res_dir.query_file), 0, ITEM_REQUIRED, 0},
344    {"WorkingDirectory", store_dir, ITEM(res_dir.working_directory), 0, ITEM_REQUIRED, 0},
345    {"PluginDirectory",  store_dir, ITEM(res_dir.plugin_directory),  0, 0, 0},
346    {"ScriptsDirectory", store_dir, ITEM(res_dir.scripts_directory), 0, 0, 0},
347    {"PidDirectory",     store_dir, ITEM(res_dir.pid_directory),     0, ITEM_REQUIRED, 0},
348    {"SubsysDirectory",  store_dir, ITEM(res_dir.subsys_directory),  0, 0, 0},
349    {"MaximumConcurrentJobs", store_pint32, ITEM(res_dir.MaxConcurrentJobs), 0, ITEM_DEFAULT, 20},
350    {"MaximumReloadRequests", store_pint32, ITEM(res_dir.MaxReload), 0, ITEM_DEFAULT, 32},
351    {"MaximumConsoleConnections", store_pint32, ITEM(res_dir.MaxConsoleConnect), 0, ITEM_DEFAULT, 20},
352    {"Password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
353    {"FdConnectTimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 3 * 60},
354    {"SdConnectTimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 30 * 60},
355    {"HeartbeatInterval", store_time, ITEM(res_dir.heartbeat_interval), 0, ITEM_DEFAULT, 5 * 60},
356    {"TlsAuthenticate",      store_bool,      ITEM(res_dir.tls_authenticate), 0, 0, 0},
357    {"TlsEnable",            store_bool,      ITEM(res_dir.tls_enable), 0, 0, 0},
358    {"TlsRequire",           store_bool,      ITEM(res_dir.tls_require), 0, 0, 0},
359    {"TlsVerifyPeer",        store_bool,      ITEM(res_dir.tls_verify_peer), 0, ITEM_DEFAULT, true},
360    {"TlsCaCertificateFile", store_dir,       ITEM(res_dir.tls_ca_certfile), 0, 0, 0},
361    {"TlsCaCertificateDir",  store_dir,       ITEM(res_dir.tls_ca_certdir), 0, 0, 0},
362    {"TlsCertificate",       store_dir,       ITEM(res_dir.tls_certfile), 0, 0, 0},
363    {"TlsKey",               store_dir,       ITEM(res_dir.tls_keyfile), 0, 0, 0},
364    {"TlsDhFile",            store_dir,       ITEM(res_dir.tls_dhfile), 0, 0, 0},
365    {"TlsAllowedCn",         store_alist_str, ITEM(res_dir.tls_allowed_cns), 0, 0, 0},
366    {"StatisticsRetention",  store_time,      ITEM(res_dir.stats_retention),  0, ITEM_DEFAULT, 60*60*24*31*12*5},
367    {"VerId",                store_str,       ITEM(res_dir.verid), 0, 0, 0},
368    {"CommCompression",      store_bool,      ITEM(res_dir.comm_compression), 0, ITEM_DEFAULT, true},
369    {NULL, NULL, {0}, 0, 0, 0}
370 };
371
372 /*
373  *    Console Resource
374  *
375  *   name          handler     value                 code flags    default_value
376  */
377 static RES_ITEM con_items[] = {
378    {"Name",        store_name,     ITEM(res_con.hdr.name), 0, ITEM_REQUIRED, 0},
379    {"Description", store_str,      ITEM(res_con.hdr.desc), 0, 0, 0},
380    {"Password",    store_password, ITEM(res_con.password), 0, ITEM_REQUIRED, 0},
381    {"JobAcl",      store_acl,      ITEM(res_con.ACL_lists), Job_ACL, 0, 0},
382    {"ClientAcl",   store_acl,      ITEM(res_con.ACL_lists), Client_ACL, 0, 0},
383    {"StorageAcl",  store_acl,      ITEM(res_con.ACL_lists), Storage_ACL, 0, 0},
384    {"ScheduleAcl", store_acl,      ITEM(res_con.ACL_lists), Schedule_ACL, 0, 0},
385    {"RunAcl",      store_acl,      ITEM(res_con.ACL_lists), Run_ACL, 0, 0},
386    {"PoolAcl",     store_acl,      ITEM(res_con.ACL_lists), Pool_ACL, 0, 0},
387    {"CommandAcl",  store_acl,      ITEM(res_con.ACL_lists), Command_ACL, 0, 0},
388    {"FilesetAcl",  store_acl,      ITEM(res_con.ACL_lists), FileSet_ACL, 0, 0},
389    {"CatalogAcl",  store_acl,      ITEM(res_con.ACL_lists), Catalog_ACL, 0, 0},
390    {"WhereAcl",    store_acl,      ITEM(res_con.ACL_lists), Where_ACL, 0, 0},
391    {"RestoreClientAcl", store_acl, ITEM(res_con.ACL_lists), RestoreClient_ACL, 0, 0},
392    {"BackupClientAcl",  store_acl, ITEM(res_con.ACL_lists), BackupClient_ACL, 0, 0},
393    {"PluginOptionsAcl", store_acl, ITEM(res_con.ACL_lists), PluginOptions_ACL, 0, 0},
394    {"DirectoryAcl",     store_acl, ITEM(res_con.ACL_lists), Directory_ACL, 0, 0},
395    {"TlsAuthenticate",      store_bool,      ITEM(res_con.tls_authenticate), 0, 0, 0},
396    {"TlsEnable",            store_bool,      ITEM(res_con.tls_enable), 0, 0, 0},
397    {"TlsRequire",           store_bool,      ITEM(res_con.tls_require), 0, 0, 0},
398    {"TlsVerifyPeer",        store_bool,      ITEM(res_con.tls_verify_peer), 0, ITEM_DEFAULT, true},
399    {"TlsCaCertificateFile", store_dir,       ITEM(res_con.tls_ca_certfile), 0, 0, 0},
400    {"TlsCaCertificateDir",  store_dir,       ITEM(res_con.tls_ca_certdir), 0, 0, 0},
401    {"TlsCertificate",       store_dir,       ITEM(res_con.tls_certfile), 0, 0, 0},
402    {"TlsKey",               store_dir,       ITEM(res_con.tls_keyfile), 0, 0, 0},
403    {"TlsDhFile",            store_dir,       ITEM(res_con.tls_dhfile), 0, 0, 0},
404    {"TlsAllowedCn",         store_alist_str, ITEM(res_con.tls_allowed_cns), 0, 0, 0},
405    {NULL, NULL, {0}, 0, 0, 0}
406 };
407
408
409 /*
410  *    Client or File daemon resource
411  *
412  *   name          handler     value                 code flags    default_value
413  */
414
415 static RES_ITEM cli_items[] = {
416    {"Name",     store_name,       ITEM(res_client.hdr.name), 0, ITEM_REQUIRED, 0},
417    {"Description", store_str,     ITEM(res_client.hdr.desc), 0, 0, 0},
418    {"fdaddress",  store_str,      ITEM(res_client.client_address),  0, 0, 0},
419    {"Address",  store_str,        ITEM(res_client.client_address),  0, ITEM_REQUIRED, 0},
420    {"FdPort",   store_pint32,     ITEM(res_client.FDport),   0, ITEM_DEFAULT, 9102},
421    {"fdpassword", store_password, ITEM(res_client.password), 0, 0, 0},
422    {"Password", store_password,   ITEM(res_client.password), 0, ITEM_REQUIRED, 0},
423    {"FdStorageAddress", store_str, ITEM(res_client.fd_storage_address), 0, 0, 0},
424    {"Catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, ITEM_REQUIRED, 0},
425    {"FileRetention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*60},
426    {"JobRetention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*180},
427    {"HeartbeatInterval",    store_time, ITEM(res_client.heartbeat_interval), 0, ITEM_DEFAULT, 5 * 60},
428    {"AutoPrune",            store_bool, ITEM(res_client.AutoPrune), 0, ITEM_DEFAULT, true},
429    {"SDCallsClient",        store_bool, ITEM(res_client.sd_calls_client), 0, ITEM_DEFAULT, false},
430    {"SnapshotRetention",  store_time,  ITEM(res_client.SnapRetention),  0, ITEM_DEFAULT, 0},
431    {"MaximumConcurrentJobs", store_pint32,   ITEM(res_client.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
432    {"TlsAuthenticate",      store_bool,      ITEM(res_client.tls_authenticate), 0, 0, 0},
433    {"TlsEnable",            store_bool,      ITEM(res_client.tls_enable), 0, 0, 0},
434    {"TlsRequire",           store_bool,      ITEM(res_client.tls_require), 0, 0, 0},
435    {"TlsCaCertificateFile", store_dir,       ITEM(res_client.tls_ca_certfile), 0, 0, 0},
436    {"TlsCaCertificateDir",  store_dir,       ITEM(res_client.tls_ca_certdir), 0, 0, 0},
437    {"TlsCertificate",       store_dir,       ITEM(res_client.tls_certfile), 0, 0, 0},
438    {"TlsKey",               store_dir,       ITEM(res_client.tls_keyfile), 0, 0, 0},
439    {"TlsAllowedCn",         store_alist_str, ITEM(res_client.tls_allowed_cns), 0, 0, 0},
440    {"MaximumBandwidthPerJob", store_speed, ITEM(res_client.max_bandwidth), 0, 0, 0},
441    {"Enabled",     store_bool, ITEM(res_client.Enabled), 0, ITEM_DEFAULT, true},
442    {NULL, NULL, {0}, 0, 0, 0}
443 };
444
445 /* Storage daemon resource
446  *
447  *   name          handler     value                 code flags    default_value
448  */
449 static RES_ITEM store_items[] = {
450    {"Name",        store_name,     ITEM(res_store.hdr.name),   0, ITEM_REQUIRED, 0},
451    {"Description", store_str,      ITEM(res_store.hdr.desc),   0, 0, 0},
452    {"SdPort",      store_pint32,   ITEM(res_store.SDport),     0, ITEM_DEFAULT, 9103},
453    {"sdaddress",   store_str,      ITEM(res_store.address),    0, 0, 0},
454    {"Address",     store_str,      ITEM(res_store.address),    0, ITEM_REQUIRED, 0},
455    {"FdStorageAddress", store_str, ITEM(res_store.fd_storage_address), 0, 0, 0},
456    {"sdpassword",  store_password, ITEM(res_store.password),   0, 0, 0},
457    {"Password",    store_password, ITEM(res_store.password),   0, ITEM_REQUIRED, 0},
458    {"Device",      store_device,   ITEM(res_store.device),     R_DEVICE, ITEM_REQUIRED, 0},
459    {"MediaType",   store_strname,  ITEM(res_store.media_type), 0, ITEM_REQUIRED, 0},
460    /*                   _bool,
461     * Big kludge, these two autochanger definitions must be in
462     * this order and together.
463     */
464    {"Autochanger", store_ac_res,   ITEM(res_store.changer), 0, ITEM_DEFAULT, 0},
465    {"Autochanger", store_bool,     ITEM(res_store.autochanger), 0, ITEM_DEFAULT, false},
466    {"SharedStorage", store_ac_res, ITEM(res_store.shared_storage), 1, ITEM_DEFAULT, 0},
467    {"Enabled",     store_bool,     ITEM(res_store.Enabled),     0, ITEM_DEFAULT, true},
468    {"AllowCompression",  store_bool, ITEM(res_store.AllowCompress), 0, ITEM_DEFAULT, true},
469    {"HeartbeatInterval", store_time, ITEM(res_store.heartbeat_interval), 0, ITEM_DEFAULT, 5 * 60},
470    {"MaximumConcurrentJobs", store_pint32, ITEM(res_store.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
471    {"MaximumConcurrentReadjobs", store_pint32, ITEM(res_store.MaxConcurrentReadJobs), 0, ITEM_DEFAULT, 0},
472    {"sddport", store_pint32, ITEM(res_store.SDDport), 0, 0, 0}, /* deprecated */
473    {"TlsAuthenticate",      store_bool,      ITEM(res_store.tls_authenticate), 0, 0, 0},
474    {"TlsEnable",            store_bool,      ITEM(res_store.tls_enable), 0, 0, 0},
475    {"TlsRequire",           store_bool,      ITEM(res_store.tls_require), 0, 0, 0},
476    {"TlsCaCertificateFile", store_dir,       ITEM(res_store.tls_ca_certfile), 0, 0, 0},
477    {"TlsCaCertificateDir",  store_dir,       ITEM(res_store.tls_ca_certdir), 0, 0, 0},
478    {"TlsCertificate",       store_dir,       ITEM(res_store.tls_certfile), 0, 0, 0},
479    {"TlsKey",               store_dir,       ITEM(res_store.tls_keyfile), 0, 0, 0},
480    {NULL, NULL, {0}, 0, 0, 0}
481 };
482
483 /*
484  *    Catalog Resource Directives
485  *
486  *   name          handler     value                 code flags    default_value
487  */
488 static RES_ITEM cat_items[] = {
489    {"Name",     store_name,     ITEM(res_cat.hdr.name),    0, ITEM_REQUIRED, 0},
490    {"Description", store_str,   ITEM(res_cat.hdr.desc),    0, 0, 0},
491    {"dbaddress", store_str,     ITEM(res_cat.db_address),  0, 0, 0},
492    {"Address",  store_str,      ITEM(res_cat.db_address),  0, 0, 0},
493    {"DbPort",   store_pint32,   ITEM(res_cat.db_port),      0, 0, 0},
494    /* keep this password as store_str for the moment */
495    {"dbpassword", store_str,    ITEM(res_cat.db_password), 0, 0, 0},
496    {"Password", store_str,      ITEM(res_cat.db_password), 0, 0, 0},
497    {"dbuser",   store_str,      ITEM(res_cat.db_user),     0, 0, 0},
498    {"User",     store_str,      ITEM(res_cat.db_user),     0, 0, 0},
499    {"DbName",   store_str,      ITEM(res_cat.db_name),     0, ITEM_REQUIRED, 0},
500    {"dbdriver", store_str,      ITEM(res_cat.db_driver),   0, 0, 0},
501    {"DbSocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0},
502    {"dbsslmode", store_str,     ITEM(res_cat.db_ssl_mode),  0, 0, 0},
503    {"dbsslkey", store_str,      ITEM(res_cat.db_ssl_key),  0, 0, 0},
504    {"dbsslcert", store_str,     ITEM(res_cat.db_ssl_cert),  0, 0, 0},
505    {"dbsslca", store_str,       ITEM(res_cat.db_ssl_ca),  0, 0, 0},
506    {"dbsslcapath", store_str,   ITEM(res_cat.db_ssl_capath),  0, 0, 0},
507    {"DbSocket", store_str,      ITEM(res_cat.db_socket),   0, 0, 0},
508    /* Turned off for the moment */
509    {"MultipleConnections", store_bit, ITEM(res_cat.mult_db_connections), 0, 0, 0},
510    {"DisableBatchInsert", store_bool, ITEM(res_cat.disable_batch_insert), 0, ITEM_DEFAULT, false},
511    {NULL, NULL, {0}, 0, 0, 0}
512 };
513
514 /*
515  *    Job Resource Directives
516  *
517  *   name          handler     value                 code flags    default_value
518  */
519 RES_ITEM job_items[] = {
520    {"Name",      store_name,    ITEM(res_job.hdr.name),  0, ITEM_REQUIRED, 0},
521    {"Description", store_str,   ITEM(res_job.hdr.desc),  0, 0, 0},
522    {"Type",      store_jobtype, ITEM(res_job.JobType),   0, ITEM_REQUIRED, 0},
523    {"Level",     store_level,   ITEM(res_job.JobLevel),    0, 0, 0},
524    {"Messages",  store_res,     ITEM(res_job.messages),  R_MSGS, ITEM_REQUIRED, 0},
525    {"Storage",   store_alist_res, ITEM(res_job.storage),  R_STORAGE, 0, 0},
526    {"Pool",      store_res,     ITEM(res_job.pool),      R_POOL, ITEM_REQUIRED, 0},
527    {"NextPool",  store_res,     ITEM(res_job.next_pool), R_POOL, 0, 0},
528    {"FullBackupPool",  store_res, ITEM(res_job.full_pool),   R_POOL, 0, 0},
529    {"VirtualFullBackupPool", store_res, ITEM(res_job.vfull_pool), R_POOL, 0, 0},
530    {"IncrementalBackupPool",  store_res, ITEM(res_job.inc_pool), R_POOL, 0, 0},
531    {"DifferentialBackupPool", store_res, ITEM(res_job.diff_pool), R_POOL, 0, 0},
532    {"Client",    store_res,     ITEM(res_job.client),   R_CLIENT, ITEM_REQUIRED, 0},
533    {"Fileset",   store_res,     ITEM(res_job.fileset),  R_FILESET, ITEM_REQUIRED, 0},
534    {"Schedule",  store_res,     ITEM(res_job.schedule), R_SCHEDULE, 0, 0},
535    {"VerifyJob", store_res,     ITEM(res_job.verify_job), R_JOB, 0, 0},
536    {"JobToVerify", store_res,   ITEM(res_job.verify_job), R_JOB, 0, 0},
537    {"JobDefs",   store_res,     ITEM(res_job.jobdefs),    R_JOBDEFS, 0, 0},
538    {"Run",       store_alist_str, ITEM(res_job.run_cmds), 0, 0, 0},
539    /* Root of where to restore files */
540    {"Where",    store_dir,      ITEM(res_job.RestoreWhere), 0, 0, 0},
541    {"RegexWhere",    store_str, ITEM(res_job.RegexWhere), 0, 0, 0},
542    {"StripPrefix",   store_str, ITEM(res_job.strip_prefix), 0, 0, 0},
543    {"AddPrefix",    store_str,  ITEM(res_job.add_prefix), 0, 0, 0},
544    {"AddSuffix",    store_str,  ITEM(res_job.add_suffix), 0, 0, 0},
545    /* Where to find bootstrap during restore */
546    {"Bootstrap",store_dir,      ITEM(res_job.RestoreBootstrap), 0, 0, 0},
547    /* Where to write bootstrap file during backup */
548    {"WriteBootstrap",store_dir, ITEM(res_job.WriteBootstrap), 0, 0, 0},
549    {"WriteVerifyList",store_dir,ITEM(res_job.WriteVerifyList), 0, 0, 0},
550    {"Replace",  store_replace,  ITEM(res_job.replace), 0, ITEM_DEFAULT, REPLACE_ALWAYS},
551    {"MaximumBandwidth", store_speed, ITEM(res_job.max_bandwidth), 0, 0, 0},
552    {"MaxRunSchedTime", store_time, ITEM(res_job.MaxRunSchedTime), 0, 0, 0},
553    {"MaxRunTime",   store_time, ITEM(res_job.MaxRunTime), 0, 0, 0},
554    /* xxxMaxWaitTime are deprecated */
555    {"fullmaxwaittime",  store_time, ITEM(res_job.FullMaxRunTime), 0, 0, 0},
556    {"incrementalmaxwaittime",  store_time, ITEM(res_job.IncMaxRunTime), 0, 0, 0},
557    {"differentialmaxwaittime", store_time, ITEM(res_job.DiffMaxRunTime), 0, 0, 0},
558    {"FullMaxRunTime",  store_time, ITEM(res_job.FullMaxRunTime), 0, 0, 0},
559    {"IncrementalMaxRunTime",  store_time, ITEM(res_job.IncMaxRunTime), 0, 0, 0},
560    {"DifferentialMaxRunTime", store_time, ITEM(res_job.DiffMaxRunTime), 0, 0, 0},
561    {"MaxWaitTime",  store_time, ITEM(res_job.MaxWaitTime), 0, 0, 0},
562    {"MaxStartDelay",store_time, ITEM(res_job.MaxStartDelay), 0, 0, 0},
563    {"MaxFullInterval",  store_time, ITEM(res_job.MaxFullInterval), 0, 0, 0},
564    {"MaxVirtualFullInterval",  store_time, ITEM(res_job.MaxVirtualFullInterval), 0, 0, 0},
565    {"MaxDiffInterval",  store_time, ITEM(res_job.MaxDiffInterval), 0, 0, 0},
566    {"PrefixLinks", store_bool, ITEM(res_job.PrefixLinks), 0, ITEM_DEFAULT, false},
567    {"PruneJobs",   store_bool, ITEM(res_job.PruneJobs), 0, ITEM_DEFAULT, false},
568    {"PruneFiles",  store_bool, ITEM(res_job.PruneFiles), 0, ITEM_DEFAULT, false},
569    {"PruneVolumes",store_bool, ITEM(res_job.PruneVolumes), 0, ITEM_DEFAULT, false},
570    {"PurgeMigrationJob",  store_bool, ITEM(res_job.PurgeMigrateJob), 0, ITEM_DEFAULT, false},
571    {"Enabled",     store_bool, ITEM(res_job.Enabled), 0, ITEM_DEFAULT, true},
572    {"SnapshotRetention",  store_time,  ITEM(res_job.SnapRetention),  0, ITEM_DEFAULT, 0},
573    {"SpoolAttributes",store_bool, ITEM(res_job.SpoolAttributes), 0, ITEM_DEFAULT, true},
574    {"SpoolData",   store_bool, ITEM(res_job.spool_data), 0, ITEM_DEFAULT, false},
575    {"SpoolSize",   store_size64, ITEM(res_job.spool_size), 0, 0, 0},
576    {"ReRunFailedLevels",   store_bool, ITEM(res_job.rerun_failed_levels), 0, ITEM_DEFAULT, false},
577    {"PreferMountedVolumes", store_bool, ITEM(res_job.PreferMountedVolumes), 0, ITEM_DEFAULT, true},
578    /*
579     * JSON tools skip Directive in lowercase. They are deprecated or
580     * are synonym with an other one that follows. Like User and dbuser.
581     */
582    {"runbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
583    {"runafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
584    {"runafterfailedjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
585    {"clientrunbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
586    {"clientrunafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
587    {"consolerunbeforejob", store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
588    {"consolerunafterjob",  store_short_runscript,  ITEM(res_job.RunScripts),  0, 0, 0},
589    {"Runscript",          store_runscript, ITEM(res_job.RunScripts), 0, ITEM_NO_EQUALS, 0},
590    {"MaximumConcurrentJobs", store_pint32, ITEM(res_job.MaxConcurrentJobs), 0, ITEM_DEFAULT, 1},
591    {"MaximumSpawnedJobs", store_pint32, ITEM(res_job.MaxSpawnedJobs), 0, ITEM_DEFAULT, 600},
592    {"RescheduleOnError", store_bool, ITEM(res_job.RescheduleOnError), 0, ITEM_DEFAULT, false},
593    {"RescheduleIncompleteJobs", store_bool, ITEM(res_job.RescheduleIncompleteJobs), 0, ITEM_DEFAULT, true},
594    {"RescheduleInterval", store_time, ITEM(res_job.RescheduleInterval), 0, ITEM_DEFAULT, 60 * 30},
595    {"RescheduleTimes",    store_pint32, ITEM(res_job.RescheduleTimes), 0, 0, 0},
596    {"Priority",           store_pint32, ITEM(res_job.Priority), 0, ITEM_DEFAULT, 10},
597    {"BackupsToKeep",      store_pint32, ITEM(res_job.BackupsToKeep), 0, ITEM_DEFAULT, 0},
598    {"AllowMixedPriority", store_bool, ITEM(res_job.allow_mixed_priority), 0, ITEM_DEFAULT, false},
599    {"WritePartAfterJob",  store_bool, ITEM(res_job.write_part_after_job), 0, ITEM_DEFAULT, true},
600    {"SelectionPattern",   store_str, ITEM(res_job.selection_pattern), 0, 0, 0},
601    {"SelectionType",      store_migtype, ITEM(res_job.selection_type), 0, 0, 0},
602    {"Accurate",           store_bool, ITEM(res_job.accurate), 0,0,0},
603    {"AllowDuplicateJobs", store_bool, ITEM(res_job.AllowDuplicateJobs), 0, ITEM_DEFAULT, true},
604    {"allowhigherduplicates",   store_bool, ITEM(res_job.AllowHigherDuplicates), 0, ITEM_DEFAULT, true},
605    {"CancelLowerLevelDuplicates", store_bool, ITEM(res_job.CancelLowerLevelDuplicates), 0, ITEM_DEFAULT, false},
606    {"CancelQueuedDuplicates",  store_bool, ITEM(res_job.CancelQueuedDuplicates), 0, ITEM_DEFAULT, false},
607    {"CancelRunningDuplicates", store_bool, ITEM(res_job.CancelRunningDuplicates), 0, ITEM_DEFAULT, false},
608    {"DeleteConsolidatedJobs",  store_bool, ITEM(res_job.DeleteConsolidatedJobs), 0, ITEM_DEFAULT, false},
609    {"PluginOptions", store_str, ITEM(res_job.PluginOptions), 0, 0, 0},
610    {"Base", store_alist_res, ITEM(res_job.base),  R_JOB, 0, 0},
611    {NULL, NULL, {0}, 0, 0, 0}
612 };
613
614 /* Fileset resource
615  *
616  *   Name          handler     value                 code flags    default_value
617  */
618 static RES_ITEM fs_items[] = {
619    {"Name",        store_name, ITEM(res_fs.hdr.name), 0, ITEM_REQUIRED, 0},
620    {"Description", store_str,  ITEM(res_fs.hdr.desc), 0, 0, 0},
621    {"IgnoreFilesetChanges", store_bool, ITEM(res_fs.ignore_fs_changes), 0, ITEM_DEFAULT, false},
622    {"EnableVss",     store_bool, ITEM(res_fs.enable_vss), 0, ITEM_DEFAULT, true},
623    {"EnableSnapshot",store_bool, ITEM(res_fs.enable_snapshot), 0, ITEM_DEFAULT, false},
624    {"Include",     store_inc,  {0},                   0, ITEM_NO_EQUALS, 0},
625    {"Exclude",     store_inc,  {0},                   1, ITEM_NO_EQUALS, 0},
626    {NULL,          NULL,       {0},                  0, 0, 0}
627 };
628
629 /* Schedule -- see run_conf.c */
630 /* Schedule
631  *
632  *   name          handler     value                 code flags    default_value
633  */
634 static RES_ITEM sch_items[] = {
635    {"Name",        store_name,  ITEM(res_sch.hdr.name), 0, ITEM_REQUIRED, 0},
636    {"Description", store_str,   ITEM(res_sch.hdr.desc), 0, 0, 0},
637    {"Run",         store_run,   ITEM(res_sch.run),      0, 0, 0},
638    {"Enabled",     store_bool,  ITEM(res_sch.Enabled),  0, ITEM_DEFAULT, true},
639    {NULL, NULL, {0}, 0, 0, 0}
640 };
641
642 /* Pool resource
643  *
644  *   name             handler     value                        code flags default_value
645  */
646 static RES_ITEM pool_items[] = {
647    {"Name",            store_name,    ITEM(res_pool.hdr.name),      0, ITEM_REQUIRED, 0},
648    {"Description",     store_str,     ITEM(res_pool.hdr.desc),      0, 0,     0},
649    {"PoolType",        store_strname, ITEM(res_pool.pool_type),     0, ITEM_REQUIRED, 0},
650    {"LabelFormat",     store_strname, ITEM(res_pool.label_format),  0, 0,     0},
651    {"LabelType",       store_label,   ITEM(res_pool.LabelType),     0, 0,     0},
652    {"CleaningPrefix",  store_strname, ITEM(res_pool.cleaning_prefix), 0, 0,   0},
653    {"UseCatalog",      store_bool,    ITEM(res_pool.use_catalog),    0, ITEM_DEFAULT, true},
654    {"UseVolumeOnce",   store_bool,    ITEM(res_pool.use_volume_once), 0, 0,   0},
655    {"PurgeOldestVolume", store_bool,  ITEM(res_pool.purge_oldest_volume), 0, 0, 0},
656    {"ActionOnPurge",   store_actiononpurge, ITEM(res_pool.action_on_purge), 0, 0, 0},
657    {"RecycleOldestVolume", store_bool,  ITEM(res_pool.recycle_oldest_volume), 0, 0, 0},
658    {"RecycleCurrentVolume", store_bool, ITEM(res_pool.recycle_current_volume), 0, 0, 0},
659    {"MaximumVolumes",  store_pint32,    ITEM(res_pool.max_volumes),   0, 0,        0},
660    {"MaximumVolumeJobs", store_pint32,  ITEM(res_pool.MaxVolJobs),    0, 0,       0},
661    {"MaximumVolumeFiles", store_pint32, ITEM(res_pool.MaxVolFiles),   0, 0,       0},
662    {"MaximumVolumeBytes", store_size64, ITEM(res_pool.MaxVolBytes),   0, 0,       0},
663    {"CatalogFiles",    store_bool,    ITEM(res_pool.catalog_files),  0, ITEM_DEFAULT, true},
664    {"CacheRetention", store_time,    ITEM(res_pool.CacheRetention),   0, 0, 0},
665    {"VolumeRetention", store_time,    ITEM(res_pool.VolRetention),   0, ITEM_DEFAULT, 60*60*24*365},
666    {"VolumeUseDuration", store_time,  ITEM(res_pool.VolUseDuration), 0, 0, 0},
667    {"MigrationTime",  store_time,     ITEM(res_pool.MigrationTime), 0, 0, 0},
668    {"MigrationHighBytes", store_size64, ITEM(res_pool.MigrationHighBytes), 0, 0, 0},
669    {"MigrationLowBytes", store_size64,  ITEM(res_pool.MigrationLowBytes), 0, 0, 0},
670    {"NextPool",      store_res,       ITEM(res_pool.NextPool), R_POOL, 0, 0},
671    {"Storage",       store_alist_res, ITEM(res_pool.storage),  R_STORAGE, 0, 0},
672    {"AutoPrune",     store_bool,      ITEM(res_pool.AutoPrune), 0, ITEM_DEFAULT, true},
673    {"Recycle",       store_bool,      ITEM(res_pool.Recycle),   0, ITEM_DEFAULT, true},
674    {"RecyclePool",   store_res,       ITEM(res_pool.RecyclePool), R_POOL, 0, 0},
675    {"ScratchPool",   store_res,       ITEM(res_pool.ScratchPool), R_POOL, 0, 0},
676    {"CopyPool",      store_alist_res, ITEM(res_pool.CopyPool), R_POOL, 0, 0},
677    {"Catalog",       store_res,       ITEM(res_pool.catalog), R_CATALOG, 0, 0},
678    {"FileRetention", store_time,      ITEM(res_pool.FileRetention), 0, 0, 0},
679    {"JobRetention",  store_time,      ITEM(res_pool.JobRetention),  0, 0, 0},
680
681    {NULL, NULL, {0}, 0, 0, 0}
682 };
683
684 /*
685  * Counter Resource
686  *   name             handler     value                        code flags default_value
687  */
688 static RES_ITEM counter_items[] = {
689    {"Name",            store_name,    ITEM(res_counter.hdr.name),        0, ITEM_REQUIRED, 0},
690    {"Description",     store_str,     ITEM(res_counter.hdr.desc),        0, 0,     0},
691    {"Minimum",         store_int32,   ITEM(res_counter.MinValue),        0, ITEM_DEFAULT, 0},
692    {"Maximum",         store_pint32,  ITEM(res_counter.MaxValue),        0, ITEM_DEFAULT, INT32_MAX},
693    {"WrapCounter",     store_res,     ITEM(res_counter.WrapCounter),     R_COUNTER, 0, 0},
694    {"Catalog",         store_res,     ITEM(res_counter.Catalog),         R_CATALOG, 0, 0},
695    {NULL, NULL, {0}, 0, 0, 0}
696 };
697
698
699 /* Message resource */
700 extern RES_ITEM msgs_items[];
701
702 /*
703  * This is the master resource definition.
704  * It must have one item for each of the resources.
705  *
706  *  NOTE!!! keep it in the same order as the R_codes
707  *    or eliminate all resources[rindex].name
708  *
709  *  name             items        rcode
710  */
711 RES_TABLE resources[] = {
712    {"Director",      dir_items,   R_DIRECTOR},
713    {"Client",        cli_items,   R_CLIENT},
714    {"Job",           job_items,   R_JOB},
715    {"Storage",       store_items, R_STORAGE},
716    {"Catalog",       cat_items,   R_CATALOG},
717    {"Schedule",      sch_items,   R_SCHEDULE},
718    {"Fileset",       fs_items,    R_FILESET},
719    {"Pool",          pool_items,  R_POOL},
720    {"Messages",      msgs_items,  R_MSGS},
721    {"Counter",       counter_items, R_COUNTER},
722    {"Console",       con_items,   R_CONSOLE},
723    {"JobDefs",       job_items,   R_JOBDEFS},
724    {"Device",        NULL,        R_DEVICE},  /* info obtained from SD */
725    {"Autochanger",   store_items, R_AUTOCHANGER},  /* alias for R_STORAGE */
726    {NULL,            NULL,        0}
727 };
728
729
730 /* Keywords (RHS) permitted in Job Level records
731  *
732  *   level_name      level              job_type
733  */
734 struct s_jl joblevels[] = {
735    {"Full",          L_FULL,            JT_BACKUP},
736    {"Base",          L_BASE,            JT_BACKUP},
737    {"Incremental",   L_INCREMENTAL,     JT_BACKUP},
738    {"Differential",  L_DIFFERENTIAL,    JT_BACKUP},
739    {"Since",         L_SINCE,           JT_BACKUP},
740    {"VirtualFull",   L_VIRTUAL_FULL,    JT_BACKUP},
741    {"Catalog",       L_VERIFY_CATALOG,  JT_VERIFY},
742    {"InitCatalog",   L_VERIFY_INIT,     JT_VERIFY},
743    {"VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG,   JT_VERIFY},
744    {"DiskToCatalog", L_VERIFY_DISK_TO_CATALOG,   JT_VERIFY},
745    {"Data",          L_VERIFY_DATA,     JT_VERIFY},
746    {"Full",          L_FULL,            JT_COPY},
747    {"Incremental",   L_INCREMENTAL,     JT_COPY},
748    {"Differential",  L_DIFFERENTIAL,    JT_COPY},
749    {"Full",          L_FULL,            JT_MIGRATE},
750    {"Incremental",   L_INCREMENTAL,     JT_MIGRATE},
751    {"Differential",  L_DIFFERENTIAL,    JT_MIGRATE},
752    {" ",             L_NONE,            JT_ADMIN},
753    {" ",             L_NONE,            JT_RESTORE},
754    {NULL,            0,                          0}
755 };
756
757
758 /* Keywords (RHS) permitted in Job type records
759  *
760  *   type_name       job_type
761  */
762 s_jt jobtypes[] = {
763    {"Backup",        JT_BACKUP},
764    {"Admin",         JT_ADMIN},
765    {"Verify",        JT_VERIFY},
766    {"Restore",       JT_RESTORE},
767    {"Migrate",       JT_MIGRATE},
768    {"Copy",          JT_COPY},
769    {NULL,            0}
770 };
771
772
773 /* Keywords (RHS) permitted in Selection type records
774  *
775  *   type_name       job_type
776  */
777 s_jt migtypes[] = {
778    {"SmallestVolume",   MT_SMALLEST_VOL},
779    {"OldestVolume",     MT_OLDEST_VOL},
780    {"PoolOccupancy",    MT_POOL_OCCUPANCY},
781    {"PoolTime",         MT_POOL_TIME},
782    {"PoolUncopiedJobs", MT_POOL_UNCOPIED_JOBS},
783    {"Client",           MT_CLIENT},
784    {"Volume",           MT_VOLUME},
785    {"Job",              MT_JOB},
786    {"SqlQuery",         MT_SQLQUERY},
787    {NULL,            0}
788 };
789
790
791
792 /* Options permitted in Restore replace= */
793 s_kw ReplaceOptions[] = {
794    {"Always",         REPLACE_ALWAYS},
795    {"IfNewer",        REPLACE_IFNEWER},
796    {"IfOlder",        REPLACE_IFOLDER},
797    {"Never",          REPLACE_NEVER},
798    {NULL,               0}
799 };
800
801 char *CAT::display(POOLMEM *dst) {
802    Mmsg(dst,"catalog=%s\ndb_name=%s\ndb_driver=%s\ndb_user=%s\n"
803         "db_password=%s\ndb_address=%s\ndb_port=%i\n"
804         "db_socket=%s\n",
805         name(), NPRTB(db_name),
806         NPRTB(db_driver), NPRTB(db_user), NPRTB(db_password),
807         NPRTB(db_address), db_port, NPRTB(db_socket));
808    return dst;
809 }
810
811 const char *level_to_str(int level)
812 {
813    int i;
814    static char level_no[30];
815    const char *str = level_no;
816
817    bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level);    /* default if not found */
818    for (i=0; joblevels[i].level_name; i++) {
819       if (level == (int)joblevels[i].level) {
820          str = joblevels[i].level_name;
821          break;
822       }
823    }
824    return str;
825 }
826
827 /* Dump contents of resource */
828 void dump_resource(int type, RES *ares, void sendit(void *sock, const char *fmt, ...), void *sock)
829 {
830    RES *next;
831    URES *res = (URES *)ares;
832    bool recurse = true;
833    char ed1[100], ed2[100], ed3[100];
834    DEVICE *dev;
835    UAContext *ua = (UAContext *)sock;
836
837    if (res == NULL) {
838       sendit(sock, _("No %s resource defined\n"), res_to_str(type));
839       return;
840    }
841    if (type < 0) {                    /* no recursion */
842       type = -type;
843       recurse = false;
844    }
845    switch (type) {
846    case R_DIRECTOR:
847       sendit(sock, _("Director: name=%s MaxJobs=%d FDtimeout=%s SDtimeout=%s\n"),
848          ares->name, res->res_dir.MaxConcurrentJobs,
849          edit_uint64(res->res_dir.FDConnectTimeout, ed1),
850          edit_uint64(res->res_dir.SDConnectTimeout, ed2));
851       if (res->res_dir.query_file) {
852          sendit(sock, _("   query_file=%s\n"), res->res_dir.query_file);
853       }
854       if (res->res_dir.messages) {
855          sendit(sock, _("  --> "));
856          dump_resource(-R_MSGS, (RES *)res->res_dir.messages, sendit, sock);
857       }
858       break;
859    case R_CONSOLE:
860       sendit(sock, _("Console: name=%s SSL=%d\n"),
861          res->res_con.hdr.name, res->res_con.tls_enable);
862       break;
863    case R_COUNTER:
864       if (res->res_counter.WrapCounter) {
865          sendit(sock, _("Counter: name=%s min=%d max=%d cur=%d wrapcntr=%s\n"),
866             res->res_counter.hdr.name, res->res_counter.MinValue,
867             res->res_counter.MaxValue, res->res_counter.CurrentValue,
868             res->res_counter.WrapCounter->hdr.name);
869       } else {
870          sendit(sock, _("Counter: name=%s min=%d max=%d\n"),
871             res->res_counter.hdr.name, res->res_counter.MinValue,
872             res->res_counter.MaxValue);
873       }
874       if (res->res_counter.Catalog) {
875          sendit(sock, _("  --> "));
876          dump_resource(-R_CATALOG, (RES *)res->res_counter.Catalog, sendit, sock);
877       }
878       break;
879
880    case R_CLIENT:
881       if (!acl_access_ok(ua, Client_ACL, res->res_client.name())) {
882          break;
883       }
884       sendit(sock, _("Client: Name=%s Enabled=%d Address=%s FDport=%d MaxJobs=%u NumJobs=%u\n"),
885          res->res_client.name(), res->res_client.is_enabled(),
886          res->res_client.address(), res->res_client.FDport,
887          res->res_client.MaxConcurrentJobs, res->res_client.getNumConcurrentJobs());
888       sendit(sock, _("      JobRetention=%s FileRetention=%s AutoPrune=%d\n"),
889          edit_utime(res->res_client.JobRetention, ed1, sizeof(ed1)),
890          edit_utime(res->res_client.FileRetention, ed2, sizeof(ed2)),
891          res->res_client.AutoPrune);
892       if (res->res_client.fd_storage_address) {
893          sendit(sock, "      FDStorageAddress=%s\n", res->res_client.fd_storage_address);
894       }
895       if (res->res_client.max_bandwidth) {
896          sendit(sock, _("     MaximumBandwidth=%lld\n"),
897                 res->res_client.max_bandwidth);
898       }
899       if (res->res_client.catalog) {
900          sendit(sock, _("  --> "));
901          dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
902       }
903       break;
904
905    case R_DEVICE:
906       dev = &res->res_dev;
907       char ed1[50];
908       sendit(sock, _("Device: name=%s ok=%d num_writers=%d max_writers=%d\n"
909 "      reserved=%d open=%d append=%d read=%d labeled=%d offline=%d autochgr=%d\n"
910 "      poolid=%s volname=%s MediaType=%s\n"),
911          dev->hdr.name, dev->found, dev->num_writers, dev->max_writers,
912          dev->reserved, dev->open, dev->append, dev->read, dev->labeled,
913          dev->offline, dev->autochanger,
914          edit_uint64(dev->PoolId, ed1),
915          dev->VolumeName, dev->MediaType);
916       break;
917
918    case R_AUTOCHANGER:
919    case R_STORAGE:
920       if (!acl_access_ok(ua, Storage_ACL, res->res_store.hdr.name)) {
921          break;
922       }
923       sendit(sock, _("%s: name=%s address=%s SDport=%d MaxJobs=%u NumJobs=%u\n"
924 "      DeviceName=%s MediaType=%s StorageId=%s Autochanger=%d\n"),
925          res->res_store.changer == &res->res_store ? "Autochanger" : "Storage",
926          res->res_store.hdr.name, res->res_store.address, res->res_store.SDport,
927          res->res_store.MaxConcurrentJobs,
928          res->res_store.getNumConcurrentJobs(),
929          res->res_store.dev_name(),
930          res->res_store.media_type,
931          edit_int64(res->res_store.StorageId, ed1),
932          res->res_store.autochanger);
933       if (res->res_store.fd_storage_address) {
934          sendit(sock, "      FDStorageAddress=%s\n", res->res_store.fd_storage_address);
935       }
936       if (res->res_store.ac_group) {
937          STORE *shstore = res->res_store.shared_storage;
938          sendit(sock, "      AC group=%s ShareStore=%s\n", res->res_store.ac_group,
939             shstore?shstore->name():"*none*");
940       }
941       if (res->res_store.changer && res->res_store.changer != &res->res_store) {
942          sendit(sock, _("   Parent --> "));
943          dump_resource(-R_STORAGE, (RES *)res->res_store.changer, sendit, sock);
944       }
945       break;
946
947    case R_CATALOG:
948       if (!acl_access_ok(ua, Catalog_ACL, res->res_cat.hdr.name)) {
949          break;
950       }
951       sendit(sock, _("Catalog: name=%s address=%s DBport=%d db_name=%s\n"
952 "      db_driver=%s db_user=%s MutliDBConn=%d\n"),
953          res->res_cat.hdr.name, NPRT(res->res_cat.db_address),
954          res->res_cat.db_port, res->res_cat.db_name,
955          NPRT(res->res_cat.db_driver), NPRT(res->res_cat.db_user),
956          res->res_cat.mult_db_connections);
957       break;
958
959    case R_JOB:
960    case R_JOBDEFS:
961       if (!acl_access_ok(ua, Job_ACL, res->res_job.hdr.name)) {
962          break;
963       }
964       sendit(sock, _("%s: name=%s JobType=%d level=%s Priority=%d Enabled=%d\n"),
965          type == R_JOB ? _("Job") : _("JobDefs"),
966          res->res_job.hdr.name, res->res_job.JobType,
967          level_to_str(res->res_job.JobLevel), res->res_job.Priority,
968          res->res_job.is_enabled());
969       sendit(sock, _("     MaxJobs=%u NumJobs=%u Resched=%d Times=%d Interval=%s Spool=%d WritePartAfterJob=%d\n"),
970          res->res_job.MaxConcurrentJobs,
971          res->res_job.getNumConcurrentJobs(),
972          res->res_job.RescheduleOnError, res->res_job.RescheduleTimes,
973          edit_uint64_with_commas(res->res_job.RescheduleInterval, ed1),
974          res->res_job.spool_data, res->res_job.write_part_after_job);
975       if (res->res_job.spool_size) {
976          sendit(sock, _("     SpoolSize=%s\n"),        edit_uint64(res->res_job.spool_size, ed1));
977       }
978       if (res->res_job.JobType == JT_BACKUP) {
979          sendit(sock, _("     Accurate=%d\n"), res->res_job.accurate);
980       }
981       if (res->res_job.max_bandwidth) {
982          sendit(sock, _("     MaximumBandwidth=%lld\n"),
983                 res->res_job.max_bandwidth);
984       }
985       if (res->res_job.JobType == JT_MIGRATE || res->res_job.JobType == JT_COPY) {
986          sendit(sock, _("     SelectionType=%d\n"), res->res_job.selection_type);
987       }
988       if (res->res_job.JobType == JT_RESTORE) {
989          sendit(sock, _("     PrefixLinks=%d\n"), res->res_job.PrefixLinks);
990       }
991       if (res->res_job.client) {
992          sendit(sock, _("  --> "));
993          dump_resource(-R_CLIENT, (RES *)res->res_job.client, sendit, sock);
994       }
995       if (res->res_job.fileset) {
996          sendit(sock, _("  --> "));
997          dump_resource(-R_FILESET, (RES *)res->res_job.fileset, sendit, sock);
998       }
999       if (res->res_job.schedule) {
1000          sendit(sock, _("  --> "));
1001          dump_resource(-R_SCHEDULE, (RES *)res->res_job.schedule, sendit, sock);
1002       }
1003       if (res->res_job.RestoreWhere && !res->res_job.RegexWhere) {
1004            sendit(sock, _("  --> Where=%s\n"), NPRT(res->res_job.RestoreWhere));
1005       }
1006       if (res->res_job.RegexWhere) {
1007            sendit(sock, _("  --> RegexWhere=%s\n"), NPRT(res->res_job.RegexWhere));
1008       }
1009       if (res->res_job.RestoreBootstrap) {
1010          sendit(sock, _("  --> Bootstrap=%s\n"), NPRT(res->res_job.RestoreBootstrap));
1011       }
1012       if (res->res_job.WriteBootstrap) {
1013          sendit(sock, _("  --> WriteBootstrap=%s\n"), NPRT(res->res_job.WriteBootstrap));
1014       }
1015       if (res->res_job.PluginOptions) {
1016          sendit(sock, _("  --> PluginOptions=%s\n"), NPRT(res->res_job.PluginOptions));
1017       }
1018       if (res->res_job.MaxRunTime) {
1019          sendit(sock, _("  --> MaxRunTime=%u\n"), res->res_job.MaxRunTime);
1020       }
1021       if (res->res_job.MaxWaitTime) {
1022          sendit(sock, _("  --> MaxWaitTime=%u\n"), res->res_job.MaxWaitTime);
1023       }
1024       if (res->res_job.MaxStartDelay) {
1025          sendit(sock, _("  --> MaxStartDelay=%u\n"), res->res_job.MaxStartDelay);
1026       }
1027       if (res->res_job.MaxRunSchedTime) {
1028          sendit(sock, _("  --> MaxRunSchedTime=%u\n"), res->res_job.MaxRunSchedTime);
1029       }
1030       if (res->res_job.storage) {
1031          STORE *store;
1032          foreach_alist(store, res->res_job.storage) {
1033             sendit(sock, _("  --> "));
1034             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
1035          }
1036       }
1037       if (res->res_job.base) {
1038          JOB *job;
1039          foreach_alist(job, res->res_job.base) {
1040             sendit(sock, _("  --> Base %s\n"), job->name());
1041          }
1042       }
1043       if (res->res_job.RunScripts) {
1044         RUNSCRIPT *script;
1045         foreach_alist(script, res->res_job.RunScripts) {
1046            sendit(sock, _(" --> RunScript\n"));
1047            sendit(sock, _("  --> Command=%s\n"), NPRT(script->command));
1048            sendit(sock, _("  --> Target=%s\n"),  NPRT(script->target));
1049            sendit(sock, _("  --> RunOnSuccess=%u\n"),  script->on_success);
1050            sendit(sock, _("  --> RunOnFailure=%u\n"),  script->on_failure);
1051            sendit(sock, _("  --> FailJobOnError=%u\n"),  script->fail_on_error);
1052            sendit(sock, _("  --> RunWhen=%u\n"),  script->when);
1053         }
1054       }
1055       if (res->res_job.pool) {
1056          sendit(sock, _("  --> "));
1057          dump_resource(-R_POOL, (RES *)res->res_job.pool, sendit, sock);
1058       }
1059       if (res->res_job.vfull_pool) {
1060          sendit(sock, _("  --> VFullBackup"));
1061          dump_resource(-R_POOL, (RES *)res->res_job.vfull_pool, sendit, sock);
1062       }
1063       if (res->res_job.full_pool) {
1064          sendit(sock, _("  --> FullBackup"));
1065          dump_resource(-R_POOL, (RES *)res->res_job.full_pool, sendit, sock);
1066       }
1067       if (res->res_job.inc_pool) {
1068          sendit(sock, _("  --> IncrementalBackup"));
1069          dump_resource(-R_POOL, (RES *)res->res_job.inc_pool, sendit, sock);
1070       }
1071       if (res->res_job.diff_pool) {
1072          sendit(sock, _("  --> DifferentialBackup"));
1073          dump_resource(-R_POOL, (RES *)res->res_job.diff_pool, sendit, sock);
1074       }
1075       if (res->res_job.next_pool) {
1076          sendit(sock, _("  --> Next")); /* Pool will be added by dump_resource */
1077          dump_resource(-R_POOL, (RES *)res->res_job.next_pool, sendit, sock);
1078       }
1079       if (res->res_job.JobType == JT_VERIFY && res->res_job.verify_job) {
1080          sendit(sock, _("  --> JobToVerify %s"), (RES *)res->res_job.verify_job->name());
1081       }
1082       if (res->res_job.run_cmds) {
1083          char *runcmd;
1084          foreach_alist(runcmd, res->res_job.run_cmds) {
1085             sendit(sock, _("  --> Run=%s\n"), runcmd);
1086          }
1087       }
1088       if (res->res_job.selection_pattern) {
1089          sendit(sock, _("  --> SelectionPattern=%s\n"), NPRT(res->res_job.selection_pattern));
1090       }
1091       if (res->res_job.messages) {
1092          sendit(sock, _("  --> "));
1093          dump_resource(-R_MSGS, (RES *)res->res_job.messages, sendit, sock);
1094       }
1095       break;
1096
1097    case R_FILESET:
1098    {
1099       int i, j, k;
1100       if (!acl_access_ok(ua, FileSet_ACL, res->res_fs.hdr.name)) {
1101          break;
1102       }
1103       sendit(sock, _("FileSet: name=%s IgnoreFileSetChanges=%d\n"), res->res_fs.hdr.name, res->res_fs.ignore_fs_changes);
1104       for (i=0; i<res->res_fs.num_includes; i++) {
1105          INCEXE *incexe = res->res_fs.include_items[i];
1106          for (j=0; j<incexe->num_opts; j++) {
1107             FOPTS *fo = incexe->opts_list[j];
1108             sendit(sock, "      O %s\n", fo->opts);
1109
1110             bool enhanced_wild = false;
1111             for (k=0; fo->opts[k]!='\0'; k++) {
1112                if (fo->opts[k]=='W') {
1113                   enhanced_wild = true;
1114                   break;
1115                }
1116             }
1117
1118             for (k=0; k<fo->regex.size(); k++) {
1119                sendit(sock, "      R %s\n", fo->regex.get(k));
1120             }
1121             for (k=0; k<fo->regexdir.size(); k++) {
1122                sendit(sock, "      RD %s\n", fo->regexdir.get(k));
1123             }
1124             for (k=0; k<fo->regexfile.size(); k++) {
1125                sendit(sock, "      RF %s\n", fo->regexfile.get(k));
1126             }
1127             for (k=0; k<fo->wild.size(); k++) {
1128                sendit(sock, "      W %s\n", fo->wild.get(k));
1129             }
1130             for (k=0; k<fo->wilddir.size(); k++) {
1131                sendit(sock, "      WD %s\n", fo->wilddir.get(k));
1132             }
1133             for (k=0; k<fo->wildfile.size(); k++) {
1134                sendit(sock, "      WF %s\n", fo->wildfile.get(k));
1135             }
1136             for (k=0; k<fo->wildbase.size(); k++) {
1137                sendit(sock, "      W%c %s\n", enhanced_wild ? 'B' : 'F', fo->wildbase.get(k));
1138             }
1139             for (k=0; k<fo->base.size(); k++) {
1140                sendit(sock, "      B %s\n", fo->base.get(k));
1141             }
1142             for (k=0; k<fo->fstype.size(); k++) {
1143                sendit(sock, "      X %s\n", fo->fstype.get(k));
1144             }
1145             for (k=0; k<fo->drivetype.size(); k++) {
1146                sendit(sock, "      XD %s\n", fo->drivetype.get(k));
1147             }
1148             if (fo->plugin) {
1149                sendit(sock, "      G %s\n", fo->plugin);
1150             }
1151             if (fo->reader) {
1152                sendit(sock, "      D %s\n", fo->reader);
1153             }
1154             if (fo->writer) {
1155                sendit(sock, "      T %s\n", fo->writer);
1156             }
1157             sendit(sock, "      N\n");
1158          }
1159          if (incexe->ignoredir) {
1160             sendit(sock, "      Z %s\n", incexe->ignoredir);
1161          }
1162          for (j=0; j<incexe->name_list.size(); j++) {
1163             sendit(sock, "      I %s\n", incexe->name_list.get(j));
1164          }
1165          if (incexe->name_list.size()) {
1166             sendit(sock, "      N\n");
1167          }
1168          for (j=0; j<incexe->plugin_list.size(); j++) {
1169             sendit(sock, "      P %s\n", incexe->plugin_list.get(j));
1170          }
1171          if (incexe->plugin_list.size()) {
1172             sendit(sock, "      N\n");
1173          }
1174       } /* end for over includes */
1175
1176       for (i=0; i<res->res_fs.num_excludes; i++) {
1177          INCEXE *incexe = res->res_fs.exclude_items[i];
1178          for (j=0; j<incexe->name_list.size(); j++) {
1179             sendit(sock, "      E %s\n", incexe->name_list.get(j));
1180          }
1181          if (incexe->name_list.size()) {
1182             sendit(sock, "      N\n");
1183          }
1184       }
1185       break;
1186    } /* end case R_FILESET */
1187
1188    case R_SCHEDULE:
1189       if (!acl_access_ok(ua, Schedule_ACL, res->res_sch.hdr.name)) {
1190          break;
1191       }
1192
1193       if (res->res_sch.run) {
1194          int i;
1195          RUN *run = res->res_sch.run;
1196          char buf[1000], num[30];
1197          sendit(sock, _("Schedule: Name=%s Enabled=%d\n"), 
1198             res->res_sch.hdr.name, res->res_sch.is_enabled());
1199          if (!run) {
1200             break;
1201          }
1202 next_run:
1203          sendit(sock, _("  --> Run Level=%s\n"), level_to_str(run->level));
1204          if (run->MaxRunSchedTime) {
1205             sendit(sock, _("      MaxRunSchedTime=%u\n"), run->MaxRunSchedTime);
1206          }
1207          if (run->Priority) {
1208             sendit(sock, _("      Priority=%u\n"), run->Priority);
1209          }
1210          bstrncpy(buf, _("      hour="), sizeof(buf));
1211          for (i=0; i<24; i++) {
1212             if (bit_is_set(i, run->hour)) {
1213                bsnprintf(num, sizeof(num), "%d ", i);
1214                bstrncat(buf, num, sizeof(buf));
1215             }
1216          }
1217          bstrncat(buf, "\n", sizeof(buf));
1218          sendit(sock, buf);
1219          bstrncpy(buf, _("      mday="), sizeof(buf));
1220          for (i=0; i<32; i++) {
1221             if (bit_is_set(i, run->mday)) {
1222                bsnprintf(num, sizeof(num), "%d ", i);
1223                bstrncat(buf, num, sizeof(buf));
1224             }
1225          }
1226          bstrncat(buf, "\n", sizeof(buf));
1227          sendit(sock, buf);
1228          bstrncpy(buf, _("      month="), sizeof(buf));
1229          for (i=0; i<12; i++) {
1230             if (bit_is_set(i, run->month)) {
1231                bsnprintf(num, sizeof(num), "%d ", i);
1232                bstrncat(buf, num, sizeof(buf));
1233             }
1234          }
1235          bstrncat(buf, "\n", sizeof(buf));
1236          sendit(sock, buf);
1237          bstrncpy(buf, _("      wday="), sizeof(buf));
1238          for (i=0; i<7; i++) {
1239             if (bit_is_set(i, run->wday)) {
1240                bsnprintf(num, sizeof(num), "%d ", i);
1241                bstrncat(buf, num, sizeof(buf));
1242             }
1243          }
1244          bstrncat(buf, "\n", sizeof(buf));
1245          sendit(sock, buf);
1246          bstrncpy(buf, _("      wom="), sizeof(buf));
1247          for (i=0; i<6; i++) {
1248             if (bit_is_set(i, run->wom)) {
1249                bsnprintf(num, sizeof(num), "%d ", i);
1250                bstrncat(buf, num, sizeof(buf));
1251             }
1252          }
1253          bstrncat(buf, "\n", sizeof(buf));
1254          sendit(sock, buf);
1255          bstrncpy(buf, _("      woy="), sizeof(buf));
1256          for (i=0; i<54; i++) {
1257             if (bit_is_set(i, run->woy)) {
1258                bsnprintf(num, sizeof(num), "%d ", i);
1259                bstrncat(buf, num, sizeof(buf));
1260             }
1261          }
1262          bstrncat(buf, "\n", sizeof(buf));
1263          sendit(sock, buf);
1264          sendit(sock, _("      mins=%d\n"), run->minute);
1265          if (run->pool) {
1266             sendit(sock, _("     --> "));
1267             dump_resource(-R_POOL, (RES *)run->pool, sendit, sock);
1268          }
1269          if (run->next_pool) {
1270             sendit(sock, _("     --> Next")); /* Pool will be added by dump_resource */
1271             dump_resource(-R_POOL, (RES *)run->next_pool, sendit, sock);
1272          }
1273          if (run->storage) {
1274             sendit(sock, _("     --> "));
1275             dump_resource(-R_STORAGE, (RES *)run->storage, sendit, sock);
1276          }
1277          if (run->msgs) {
1278             sendit(sock, _("     --> "));
1279             dump_resource(-R_MSGS, (RES *)run->msgs, sendit, sock);
1280          }
1281          /* If another Run record is chained in, go print it */
1282          if (run->next) {
1283             run = run->next;
1284             goto next_run;
1285          }
1286       } else {
1287          sendit(sock, _("Schedule: name=%s\n"), res->res_sch.hdr.name);
1288       }
1289       break;
1290
1291    case R_POOL:
1292       if (!acl_access_ok(ua, Pool_ACL, res->res_pool.hdr.name)) {
1293          break;
1294       }
1295       sendit(sock, _("Pool: name=%s PoolType=%s\n"), res->res_pool.hdr.name,
1296               res->res_pool.pool_type);
1297       sendit(sock, _("      use_cat=%d use_once=%d cat_files=%d\n"),
1298               res->res_pool.use_catalog, res->res_pool.use_volume_once,
1299               res->res_pool.catalog_files);
1300       sendit(sock, _("      max_vols=%d auto_prune=%d VolRetention=%s\n"),
1301               res->res_pool.max_volumes, res->res_pool.AutoPrune,
1302               edit_utime(res->res_pool.VolRetention, ed1, sizeof(ed1)));
1303       sendit(sock, _("      VolUse=%s recycle=%d LabelFormat=%s\n"),
1304               edit_utime(res->res_pool.VolUseDuration, ed1, sizeof(ed1)),
1305               res->res_pool.Recycle,
1306               NPRT(res->res_pool.label_format));
1307       sendit(sock, _("      CleaningPrefix=%s LabelType=%d\n"),
1308               NPRT(res->res_pool.cleaning_prefix), res->res_pool.LabelType);
1309       sendit(sock, _("      RecyleOldest=%d PurgeOldest=%d ActionOnPurge=%d\n"),
1310               res->res_pool.recycle_oldest_volume,
1311               res->res_pool.purge_oldest_volume,
1312               res->res_pool.action_on_purge);
1313       sendit(sock, _("      MaxVolJobs=%d MaxVolFiles=%d MaxVolBytes=%s\n"),
1314               res->res_pool.MaxVolJobs,
1315               res->res_pool.MaxVolFiles,
1316               edit_uint64(res->res_pool.MaxVolBytes, ed1));
1317       sendit(sock, _("      MigTime=%s MigHiBytes=%s MigLoBytes=%s\n"),
1318               edit_utime(res->res_pool.MigrationTime, ed1, sizeof(ed1)),
1319               edit_uint64(res->res_pool.MigrationHighBytes, ed2),
1320               edit_uint64(res->res_pool.MigrationLowBytes, ed3));
1321       sendit(sock, _("      CacheRetention=%s\n"),
1322              edit_utime(res->res_pool.CacheRetention, ed1, sizeof(ed1)));
1323       sendit(sock, _("      JobRetention=%s FileRetention=%s\n"),
1324          edit_utime(res->res_pool.JobRetention, ed1, sizeof(ed1)),
1325          edit_utime(res->res_pool.FileRetention, ed2, sizeof(ed2)));
1326       if (res->res_pool.NextPool) {
1327          sendit(sock, _("      NextPool=%s\n"), res->res_pool.NextPool->name());
1328       }
1329       if (res->res_pool.RecyclePool) {
1330          sendit(sock, _("      RecyclePool=%s\n"), res->res_pool.RecyclePool->name());
1331       }
1332       if (res->res_pool.ScratchPool) {
1333          sendit(sock, _("      ScratchPool=%s\n"), res->res_pool.ScratchPool->name());
1334       }
1335       if (res->res_pool.catalog) {
1336          sendit(sock, _("      Catalog=%s\n"), res->res_pool.catalog->name());
1337       }
1338       if (res->res_pool.storage) {
1339          STORE *store;
1340          foreach_alist(store, res->res_pool.storage) {
1341             sendit(sock, _("  --> "));
1342             dump_resource(-R_STORAGE, (RES *)store, sendit, sock);
1343          }
1344       }
1345       if (res->res_pool.CopyPool) {
1346          POOL *copy;
1347          foreach_alist(copy, res->res_pool.CopyPool) {
1348             sendit(sock, _("  --> "));
1349             dump_resource(-R_POOL, (RES *)copy, sendit, sock);
1350          }
1351       }
1352
1353       break;
1354
1355    case R_MSGS:
1356       sendit(sock, _("Messages: name=%s\n"), res->res_msgs.hdr.name);
1357       if (res->res_msgs.mail_cmd)
1358          sendit(sock, _("      mailcmd=%s\n"), res->res_msgs.mail_cmd);
1359       if (res->res_msgs.operator_cmd)
1360          sendit(sock, _("      opcmd=%s\n"), res->res_msgs.operator_cmd);
1361       break;
1362
1363    default:
1364       sendit(sock, _("Unknown resource type %d in dump_resource.\n"), type);
1365       break;
1366    }
1367    if (recurse) {
1368       next = GetNextRes(0, (RES *)res);
1369       if (next) {
1370          dump_resource(type, next, sendit, sock);
1371       }
1372    }
1373 }
1374
1375 /*
1376  * Free all the members of an INCEXE structure
1377  */
1378 static void free_incexe(INCEXE *incexe)
1379 {
1380    incexe->name_list.destroy();
1381    incexe->plugin_list.destroy();
1382    for (int i=0; i<incexe->num_opts; i++) {
1383       FOPTS *fopt = incexe->opts_list[i];
1384       fopt->regex.destroy();
1385       fopt->regexdir.destroy();
1386       fopt->regexfile.destroy();
1387       fopt->wild.destroy();
1388       fopt->wilddir.destroy();
1389       fopt->wildfile.destroy();
1390       fopt->wildbase.destroy();
1391       fopt->base.destroy();
1392       fopt->fstype.destroy();
1393       fopt->drivetype.destroy();
1394       if (fopt->plugin) {
1395          free(fopt->plugin);
1396       }
1397       if (fopt->reader) {
1398          free(fopt->reader);
1399       }
1400       if (fopt->writer) {
1401          free(fopt->writer);
1402       }
1403       free(fopt);
1404    }
1405    if (incexe->opts_list) {
1406       free(incexe->opts_list);
1407    }
1408    if (incexe->ignoredir) {
1409       free(incexe->ignoredir);
1410    }
1411    free(incexe);
1412 }
1413
1414
1415 /*
1416  * Free memory of resource -- called when daemon terminates.
1417  * NB, we don't need to worry about freeing any references
1418  * to other resources as they will be freed when that
1419  * resource chain is traversed.  Mainly we worry about freeing
1420  * allocated strings (names).
1421  */
1422 void free_resource(RES *rres, int type)
1423 {
1424    int num;
1425    URES *res = (URES *)rres;
1426
1427    if (res == NULL) {
1428       return;
1429    }
1430
1431    Dmsg3(200, "type=%d res=%p name=%s\n", type, res, res->res_dir.hdr.name);
1432    /* common stuff -- free the resource name and description */
1433    if (res->res_dir.hdr.name) {
1434       free(res->res_dir.hdr.name);
1435    }
1436    if (res->res_dir.hdr.desc) {
1437       free(res->res_dir.hdr.desc);
1438    }
1439
1440    switch (type) {
1441    case R_DIRECTOR:
1442       if (res->res_dir.working_directory) {
1443          free(res->res_dir.working_directory);
1444       }
1445       if (res->res_dir.scripts_directory) {
1446          free((char *)res->res_dir.scripts_directory);
1447       }
1448       if (res->res_dir.plugin_directory) {
1449          free((char *)res->res_dir.plugin_directory);
1450       }
1451       if (res->res_dir.pid_directory) {
1452          free(res->res_dir.pid_directory);
1453       }
1454       if (res->res_dir.subsys_directory) {
1455          free(res->res_dir.subsys_directory);
1456       }
1457       if (res->res_dir.password) {
1458          free(res->res_dir.password);
1459       }
1460       if (res->res_dir.query_file) {
1461          free(res->res_dir.query_file);
1462       }
1463       if (res->res_dir.DIRaddrs) {
1464          free_addresses(res->res_dir.DIRaddrs);
1465       }
1466       if (res->res_dir.DIRsrc_addr) {
1467          free_addresses(res->res_dir.DIRsrc_addr);
1468       }
1469       if (res->res_dir.tls_ctx) {
1470          free_tls_context(res->res_dir.tls_ctx);
1471       }
1472       if (res->res_dir.tls_ca_certfile) {
1473          free(res->res_dir.tls_ca_certfile);
1474       }
1475       if (res->res_dir.tls_ca_certdir) {
1476          free(res->res_dir.tls_ca_certdir);
1477       }
1478       if (res->res_dir.tls_certfile) {
1479          free(res->res_dir.tls_certfile);
1480       }
1481       if (res->res_dir.tls_keyfile) {
1482          free(res->res_dir.tls_keyfile);
1483       }
1484       if (res->res_dir.tls_dhfile) {
1485          free(res->res_dir.tls_dhfile);
1486       }
1487       if (res->res_dir.tls_allowed_cns) {
1488          delete res->res_dir.tls_allowed_cns;
1489       }
1490       if (res->res_dir.verid) {
1491          free(res->res_dir.verid);
1492       }
1493       break;
1494    case R_DEVICE:
1495    case R_COUNTER:
1496        break;
1497    case R_CONSOLE:
1498       if (res->res_con.password) {
1499          free(res->res_con.password);
1500       }
1501       if (res->res_con.tls_ctx) {
1502          free_tls_context(res->res_con.tls_ctx);
1503       }
1504       if (res->res_con.tls_ca_certfile) {
1505          free(res->res_con.tls_ca_certfile);
1506       }
1507       if (res->res_con.tls_ca_certdir) {
1508          free(res->res_con.tls_ca_certdir);
1509       }
1510       if (res->res_con.tls_certfile) {
1511          free(res->res_con.tls_certfile);
1512       }
1513       if (res->res_con.tls_keyfile) {
1514          free(res->res_con.tls_keyfile);
1515       }
1516       if (res->res_con.tls_dhfile) {
1517          free(res->res_con.tls_dhfile);
1518       }
1519       if (res->res_con.tls_allowed_cns) {
1520          delete res->res_con.tls_allowed_cns;
1521       }
1522       for (int i=0; i<Num_ACL; i++) {
1523          if (res->res_con.ACL_lists[i]) {
1524             delete res->res_con.ACL_lists[i];
1525             res->res_con.ACL_lists[i] = NULL;
1526          }
1527       }
1528       break;
1529    case R_CLIENT:
1530       if (res->res_client.client_address) {
1531          free(res->res_client.client_address);
1532       }
1533       if (res->res_client.fd_storage_address) {
1534          free(res->res_client.fd_storage_address);
1535       }
1536       if (res->res_client.password) {
1537          free(res->res_client.password);
1538       }
1539       if (res->res_client.tls_ctx) {
1540          free_tls_context(res->res_client.tls_ctx);
1541       }
1542       if (res->res_client.tls_ca_certfile) {
1543          free(res->res_client.tls_ca_certfile);
1544       }
1545       if (res->res_client.tls_ca_certdir) {
1546          free(res->res_client.tls_ca_certdir);
1547       }
1548       if (res->res_client.tls_certfile) {
1549          free(res->res_client.tls_certfile);
1550       }
1551       if (res->res_client.tls_keyfile) {
1552          free(res->res_client.tls_keyfile);
1553       }
1554       if (res->res_client.tls_allowed_cns) {
1555          delete res->res_client.tls_allowed_cns;
1556       }
1557       break;
1558    case R_AUTOCHANGER:
1559    case R_STORAGE:
1560       if (res->res_store.address) {
1561          free(res->res_store.address);
1562       }
1563       if (res->res_store.fd_storage_address) {
1564          free(res->res_store.fd_storage_address);
1565       }
1566       if (res->res_store.password) {
1567          free(res->res_store.password);
1568       }
1569       if (res->res_store.media_type) {
1570          free(res->res_store.media_type);
1571       }
1572       if (res->res_store.ac_group) {
1573          free_pool_memory(res->res_store.ac_group);
1574       }
1575       if (res->res_store.device) {
1576          delete res->res_store.device;
1577       }
1578       if (res->res_store.tls_ctx) {
1579          free_tls_context(res->res_store.tls_ctx);
1580       }
1581       if (res->res_store.tls_ca_certfile) {
1582          free(res->res_store.tls_ca_certfile);
1583       }
1584       if (res->res_store.tls_ca_certdir) {
1585          free(res->res_store.tls_ca_certdir);
1586       }
1587       if (res->res_store.tls_certfile) {
1588          free(res->res_store.tls_certfile);
1589       }
1590       if (res->res_store.tls_keyfile) {
1591          free(res->res_store.tls_keyfile);
1592       }
1593       break;
1594    case R_CATALOG:
1595       if (res->res_cat.db_address) {
1596          free(res->res_cat.db_address);
1597       }
1598       if (res->res_cat.db_socket) {
1599          free(res->res_cat.db_socket);
1600       }
1601       if (res->res_cat.db_user) {
1602          free(res->res_cat.db_user);
1603       }
1604       if (res->res_cat.db_name) {
1605          free(res->res_cat.db_name);
1606       }
1607       if (res->res_cat.db_driver) {
1608          free(res->res_cat.db_driver);
1609       }
1610       if (res->res_cat.db_password) {
1611          free(res->res_cat.db_password);
1612       }
1613       if (res->res_cat.db_ssl_mode) {
1614          free(res->res_cat.db_ssl_mode);
1615       }
1616       if (res->res_cat.db_ssl_key) {
1617          free(res->res_cat.db_ssl_key);
1618       }
1619       if (res->res_cat.db_ssl_cert) {
1620          free(res->res_cat.db_ssl_cert);
1621       }
1622       if (res->res_cat.db_ssl_ca) {
1623          free(res->res_cat.db_ssl_ca);
1624       }
1625       if (res->res_cat.db_ssl_capath) {
1626          free(res->res_cat.db_ssl_capath);
1627       }
1628       if (res->res_cat.db_ssl_cipher) {
1629          free(res->res_cat.db_ssl_cipher);
1630       }
1631       break;
1632    case R_FILESET:
1633       if ((num=res->res_fs.num_includes)) {
1634          while (--num >= 0) {
1635             free_incexe(res->res_fs.include_items[num]);
1636          }
1637          free(res->res_fs.include_items);
1638       }
1639       res->res_fs.num_includes = 0;
1640       if ((num=res->res_fs.num_excludes)) {
1641          while (--num >= 0) {
1642             free_incexe(res->res_fs.exclude_items[num]);
1643          }
1644          free(res->res_fs.exclude_items);
1645       }
1646       res->res_fs.num_excludes = 0;
1647       break;
1648    case R_POOL:
1649       if (res->res_pool.pool_type) {
1650          free(res->res_pool.pool_type);
1651       }
1652       if (res->res_pool.label_format) {
1653          free(res->res_pool.label_format);
1654       }
1655       if (res->res_pool.cleaning_prefix) {
1656          free(res->res_pool.cleaning_prefix);
1657       }
1658       if (res->res_pool.storage) {
1659          delete res->res_pool.storage;
1660       }
1661       break;
1662    case R_SCHEDULE:
1663       if (res->res_sch.run) {
1664          RUN *nrun, *next;
1665          nrun = res->res_sch.run;
1666          while (nrun) {
1667             next = nrun->next;
1668             free(nrun);
1669             nrun = next;
1670          }
1671       }
1672       break;
1673    case R_JOB:
1674    case R_JOBDEFS:
1675       if (res->res_job.RestoreWhere) {
1676          free(res->res_job.RestoreWhere);
1677       }
1678       if (res->res_job.RegexWhere) {
1679          free(res->res_job.RegexWhere);
1680       }
1681       if (res->res_job.strip_prefix) {
1682          free(res->res_job.strip_prefix);
1683       }
1684       if (res->res_job.add_prefix) {
1685          free(res->res_job.add_prefix);
1686       }
1687       if (res->res_job.add_suffix) {
1688          free(res->res_job.add_suffix);
1689       }
1690       if (res->res_job.RestoreBootstrap) {
1691          free(res->res_job.RestoreBootstrap);
1692       }
1693       if (res->res_job.WriteBootstrap) {
1694          free(res->res_job.WriteBootstrap);
1695       }
1696       if (res->res_job.PluginOptions) {
1697          free(res->res_job.PluginOptions);
1698       }
1699       if (res->res_job.selection_pattern) {
1700          free(res->res_job.selection_pattern);
1701       }
1702       if (res->res_job.run_cmds) {
1703          delete res->res_job.run_cmds;
1704       }
1705       if (res->res_job.storage) {
1706          delete res->res_job.storage;
1707       }
1708       if (res->res_job.base) {
1709          delete res->res_job.base;
1710       }
1711       if (res->res_job.RunScripts) {
1712          free_runscripts(res->res_job.RunScripts);
1713          delete res->res_job.RunScripts;
1714       }
1715       break;
1716    case R_MSGS:
1717       if (res->res_msgs.mail_cmd) {
1718          free(res->res_msgs.mail_cmd);
1719       }
1720       if (res->res_msgs.operator_cmd) {
1721          free(res->res_msgs.operator_cmd);
1722       }
1723       free_msgs_res((MSGS *)res);  /* free message resource */
1724       res = NULL;
1725       break;
1726    default:
1727       printf(_("Unknown resource type %d in free_resource.\n"), type);
1728    }
1729    /* Common stuff again -- free the resource, recurse to next one */
1730    if (res) {
1731       free(res);
1732    }
1733 }
1734
1735 /*
1736  * Save the new resource by chaining it into the head list for
1737  * the resource. If this is pass 2, we update any resource
1738  * pointers because they may not have been defined until
1739  * later in pass 1.
1740  */
1741 bool save_resource(CONFIG *config, int type, RES_ITEM *items, int pass)
1742 {
1743    URES *res;
1744    int rindex = type - r_first;
1745    int i, size = 0;
1746    bool error = false;
1747
1748    /* Check Job requirements after applying JobDefs */
1749    if (type != R_JOB && type != R_JOBDEFS) {
1750       /*
1751        * Ensure that all required items are present
1752        */
1753       for (i=0; items[i].name; i++) {
1754          if (items[i].flags & ITEM_REQUIRED) {
1755             if (!bit_is_set(i, res_all.res_dir.hdr.item_present)) {
1756                Mmsg(config->m_errmsg, _("\"%s\" directive is required in \"%s\" resource, but not found.\n"),
1757                     items[i].name, resources[rindex].name);
1758                return false;
1759             }
1760          }
1761          /* If this triggers, take a look at lib/parse_conf.h */
1762          if (i >= MAX_RES_ITEMS) {
1763             Mmsg(config->m_errmsg, _("Too many directives in \"%s\" resource\n"), resources[rindex].name);
1764             return false;
1765          }
1766       }
1767    } else if (type == R_JOB) {
1768       /*
1769        * Ensure that the name item is present
1770        */
1771       if (items[0].flags & ITEM_REQUIRED) {
1772          if (!bit_is_set(0, res_all.res_dir.hdr.item_present)) {
1773             Mmsg(config->m_errmsg, _("\"%s\" directive is required in \"%s\" resource, but not found.\n"),
1774                  items[0].name, resources[rindex].name);
1775             return false;
1776          }
1777       }
1778    }
1779
1780    /*
1781     * During pass 2 in each "store" routine, we looked up pointers
1782     * to all the resources referrenced in the current resource, now we
1783     * must copy their addresses from the static record to the allocated
1784     * record.
1785     */
1786    if (pass == 2) {
1787       switch (type) {
1788       /* Resources not containing a resource */
1789       case R_CATALOG:
1790       case R_MSGS:
1791       case R_FILESET:
1792       case R_DEVICE:
1793          break;
1794
1795       /*
1796        * Resources containing another resource or alist. First
1797        *  look up the resource which contains another resource. It
1798        *  was written during pass 1.  Then stuff in the pointers to
1799        *  the resources it contains, which were inserted this pass.
1800        *  Finally, it will all be stored back.
1801        */
1802       case R_POOL:
1803          /* Find resource saved in pass 1 */
1804          if ((res = (URES *)GetResWithName(R_POOL, res_all.res_con.hdr.name)) == NULL) {
1805             Mmsg(config->m_errmsg, _("Cannot find Pool resource %s\n"), res_all.res_con.hdr.name);
1806             return false;
1807          }
1808          /* Explicitly copy resource pointers from this pass (res_all) */
1809          res->res_pool.NextPool = res_all.res_pool.NextPool;
1810          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
1811          res->res_pool.ScratchPool = res_all.res_pool.ScratchPool;
1812          res->res_pool.storage    = res_all.res_pool.storage;
1813          res->res_pool.catalog    = res_all.res_pool.catalog;
1814          break;
1815       case R_CONSOLE:
1816          if ((res = (URES *)GetResWithName(R_CONSOLE, res_all.res_con.hdr.name)) == NULL) {
1817             Mmsg(config->m_errmsg, _("Cannot find Console resource %s\n"), res_all.res_con.hdr.name);
1818             return false;
1819          }
1820          res->res_con.tls_allowed_cns = res_all.res_con.tls_allowed_cns;
1821          break;
1822       case R_DIRECTOR:
1823          if ((res = (URES *)GetResWithName(R_DIRECTOR, res_all.res_dir.hdr.name)) == NULL) {
1824             Mmsg(config->m_errmsg, _("Cannot find Director resource %s\n"), res_all.res_dir.hdr.name);
1825             return false;
1826          }
1827          res->res_dir.messages = res_all.res_dir.messages;
1828          res->res_dir.tls_allowed_cns = res_all.res_dir.tls_allowed_cns;
1829          break;
1830       case R_AUTOCHANGER:          /* alias for R_STORAGE */
1831       case R_STORAGE:
1832          type = R_STORAGE;         /* force Storage type */
1833          if ((res = (URES *)GetResWithName(type, res_all.res_store.hdr.name)) == NULL) {
1834             Mmsg(config->m_errmsg, _("Cannot find Storage resource %s\n"),
1835                  res_all.res_dir.hdr.name);
1836             return false;
1837          }
1838          /* we must explicitly copy the device alist pointer */
1839          res->res_store.device   = res_all.res_store.device;
1840          res->res_store.changer = res_all.res_store.changer;
1841          res->res_store.shared_storage = res_all.res_store.shared_storage;
1842          res->res_store.autochanger = res_all.res_store.autochanger;
1843          if (strcasecmp(resources[rindex].name, "autochanger") == 0) {
1844             res->res_store.changer = &res->res_store;
1845             res->res_store.autochanger = true;
1846          }
1847          break;
1848       case R_JOB:
1849       case R_JOBDEFS:
1850          if ((res = (URES *)GetResWithName(type, res_all.res_dir.hdr.name)) == NULL) {
1851             Mmsg(config->m_errmsg, _("Cannot find Job resource %s\n"),
1852                  res_all.res_dir.hdr.name);
1853             return false;
1854          }
1855          res->res_job.messages   = res_all.res_job.messages;
1856          res->res_job.schedule   = res_all.res_job.schedule;
1857          res->res_job.client     = res_all.res_job.client;
1858          res->res_job.fileset    = res_all.res_job.fileset;
1859          res->res_job.storage    = res_all.res_job.storage;
1860          res->res_job.base       = res_all.res_job.base;
1861          res->res_job.pool       = res_all.res_job.pool;
1862          res->res_job.next_pool  = res_all.res_job.next_pool;
1863          res->res_job.full_pool  = res_all.res_job.full_pool;
1864          res->res_job.vfull_pool = res_all.res_job.vfull_pool;
1865          res->res_job.inc_pool   = res_all.res_job.inc_pool;
1866          res->res_job.diff_pool  = res_all.res_job.diff_pool;
1867          res->res_job.verify_job = res_all.res_job.verify_job;
1868          res->res_job.jobdefs    = res_all.res_job.jobdefs;
1869          res->res_job.run_cmds   = res_all.res_job.run_cmds;
1870          res->res_job.RunScripts = res_all.res_job.RunScripts;
1871
1872          /* TODO: JobDefs where/regexwhere doesn't work well (but this
1873           * is not very useful)
1874           * We have to set_bit(index, res_all.hdr.item_present);
1875           * or something like that
1876           */
1877
1878          /* we take RegexWhere before all other options */
1879          if (!res->res_job.RegexWhere
1880              &&
1881              (res->res_job.strip_prefix ||
1882               res->res_job.add_suffix   ||
1883               res->res_job.add_prefix))
1884          {
1885             int len = bregexp_get_build_where_size(res->res_job.strip_prefix,
1886                                                    res->res_job.add_prefix,
1887                                                    res->res_job.add_suffix);
1888             res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
1889             bregexp_build_where(res->res_job.RegexWhere, len,
1890                                 res->res_job.strip_prefix,
1891                                 res->res_job.add_prefix,
1892                                 res->res_job.add_suffix);
1893             /* TODO: test bregexp */
1894          }
1895
1896          if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
1897             free(res->res_job.RestoreWhere);
1898             res->res_job.RestoreWhere = NULL;
1899          }
1900
1901          break;
1902       case R_COUNTER:
1903          if ((res = (URES *)GetResWithName(R_COUNTER, res_all.res_counter.hdr.name)) == NULL) {
1904             Mmsg(config->m_errmsg, _("Cannot find Counter resource %s\n"), res_all.res_counter.hdr.name);
1905             return false;
1906          }
1907          res->res_counter.Catalog = res_all.res_counter.Catalog;
1908          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
1909          break;
1910
1911       case R_CLIENT:
1912          if ((res = (URES *)GetResWithName(R_CLIENT, res_all.res_client.name())) == NULL) {
1913             Mmsg(config->m_errmsg, _("Cannot find Client resource %s\n"), res_all.res_client.name());
1914             return false;
1915          }
1916          res->res_client.catalog = res_all.res_client.catalog;
1917          res->res_client.tls_allowed_cns = res_all.res_client.tls_allowed_cns;
1918          break;
1919       case R_SCHEDULE:
1920          /*
1921           * Schedule is a bit different in that it contains a RUN record
1922           * chain which isn't a "named" resource. This chain was linked
1923           * in by run_conf.c during pass 2, so here we jam the pointer
1924           * into the Schedule resource.
1925           */
1926          if ((res = (URES *)GetResWithName(R_SCHEDULE, res_all.res_client.name())) == NULL) {
1927             Mmsg(config->m_errmsg, _("Cannot find Schedule resource %s\n"), res_all.res_client.name());
1928             return false;
1929          }
1930          res->res_sch.run = res_all.res_sch.run;
1931          break;
1932       default:
1933          Emsg1(M_ERROR, 0, _("Unknown resource type %d in save_resource.\n"), type);
1934          error = true;
1935          break;
1936       }
1937       /* Note, the resource name was already saved during pass 1,
1938        * so here, we can just release it.
1939        */
1940       if (res_all.res_dir.hdr.name) {
1941          free(res_all.res_dir.hdr.name);
1942          res_all.res_dir.hdr.name = NULL;
1943       }
1944       if (res_all.res_dir.hdr.desc) {
1945          free(res_all.res_dir.hdr.desc);
1946          res_all.res_dir.hdr.desc = NULL;
1947       }
1948       return true;
1949    }
1950
1951    /* R_AUTOCHANGER is alias so turn it into an R_STORAGE */
1952    if (type == R_AUTOCHANGER) {
1953       type = R_STORAGE;
1954       rindex = type - r_first;
1955    }
1956
1957    /*
1958     * The following code is only executed during pass 1
1959     */
1960    switch (type) {
1961    case R_DIRECTOR:
1962       size = sizeof(DIRRES);
1963       break;
1964    case R_CONSOLE:
1965       size = sizeof(CONRES);
1966       break;
1967    case R_CLIENT:
1968       size =sizeof(CLIENT);
1969       break;
1970    case R_STORAGE:
1971       size = sizeof(STORE);
1972       break;
1973    case R_CATALOG:
1974       size = sizeof(CAT);
1975       break;
1976    case R_JOB:
1977    case R_JOBDEFS:
1978       size = sizeof(JOB);
1979       break;
1980    case R_FILESET:
1981       size = sizeof(FILESET);
1982       break;
1983    case R_SCHEDULE:
1984       size = sizeof(SCHED);
1985       break;
1986    case R_POOL:
1987       size = sizeof(POOL);
1988       break;
1989    case R_MSGS:
1990       size = sizeof(MSGS);
1991       break;
1992    case R_COUNTER:
1993       size = sizeof(COUNTER);
1994       break;
1995    case R_DEVICE:
1996       error = true;
1997       break;
1998    default:
1999       printf(_("Unknown resource type %d in save_resource.\n"), type);
2000       error = true;
2001       break;
2002    }
2003    /* Common */
2004    if (!error) {
2005       if (!config->insert_res(rindex, size)) {
2006          return false;
2007       }
2008    }
2009    return true;
2010 }
2011
2012 void store_actiononpurge(LEX *lc, RES_ITEM *item, int index, int pass)
2013 {
2014    uint32_t *destination = (uint32_t*)item->value;
2015    lex_get_token(lc, T_NAME);
2016    if (strcasecmp(lc->str, "truncate") == 0) {
2017       *destination = (*destination) | ON_PURGE_TRUNCATE;
2018    } else {
2019       scan_err2(lc, _("Expected one of: %s, got: %s"), "Truncate", lc->str);
2020       return;
2021    }
2022    scan_to_eol(lc);
2023    set_bit(index, res_all.hdr.item_present);
2024 }
2025
2026 /*
2027  * Store an autochanger resource. Used by Autochanger and
2028  * SharedStorage direcives.
2029  */
2030 void store_ac_res(LEX *lc, RES_ITEM *item, int index, int pass)
2031 {
2032    RES *res;
2033    RES_ITEM *next = item + 1;
2034
2035    lex_get_token(lc, T_NAME);
2036    Dmsg1(100, "Got name=%s\n", lc->str);
2037    /*
2038     * For backward compatibility, if yes/no, set the next item
2039     */
2040    if (strcasecmp(item->name, "autochanger") == 0) {
2041       if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
2042          *(bool *)(next->value) = true;
2043          Dmsg2(100, "Item=%s got value=%s\n", item->name, lc->str);
2044          scan_to_eol(lc);
2045          return;
2046       } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
2047          *(bool *)(next->value) = false;
2048          Dmsg2(100, "Item=%s got value=%s\n", item->name, lc->str);
2049          scan_to_eol(lc);
2050          return;
2051       }
2052    }
2053    Dmsg2(100, "Item=%s got value=%s\n", item->name, lc->str);
2054
2055    if (pass == 2) {
2056       res = GetResWithName(R_STORAGE, lc->str);
2057       if (res == NULL) {
2058          scan_err3(lc, _("Could not find Storage Resource %s referenced on line %d : %s\n"),
2059             lc->str, lc->line_no, lc->line);
2060          return;
2061       }
2062       if (*(item->value)) {
2063          scan_err3(lc, _("Attempt to redefine Storage resource \"%s\" referenced on line %d : %s\n"),
2064             item->name, lc->line_no, lc->line);
2065          return;
2066       }
2067       Dmsg2(100, "Store %s value=%p\n", lc->str, res);
2068       *(item->value) = (char *)res;
2069       *(bool *)(next->value) = true;
2070    }
2071    scan_to_eol(lc);
2072    set_bit(index, res_all.hdr.item_present);
2073 }
2074
2075
2076 /*
2077  * Store Device. Note, the resource is created upon the
2078  *  first reference. The details of the resource are obtained
2079  *  later from the SD.
2080  */
2081 void store_device(LEX *lc, RES_ITEM *item, int index, int pass)
2082 {
2083    int rindex = R_DEVICE - r_first;
2084    int size = sizeof(DEVICE);
2085
2086    if (pass == 1) {
2087       URES *ures;
2088       RES *res;
2089
2090       lex_get_token(lc, T_NAME);
2091       rblist *list = res_head[rindex]->res_list;
2092       ures = (URES *)malloc(size);
2093       memset(ures, 0, size);
2094       ures->res_dev.hdr.name = bstrdup(lc->str);
2095       res = (RES *)ures;
2096       if (list->empty()) {
2097          list->insert(res, res_compare);
2098          res_head[rindex]->first = res;
2099          res_head[rindex]->last = res;
2100       } else {
2101          RES *item, *prev;
2102          prev = res_head[rindex]->last;
2103          item = (RES *)list->insert(res, res_compare);
2104          if (item == res) {
2105             prev->res_next = res;
2106             res_head[rindex]->last = res;
2107          } else {
2108             /* res not inserted */
2109             free(ures->res_dev.hdr.name);
2110             free(ures);
2111          }
2112       }
2113       scan_to_eol(lc);
2114       set_bit(index, res_all.hdr.item_present);
2115    } else {
2116       store_alist_res(lc, item, index, pass);
2117    }
2118 }
2119
2120 /*
2121  * Store Migration/Copy type
2122  *
2123  */
2124 void store_migtype(LEX *lc, RES_ITEM *item, int index, int pass)
2125 {
2126    int i;
2127
2128    lex_get_token(lc, T_NAME);
2129    /* Store the type both pass 1 and pass 2 */
2130    for (i=0; migtypes[i].type_name; i++) {
2131       if (strcasecmp(lc->str, migtypes[i].type_name) == 0) {
2132          *(uint32_t *)(item->value) = migtypes[i].job_type;
2133          i = 0;
2134          break;
2135       }
2136    }
2137    if (i != 0) {
2138       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
2139    }
2140    scan_to_eol(lc);
2141    set_bit(index, res_all.hdr.item_present);
2142 }
2143
2144
2145
2146 /*
2147  * Store JobType (backup, verify, restore)
2148  *
2149  */
2150 void store_jobtype(LEX *lc, RES_ITEM *item, int index, int pass)
2151 {
2152    int i;
2153
2154    lex_get_token(lc, T_NAME);
2155    /* Store the type both pass 1 and pass 2 */
2156    for (i=0; jobtypes[i].type_name; i++) {
2157       if (strcasecmp(lc->str, jobtypes[i].type_name) == 0) {
2158          *(uint32_t *)(item->value) = jobtypes[i].job_type;
2159          i = 0;
2160          break;
2161       }
2162    }
2163    if (i != 0) {
2164       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
2165    }
2166    scan_to_eol(lc);
2167    set_bit(index, res_all.hdr.item_present);
2168 }
2169
2170 /*
2171  * Store Job Level (Full, Incremental, ...)
2172  *
2173  */
2174 void store_level(LEX *lc, RES_ITEM *item, int index, int pass)
2175 {
2176    int i;
2177
2178    lex_get_token(lc, T_NAME);
2179    /* Store the level pass 2 so that type is defined */
2180    for (i=0; joblevels[i].level_name; i++) {
2181       if (strcasecmp(lc->str, joblevels[i].level_name) == 0) {
2182          *(uint32_t *)(item->value) = joblevels[i].level;
2183          i = 0;
2184          break;
2185       }
2186    }
2187    if (i != 0) {
2188       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
2189    }
2190    scan_to_eol(lc);
2191    set_bit(index, res_all.hdr.item_present);
2192 }
2193
2194
2195 void store_replace(LEX *lc, RES_ITEM *item, int index, int pass)
2196 {
2197    int i;
2198    lex_get_token(lc, T_NAME);
2199    /* Scan Replacement options */
2200    for (i=0; ReplaceOptions[i].name; i++) {
2201       if (strcasecmp(lc->str, ReplaceOptions[i].name) == 0) {
2202          *(uint32_t *)(item->value) = ReplaceOptions[i].token;
2203          i = 0;
2204          break;
2205       }
2206    }
2207    if (i != 0) {
2208       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
2209    }
2210    scan_to_eol(lc);
2211    set_bit(index, res_all.hdr.item_present);
2212 }
2213
2214 /*
2215  * Store ACL (access control list)
2216  *
2217  */
2218 void store_acl(LEX *lc, RES_ITEM *item, int index, int pass)
2219 {
2220    int token;
2221
2222    for (;;) {
2223       lex_get_token(lc, T_STRING);
2224       if (pass == 1) {
2225          if (((alist **)item->value)[item->code] == NULL) {
2226             ((alist **)item->value)[item->code] = New(alist(10, owned_by_alist));
2227             Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
2228          }
2229          ((alist **)item->value)[item->code]->append(bstrdup(lc->str));
2230          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
2231       }
2232       token = lex_get_token(lc, T_ALL);
2233       if (token == T_COMMA) {
2234          continue;                    /* get another ACL */
2235       }
2236       break;
2237    }
2238    set_bit(index, res_all.hdr.item_present);
2239 }
2240
2241 /* We build RunScripts items here */
2242 static RUNSCRIPT res_runscript;
2243
2244 /* Store a runscript->when in a bit field */
2245 static void store_runscript_when(LEX *lc, RES_ITEM *item, int index, int pass)
2246 {
2247    lex_get_token(lc, T_NAME);
2248
2249    if (strcasecmp(lc->str, "before") == 0) {
2250       *(uint32_t *)(item->value) = SCRIPT_Before ;
2251    } else if (strcasecmp(lc->str, "after") == 0) {
2252       *(uint32_t *)(item->value) = SCRIPT_After;
2253    } else if (strcasecmp(lc->str, "aftervss") == 0) {
2254       *(uint32_t *)(item->value) = SCRIPT_AfterVSS;
2255    } else if (strcasecmp(lc->str, "aftersnapshot") == 0) {
2256       *(uint32_t *)(item->value) = SCRIPT_AfterVSS;
2257    } else if (strcasecmp(lc->str, "always") == 0) {
2258       *(uint32_t *)(item->value) = SCRIPT_Any;
2259    } else {
2260       scan_err2(lc, _("Expect %s, got: %s"), "Before, After, AfterVSS or Always", lc->str);
2261    }
2262    scan_to_eol(lc);
2263 }
2264
2265 /* Store a runscript->target
2266  *
2267  */
2268 static void store_runscript_target(LEX *lc, RES_ITEM *item, int index, int pass)
2269 {
2270    lex_get_token(lc, T_STRING);
2271
2272    if (pass == 2) {
2273       if (strcmp(lc->str, "%c") == 0) {
2274          ((RUNSCRIPT*) item->value)->set_target(lc->str);
2275       } else if (strcasecmp(lc->str, "yes") == 0) {
2276          ((RUNSCRIPT*) item->value)->set_target("%c");
2277       } else if (strcasecmp(lc->str, "no") == 0) {
2278          ((RUNSCRIPT*) item->value)->set_target("");
2279       } else {
2280          RES *res = GetResWithName(R_CLIENT, lc->str);
2281          if (res == NULL) {
2282             scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"),
2283                       lc->str, lc->line_no, lc->line);
2284          }
2285
2286          ((RUNSCRIPT*) item->value)->set_target(lc->str);
2287       }
2288    }
2289    scan_to_eol(lc);
2290 }
2291
2292 /*
2293  * Store a runscript->command as a string and runscript->cmd_type as a pointer
2294  */
2295 static void store_runscript_cmd(LEX *lc, RES_ITEM *item, int index, int pass)
2296 {
2297    lex_get_token(lc, T_STRING);
2298
2299    if (pass == 2) {
2300       Dmsg2(1, "runscript cmd=%s type=%c\n", lc->str, item->code);
2301       POOLMEM *c = get_pool_memory(PM_FNAME);
2302       /* Each runscript command takes 2 entries in commands list */
2303       pm_strcpy(c, lc->str);
2304       ((RUNSCRIPT*) item->value)->commands->prepend(c); /* command line */
2305       ((RUNSCRIPT*) item->value)->commands->prepend((void *)(intptr_t)item->code); /* command type */
2306    }
2307    scan_to_eol(lc);
2308 }
2309
2310 static void store_short_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
2311 {
2312    lex_get_token(lc, T_STRING);
2313    alist **runscripts = (alist **)(item->value) ;
2314
2315    if (pass == 2) {
2316       RUNSCRIPT *script = new_runscript();
2317       script->set_job_code_callback(job_code_callback_director);
2318
2319       script->set_command(lc->str);
2320
2321       /* TODO: remove all script->old_proto with bacula 1.42 */
2322
2323       if (strcasecmp(item->name, "runbeforejob") == 0) {
2324          script->when = SCRIPT_Before;
2325          script->fail_on_error = true;
2326          script->set_target("");
2327
2328       } else if (strcasecmp(item->name, "runafterjob") == 0) {
2329          script->when = SCRIPT_After;
2330          script->on_success = true;
2331          script->on_failure = false;
2332          script->set_target("");
2333
2334       } else if (strcasecmp(item->name, "clientrunbeforejob") == 0) {
2335          script->old_proto = true;
2336          script->when = SCRIPT_Before;
2337          script->set_target("%c");
2338          script->fail_on_error = true;
2339
2340       } else if (strcasecmp(item->name, "clientrunafterjob") == 0) {
2341          script->old_proto = true;
2342          script->when = SCRIPT_After;
2343          script->set_target("%c");
2344          script->on_success = true;
2345          script->on_failure = false;
2346
2347       } else if (strcasecmp(item->name, "consolerunbeforejob") == 0) {
2348          script->when = SCRIPT_Before;
2349          script->set_target("");
2350          script->fail_on_error = true;
2351          script->set_command(NPRT(script->command), CONSOLE_CMD);
2352
2353       } else if (strcasecmp(item->name, "consolerunafterjob") == 0) {
2354          script->when = SCRIPT_After;
2355          script->set_target("");
2356          script->on_success = true;
2357          script->on_failure = false;
2358          script->set_command(NPRT(script->command), CONSOLE_CMD);
2359
2360       } else if (strcasecmp(item->name, "runafterfailedjob") == 0) {
2361          script->when = SCRIPT_After;
2362          script->on_failure = true;
2363          script->on_success = false;
2364          script->set_target("");
2365       }
2366
2367       if (*runscripts == NULL) {
2368         *runscripts = New(alist(10, not_owned_by_alist));
2369       }
2370
2371       (*runscripts)->append(script);
2372       script->debug();
2373    }
2374    scan_to_eol(lc);
2375    set_bit(index, res_all.hdr.item_present);
2376 }
2377
2378 /* Store a bool in a bit field without modifing res_all.hdr
2379  * We can also add an option to store_bool to skip res_all.hdr
2380  */
2381 void store_runscript_bool(LEX *lc, RES_ITEM *item, int index, int pass)
2382 {
2383    lex_get_token(lc, T_NAME);
2384    if (strcasecmp(lc->str, "yes") == 0 || strcasecmp(lc->str, "true") == 0) {
2385       *(bool *)(item->value) = true;
2386    } else if (strcasecmp(lc->str, "no") == 0 || strcasecmp(lc->str, "false") == 0) {
2387       *(bool *)(item->value) = false;
2388    } else {
2389       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
2390    }
2391    scan_to_eol(lc);
2392 }
2393
2394 /*
2395  * new RunScript items
2396  *   name     handler     value               code flags default_value
2397  */
2398 static RES_ITEM runscript_items[] = {
2399  {"command",        store_runscript_cmd,  {(char **)&res_runscript},     SHELL_CMD, 0, 0},
2400  {"console",        store_runscript_cmd,  {(char **)&res_runscript},     CONSOLE_CMD, 0, 0},
2401  {"target",         store_runscript_target,{(char **)&res_runscript},          0,  0, 0},
2402  {"runsonsuccess",  store_runscript_bool, {(char **)&res_runscript.on_success},0,  0, 0},
2403  {"runsonfailure",  store_runscript_bool, {(char **)&res_runscript.on_failure},0,  0, 0},
2404  {"failjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
2405  {"abortjobonerror",store_runscript_bool, {(char **)&res_runscript.fail_on_error},0, 0, 0},
2406  {"runswhen",       store_runscript_when, {(char **)&res_runscript.when},      0,  0, 0},
2407  {"runsonclient",   store_runscript_target,{(char **)&res_runscript},          0,  0, 0}, /* TODO */
2408  {NULL, NULL, {0}, 0, 0, 0}
2409 };
2410
2411 /*
2412  * Store RunScript info
2413  *
2414  *  Note, when this routine is called, we are inside a Job
2415  *  resource.  We treat the RunScript like a sort of
2416  *  mini-resource within the Job resource.
2417  */
2418 void store_runscript(LEX *lc, RES_ITEM *item, int index, int pass)
2419 {
2420    char *c;
2421    int token, i, t;
2422    alist **runscripts = (alist **)(item->value) ;
2423
2424    Dmsg1(200, "store_runscript: begin store_runscript pass=%i\n", pass);
2425
2426    token = lex_get_token(lc, T_SKIP_EOL);
2427
2428    if (token != T_BOB) {
2429       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
2430    }
2431    /* setting on_success, on_failure, fail_on_error */
2432    res_runscript.reset_default();
2433
2434    if (pass == 2) {
2435       res_runscript.commands = New(alist(10, not_owned_by_alist));
2436    }
2437
2438    while ((token = lex_get_token(lc, T_SKIP_EOL)) != T_EOF) {
2439       if (token == T_EOB) {
2440         break;
2441       }
2442       if (token != T_IDENTIFIER) {
2443         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
2444       }
2445       for (i=0; runscript_items[i].name; i++) {
2446         if (strcasecmp(runscript_items[i].name, lc->str) == 0) {
2447            token = lex_get_token(lc, T_SKIP_EOL);
2448            if (token != T_EQUALS) {
2449               scan_err1(lc, _("expected an equals, got: %s"), lc->str);
2450            }
2451
2452            /* Call item handler */
2453            runscript_items[i].handler(lc, &runscript_items[i], i, pass);
2454            i = -1;
2455            break;
2456         }
2457       }
2458
2459       if (i >=0) {
2460         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
2461       }
2462    }
2463
2464    if (pass == 2) {
2465       /* run on client by default */
2466       if (res_runscript.target == NULL) {
2467          res_runscript.set_target("%c");
2468       }
2469       if (*runscripts == NULL) {
2470          *runscripts = New(alist(10, not_owned_by_alist));
2471       }
2472       /*
2473        * commands list contains 2 values per command
2474        *  - POOLMEM command string (ex: /bin/true)
2475        *  - int command type (ex: SHELL_CMD)
2476        */
2477       res_runscript.set_job_code_callback(job_code_callback_director);
2478       while ((c=(char*)res_runscript.commands->pop()) != NULL) {
2479          t = (intptr_t)res_runscript.commands->pop();
2480          RUNSCRIPT *script = new_runscript();
2481          memcpy(script, &res_runscript, sizeof(RUNSCRIPT));
2482          script->command = c;
2483          script->cmd_type = t;
2484          /* target is taken from res_runscript, each runscript object have
2485           * a copy
2486           */
2487          script->target = NULL;
2488          script->set_target(res_runscript.target);
2489
2490          (*runscripts)->append(script);
2491          script->debug();
2492       }
2493       delete res_runscript.commands;
2494       /* setting on_success, on_failure... cleanup target field */
2495       res_runscript.reset_default(true);
2496    }
2497
2498    scan_to_eol(lc);
2499    set_bit(index, res_all.hdr.item_present);
2500 }
2501
2502 /* callback function for edit_job_codes */
2503 /* See ../lib/util.c, function edit_job_codes, for more remaining codes */
2504 extern "C" char *job_code_callback_director(JCR *jcr, const char* param, char *buf, int buflen)
2505 {
2506    static char yes[] = "yes";
2507    static char no[] = "no";
2508    static char nothing[] = "";
2509
2510    if (jcr == NULL) {
2511       return nothing;
2512    }
2513    switch (param[0]) {
2514       case 'f':
2515          if (jcr->fileset) {
2516             return jcr->fileset->name();
2517          }
2518          break;
2519       case 'h':
2520          if (jcr->client && jcr->client->address()) {
2521             return jcr->client->address();
2522          }
2523          break;
2524       case 'p':
2525          if (jcr->pool) {
2526             return jcr->pool->name();
2527          }
2528          break;
2529       case 'w':
2530          if (jcr->wstore) {
2531             return jcr->wstore->name();
2532          }
2533          break;
2534       case 'x':
2535          return jcr->spool_data ? yes : no;
2536       case 'D':
2537          return my_name;
2538       case 'C':
2539          return jcr->cloned ? yes : no;
2540       case 'I':
2541          if (buflen >= 50) {
2542             if (jcr->wjcr) {
2543                edit_uint64(jcr->wjcr->JobId, buf);
2544                return buf;
2545             } else {
2546                edit_uint64(0, buf);
2547                return buf;
2548             }
2549          }
2550    }
2551    return nothing;
2552 }
2553
2554 bool parse_dir_config(CONFIG *config, const char *configfile, int exit_code)
2555 {
2556    config->init(configfile, NULL, exit_code, (void *)&res_all, res_all_size,
2557       r_first, r_last, resources, &res_head);
2558    return config->parse_config();
2559 }