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