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