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