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