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