1 /**
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2000-2011 Free Software Foundation Europe e.V.
5    Copyright (C) 2011-2012 Planets Communications B.V.
6    Copyright (C) 2013-2018 Bareos GmbH & Co. KG
7 
8    This program is Free Software; you can redistribute it and/or
9    modify it under the terms of version three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    in the file LICENSE.
12 
13    This program is distributed in the hope that it will be useful, but
14    WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16    Affero General Public License for more details.
17 
18    You should have received a copy of the GNU Affero General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301, USA.
22 */
23 /**
24  * Kern Sibbald, January MM
25  */
26 /***
27  * @file
28  * Main configuration file parser for BAREOS Directors,
29  * some parts may be split into separate files such as
30  * the schedule configuration (run_config.c).
31  *
32  * Note, the configuration file parser consists of three parts
33  *
34  * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
35  *
36  * 2. The generic config  scanner in lib/parse_config.c and
37  *    lib/parse_config.h.
38  *
39  *    These files contain the parser code, some utility
40  *    routines, and the common store routines (name, int,
41  *    string).
42  *
43  * 3. The daemon specific file, which contains the Resource
44  *    definitions as well as any specific store routines
45  *    for the resource records.
46  *
47  */
48 
49 #define NEED_JANSSON_NAMESPACE 1
50 #include "include/bareos.h"
51 #include "dird.h"
52 #include "dird/inc_conf.h"
53 #include "dird/dird_globals.h"
54 #include "lib/tls_conf.h"
55 #include "lib/qualified_resource_name_type_converter.h"
56 
57 namespace directordaemon {
58 
59 /**
60  * Used by print_config_schema_json
61  */
62 extern struct s_kw RunFields[];
63 
64 /**
65  * Define the first and last resource ID record
66  * types. Note, these should be unique for each
67  * daemon though not a requirement.
68  */
69 static CommonResourceHeader *sres_head[R_LAST - R_FIRST + 1];
70 static CommonResourceHeader **res_head = sres_head;
71 static PoolMem *configure_usage_string = NULL;
72 
73 /**
74  * Set default indention e.g. 2 spaces.
75  */
76 #define DEFAULT_INDENT_STRING "  "
77 
78 /**
79  * Imported subroutines
80  */
81 extern void StoreInc(LEX *lc, ResourceItem *item, int index, int pass);
82 extern void StoreRun(LEX *lc, ResourceItem *item, int index, int pass);
83 
84 /**
85  * Forward referenced subroutines
86  */
87 static void CreateAndAddUserAgentConsoleResource(ConfigurationParser &my_config);
88 static bool SaveResource(int type, ResourceItem *items, int pass);
89 static void FreeResource(CommonResourceHeader *sres, int type);
90 static void DumpResource(int type,
91                   CommonResourceHeader *ures,
92                   void sendit(void *sock, const char *fmt, ...),
93                   void *sock,
94                   bool hide_sensitive_data,
95                   bool verbose);
96 /**
97  * We build the current resource here as we are
98  * scanning the resource configuration definition,
99  * then move it to allocated memory when the resource
100  * scan is complete.
101  */
102 static UnionOfResources res_all;
103 static int32_t res_all_size = sizeof(res_all);
104 
105 /**
106  * Definition of records permitted within each
107  * resource with the routine to process the record
108  * information.
109  *
110  * Director Resource
111  *
112  * name handler value code flags default_value
113  */
114 static ResourceItem dir_items[] = {
115    { "Name", CFG_TYPE_NAME, ITEM(res_dir.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
116      "The name of the resource." },
117    { "Description", CFG_TYPE_STR, ITEM(res_dir.hdr.desc), 0, 0, NULL, NULL, NULL },
118    { "Messages", CFG_TYPE_RES, ITEM(res_dir.messages), R_MSGS, 0, NULL, NULL, NULL },
119    { "DirPort", CFG_TYPE_ADDRESSES_PORT, ITEM(res_dir.DIRaddrs), 0, CFG_ITEM_DEFAULT, DIR_DEFAULT_PORT, NULL, NULL },
120    { "DirAddress", CFG_TYPE_ADDRESSES_ADDRESS, ITEM(res_dir.DIRaddrs), 0, CFG_ITEM_DEFAULT, DIR_DEFAULT_PORT, NULL, NULL },
121    { "DirAddresses", CFG_TYPE_ADDRESSES, ITEM(res_dir.DIRaddrs), 0, CFG_ITEM_DEFAULT, DIR_DEFAULT_PORT, NULL, NULL },
122    { "DirSourceAddress", CFG_TYPE_ADDRESSES_ADDRESS, ITEM(res_dir.DIRsrc_addr), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
123    { "QueryFile", CFG_TYPE_DIR, ITEM(res_dir.query_file), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
124    { "WorkingDirectory", CFG_TYPE_DIR, ITEM(res_dir.working_directory), 0, CFG_ITEM_DEFAULT | CFG_ITEM_PLATFORM_SPECIFIC, _PATH_BAREOS_WORKINGDIR, NULL, NULL },
125    { "PidDirectory", CFG_TYPE_DIR, ITEM(res_dir.pid_directory), 0, CFG_ITEM_DEFAULT | CFG_ITEM_PLATFORM_SPECIFIC, _PATH_BAREOS_PIDDIR, NULL, NULL },
126    { "PluginDirectory", CFG_TYPE_DIR, ITEM(res_dir.plugin_directory), 0, 0, NULL,
127      "14.2.0-", "Plugins are loaded from this directory. To load only specific plugins, use 'Plugin Names'." },
128    { "PluginNames", CFG_TYPE_PLUGIN_NAMES, ITEM(res_dir.plugin_names), 0, 0, NULL,
129       "14.2.0-", "List of plugins, that should get loaded from 'Plugin Directory' (only basenames, '-dir.so' is added automatically). If empty, all plugins will get loaded." },
130    { "ScriptsDirectory", CFG_TYPE_DIR, ITEM(res_dir.scripts_directory), 0, 0, NULL, NULL, "This directive is currently unused." },
131 #if defined(HAVE_DYNAMIC_CATS_BACKENDS)
132    { "BackendDirectory", CFG_TYPE_ALIST_DIR, ITEM(res_dir.backend_directories), 0, CFG_ITEM_DEFAULT | CFG_ITEM_PLATFORM_SPECIFIC, _PATH_BAREOS_BACKENDDIR, NULL, NULL },
133 #endif
134    { "Subscriptions", CFG_TYPE_PINT32, ITEM(res_dir.subscriptions), 0, CFG_ITEM_DEFAULT, "0", "12.4.4-", NULL },
135    { "SubSysDirectory", CFG_TYPE_DIR, ITEM(res_dir.subsys_directory), 0, CFG_ITEM_DEPRECATED, NULL, "-12.4.0", NULL },
136    { "MaximumConcurrentJobs", CFG_TYPE_PINT32, ITEM(res_dir.MaxConcurrentJobs), 0, CFG_ITEM_DEFAULT, "1", NULL, NULL },
137    { "MaximumConnections", CFG_TYPE_PINT32, ITEM(res_dir.MaxConnections), 0, CFG_ITEM_DEFAULT, "30", NULL, NULL },
138    { "MaximumConsoleConnections", CFG_TYPE_PINT32, ITEM(res_dir.MaxConsoleConnections), 0, CFG_ITEM_DEFAULT, "20", NULL, NULL },
139    { "Password", CFG_TYPE_AUTOPASSWORD, ITEM(res_dir.password_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
140    { "FdConnectTimeout", CFG_TYPE_TIME, ITEM(res_dir.FDConnectTimeout), 0, CFG_ITEM_DEFAULT, "180" /* 3 minutes */, NULL, NULL },
141    { "SdConnectTimeout", CFG_TYPE_TIME, ITEM(res_dir.SDConnectTimeout), 0, CFG_ITEM_DEFAULT, "1800" /* 30 minutes */, NULL, NULL },
142    { "HeartbeatInterval", CFG_TYPE_TIME, ITEM(res_dir.heartbeat_interval), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
143    { "StatisticsRetention", CFG_TYPE_TIME, ITEM(res_dir.stats_retention), 0, CFG_ITEM_DEFAULT, "160704000" /* 5 years */, NULL, NULL },
144    { "StatisticsCollectInterval", CFG_TYPE_PINT32, ITEM(res_dir.stats_collect_interval), 0, CFG_ITEM_DEFAULT, "150", "14.2.0-", NULL },
145    { "VerId", CFG_TYPE_STR, ITEM(res_dir.verid), 0, 0, NULL, NULL, NULL },
146    { "OptimizeForSize", CFG_TYPE_BOOL, ITEM(res_dir.optimize_for_size), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
147    { "OptimizeForSpeed", CFG_TYPE_BOOL, ITEM(res_dir.optimize_for_speed), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
148    { "OmitDefaults", CFG_TYPE_BOOL, ITEM(res_dir.omit_defaults), 0, CFG_ITEM_DEFAULT | CFG_ITEM_DEPRECATED, "true",
149       NULL, "Omit config variables with default values when dumping the config." },
150    { "KeyEncryptionKey", CFG_TYPE_AUTOPASSWORD, ITEM(res_dir.keyencrkey), 1, 0, NULL, NULL, NULL },
151    { "NdmpSnooping", CFG_TYPE_BOOL, ITEM(res_dir.ndmp_snooping), 0, 0, NULL, "13.2.0-", NULL },
152    { "NdmpLogLevel", CFG_TYPE_PINT32, ITEM(res_dir.ndmp_loglevel), 0, CFG_ITEM_DEFAULT, "4", "13.2.0-", NULL },
153    { "AbsoluteJobTimeout", CFG_TYPE_PINT32, ITEM(res_dir.jcr_watchdog_time), 0, 0, NULL, "14.2.0-", NULL },
154    { "Auditing", CFG_TYPE_BOOL, ITEM(res_dir.auditing), 0, CFG_ITEM_DEFAULT, "false", "14.2.0-", NULL },
155    { "AuditEvents", CFG_TYPE_AUDIT, ITEM(res_dir.audit_events), 0, 0, NULL, "14.2.0-", NULL },
156    { "SecureEraseCommand", CFG_TYPE_STR, ITEM(res_dir.secure_erase_cmdline), 0, 0, NULL, "15.2.1-",
157      "Specify command that will be called when bareos unlinks files." },
158    { "LogTimestampFormat", CFG_TYPE_STR, ITEM(res_dir.log_timestamp_format), 0, 0, NULL, "15.2.3-", NULL },
159    TLS_COMMON_CONFIG(res_dir),
160    TLS_CERT_CONFIG(res_dir),
161    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
162 };
163 
164 /**
165  * Profile Resource
166  *
167  * name handler value code flags default_value
168  */
169 static ResourceItem profile_items[] = {
170    { "Name", CFG_TYPE_NAME, ITEM(res_profile.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
171      "The name of the resource." },
172    { "Description", CFG_TYPE_STR, ITEM(res_profile.hdr.desc), 0, 0, NULL, NULL,
173      "Additional information about the resource. Only used for UIs." },
174    { "JobACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), Job_ACL, 0, NULL, NULL,
175      "Lists the Job resources, this resource has access to. The special keyword *all* allows access to all Job resources." },
176    { "ClientACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), Client_ACL, 0, NULL, NULL,
177      "Lists the Client resources, this resource has access to. The special keyword *all* allows access to all Client resources." },
178    { "StorageACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), Storage_ACL, 0, NULL, NULL,
179      "Lists the Storage resources, this resource has access to. The special keyword *all* allows access to all Storage resources." },
180    { "ScheduleACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), Schedule_ACL, 0, NULL, NULL,
181      "Lists the Schedule resources, this resource has access to. The special keyword *all* allows access to all Schedule resources." },
182    { "PoolACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), Pool_ACL, 0, NULL, NULL,
183      "Lists the Pool resources, this resource has access to. The special keyword *all* allows access to all Pool resources." },
184    { "CommandACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), Command_ACL, 0, NULL, NULL,
185      "Lists the commands, this resource has access to. The special keyword *all* allows using commands." },
186    { "FileSetACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), FileSet_ACL, 0, NULL, NULL,
187      "Lists the File Set resources, this resource has access to. The special keyword *all* allows access to all File Set resources." },
188    { "CatalogACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), Catalog_ACL, 0, NULL, NULL,
189      "Lists the Catalog resources, this resource has access to. The special keyword *all* allows access to all Catalog resources." },
190    { "WhereACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), Where_ACL, 0, NULL, NULL,
191      "Specifies the base directories, where files could be restored. An empty string allows restores to all directories." },
192    { "PluginOptionsACL", CFG_TYPE_ACL, ITEM(res_profile.ACL_lists), PluginOptions_ACL, 0, NULL, NULL,
193      "Specifies the allowed plugin options. An empty strings allows all Plugin Options." },
194    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
195 };
196 
197 /**
198  * Console Resource
199  *
200  * name handler value code flags default_value
201  */
202 static ResourceItem con_items[] = {
203    { "Name", CFG_TYPE_NAME, ITEM(res_con.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
204    { "Description", CFG_TYPE_STR, ITEM(res_con.hdr.desc), 0, 0, NULL, NULL, NULL },
205    { "Password", CFG_TYPE_AUTOPASSWORD, ITEM(res_con.password_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
206    { "JobACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Job_ACL, 0, NULL, NULL, NULL },
207    { "ClientACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Client_ACL, 0, NULL, NULL, NULL },
208    { "StorageACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Storage_ACL, 0, NULL, NULL, NULL },
209    { "ScheduleACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Schedule_ACL, 0, NULL, NULL, NULL },
210    { "RunACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Run_ACL, 0, NULL, NULL, NULL },
211    { "PoolACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Pool_ACL, 0, NULL, NULL, NULL },
212    { "CommandACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Command_ACL, 0, NULL, NULL, NULL },
213    { "FileSetACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), FileSet_ACL, 0, NULL, NULL, NULL },
214    { "CatalogACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Catalog_ACL, 0, NULL, NULL, NULL },
215    { "WhereACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), Where_ACL, 0, NULL, NULL, NULL },
216    { "PluginOptionsACL", CFG_TYPE_ACL, ITEM(res_con.ACL_lists), PluginOptions_ACL, 0, NULL, NULL, NULL },
217    { "Profile", CFG_TYPE_ALIST_RES, ITEM(res_con.profiles), R_PROFILE, 0, NULL, "14.2.3-",
218      "Profiles can be assigned to a Console. ACL are checked until either a deny ACL is found or an allow ACL. "
219      "First the console ACL is checked then any profile the console is linked to." },
220    { "UsePamAuthentication", CFG_TYPE_BOOL, ITEM(res_con.use_pam_authentication_), 0, CFG_ITEM_DEFAULT,
221      "false", "18.2.4-", NULL },
222    TLS_COMMON_CONFIG(res_con),
223    TLS_CERT_CONFIG(res_con),
224    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
225 };
226 
227 /**
228  * Client or File daemon resource
229  *
230  * name handler value code flags default_value
231  */
232 static ResourceItem cli_items[] = {
233    { "Name", CFG_TYPE_NAME, ITEM(res_client.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
234      "The name of the resource." },
235    { "Description", CFG_TYPE_STR, ITEM(res_client.hdr.desc), 0, 0, NULL, NULL, NULL },
236    { "Protocol", CFG_TYPE_AUTHPROTOCOLTYPE, ITEM(res_client.Protocol), 0, CFG_ITEM_DEFAULT, "Native", "13.2.0-", NULL },
237    { "AuthType", CFG_TYPE_AUTHTYPE, ITEM(res_client.AuthType), 0, CFG_ITEM_DEFAULT, "None", NULL, NULL },
238    { "Address", CFG_TYPE_STR, ITEM(res_client.address), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
239    { "LanAddress", CFG_TYPE_STR, ITEM(res_client.lanaddress), 0, CFG_ITEM_DEFAULT, NULL, "16.2.6-",
240      "Sets additional address used for connections between Client and Storage Daemon inside separate network."},
241    { "FdAddress", CFG_TYPE_STR, ITEM(res_client.address), 0, CFG_ITEM_ALIAS, NULL, NULL, "Alias for Address." },
242    { "Port", CFG_TYPE_PINT32, ITEM(res_client.FDport), 0, CFG_ITEM_DEFAULT, FD_DEFAULT_PORT, NULL, NULL },
243    { "FdPort", CFG_TYPE_PINT32, ITEM(res_client.FDport), 0, CFG_ITEM_DEFAULT | CFG_ITEM_ALIAS, FD_DEFAULT_PORT, NULL, NULL },
244    { "Username", CFG_TYPE_STR, ITEM(res_client.username), 0, 0, NULL, NULL, NULL },
245    { "Password", CFG_TYPE_AUTOPASSWORD, ITEM(res_client.password_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
246    { "FdPassword", CFG_TYPE_AUTOPASSWORD, ITEM(res_client.password_), 0, CFG_ITEM_ALIAS, NULL, NULL, NULL },
247    { "Catalog", CFG_TYPE_RES, ITEM(res_client.catalog), R_CATALOG, 0, NULL, NULL, NULL },
248    { "Passive", CFG_TYPE_BOOL, ITEM(res_client.passive), 0, CFG_ITEM_DEFAULT, "false", "13.2.0-",
249      "If enabled, the Storage Daemon will initiate the network connection to the Client. If disabled, the Client will initiate the network connection to the Storage Daemon." },
250    { "ConnectionFromDirectorToClient", CFG_TYPE_BOOL, ITEM(res_client.conn_from_dir_to_fd), 0, CFG_ITEM_DEFAULT, "true", "16.2.2",
251      "Let the Director initiate the network connection to the Client." },
252    { "AllowClientConnect", CFG_TYPE_BOOL, ITEM(res_client.conn_from_fd_to_dir), 0, CFG_ITEM_DEPRECATED | CFG_ITEM_ALIAS, NULL, NULL,
253      "Alias of \"Connection From Client To Director\"." },
254    { "ConnectionFromClientToDirector", CFG_TYPE_BOOL, ITEM(res_client.conn_from_fd_to_dir), 0, CFG_ITEM_DEFAULT, "false", "16.2.2",
255      "The Director will accept incoming network connection from this Client." },
256    { "Enabled", CFG_TYPE_BOOL, ITEM(res_client.enabled), 0, CFG_ITEM_DEFAULT, "true", NULL,
257      "En- or disable this resource." },
258    { "HardQuota", CFG_TYPE_SIZE64, ITEM(res_client.HardQuota), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
259    { "SoftQuota", CFG_TYPE_SIZE64, ITEM(res_client.SoftQuota), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
260    { "SoftQuotaGracePeriod", CFG_TYPE_TIME, ITEM(res_client.SoftQuotaGracePeriod), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
261    { "StrictQuotas", CFG_TYPE_BOOL, ITEM(res_client.StrictQuotas), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
262    { "QuotaIncludeFailedJobs", CFG_TYPE_BOOL, ITEM(res_client.QuotaIncludeFailedJobs), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
263    { "FileRetention", CFG_TYPE_TIME, ITEM(res_client.FileRetention), 0, CFG_ITEM_DEFAULT, "5184000" /* 60 days */, NULL, NULL },
264    { "JobRetention", CFG_TYPE_TIME, ITEM(res_client.JobRetention), 0, CFG_ITEM_DEFAULT, "15552000" /* 180 days */, NULL, NULL },
265    { "HeartbeatInterval", CFG_TYPE_TIME, ITEM(res_client.heartbeat_interval), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
266    { "AutoPrune", CFG_TYPE_BOOL, ITEM(res_client.AutoPrune), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
267    { "MaximumConcurrentJobs", CFG_TYPE_PINT32, ITEM(res_client.MaxConcurrentJobs), 0, CFG_ITEM_DEFAULT, "1", NULL, NULL },
268    { "MaximumBandwidthPerJob", CFG_TYPE_SPEED, ITEM(res_client.max_bandwidth), 0, 0, NULL, NULL, NULL },
269    { "NdmpLogLevel", CFG_TYPE_PINT32, ITEM(res_client.ndmp_loglevel), 0, CFG_ITEM_DEFAULT, "4", NULL, NULL },
270    { "NdmpBlockSize", CFG_TYPE_SIZE32, ITEM(res_client.ndmp_blocksize), 0, CFG_ITEM_DEFAULT, "64512", NULL, NULL },
271    { "NdmpUseLmdb", CFG_TYPE_BOOL, ITEM(res_client.ndmp_use_lmdb), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
272    TLS_COMMON_CONFIG(res_client),
273    TLS_CERT_CONFIG(res_client),
274    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
275 };
276 
277 /** Storage daemon resource
278  *
279  * name handler value code flags default_value
280  */
281 static ResourceItem store_items[] = {
282    { "Name", CFG_TYPE_NAME, ITEM(res_store.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
283       "The name of the resource." },
284    { "Description", CFG_TYPE_STR, ITEM(res_store.hdr.desc), 0, 0, NULL, NULL, NULL },
285    { "Protocol", CFG_TYPE_AUTHPROTOCOLTYPE, ITEM(res_store.Protocol), 0, CFG_ITEM_DEFAULT, "Native", NULL, NULL },
286    { "AuthType", CFG_TYPE_AUTHTYPE, ITEM(res_store.AuthType), 0, CFG_ITEM_DEFAULT, "None", NULL, NULL },
287    { "Address", CFG_TYPE_STR, ITEM(res_store.address), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
288    { "LanAddress", CFG_TYPE_STR, ITEM(res_store.lanaddress), 0, CFG_ITEM_DEFAULT, NULL, "16.2.6-",
289      "Sets additional address used for connections between Client and Storage Daemon inside separate network."},
290    { "SdAddress", CFG_TYPE_STR, ITEM(res_store.address), 0, CFG_ITEM_ALIAS, NULL, NULL, "Alias for Address." },
291    { "Port", CFG_TYPE_PINT32, ITEM(res_store.SDport), 0, CFG_ITEM_DEFAULT, SD_DEFAULT_PORT, NULL, NULL },
292    { "SdPort", CFG_TYPE_PINT32, ITEM(res_store.SDport), 0, CFG_ITEM_DEFAULT | CFG_ITEM_ALIAS, SD_DEFAULT_PORT, NULL, "Alias for Port." },
293    { "Username", CFG_TYPE_STR, ITEM(res_store.username), 0, 0, NULL, NULL, NULL },
294    { "Password", CFG_TYPE_AUTOPASSWORD, ITEM(res_store.password_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
295    { "SdPassword", CFG_TYPE_AUTOPASSWORD, ITEM(res_store.password_), 0, CFG_ITEM_ALIAS, NULL, NULL, "Alias for Password." },
296    { "Device", CFG_TYPE_DEVICE, ITEM(res_store.device), R_DEVICE, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
297    { "MediaType", CFG_TYPE_STRNAME, ITEM(res_store.media_type), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
298    { "AutoChanger", CFG_TYPE_BOOL, ITEM(res_store.autochanger), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
299    { "Enabled", CFG_TYPE_BOOL, ITEM(res_store.enabled), 0, CFG_ITEM_DEFAULT, "true", NULL,
300      "En- or disable this resource." },
301    { "AllowCompression", CFG_TYPE_BOOL, ITEM(res_store.AllowCompress), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
302    { "HeartbeatInterval", CFG_TYPE_TIME, ITEM(res_store.heartbeat_interval), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
303    { "CacheStatusInterval", CFG_TYPE_TIME, ITEM(res_store.cache_status_interval), 0, CFG_ITEM_DEFAULT, "30", NULL, NULL },
304    { "MaximumConcurrentJobs", CFG_TYPE_PINT32, ITEM(res_store.MaxConcurrentJobs), 0, CFG_ITEM_DEFAULT, "1", NULL, NULL },
305    { "MaximumConcurrentReadJobs", CFG_TYPE_PINT32, ITEM(res_store.MaxConcurrentReadJobs), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
306    { "SddPort", CFG_TYPE_PINT32, ITEM(res_store.SDDport), 0, CFG_ITEM_DEPRECATED, NULL, "-12.4.0", NULL },
307    { "PairedStorage", CFG_TYPE_RES, ITEM(res_store.paired_storage), R_STORAGE, 0, NULL, NULL, NULL },
308    { "MaximumBandwidthPerJob", CFG_TYPE_SPEED, ITEM(res_store.max_bandwidth), 0, 0, NULL, NULL, NULL },
309    { "CollectStatistics", CFG_TYPE_BOOL, ITEM(res_store.collectstats), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
310    { "NdmpChangerDevice", CFG_TYPE_STRNAME, ITEM(res_store.ndmp_changer_device), 0, 0, NULL, "16.2.4-",
311      "Allows direct control of a Storage Daemon Auto Changer device by the Director. Only used in NDMP_NATIVE environments." },
312    // TapeDevice has never been used in production and intended for NDMP_NATIVE only.
313    // Instead of TapeDevice, the required Devices should be configured using the Device directive.
314    //{ "TapeDevice", CFG_TYPE_ALIST_STR, ITEM(res_store.tape_devices), 0, CFG_ITEM_ALIAS | CFG_ITEM_DEPRECATED, NULL, "16.2.4-17.2.2",
315    //  "Allows direct control of Storage Daemon Tape devices by the Director. Only used in NDMP_NATIVE environments." },
316    TLS_COMMON_CONFIG(res_store),
317    TLS_CERT_CONFIG(res_store),
318    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
319 };
320 
321 /**
322  * Catalog Resource Directives
323  *
324  * name handler value code flags default_value
325  */
326 static ResourceItem cat_items[] = {
327    { "Name", CFG_TYPE_NAME, ITEM(res_cat.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
328      "The name of the resource." },
329    { "Description", CFG_TYPE_STR, ITEM(res_cat.hdr.desc), 0, 0, NULL, NULL, NULL },
330    { "Address", CFG_TYPE_STR, ITEM(res_cat.db_address), 0, CFG_ITEM_ALIAS, NULL, NULL, NULL },
331    { "DbAddress", CFG_TYPE_STR, ITEM(res_cat.db_address), 0, 0, NULL, NULL, NULL },
332    { "DbPort", CFG_TYPE_PINT32, ITEM(res_cat.db_port), 0, 0, NULL, NULL, NULL },
333    { "Password", CFG_TYPE_AUTOPASSWORD, ITEM(res_cat.db_password), 0, CFG_ITEM_ALIAS, NULL, NULL, NULL },
334    { "DbPassword", CFG_TYPE_AUTOPASSWORD, ITEM(res_cat.db_password), 0, 0, NULL, NULL, NULL },
335    { "DbUser", CFG_TYPE_STR, ITEM(res_cat.db_user), 0, 0, NULL, NULL, NULL },
336    { "User", CFG_TYPE_STR, ITEM(res_cat.db_user), 0, CFG_ITEM_ALIAS, NULL, NULL, NULL },
337    { "DbName", CFG_TYPE_STR, ITEM(res_cat.db_name), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
338 #ifdef HAVE_DYNAMIC_CATS_BACKENDS
339    { "DbDriver", CFG_TYPE_STR, ITEM(res_cat.db_driver), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
340 #else
341    { "DbDriver", CFG_TYPE_STR, ITEM(res_cat.db_driver), 0, 0, NULL, NULL, NULL },
342 #endif
343    { "DbSocket", CFG_TYPE_STR, ITEM(res_cat.db_socket), 0, 0, NULL, NULL, NULL },
344    /* Turned off for the moment */
345    { "MultipleConnections", CFG_TYPE_BIT, ITEM(res_cat.mult_db_connections), 0, 0, NULL, NULL, NULL },
346    { "DisableBatchInsert", CFG_TYPE_BOOL, ITEM(res_cat.disable_batch_insert), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
347    { "Reconnect", CFG_TYPE_BOOL, ITEM(res_cat.try_reconnect), 0, CFG_ITEM_DEFAULT, "false",
348      "15.1.0-", "Try to reconnect a database connection when its dropped" },
349    { "ExitOnFatal", CFG_TYPE_BOOL, ITEM(res_cat.exit_on_fatal), 0, CFG_ITEM_DEFAULT, "false",
350      "15.1.0-", "Make any fatal error in the connection to the database exit the program" },
351    { "MinConnections", CFG_TYPE_PINT32, ITEM(res_cat.pooling_min_connections), 0, CFG_ITEM_DEFAULT, "1", NULL,
352      "This directive is used by the experimental database pooling functionality. Only use this for non production sites. This sets the minimum number of connections to a database to keep in this database pool." },
353    { "MaxConnections", CFG_TYPE_PINT32, ITEM(res_cat.pooling_max_connections), 0, CFG_ITEM_DEFAULT, "5", NULL,
354      "This directive is used by the experimental database pooling functionality. Only use this for non production sites. This sets the maximum number of connections to a database to keep in this database pool." },
355   { "IncConnections", CFG_TYPE_PINT32, ITEM(res_cat.pooling_increment_connections), 0, CFG_ITEM_DEFAULT, "1", NULL,
356     "This directive is used by the experimental database pooling functionality. Only use this for non production sites. This sets the number of connections to add to a database pool when not enough connections are available on the pool anymore." },
357    { "IdleTimeout", CFG_TYPE_PINT32, ITEM(res_cat.pooling_idle_timeout), 0, CFG_ITEM_DEFAULT, "30", NULL,
358      "This directive is used by the experimental database pooling functionality. Only use this for non production sites.  This sets the idle time after which a database pool should be shrinked." },
359    { "ValidateTimeout", CFG_TYPE_PINT32, ITEM(res_cat.pooling_validate_timeout), 0, CFG_ITEM_DEFAULT, "120", NULL,
360      "This directive is used by the experimental database pooling functionality. Only use this for non production sites. This sets the validation timeout after which the database connection is polled to see if its still alive." },
361    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
362 };
363 
364 /**
365  * Job Resource Directives
366  *
367  * name handler value code flags default_value
368  */
369 ResourceItem job_items[] = {
370    { "Name", CFG_TYPE_NAME, ITEM(res_job.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
371      "The name of the resource." },
372    { "Description", CFG_TYPE_STR, ITEM(res_job.hdr.desc), 0, 0, NULL, NULL, NULL },
373    { "Type", CFG_TYPE_JOBTYPE, ITEM(res_job.JobType), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
374    { "Protocol", CFG_TYPE_PROTOCOLTYPE, ITEM(res_job.Protocol), 0, CFG_ITEM_DEFAULT, "Native", NULL, NULL },
375    { "BackupFormat", CFG_TYPE_STR, ITEM(res_job.backup_format), 0, CFG_ITEM_DEFAULT, "Native", NULL, NULL },
376    { "Level", CFG_TYPE_LEVEL, ITEM(res_job.JobLevel), 0, 0, NULL, NULL, NULL },
377    { "Messages", CFG_TYPE_RES, ITEM(res_job.messages), R_MSGS, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
378    { "Storage", CFG_TYPE_ALIST_RES, ITEM(res_job.storage), R_STORAGE, 0, NULL, NULL, NULL },
379    { "Pool", CFG_TYPE_RES, ITEM(res_job.pool), R_POOL, CFG_ITEM_REQUIRED, NULL, NULL, NULL },
380    { "FullBackupPool", CFG_TYPE_RES, ITEM(res_job.full_pool), R_POOL, 0, NULL, NULL, NULL },
381    { "VirtualFullBackupPool", CFG_TYPE_RES, ITEM(res_job.vfull_pool), R_POOL, 0, NULL, NULL, NULL },
382    { "IncrementalBackupPool", CFG_TYPE_RES, ITEM(res_job.inc_pool), R_POOL, 0, NULL, NULL, NULL },
383    { "DifferentialBackupPool", CFG_TYPE_RES, ITEM(res_job.diff_pool), R_POOL, 0, NULL, NULL, NULL },
384    { "NextPool", CFG_TYPE_RES, ITEM(res_job.next_pool), R_POOL, 0, NULL, NULL, NULL },
385    { "Client", CFG_TYPE_RES, ITEM(res_job.client), R_CLIENT, 0, NULL, NULL, NULL },
386    { "FileSet", CFG_TYPE_RES, ITEM(res_job.fileset), R_FILESET, 0, NULL, NULL, NULL },
387    { "Schedule", CFG_TYPE_RES, ITEM(res_job.schedule), R_SCHEDULE, 0, NULL, NULL, NULL },
388    { "VerifyJob", CFG_TYPE_RES, ITEM(res_job.verify_job), R_JOB, CFG_ITEM_ALIAS, NULL, NULL, NULL },
389    { "JobToVerify", CFG_TYPE_RES, ITEM(res_job.verify_job), R_JOB, 0, NULL, NULL, NULL },
390    { "Catalog", CFG_TYPE_RES, ITEM(res_job.catalog), R_CATALOG, 0, NULL, "13.4.0-", NULL },
391    { "JobDefs", CFG_TYPE_RES, ITEM(res_job.jobdefs), R_JOBDEFS, 0, NULL, NULL, NULL },
392    { "Run", CFG_TYPE_ALIST_STR, ITEM(res_job.run_cmds), 0, 0, NULL, NULL, NULL },
393    /* Root of where to restore files */
394    { "Where", CFG_TYPE_DIR, ITEM(res_job.RestoreWhere), 0, 0, NULL, NULL, NULL },
395    { "RegexWhere", CFG_TYPE_STR, ITEM(res_job.RegexWhere), 0, 0, NULL, NULL, NULL },
396    { "StripPrefix", CFG_TYPE_STR, ITEM(res_job.strip_prefix), 0, 0, NULL, NULL, NULL },
397    { "AddPrefix", CFG_TYPE_STR, ITEM(res_job.add_prefix), 0, 0, NULL, NULL, NULL },
398    { "AddSuffix", CFG_TYPE_STR, ITEM(res_job.add_suffix), 0, 0, NULL, NULL, NULL },
399    /* Where to find bootstrap during restore */
400    { "Bootstrap", CFG_TYPE_DIR, ITEM(res_job.RestoreBootstrap), 0, 0, NULL, NULL, NULL },
401    /* Where to write bootstrap file during backup */
402    { "WriteBootstrap", CFG_TYPE_DIR, ITEM(res_job.WriteBootstrap), 0, 0, NULL, NULL, NULL },
403    { "WriteVerifyList", CFG_TYPE_DIR, ITEM(res_job.WriteVerifyList), 0, 0, NULL, NULL, NULL },
404    { "Replace", CFG_TYPE_REPLACE, ITEM(res_job.replace), 0, CFG_ITEM_DEFAULT, "Always", NULL, NULL },
405    { "MaximumBandwidth", CFG_TYPE_SPEED, ITEM(res_job.max_bandwidth), 0, 0, NULL, NULL, NULL },
406    { "MaxRunSchedTime", CFG_TYPE_TIME, ITEM(res_job.MaxRunSchedTime), 0, 0, NULL, NULL, NULL },
407    { "MaxRunTime", CFG_TYPE_TIME, ITEM(res_job.MaxRunTime), 0, 0, NULL, NULL, NULL },
408    { "FullMaxWaitTime", CFG_TYPE_TIME, ITEM(res_job.FullMaxRunTime), 0, CFG_ITEM_DEPRECATED, NULL, "-12.4.0", NULL },
409    { "IncrementalMaxWaitTime", CFG_TYPE_TIME, ITEM(res_job.IncMaxRunTime), 0, CFG_ITEM_DEPRECATED, NULL, "-12.4.0", NULL },
410    { "DifferentialMaxWaitTime", CFG_TYPE_TIME, ITEM(res_job.DiffMaxRunTime), 0, CFG_ITEM_DEPRECATED, NULL, "-12.4.0", NULL },
411    { "FullMaxRuntime", CFG_TYPE_TIME, ITEM(res_job.FullMaxRunTime), 0, 0, NULL, NULL, NULL },
412    { "IncrementalMaxRuntime", CFG_TYPE_TIME, ITEM(res_job.IncMaxRunTime), 0, 0, NULL, NULL, NULL },
413    { "DifferentialMaxRuntime", CFG_TYPE_TIME, ITEM(res_job.DiffMaxRunTime), 0, 0, NULL, NULL, NULL },
414    { "MaxWaitTime", CFG_TYPE_TIME, ITEM(res_job.MaxWaitTime), 0, 0, NULL, NULL, NULL },
415    { "MaxStartDelay", CFG_TYPE_TIME, ITEM(res_job.MaxStartDelay), 0, 0, NULL, NULL, NULL },
416    { "MaxFullInterval", CFG_TYPE_TIME, ITEM(res_job.MaxFullInterval), 0, 0, NULL, NULL, NULL },
417    { "MaxVirtualFullInterval", CFG_TYPE_TIME, ITEM(res_job.MaxVFullInterval), 0, 0, NULL, "14.4.0-", NULL },
418    { "MaxDiffInterval", CFG_TYPE_TIME, ITEM(res_job.MaxDiffInterval), 0, 0, NULL, NULL, NULL },
419    { "PrefixLinks", CFG_TYPE_BOOL, ITEM(res_job.PrefixLinks), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
420    { "PruneJobs", CFG_TYPE_BOOL, ITEM(res_job.PruneJobs), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
421    { "PruneFiles", CFG_TYPE_BOOL, ITEM(res_job.PruneFiles), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
422    { "PruneVolumes", CFG_TYPE_BOOL, ITEM(res_job.PruneVolumes), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
423    { "PurgeMigrationJob", CFG_TYPE_BOOL, ITEM(res_job.PurgeMigrateJob), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
424    { "Enabled", CFG_TYPE_BOOL, ITEM(res_job.enabled), 0, CFG_ITEM_DEFAULT, "true", NULL,
425      "En- or disable this resource." },
426    { "SpoolAttributes", CFG_TYPE_BOOL, ITEM(res_job.SpoolAttributes), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
427    { "SpoolData", CFG_TYPE_BOOL, ITEM(res_job.spool_data), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
428    { "SpoolSize", CFG_TYPE_SIZE64, ITEM(res_job.spool_size), 0, 0, NULL, NULL, NULL },
429    { "RerunFailedLevels", CFG_TYPE_BOOL, ITEM(res_job.rerun_failed_levels), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
430    { "PreferMountedVolumes", CFG_TYPE_BOOL, ITEM(res_job.PreferMountedVolumes), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
431    { "RunBeforeJob", CFG_TYPE_SHRTRUNSCRIPT, ITEM(res_job.RunScripts), 0, 0, NULL, NULL, NULL },
432    { "RunAfterJob", CFG_TYPE_SHRTRUNSCRIPT, ITEM(res_job.RunScripts), 0, 0, NULL, NULL, NULL },
433    { "RunAfterFailedJob", CFG_TYPE_SHRTRUNSCRIPT, ITEM(res_job.RunScripts), 0, 0, NULL, NULL, NULL },
434    { "ClientRunBeforeJob", CFG_TYPE_SHRTRUNSCRIPT, ITEM(res_job.RunScripts), 0, 0, NULL, NULL, NULL },
435    { "ClientRunAfterJob", CFG_TYPE_SHRTRUNSCRIPT, ITEM(res_job.RunScripts), 0, 0, NULL, NULL, NULL },
436    { "MaximumConcurrentJobs", CFG_TYPE_PINT32, ITEM(res_job.MaxConcurrentJobs), 0, CFG_ITEM_DEFAULT, "1", NULL, NULL },
437    { "RescheduleOnError", CFG_TYPE_BOOL, ITEM(res_job.RescheduleOnError), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
438    { "RescheduleInterval", CFG_TYPE_TIME, ITEM(res_job.RescheduleInterval), 0, CFG_ITEM_DEFAULT, "1800" /* 30 minutes */, NULL, NULL },
439    { "RescheduleTimes", CFG_TYPE_PINT32, ITEM(res_job.RescheduleTimes), 0, CFG_ITEM_DEFAULT, "5", NULL, NULL },
440    { "Priority", CFG_TYPE_PINT32, ITEM(res_job.Priority), 0, CFG_ITEM_DEFAULT, "10", NULL, NULL },
441    { "AllowMixedPriority", CFG_TYPE_BOOL, ITEM(res_job.allow_mixed_priority), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
442    { "WritePartAfterJob", CFG_TYPE_BOOL, ITEM(res_job.write_part_after_job), 0, CFG_ITEM_DEPRECATED, NULL, "-12.4.0", NULL },
443    { "SelectionPattern", CFG_TYPE_STR, ITEM(res_job.selection_pattern), 0, 0, NULL, NULL, NULL },
444    { "RunScript", CFG_TYPE_RUNSCRIPT, ITEM(res_job.RunScripts), 0, CFG_ITEM_NO_EQUALS, NULL, NULL, NULL },
445    { "SelectionType", CFG_TYPE_MIGTYPE, ITEM(res_job.selection_type), 0, 0, NULL, NULL, NULL },
446    { "Accurate", CFG_TYPE_BOOL, ITEM(res_job.accurate), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
447    { "AllowDuplicateJobs", CFG_TYPE_BOOL, ITEM(res_job.AllowDuplicateJobs), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
448    { "AllowHigherDuplicates", CFG_TYPE_BOOL, ITEM(res_job.AllowHigherDuplicates), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
449    { "CancelLowerLevelDuplicates", CFG_TYPE_BOOL, ITEM(res_job.CancelLowerLevelDuplicates), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
450    { "CancelQueuedDuplicates", CFG_TYPE_BOOL, ITEM(res_job.CancelQueuedDuplicates), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
451    { "CancelRunningDuplicates", CFG_TYPE_BOOL, ITEM(res_job.CancelRunningDuplicates), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
452    { "SaveFileHistory", CFG_TYPE_BOOL, ITEM(res_job.SaveFileHist), 0, CFG_ITEM_DEFAULT, "true", "14.2.0-", NULL },
453    { "FileHistorySize", CFG_TYPE_SIZE64, ITEM(res_job.FileHistSize), 0, CFG_ITEM_DEFAULT, "10000000", "15.2.4-", NULL },
454    { "PluginOptions", CFG_TYPE_ALIST_STR, ITEM(res_job.FdPluginOptions), 0, CFG_ITEM_DEPRECATED | CFG_ITEM_ALIAS, NULL, "-12.4.0", NULL },
455    { "FdPluginOptions", CFG_TYPE_ALIST_STR, ITEM(res_job.FdPluginOptions), 0, 0, NULL, NULL, NULL },
456    { "SdPluginOptions", CFG_TYPE_ALIST_STR, ITEM(res_job.SdPluginOptions), 0, 0, NULL, NULL, NULL },
457    { "DirPluginOptions", CFG_TYPE_ALIST_STR, ITEM(res_job.DirPluginOptions), 0, 0, NULL, NULL, NULL },
458    { "Base", CFG_TYPE_ALIST_RES, ITEM(res_job.base), R_JOB, 0, NULL, NULL, NULL },
459    { "MaxConcurrentCopies", CFG_TYPE_PINT32, ITEM(res_job.MaxConcurrentCopies), 0, CFG_ITEM_DEFAULT, "100", NULL, NULL },
460    /* Settings for always incremental */
461    { "AlwaysIncremental", CFG_TYPE_BOOL, ITEM(res_job.AlwaysIncremental), 0, CFG_ITEM_DEFAULT, "false", "16.2.4-",
462      "Enable/disable always incremental backup scheme." },
463    { "AlwaysIncrementalJobRetention", CFG_TYPE_TIME, ITEM(res_job.AlwaysIncrementalJobRetention), 0, CFG_ITEM_DEFAULT, "0", "16.2.4-",
464      "Backup Jobs older than the specified time duration will be merged into a new Virtual backup." },
465    { "AlwaysIncrementalKeepNumber", CFG_TYPE_PINT32, ITEM(res_job.AlwaysIncrementalKeepNumber), 0, CFG_ITEM_DEFAULT, "0", "16.2.4-",
466      "Guarantee that at least the specified number of Backup Jobs will persist, even if they are older than \"Always Incremental Job Retention\"."},
467    { "AlwaysIncrementalMaxFullAge", CFG_TYPE_TIME, ITEM(res_job.AlwaysIncrementalMaxFullAge), 0, 0, NULL, "16.2.4-",
468      "If \"AlwaysIncrementalMaxFullAge\" is set, during consolidations only incremental backups will be considered while the Full Backup remains to reduce the amount of data being consolidated. Only if the Full Backup is older than \"AlwaysIncrementalMaxFullAge\", the Full Backup will be part of the consolidation to avoid the Full Backup becoming too old ." },
469    { "MaxFullConsolidations", CFG_TYPE_PINT32, ITEM(res_job.MaxFullConsolidations), 0, CFG_ITEM_DEFAULT, "0", "16.2.4-",
470      "If \"AlwaysIncrementalMaxFullAge\" is configured, do not run more than \"MaxFullConsolidations\" consolidation jobs that include the Full backup."},
471    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
472 };
473 
474 /**
475  * FileSet resource
476  *
477  * name handler value code flags default_value
478  */
479 static ResourceItem fs_items[] = {
480    { "Name", CFG_TYPE_NAME, ITEM(res_fs.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
481      "The name of the resource." },
482    { "Description", CFG_TYPE_STR, ITEM(res_fs.hdr.desc), 0, 0, NULL, NULL, NULL },
483    { "Include", CFG_TYPE_INCEXC, { 0 }, 0, CFG_ITEM_NO_EQUALS, NULL, NULL, NULL },
484    { "Exclude", CFG_TYPE_INCEXC, { 0 }, 1, CFG_ITEM_NO_EQUALS, NULL, NULL, NULL },
485    { "IgnoreFileSetChanges", CFG_TYPE_BOOL, ITEM(res_fs.ignore_fs_changes), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
486    { "EnableVSS", CFG_TYPE_BOOL, ITEM(res_fs.enable_vss), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
487    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
488 };
489 
490 /**
491  * Schedule -- see run_conf.c
492  *
493  * name handler value code flags default_value
494  */
495 static ResourceItem sch_items[] = {
496    { "Name", CFG_TYPE_NAME, ITEM(res_sch.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
497      "The name of the resource." },
498    { "Description", CFG_TYPE_STR, ITEM(res_sch.hdr.desc), 0, 0, NULL, NULL, NULL },
499    { "Run", CFG_TYPE_RUN, ITEM(res_sch.run), 0, 0, NULL, NULL, NULL },
500    { "Enabled", CFG_TYPE_BOOL, ITEM(res_sch.enabled), 0, CFG_ITEM_DEFAULT, "true", NULL,
501      "En- or disable this resource." },
502    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
503 };
504 
505 /**
506  * Pool resource
507  *
508  * name handler value code flags default_value
509  */
510 static ResourceItem pool_items[] = {
511    { "Name", CFG_TYPE_NAME, ITEM(res_pool.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
512      "The name of the resource." },
513    { "Description", CFG_TYPE_STR, ITEM(res_pool.hdr.desc), 0, 0, NULL, NULL, NULL },
514    { "PoolType", CFG_TYPE_POOLTYPE, ITEM(res_pool.pool_type), 0, CFG_ITEM_DEFAULT, "Backup", NULL, NULL },
515    { "LabelFormat", CFG_TYPE_STRNAME, ITEM(res_pool.label_format), 0, 0, NULL, NULL, NULL },
516    { "LabelType", CFG_TYPE_LABEL, ITEM(res_pool.LabelType), 0, 0, NULL, NULL, NULL },
517    { "CleaningPrefix", CFG_TYPE_STRNAME, ITEM(res_pool.cleaning_prefix), 0, CFG_ITEM_DEFAULT, "CLN", NULL, NULL },
518    { "UseCatalog", CFG_TYPE_BOOL, ITEM(res_pool.use_catalog), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
519    { "UseVolumeOnce", CFG_TYPE_BOOL, ITEM(res_pool.use_volume_once), 0, CFG_ITEM_DEPRECATED, NULL, "-12.4.0", NULL },
520    { "PurgeOldestVolume", CFG_TYPE_BOOL, ITEM(res_pool.purge_oldest_volume), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
521    { "ActionOnPurge", CFG_TYPE_ACTIONONPURGE, ITEM(res_pool.action_on_purge), 0, 0, NULL, NULL, NULL },
522    { "RecycleOldestVolume", CFG_TYPE_BOOL, ITEM(res_pool.recycle_oldest_volume), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
523    { "RecycleCurrentVolume", CFG_TYPE_BOOL, ITEM(res_pool.recycle_current_volume), 0, CFG_ITEM_DEFAULT, "false", NULL, NULL },
524    { "MaximumVolumes", CFG_TYPE_PINT32, ITEM(res_pool.max_volumes), 0, 0, NULL, NULL, NULL },
525    { "MaximumVolumeJobs", CFG_TYPE_PINT32, ITEM(res_pool.MaxVolJobs), 0, 0, NULL, NULL, NULL },
526    { "MaximumVolumeFiles", CFG_TYPE_PINT32, ITEM(res_pool.MaxVolFiles), 0, 0, NULL, NULL, NULL },
527    { "MaximumVolumeBytes", CFG_TYPE_SIZE64, ITEM(res_pool.MaxVolBytes), 0, 0, NULL, NULL, NULL },
528    { "CatalogFiles", CFG_TYPE_BOOL, ITEM(res_pool.catalog_files), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
529    { "VolumeRetention", CFG_TYPE_TIME, ITEM(res_pool.VolRetention), 0, CFG_ITEM_DEFAULT, "31536000" /* 365 days */, NULL, NULL },
530    { "VolumeUseDuration", CFG_TYPE_TIME, ITEM(res_pool.VolUseDuration), 0, 0, NULL, NULL, NULL },
531    { "MigrationTime", CFG_TYPE_TIME, ITEM(res_pool.MigrationTime), 0, 0, NULL, NULL, NULL },
532    { "MigrationHighBytes", CFG_TYPE_SIZE64, ITEM(res_pool.MigrationHighBytes), 0, 0, NULL, NULL, NULL },
533    { "MigrationLowBytes", CFG_TYPE_SIZE64, ITEM(res_pool.MigrationLowBytes), 0, 0, NULL, NULL, NULL },
534    { "NextPool", CFG_TYPE_RES, ITEM(res_pool.NextPool), R_POOL, 0, NULL, NULL, NULL },
535    { "Storage", CFG_TYPE_ALIST_RES, ITEM(res_pool.storage), R_STORAGE, 0, NULL, NULL, NULL },
536    { "AutoPrune", CFG_TYPE_BOOL, ITEM(res_pool.AutoPrune), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
537    { "Recycle", CFG_TYPE_BOOL, ITEM(res_pool.Recycle), 0, CFG_ITEM_DEFAULT, "true", NULL, NULL },
538    { "RecyclePool", CFG_TYPE_RES, ITEM(res_pool.RecyclePool), R_POOL, 0, NULL, NULL, NULL },
539    { "ScratchPool", CFG_TYPE_RES, ITEM(res_pool.ScratchPool), R_POOL, 0, NULL, NULL, NULL },
540    { "Catalog", CFG_TYPE_RES, ITEM(res_pool.catalog), R_CATALOG, 0, NULL, NULL, NULL },
541    { "FileRetention", CFG_TYPE_TIME, ITEM(res_pool.FileRetention), 0, 0, NULL, NULL, NULL },
542    { "JobRetention", CFG_TYPE_TIME, ITEM(res_pool.JobRetention), 0, 0, NULL, NULL, NULL },
543    { "MinimumBlockSize", CFG_TYPE_SIZE32, ITEM(res_pool.MinBlocksize), 0, 0, NULL, NULL, NULL },
544    { "MaximumBlockSize", CFG_TYPE_SIZE32, ITEM(res_pool.MaxBlocksize), 0, 0, NULL, "14.2.0-", NULL },
545    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
546 };
547 
548 /**
549  * Counter Resource
550  *
551  * name handler value code flags default_value
552  */
553 static ResourceItem counter_items[] = {
554    { "Name", CFG_TYPE_NAME, ITEM(res_counter.hdr.name), 0, CFG_ITEM_REQUIRED, NULL, NULL,
555      "The name of the resource." },
556    { "Description", CFG_TYPE_STR, ITEM(res_counter.hdr.desc), 0, 0, NULL, NULL, NULL },
557    { "Minimum", CFG_TYPE_INT32, ITEM(res_counter.MinValue), 0, CFG_ITEM_DEFAULT, "0", NULL, NULL },
558    { "Maximum", CFG_TYPE_PINT32, ITEM(res_counter.MaxValue), 0, CFG_ITEM_DEFAULT, "2147483647" /* INT32_MAX */, NULL, NULL },
559    { "WrapCounter", CFG_TYPE_RES, ITEM(res_counter.WrapCounter), R_COUNTER, 0, NULL, NULL, NULL },
560    { "Catalog", CFG_TYPE_RES, ITEM(res_counter.Catalog), R_CATALOG, 0, NULL, NULL, NULL },
561    { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
562 };
563 
564 /**
565  * Message resource
566  */
567 #include "lib/msg_res.h"
568 #include "lib/json.h"
569 
570 /**
571  * This is the master resource definition.
572  * It must have one item for each of the resources.
573  *
574  *  NOTE!!! keep it in the same order as the R_codes
575  *    or eliminate all resources[rindex].name
576  *
577  * name handler value code flags default_value
578  */
579 static ResourceTable resources[] = {
__anon487dd5fe0102()580    { "Director", dir_items, R_DIRECTOR, sizeof(DirectorResource), [] (void *res){ return new((DirectorResource *) res) DirectorResource(); } },
__anon487dd5fe0202()581    { "Client", cli_items, R_CLIENT, sizeof(ClientResource), [] (void *res){ return new((ClientResource*) res) ClientResource(); }  },
582    { "JobDefs", job_items, R_JOBDEFS, sizeof(JobResource)},
583    { "Job", job_items, R_JOB, sizeof(JobResource)  },
__anon487dd5fe0302()584    { "Storage", store_items, R_STORAGE, sizeof(StorageResource), [] (void *res){ return new((StorageResource *) res) StorageResource(); }   },
585    { "Catalog", cat_items, R_CATALOG, sizeof(CatalogResource)   },
586    { "Schedule", sch_items, R_SCHEDULE, sizeof(ScheduleResource)   },
587    { "FileSet", fs_items, R_FILESET, sizeof(FilesetResource)   },
588    { "Pool", pool_items, R_POOL, sizeof(PoolResource)   },
589    { "Messages", msgs_items, R_MSGS, sizeof(MessagesResource)   },
590    { "Counter", counter_items, R_COUNTER, sizeof(CounterResource)   },
591    { "Profile", profile_items, R_PROFILE, sizeof(ProfileResource)   },
__anon487dd5fe0402()592    { "Console", con_items, R_CONSOLE, sizeof(ConsoleResource), [] (void *res){ return new((ConsoleResource *) res) ConsoleResource(); }   },
__anon487dd5fe0502()593    { "User", con_items, R_CONSOLE, sizeof(ConsoleResource), [] (void *res){ return new((ConsoleResource *) res) ConsoleResource(); }   },
594    { "Device", NULL, R_DEVICE, sizeof(DeviceResource)   }, /* info obtained from SD */
595    { NULL, NULL, 0, 0, nullptr }
596 };
597 
598 /**
599  * Note, when this resource is used, we are inside a Job
600  * resource. We treat the RunScript like a sort of
601  * mini-resource within the Job resource. As such we
602  * don't use the UnionOfResources union because that contains
603  * a Job resource (and it would corrupt the Job resource)
604  * but use a global separate resource for holding the
605  * runscript data.
606  */
607 static RunScript res_runscript;
608 
609 /**
610  * new RunScript items
611  * name handler value code flags default_value
612  */
613 static ResourceItem runscript_items[] = {
614  { "Command", CFG_TYPE_RUNSCRIPT_CMD, { (char **)&res_runscript }, SHELL_CMD, 0, NULL, NULL, NULL },
615  { "Console", CFG_TYPE_RUNSCRIPT_CMD, { (char **)&res_runscript }, CONSOLE_CMD, 0, NULL, NULL, NULL },
616  { "Target", CFG_TYPE_RUNSCRIPT_TARGET, { (char **)&res_runscript }, 0, 0, NULL, NULL, NULL },
617  { "RunsOnSuccess", CFG_TYPE_RUNSCRIPT_BOOL, { (char **)&res_runscript.on_success }, 0, 0, NULL, NULL, NULL },
618  { "RunsOnFailure", CFG_TYPE_RUNSCRIPT_BOOL, { (char **)&res_runscript.on_failure }, 0, 0, NULL, NULL, NULL },
619  { "FailJobOnError", CFG_TYPE_RUNSCRIPT_BOOL, { (char **)&res_runscript.fail_on_error }, 0, 0, NULL, NULL, NULL },
620  { "AbortJobOnError", CFG_TYPE_RUNSCRIPT_BOOL, { (char **)&res_runscript.fail_on_error }, 0, 0, NULL, NULL, NULL },
621  { "RunsWhen", CFG_TYPE_RUNSCRIPT_WHEN, { (char **)&res_runscript.when }, 0, 0, NULL, NULL, NULL },
622  { "RunsOnClient", CFG_TYPE_RUNSCRIPT_TARGET, { (char **)&res_runscript }, 0, 0, NULL, NULL, NULL }, /* TODO */
623  { NULL, 0, { 0 }, 0, 0, NULL, NULL, NULL }
624 };
625 
626 /**
627  * The following arrays are referenced from else where and
628  * used for display to the user so the keyword are pretty
629  * printed with additional capitals. As the code uses
630  * strcasecmp anyhow this doesn't matter.
631  */
632 
633 /**
634  * Keywords (RHS) permitted in Job Level records
635  *
636  * name level job_type
637  */
638 struct s_jl joblevels[] = {
639    { "Full", L_FULL, JT_BACKUP },
640    { "Base", L_BASE, JT_BACKUP },
641    { "Incremental", L_INCREMENTAL, JT_BACKUP },
642    { "Differential", L_DIFFERENTIAL, JT_BACKUP },
643    { "Since", L_SINCE, JT_BACKUP },
644    { "VirtualFull", L_VIRTUAL_FULL, JT_BACKUP },
645    { "Catalog", L_VERIFY_CATALOG, JT_VERIFY },
646    { "InitCatalog", L_VERIFY_INIT, JT_VERIFY },
647    { "VolumeToCatalog", L_VERIFY_VOLUME_TO_CATALOG, JT_VERIFY },
648    { "DiskToCatalog", L_VERIFY_DISK_TO_CATALOG, JT_VERIFY },
649    { "Data", L_VERIFY_DATA, JT_VERIFY },
650    { "Full", L_FULL, JT_COPY },
651    { "Incremental", L_INCREMENTAL, JT_COPY },
652    { "Differential", L_DIFFERENTIAL, JT_COPY },
653    { "Full", L_FULL, JT_MIGRATE },
654    { "Incremental", L_INCREMENTAL, JT_MIGRATE },
655    { "Differential", L_DIFFERENTIAL, JT_MIGRATE },
656    { " ", L_NONE, JT_ADMIN },
657    { " ", L_NONE, JT_ARCHIVE },
658    { " ", L_NONE, JT_RESTORE },
659    { " ", L_NONE, JT_CONSOLIDATE },
660    { NULL, 0, 0 }
661 };
662 
663 /** Keywords (RHS) permitted in Job type records
664  *
665  * type_name job_type
666  */
667 struct s_jt jobtypes[] = {
668    { "Backup", JT_BACKUP },
669    { "Admin", JT_ADMIN },
670    { "Archive", JT_ARCHIVE },
671    { "Verify", JT_VERIFY },
672    { "Restore", JT_RESTORE },
673    { "Migrate",JT_MIGRATE },
674    { "Copy", JT_COPY },
675    { "Consolidate", JT_CONSOLIDATE },
676    { NULL, 0 }
677 };
678 
679 /** Keywords (RHS) permitted in Protocol type records
680  *
681  * name token
682  */
683 static struct s_kw backupprotocols[] = {
684    { "Native", PT_NATIVE },
685    { "NDMP_BAREOS", PT_NDMP_BAREOS}, /* alias for PT_NDMP */
686    { "NDMP", PT_NDMP_BAREOS},
687    { "NDMP_NATIVE", PT_NDMP_NATIVE },
688    { NULL, 0 }
689 };
690 
691 /** Keywords (RHS) permitted in AuthProtocol type records
692  *
693  * name token
694  */
695 static struct s_kw authprotocols[] = {
696    { "Native", APT_NATIVE },
697    { "NDMPV2", APT_NDMPV2 },
698    { "NDMPV3", APT_NDMPV3 },
699    { "NDMPV4", APT_NDMPV4 },
700    { NULL, 0 }
701 };
702 
703 /**
704  * Keywords (RHS) permitted in Authentication type records
705  *
706  * name token
707  */
708 static struct s_kw authmethods[] = {
709    { "None", AT_NONE },
710    { "Clear", AT_CLEAR },
711    { "MD5", AT_MD5 },
712    { NULL, 0 }
713 };
714 
715 /**
716  * Keywords (RHS) permitted in Selection type records
717  *
718  * type_name job_type
719  */
720 static struct s_jt migtypes[] = {
721    { "SmallestVolume", MT_SMALLEST_VOL },
722    { "OldestVolume", MT_OLDEST_VOL },
723    { "PoolOccupancy", MT_POOL_OCCUPANCY },
724    { "PoolTime", MT_POOL_TIME },
725    { "PoolUncopiedJobs", MT_POOL_UNCOPIED_JOBS },
726    { "Client", MT_CLIENT },
727    { "Volume", MT_VOLUME },
728    { "Job", MT_JOB },
729    { "SqlQuery", MT_SQLQUERY },
730    { NULL, 0 }
731 };
732 
733 /**
734  * Keywords (RHS) permitted in Restore replace type records
735  *
736  * name token
737  */
738 struct s_kw ReplaceOptions[] = {
739    { "Always", REPLACE_ALWAYS },
740    { "IfNewer", REPLACE_IFNEWER },
741    { "IfOlder", REPLACE_IFOLDER },
742    { "Never", REPLACE_NEVER },
743    { NULL, 0 }
744 };
745 
746 /**
747  * Keywords (RHS) permitted in ActionOnPurge type records
748  *
749  * name token
750  */
751 struct s_kw ActionOnPurgeOptions[] = {
752    { "None", ON_PURGE_NONE },
753    { "Truncate", ON_PURGE_TRUNCATE },
754    { NULL, 0 }
755 };
756 
757 /**
758  * Keywords (RHS) permitted in Volume status type records
759  *
760  * token is set to zero for all items as this
761  * is not a mapping but a filter table.
762  *
763  * name token
764  */
765 struct s_kw VolumeStatus[] = {
766    { "Append", 0 },
767    { "Full", 0 },
768    { "Used", 0 },
769    { "Recycle", 0 },
770    { "Purged", 0 },
771    { "Cleaning", 0 },
772    { "Error", 0 },
773    { NULL, 0 }
774 };
775 
776 /**
777  * Keywords (RHS) permitted in Pool type records
778  *
779  * token is set to zero for all items as this
780  * is not a mapping but a filter table.
781  *
782  * name token
783  */
784 struct s_kw PoolTypes[] = {
785    { "Backup", 0 },
786    { "Copy", 0 },
787    { "Cloned", 0 },
788    { "Archive", 0 },
789    { "Migration", 0 },
790    { "Scratch", 0 },
791    { NULL, 0 }
792 };
793 
794 #ifdef HAVE_JANSSON
json_item(s_jl * item)795 json_t *json_item(s_jl *item)
796 {
797    json_t *json = json_object();
798 
799    json_object_set_new(json, "level", json_integer(item->level));
800    json_object_set_new(json, "type", json_integer(item->job_type));
801 
802    return json;
803 }
804 
json_item(s_jt * item)805 json_t *json_item(s_jt *item)
806 {
807    json_t *json = json_object();
808 
809    json_object_set_new(json, "type", json_integer(item->job_type));
810 
811    return json;
812 }
813 
json_datatype_header(const int type,const char * typeclass)814 json_t *json_datatype_header(const int type, const char *typeclass)
815 {
816    json_t *json = json_object();
817    const char *description = datatype_to_description(type);
818 
819    json_object_set_new(json, "number", json_integer(type));
820 
821    if (description) {
822       json_object_set_new(json, "description", json_string(description));
823    }
824 
825    if (typeclass) {
826       json_object_set_new(json, "class", json_string(typeclass));
827    }
828 
829    return json;
830 }
831 
json_datatype(const int type)832 json_t *json_datatype(const int type)
833 {
834    return json_datatype_header(type, NULL);
835 }
836 
json_datatype(const int type,s_kw items[])837 json_t *json_datatype(const int type, s_kw items[])
838 {
839    json_t *json = json_datatype_header(type, "keyword");
840    if (items) {
841       json_t *values = json_object();
842       for (int i = 0; items[i].name; i++) {
843          json_object_set_new(values, items[i].name, json_item(&items[i]));
844       }
845       json_object_set_new(json, "values", values);
846    }
847    return json;
848 }
849 
json_datatype(const int type,s_jl items[])850 json_t *json_datatype(const int type, s_jl items[])
851 {
852    // FIXME: level_name keyword is not unique
853    json_t *json = json_datatype_header(type, "keyword");
854    if (items) {
855       json_t *values = json_object();
856       for (int i = 0; items[i].level_name; i++) {
857          json_object_set_new(values, items[i].level_name, json_item(&items[i]));
858       }
859       json_object_set_new(json, "values", values);
860    }
861    return json;
862 }
863 
json_datatype(const int type,s_jt items[])864 json_t *json_datatype(const int type, s_jt items[])
865 {
866    json_t *json = json_datatype_header(type, "keyword");
867    if (items) {
868       json_t *values = json_object();
869       for (int i = 0; items[i].type_name; i++) {
870          json_object_set_new(values, items[i].type_name, json_item(&items[i]));
871       }
872       json_object_set_new(json, "values", values);
873    }
874    return json;
875 }
876 
json_datatype(const int type,ResourceItem items[])877 json_t *json_datatype(const int type, ResourceItem items[])
878 {
879    json_t *json = json_datatype_header(type, "sub");
880    if (items) {
881       json_t *values = json_object();
882       for (int i = 0; items[i].name; i++) {
883          json_object_set_new(values, items[i].name, json_item(&items[i]));
884       }
885       json_object_set_new(json, "values", values);
886    }
887    return json;
888 }
889 
890 /**
891  * Print configuration file schema in json format
892  */
PrintConfigSchemaJson(PoolMem & buffer)893 bool PrintConfigSchemaJson(PoolMem &buffer)
894 {
895    DatatypeName *datatype;
896    ResourceTable *resources = my_config->resources_;
897 
898    InitializeJson();
899 
900    json_t *json = json_object();
901    json_object_set_new(json, "format-version", json_integer(2));
902    json_object_set_new(json, "component", json_string("bareos-dir"));
903    json_object_set_new(json, "version", json_string(VERSION));
904 
905    /*
906     * Resources
907     */
908    json_t *resource = json_object();
909    json_object_set(json, "resource", resource);
910    json_t *bareos_dir = json_object();
911    json_object_set(resource, "bareos-dir", bareos_dir);
912 
913    for (int r = 0; resources[r].name; r++) {
914       ResourceTable resource = my_config->resources_[r];
915       json_object_set(bareos_dir, resource.name, json_items(resource.items));
916    }
917 
918    /*
919     * Datatypes
920     */
921    json_t *json_datatype_obj = json_object();
922    json_object_set(json, "datatype", json_datatype_obj);
923 
924    int d = 0;
925    while (get_datatype(d)->name != NULL) {
926       datatype = get_datatype(d);
927 
928       switch (datatype->number) {
929       case CFG_TYPE_RUNSCRIPT:
930          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_RUNSCRIPT, runscript_items));
931          break;
932       case CFG_TYPE_INCEXC:
933          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_incexc(CFG_TYPE_INCEXC));
934          break;
935       case CFG_TYPE_OPTIONS:
936          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_options(CFG_TYPE_OPTIONS));
937          break;
938       case CFG_TYPE_PROTOCOLTYPE:
939          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_PROTOCOLTYPE, backupprotocols));
940          break;
941       case CFG_TYPE_AUTHPROTOCOLTYPE:
942          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_AUTHPROTOCOLTYPE, authprotocols));
943          break;
944       case CFG_TYPE_AUTHTYPE:
945          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_AUTHTYPE, authmethods));
946          break;
947       case CFG_TYPE_LEVEL:
948          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_LEVEL, joblevels));
949          break;
950       case CFG_TYPE_JOBTYPE:
951          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_JOBTYPE, jobtypes));
952          break;
953       case CFG_TYPE_MIGTYPE:
954          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_MIGTYPE, migtypes));
955          break;
956       case CFG_TYPE_REPLACE:
957          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_REPLACE, ReplaceOptions));
958          break;
959       case CFG_TYPE_ACTIONONPURGE:
960          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_ACTIONONPURGE, ActionOnPurgeOptions));
961          break;
962       case CFG_TYPE_POOLTYPE:
963          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_POOLTYPE, PoolTypes));
964          break;
965       case CFG_TYPE_RUN:
966          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(CFG_TYPE_RUN, RunFields));
967          break;
968       default:
969          json_object_set(json_datatype_obj, datatype_to_str(datatype->number), json_datatype(datatype->number));
970          break;
971       }
972       d++;
973    }
974 
975    /*
976     * following datatypes are ignored:
977     * - VolumeStatus: only used in ua_dotcmds, not a datatype
978     * - FS_option_kw: from inc_conf. Replaced by CFG_TYPE_OPTIONS", options_items.
979     * - FS_options: are they needed?
980     */
981 
982    PmStrcat(buffer, json_dumps(json, JSON_INDENT(2)));
983    json_decref(json);
984 
985    return true;
986 }
987 #else
PrintConfigSchemaJson(PoolMem & buffer)988 bool PrintConfigSchemaJson(PoolMem &buffer)
989 {
990    PmStrcat(buffer, "{ \"success\": false, \"message\": \"not available\" }");
991 
992    return false;
993 }
994 #endif
995 
CmdlineItem(PoolMem * buffer,ResourceItem * item)996 static inline bool CmdlineItem(PoolMem *buffer, ResourceItem *item)
997 {
998    PoolMem temp;
999    PoolMem key;
1000    const char *nomod = "";
1001    const char *mod_start = nomod;
1002    const char *mod_end = nomod;
1003 
1004    if (item->flags & CFG_ITEM_ALIAS) {
1005       return false;
1006    }
1007 
1008    if (item->flags & CFG_ITEM_DEPRECATED) {
1009       return false;
1010    }
1011 
1012    if (item->flags & CFG_ITEM_NO_EQUALS) {
1013       /* TODO: currently not supported */
1014       return false;
1015    }
1016 
1017    if (!(item->flags & CFG_ITEM_REQUIRED)) {
1018       mod_start = "[";
1019       mod_end = "]";
1020    }
1021 
1022    /*
1023     * Tab completion only supports lower case keywords.
1024     */
1025    key.strcat(item->name);
1026    key.toLower();
1027 
1028    temp.bsprintf(" %s%s=<%s>%s", mod_start, key.c_str(), datatype_to_str(item->type), mod_end);
1029    PmStrcat(buffer, temp.c_str());
1030 
1031    return true;
1032 }
1033 
CmdlineItems(PoolMem * buffer,ResourceItem items[])1034 static inline bool CmdlineItems(PoolMem *buffer, ResourceItem items[])
1035 {
1036    if (!items) {
1037       return false;
1038    }
1039 
1040    for (int i = 0; items[i].name; i++) {
1041       CmdlineItem(buffer, &items[i]);
1042    }
1043 
1044    return true;
1045 }
1046 
1047 /**
1048  * Get the usage string for the console "configure" command.
1049  *
1050  * This will be all available resource directives.
1051  * They are formated in a way to be usable for command line completion.
1052  */
get_configure_usage_string()1053 const char *get_configure_usage_string()
1054 {
1055    PoolMem resourcename;
1056 
1057    if (!configure_usage_string) {
1058       configure_usage_string = new PoolMem(PM_BSOCK);
1059    }
1060 
1061    /*
1062     * Only fill the configure_usage_string once. The content is static.
1063     */
1064    if (configure_usage_string->strlen() == 0) {
1065       /*
1066        * subcommand: add
1067        */
1068       for (int r = 0; resources[r].name; r++) {
1069          /*
1070           * Only one Director is allowed.
1071           * If the resource have not items, there is no need to add it.
1072           */
1073          if ((resources[r].rcode != R_DIRECTOR) && (resources[r].items)) {
1074             configure_usage_string->strcat("add ");
1075             resourcename.strcpy(resources[r].name);
1076             resourcename.toLower();
1077             configure_usage_string->strcat(resourcename);
1078             CmdlineItems(configure_usage_string, resources[r].items);
1079             configure_usage_string->strcat(" |\n");
1080          }
1081       }
1082       /*
1083        * subcommand: export
1084        */
1085       configure_usage_string->strcat("export client=<client>");
1086    }
1087 
1088    return configure_usage_string->c_str();
1089 }
1090 
DestroyConfigureUsageString()1091 void DestroyConfigureUsageString()
1092 {
1093    if (configure_usage_string) {
1094       delete configure_usage_string;
1095       configure_usage_string = NULL;
1096    }
1097 }
1098 
1099 /**
1100  * Propagate the settings from source BareosResource to dest BareosResource using the RES_ITEMS array.
1101  */
PropagateResource(ResourceItem * items,BareosResource * source,BareosResource * dest)1102 static void PropagateResource(ResourceItem *items, BareosResource *source, BareosResource *dest)
1103 {
1104    uint32_t offset;
1105 
1106    for (int i = 0; items[i].name; i++) {
1107       if (!BitIsSet(i, dest->hdr.item_present) &&
1108            BitIsSet(i, source->hdr.item_present)) {
1109          offset = (char *)(items[i].value) - (char *)&res_all;
1110          switch (items[i].type) {
1111          case CFG_TYPE_STR:
1112          case CFG_TYPE_DIR: {
1113             char **def_svalue, **svalue;
1114 
1115             /*
1116              * Handle strings and directory strings
1117              */
1118             def_svalue = (char **)((char *)(source) + offset);
1119             svalue = (char **)((char *)dest + offset);
1120             if (*svalue) {
1121                free(*svalue);
1122             }
1123             *svalue = bstrdup(*def_svalue);
1124             SetBit(i, dest->hdr.item_present);
1125             SetBit(i, dest->hdr.inherit_content);
1126             break;
1127          }
1128          case CFG_TYPE_RES: {
1129             char **def_svalue, **svalue;
1130 
1131             /*
1132              * Handle resources
1133              */
1134             def_svalue = (char **)((char *)(source) + offset);
1135             svalue = (char **)((char *)dest + offset);
1136             if (*svalue) {
1137                Pmsg1(000, _("Hey something is wrong. p=0x%lu\n"), *svalue);
1138             }
1139             *svalue = *def_svalue;
1140             SetBit(i, dest->hdr.item_present);
1141             SetBit(i, dest->hdr.inherit_content);
1142             break;
1143          }
1144          case CFG_TYPE_ALIST_STR: {
1145             const char *str = nullptr;
1146             alist *orig_list, **new_list;
1147 
1148             /*
1149              * Handle alist strings
1150              */
1151             orig_list = *(alist **)((char *)(source) + offset);
1152 
1153             /*
1154              * See if there is anything on the list.
1155              */
1156             if (orig_list && orig_list->size()) {
1157                new_list = (alist **)((char *)(dest) + offset);
1158 
1159                if (!*new_list) {
1160                   *new_list = New(alist(10, owned_by_alist));
1161                }
1162 
1163                foreach_alist(str, orig_list) {
1164                   (*new_list)->append(bstrdup(str));
1165                }
1166 
1167                SetBit(i, dest->hdr.item_present);
1168                SetBit(i, dest->hdr.inherit_content);
1169             }
1170             break;
1171          }
1172          case CFG_TYPE_ALIST_RES: {
1173             CommonResourceHeader *res = nullptr;
1174             alist *orig_list, **new_list;
1175 
1176             /*
1177              * Handle alist resources
1178              */
1179             orig_list = *(alist **)((char *)(source) + offset);
1180 
1181             /*
1182              * See if there is anything on the list.
1183              */
1184             if (orig_list && orig_list->size()) {
1185                new_list = (alist **)((char *)(dest) + offset);
1186 
1187                if (!*new_list) {
1188                   *new_list = New(alist(10, not_owned_by_alist));
1189                }
1190 
1191                foreach_alist(res, orig_list) {
1192                   (*new_list)->append(res);
1193                }
1194 
1195                SetBit(i, dest->hdr.item_present);
1196                SetBit(i, dest->hdr.inherit_content);
1197             }
1198             break;
1199          }
1200          case CFG_TYPE_ACL: {
1201             const char *str = nullptr;
1202             alist *orig_list, **new_list;
1203 
1204             /*
1205              * Handle ACL lists.
1206              */
1207             orig_list = ((alist **)((char *)(source) + offset))[items[i].code];
1208 
1209             /*
1210              * See if there is anything on the list.
1211              */
1212             if (orig_list && orig_list->size()) {
1213                new_list = &(((alist **)((char *)(dest) + offset))[items[i].code]);
1214 
1215                if (!*new_list) {
1216                   *new_list = New(alist(10, owned_by_alist));
1217                }
1218 
1219                foreach_alist(str, orig_list) {
1220                   (*new_list)->append(bstrdup(str));
1221                }
1222 
1223                SetBit(i, dest->hdr.item_present);
1224                SetBit(i, dest->hdr.inherit_content);
1225             }
1226             break;
1227          }
1228          case CFG_TYPE_BIT:
1229          case CFG_TYPE_PINT32:
1230          case CFG_TYPE_JOBTYPE:
1231          case CFG_TYPE_PROTOCOLTYPE:
1232          case CFG_TYPE_LEVEL:
1233          case CFG_TYPE_INT32:
1234          case CFG_TYPE_SIZE32:
1235          case CFG_TYPE_MIGTYPE:
1236          case CFG_TYPE_REPLACE: {
1237             uint32_t *def_ivalue, *ivalue;
1238 
1239             /*
1240              * Handle integer fields
1241              *    Note, our StoreBit does not handle bitmaped fields
1242              */
1243             def_ivalue = (uint32_t *)((char *)(source) + offset);
1244             ivalue = (uint32_t *)((char *)dest + offset);
1245             *ivalue = *def_ivalue;
1246             SetBit(i, dest->hdr.item_present);
1247             SetBit(i, dest->hdr.inherit_content);
1248             break;
1249          }
1250          case CFG_TYPE_TIME:
1251          case CFG_TYPE_SIZE64:
1252          case CFG_TYPE_INT64:
1253          case CFG_TYPE_SPEED: {
1254             int64_t *def_lvalue, *lvalue;
1255 
1256             /*
1257              * Handle 64 bit integer fields
1258              */
1259             def_lvalue = (int64_t *)((char *)(source) + offset);
1260             lvalue = (int64_t *)((char *)dest + offset);
1261             *lvalue = *def_lvalue;
1262             SetBit(i, dest->hdr.item_present);
1263             SetBit(i, dest->hdr.inherit_content);
1264             break;
1265          }
1266          case CFG_TYPE_BOOL: {
1267             bool *def_bvalue, *bvalue;
1268 
1269             /*
1270              * Handle bool fields
1271              */
1272             def_bvalue = (bool *)((char *)(source) + offset);
1273             bvalue = (bool *)((char *)dest + offset);
1274             *bvalue = *def_bvalue;
1275             SetBit(i, dest->hdr.item_present);
1276             SetBit(i, dest->hdr.inherit_content);
1277             break;
1278          }
1279          case CFG_TYPE_AUTOPASSWORD: {
1280             s_password *s_pwd, *d_pwd;
1281 
1282             /*
1283              * Handle password fields
1284              */
1285             s_pwd = (s_password *)((char *)(source) + offset);
1286             d_pwd = (s_password *)((char *)(dest) + offset);
1287 
1288             d_pwd->encoding = s_pwd->encoding;
1289             d_pwd->value = bstrdup(s_pwd->value);
1290             SetBit(i, dest->hdr.item_present);
1291             SetBit(i, dest->hdr.inherit_content);
1292             break;
1293          }
1294          default:
1295             Dmsg2(200, "Don't know how to propagate resource %s of configtype %d\n", items[i].name, items[i].type);
1296             break;
1297          }
1298       }
1299    }
1300 }
1301 
1302 
1303 /**
1304  * Ensure that all required items are present
1305  */
ValidateResource(int res_type,ResourceItem * items,BareosResource * res)1306 bool ValidateResource(int res_type, ResourceItem *items, BareosResource *res)
1307 {
1308    if (res_type == R_JOBDEFS) {
1309       /*
1310        * a jobdef don't have to be fully defined.
1311        */
1312       return true;
1313    } else if (res_type == R_JOB) {
1314       if (!((JobResource *)res)->validate()) {
1315          return false;
1316       }
1317    }
1318 
1319    for (int i = 0; items[i].name; i++) {
1320       if (items[i].flags & CFG_ITEM_REQUIRED) {
1321          if (!BitIsSet(i, res->hdr.item_present)) {
1322             Jmsg(NULL, M_ERROR, 0,
1323                  _("\"%s\" directive in %s \"%s\" resource is required, but not found.\n"),
1324                  items[i].name, my_config->res_to_str(res_type), res->name());
1325             return false;
1326          }
1327       }
1328 
1329       /*
1330        * If this triggers, take a look at lib/parse_conf.h
1331        */
1332       if (i >= MAX_RES_ITEMS) {
1333          Emsg1(M_ERROR, 0, _("Too many items in %s resource\n"), my_config->res_to_str(res_type));
1334          return false;
1335       }
1336    }
1337 
1338    return true;
1339 }
1340 
validate()1341 bool JobResource::validate()
1342 {
1343    /*
1344     * For Copy and Migrate we can have Jobs without a client or fileset.
1345     * As for a copy we use the original Job as a reference for the Read storage
1346     * we also don't need to check if there is an explicit storage definition in
1347     * either the Job or the Read pool.
1348     */
1349    switch (JobType) {
1350    case JT_COPY:
1351    case JT_MIGRATE:
1352       break;
1353    default:
1354       /*
1355        * All others must have a client and fileset.
1356        */
1357       if (!client) {
1358          Jmsg(NULL, M_ERROR, 0,
1359               _("\"client\" directive in Job \"%s\" resource is required, but not found.\n"), name());
1360          return false;
1361       }
1362 
1363       if (!fileset) {
1364          Jmsg(NULL, M_ERROR, 0,
1365               _("\"fileset\" directive in Job \"%s\" resource is required, but not found.\n"), name());
1366          return false;
1367       }
1368 
1369       if (!storage && (!pool || !pool->storage)) {
1370          Jmsg(NULL, M_ERROR, 0, _("No storage specified in Job \"%s\" nor in Pool.\n"), name());
1371          return false;
1372       }
1373       break;
1374    }
1375 
1376    return true;
1377 }
1378 
display(POOLMEM * dst)1379 char *CatalogResource::display(POOLMEM *dst)
1380 {
1381    Mmsg(dst, "catalog=%s\ndb_name=%s\ndb_driver=%s\ndb_user=%s\n"
1382              "db_password=%s\ndb_address=%s\ndb_port=%i\n"
1383              "db_socket=%s\n",
1384         name(), NPRTB(db_name),
1385         NPRTB(db_driver), NPRTB(db_user), NPRTB(db_password.value),
1386         NPRTB(db_address), db_port, NPRTB(db_socket));
1387 
1388    return dst;
1389 }
1390 
PrintConfigRunscript(ResourceItem * item,PoolMem & cfg_str)1391 static inline void PrintConfigRunscript(ResourceItem *item, PoolMem &cfg_str)
1392 {
1393    PoolMem temp;
1394    RunScript *runscript = nullptr;
1395    alist *list;
1396 
1397    list = *item->alistvalue;
1398    if (Bstrcasecmp(item->name, "runscript")) {
1399       if (list != NULL) {
1400          foreach_alist(runscript, list) {
1401             PoolMem esc;
1402 
1403             EscapeString(esc, runscript->command, strlen(runscript->command));
1404 
1405             /*
1406              * Don't print runscript when its inherited from a JobDef.
1407              */
1408             if (runscript->from_jobdef) {
1409                continue;
1410             }
1411 
1412             /*
1413              * Check if runscript must be written as short runscript
1414              */
1415             if (runscript->short_form) {
1416                if (runscript->when == SCRIPT_Before &&           /* runbeforejob */
1417                   (bstrcmp(runscript->target, ""))) {
1418                      Mmsg(temp, "run before job = \"%s\"\n", esc.c_str());
1419                } else if (runscript->when == SCRIPT_After &&     /* runafterjob */
1420                           runscript->on_success &&
1421                          !runscript->on_failure &&
1422                          !runscript->fail_on_error &&
1423                           bstrcmp(runscript->target, "")) {
1424                   Mmsg(temp, "run after job = \"%s\"\n", esc.c_str());
1425                } else if (runscript->when == SCRIPT_After &&     /* client run after job */
1426                           runscript->on_success &&
1427                          !runscript->on_failure &&
1428                          !runscript->fail_on_error &&
1429                           !bstrcmp(runscript->target, "")) {
1430                   Mmsg(temp, "client run after job = \"%s\"\n", esc.c_str());
1431                } else if (runscript->when == SCRIPT_Before &&      /* client run before job */
1432                           !bstrcmp(runscript->target, "")) {
1433                   Mmsg(temp, "client run before job = \"%s\"\n", esc.c_str());
1434                } else if (runscript->when == SCRIPT_After &&      /* run after failed job */
1435                           runscript->on_failure &&
1436                          !runscript->on_success &&
1437                          !runscript->fail_on_error &&
1438                           bstrcmp(runscript->target, "")) {
1439                   Mmsg(temp, "run after failed job = \"%s\"\n", esc.c_str());
1440                }
1441                IndentConfigItem(cfg_str, 1, temp.c_str());
1442             } else {
1443                Mmsg(temp, "runscript {\n");
1444                IndentConfigItem(cfg_str, 1, temp.c_str());
1445 
1446                char *cmdstring = (char *)"command"; /* '|' */
1447                if (runscript->cmd_type == '@') {
1448                   cmdstring = (char *)"console";
1449                }
1450 
1451                Mmsg(temp, "%s = \"%s\"\n", cmdstring, esc.c_str());
1452                IndentConfigItem(cfg_str, 2, temp.c_str());
1453 
1454                /*
1455                 * Default: never
1456                 */
1457                char *when = (char *)"never";
1458                switch (runscript->when) {
1459                case SCRIPT_Before:
1460                   when  = (char *)"before";
1461                   break;
1462                case SCRIPT_After:
1463                   when = (char *)"after";
1464                   break;
1465                case SCRIPT_AfterVSS:
1466                   when = (char *)"aftervss";
1467                   break;
1468                case SCRIPT_Any:
1469                   when = (char *)"always";
1470                   break;
1471                }
1472 
1473                if (!Bstrcasecmp(when, "never")) { /* suppress default value */
1474                   Mmsg(temp, "runswhen = %s\n", when);
1475                   IndentConfigItem(cfg_str, 2, temp.c_str());
1476                }
1477 
1478                /*
1479                 * Default: fail_on_error = true
1480                 */
1481                char *fail_on_error = (char *)"Yes";
1482                if (!runscript->fail_on_error){
1483                   fail_on_error = (char *)"No";
1484                   Mmsg(temp, "failonerror = %s\n", fail_on_error);
1485                   IndentConfigItem(cfg_str, 2, temp.c_str());
1486                }
1487 
1488                /*
1489                 * Default: on_success = true
1490                 */
1491                char *run_on_success = (char *)"Yes";
1492                if (!runscript->on_success){
1493                   run_on_success = (char *)"No";
1494                   Mmsg(temp, "runsonsuccess = %s\n", run_on_success);
1495                   IndentConfigItem(cfg_str, 2, temp.c_str());
1496                }
1497 
1498                /*
1499                 * Default: on_failure = false
1500                 */
1501                char *run_on_failure = (char *)"No";
1502                if (runscript->on_failure) {
1503                   run_on_failure = (char *)"Yes";
1504                   Mmsg(temp, "runsonfailure = %s\n", run_on_failure);
1505                   IndentConfigItem(cfg_str, 2, temp.c_str());
1506                }
1507 
1508                /* level is not implemented
1509                Dmsg1(200, "   level = %d\n", runscript->level);
1510                */
1511 
1512                /*
1513                 * Default: runsonclient = yes
1514                 */
1515                char *runsonclient = (char *)"Yes";
1516                if (bstrcmp(runscript->target, "")) {
1517                   runsonclient = (char *)"No";
1518                   Mmsg(temp, "runsonclient = %s\n", runsonclient);
1519                   IndentConfigItem(cfg_str, 2, temp.c_str());
1520                }
1521 
1522                IndentConfigItem(cfg_str, 1, "}\n");
1523             }
1524          }
1525       } /* foreach runscript */
1526    }
1527 }
1528 
PrintConfigRun(ResourceItem * item,PoolMem & cfg_str)1529 static inline void PrintConfigRun(ResourceItem *item, PoolMem &cfg_str)
1530 {
1531    PoolMem temp;
1532    RunResource *run;
1533    bool all_set;
1534    int i, nr_items;
1535    int interval_start;
1536    char *weekdays[] = {
1537       (char *)"Sun",
1538       (char *)"Mon",
1539       (char *)"Tue",
1540       (char *)"Wed",
1541       (char *)"Thu",
1542       (char *)"Fri",
1543       (char *)"Sat"
1544    };
1545    char *months[] = {
1546       (char *)"Jan",
1547       (char *)"Feb",
1548       (char *)"Mar",
1549       (char *)"Apr",
1550       (char *)"May",
1551       (char *)"Jun",
1552       (char *)"Jul",
1553       (char *)"Aug",
1554       (char *)"Sep",
1555       (char *)"Oct",
1556       (char *)"Nov",
1557       (char *)"Dec"
1558    };
1559    char *ordinals[] = {
1560       (char *)"1st",
1561       (char *)"2nd",
1562       (char *)"3rd",
1563       (char *)"4th",
1564       (char *)"5th"
1565    };
1566 
1567    run = (RunResource *)*(item->value);
1568    if (run != NULL) {
1569       while (run) {
1570          PoolMem run_str; /* holds the complete run= ... line */
1571          PoolMem interval; /* is one entry of day/month/week etc. */
1572 
1573          IndentConfigItem(cfg_str, 1, "run = ");
1574 
1575          /*
1576           * Overrides
1577           */
1578          if (run->pool) {
1579             Mmsg(temp, "pool=\"%s\" ", run->pool->name());
1580             PmStrcat(run_str, temp.c_str());
1581          }
1582 
1583          if (run->full_pool) {
1584             Mmsg(temp, "fullpool=\"%s\" ", run->full_pool->name());
1585             PmStrcat(run_str, temp.c_str());
1586          }
1587 
1588          if (run->vfull_pool) {
1589             Mmsg(temp, "virtualfullpool=\"%s\" ", run->vfull_pool->name());
1590             PmStrcat(run_str, temp.c_str());
1591          }
1592 
1593          if (run->inc_pool) {
1594             Mmsg(temp, "incrementalpool=\"%s\" ", run->inc_pool->name());
1595             PmStrcat(run_str, temp.c_str());
1596          }
1597 
1598          if (run->diff_pool) {
1599             Mmsg(temp, "differentialpool=\"%s\" ", run->diff_pool->name());
1600             PmStrcat(run_str, temp.c_str());
1601          }
1602 
1603          if (run->next_pool) {
1604             Mmsg(temp, "nextpool=\"%s\" ", run->next_pool->name());
1605             PmStrcat(run_str, temp.c_str());
1606          }
1607 
1608          if (run->level) {
1609             for (int j = 0; joblevels[j].level_name; j++) {
1610                if (joblevels[j].level == run->level) {
1611                   PmStrcat(run_str, joblevels[j].level_name);
1612                   PmStrcat(run_str, " ");
1613                   break;
1614                }
1615             }
1616          }
1617 
1618          if (run->storage) {
1619             Mmsg(temp, "storage=\"%s\" ", run->storage->name());
1620             PmStrcat(run_str, temp.c_str());
1621          }
1622 
1623          if (run->msgs) {
1624             Mmsg(temp, "messages=\"%s\" ", run->msgs->name());
1625             PmStrcat(run_str, temp.c_str());
1626          }
1627 
1628          if (run->Priority && run->Priority != 10) {
1629             Mmsg(temp, "priority=%d ", run->Priority);
1630             PmStrcat(run_str, temp.c_str());
1631          }
1632 
1633          if (run->MaxRunSchedTime) {
1634             Mmsg(temp, "maxrunschedtime=%d ", run->MaxRunSchedTime);
1635             PmStrcat(run_str, temp.c_str());
1636          }
1637 
1638          if (run->accurate) {
1639             /*
1640              * TODO: You cannot distinct if accurate was not set or if it was set to no
1641              *       maybe we need an additional variable like "accurate_set".
1642              */
1643             Mmsg(temp, "accurate=\"%s\" ","yes");
1644             PmStrcat(run_str, temp.c_str());
1645          }
1646 
1647          /*
1648           * Now the time specification
1649           */
1650 
1651          /*
1652           * run->mday , output is just the number comma separated
1653           */
1654          PmStrcpy(temp, "");
1655 
1656          /*
1657           * First see if not all bits are set.
1658           */
1659          all_set = true;
1660          nr_items = 31;
1661          for (i = 0; i < nr_items; i++) {
1662             if (!BitIsSet(i, run->mday)) {
1663                all_set = false;
1664             }
1665          }
1666 
1667          if (!all_set) {
1668             interval_start = -1;
1669 
1670             for (i = 0; i < nr_items; i++) {
1671                if (BitIsSet(i, run->mday)) {
1672                   if (interval_start == -1) {   /* bit is set and we are not in an interval */
1673                      interval_start = i;        /* start an interval */
1674                      Dmsg1(200, "starting interval at %d\n", i + 1);
1675                      Mmsg(interval, ",%d", i + 1);
1676                      PmStrcat(temp, interval.c_str());
1677                   }
1678                }
1679 
1680                if (!BitIsSet(i, run->mday)) {
1681                   if (interval_start != -1) {   /* bit is unset and we are in an interval */
1682                      if ((i - interval_start) > 1) {
1683                         Dmsg2(200, "found end of interval from %d to %d\n", interval_start + 1, i);
1684                         Mmsg(interval, "-%d", i);
1685                         PmStrcat(temp, interval.c_str());
1686                      }
1687                      interval_start = -1;       /* end the interval */
1688                   }
1689                }
1690             }
1691 
1692             /*
1693              * See if we are still in an interval and the last bit is also set then the interval stretches to the last item.
1694              */
1695             i = nr_items - 1;
1696             if (interval_start != -1 && BitIsSet(i, run->mday)) {
1697                if ((i - interval_start) > 1) {
1698                   Dmsg2(200, "found end of interval from %d to %d\n", interval_start + 1, i + 1);
1699                   Mmsg(interval, "-%d", i + 1);
1700                   PmStrcat(temp, interval.c_str());
1701                }
1702             }
1703 
1704             PmStrcat(temp, " ");
1705             PmStrcat(run_str, temp.c_str() + 1); /* jump over first comma*/
1706          }
1707 
1708          /*
1709           * run->wom output is 1st, 2nd... 5th comma separated
1710           *                    first, second, third... is also allowed
1711           *                    but we ignore that for now
1712           */
1713          all_set = true;
1714          nr_items = 5;
1715          for (i = 0; i < nr_items; i++) {
1716             if (!BitIsSet(i, run->wom)) {
1717                all_set = false;
1718             }
1719          }
1720 
1721          if (!all_set) {
1722             interval_start = -1;
1723 
1724             PmStrcpy(temp, "");
1725             for (i = 0; i < nr_items; i++) {
1726                if (BitIsSet(i, run->wom)) {
1727                   if (interval_start == -1) {   /* bit is set and we are not in an interval */
1728                      interval_start = i;        /* start an interval */
1729                      Dmsg1(200, "starting interval at %s\n", ordinals[i]);
1730                      Mmsg(interval, ",%s", ordinals[i]);
1731                      PmStrcat(temp, interval.c_str());
1732                   }
1733                }
1734 
1735                if (!BitIsSet(i, run->wom)) {
1736                   if (interval_start != -1) {   /* bit is unset and we are in an interval */
1737                      if ((i - interval_start) > 1) {
1738                         Dmsg2(200, "found end of interval from %s to %s\n", ordinals[interval_start], ordinals[i - 1]);
1739                         Mmsg(interval, "-%s", ordinals[i - 1]);
1740                         PmStrcat(temp, interval.c_str());
1741                      }
1742                      interval_start = -1;       /* end the interval */
1743                   }
1744                }
1745             }
1746 
1747             /*
1748              * See if we are still in an interval and the last bit is also set then the interval stretches to the last item.
1749              */
1750             i = nr_items - 1;
1751             if (interval_start != -1 && BitIsSet(i, run->wom)) {
1752                if ((i - interval_start) > 1) {
1753                   Dmsg2(200, "found end of interval from %s to %s\n", ordinals[interval_start], ordinals[i]);
1754                   Mmsg(interval, "-%s", ordinals[i]);
1755                   PmStrcat(temp, interval.c_str());
1756                }
1757             }
1758 
1759             PmStrcat(temp, " ");
1760             PmStrcat(run_str, temp.c_str() + 1); /* jump over first comma*/
1761          }
1762 
1763          /*
1764           * run->wday output is Sun, Mon, ..., Sat comma separated
1765           */
1766          all_set = true;
1767          nr_items = 7;
1768          for (i = 0; i < nr_items; i++) {
1769             if (!BitIsSet(i, run->wday)) {
1770                all_set = false;
1771             }
1772          }
1773 
1774          if (!all_set) {
1775             interval_start = -1;
1776 
1777             PmStrcpy(temp, "");
1778             for (i = 0; i < nr_items; i++) {
1779                if (BitIsSet(i, run->wday)) {
1780                   if (interval_start == -1) {   /* bit is set and we are not in an interval */
1781                      interval_start = i;        /* start an interval */
1782                      Dmsg1(200, "starting interval at %s\n", weekdays[i]);
1783                      Mmsg(interval, ",%s", weekdays[i]);
1784                      PmStrcat(temp, interval.c_str());
1785                   }
1786                }
1787 
1788                if (!BitIsSet(i, run->wday)) {
1789                   if (interval_start != -1) {   /* bit is unset and we are in an interval */
1790                      if ((i - interval_start) > 1) {
1791                         Dmsg2(200, "found end of interval from %s to %s\n", weekdays[interval_start], weekdays[i - 1]);
1792                         Mmsg(interval, "-%s", weekdays[i - 1]);
1793                         PmStrcat(temp, interval.c_str());
1794                      }
1795                      interval_start = -1;       /* end the interval */
1796                   }
1797                }
1798             }
1799 
1800             /*
1801              * See if we are still in an interval and the last bit is also set then the interval stretches to the last item.
1802              */
1803             i = nr_items - 1;
1804             if (interval_start != -1 && BitIsSet(i, run->wday)) {
1805                if ((i - interval_start) > 1) {
1806                   Dmsg2(200, "found end of interval from %s to %s\n", weekdays[interval_start], weekdays[i]);
1807                   Mmsg(interval, "-%s", weekdays[i]);
1808                   PmStrcat(temp, interval.c_str());
1809                }
1810             }
1811 
1812             PmStrcat(temp, " ");
1813             PmStrcat(run_str, temp.c_str() + 1); /* jump over first comma*/
1814          }
1815 
1816          /*
1817           * run->month output is Jan, Feb, ..., Dec comma separated
1818           */
1819          all_set = true;
1820          nr_items = 12;
1821          for (i = 0; i < nr_items; i++) {
1822             if (!BitIsSet(i, run->month)) {
1823                all_set = false;
1824             }
1825          }
1826 
1827          if (!all_set) {
1828             interval_start = -1;
1829 
1830             PmStrcpy(temp, "");
1831             for (i = 0; i < nr_items; i++) {
1832                if (BitIsSet(i, run->month)) {
1833                   if (interval_start == -1) {   /* bit is set and we are not in an interval */
1834                      interval_start = i;        /* start an interval */
1835                      Dmsg1(200, "starting interval at %s\n", months[i]);
1836                      Mmsg(interval, ",%s", months[i]);
1837                      PmStrcat(temp, interval.c_str());
1838                   }
1839                }
1840 
1841                if (!BitIsSet(i, run->month)) {
1842                   if (interval_start != -1) {   /* bit is unset and we are in an interval */
1843                      if ((i - interval_start) > 1) {
1844                         Dmsg2(200, "found end of interval from %s to %s\n", months[interval_start], months[i - 1]);
1845                         Mmsg(interval, "-%s", months[i - 1]);
1846                         PmStrcat(temp, interval.c_str());
1847                      }
1848                      interval_start = -1;       /* end the interval */
1849                   }
1850                }
1851             }
1852 
1853             /*
1854              * See if we are still in an interval and the last bit is also set then the interval stretches to the last item.
1855              */
1856             i = nr_items - 1;
1857             if (interval_start != -1 && BitIsSet(i, run->month)) {
1858                if ((i - interval_start) > 1) {
1859                   Dmsg2(200, "found end of interval from %s to %s\n", months[interval_start], months[i]);
1860                   Mmsg(interval, "-%s", months[i]);
1861                   PmStrcat(temp, interval.c_str());
1862                }
1863             }
1864 
1865             PmStrcat(temp, " ");
1866             PmStrcat(run_str, temp.c_str() + 1); /* jump over first comma*/
1867          }
1868 
1869          /*
1870           * run->woy output is w00 - w53, comma separated
1871           */
1872          all_set = true;
1873          nr_items = 54;
1874          for (i = 0; i < nr_items; i++) {
1875             if (!BitIsSet(i, run->woy)) {
1876                all_set = false;
1877             }
1878          }
1879 
1880          if (!all_set) {
1881             interval_start = -1;
1882 
1883             PmStrcpy(temp, "");
1884             for (i = 0; i < nr_items; i++) {
1885                if (BitIsSet(i, run->woy)) {
1886                   if (interval_start == -1) {   /* bit is set and we are not in an interval */
1887                      interval_start = i;        /* start an interval */
1888                      Dmsg1(200, "starting interval at w%02d\n", i);
1889                      Mmsg(interval, ",w%02d", i);
1890                      PmStrcat(temp, interval.c_str());
1891                   }
1892                }
1893 
1894                if (!BitIsSet(i, run->woy)) {
1895                   if (interval_start != -1) {   /* bit is unset and we are in an interval */
1896                      if ((i - interval_start) > 1) {
1897                         Dmsg2(200, "found end of interval from w%02d to w%02d\n", interval_start, i - 1);
1898                         Mmsg(interval, "-w%02d", i - 1);
1899                         PmStrcat(temp, interval.c_str());
1900                      }
1901                      interval_start = -1;       /* end the interval */
1902                   }
1903                }
1904             }
1905 
1906             /*
1907              * See if we are still in an interval and the last bit is also set then the interval stretches to the last item.
1908              */
1909             i = nr_items - 1;
1910             if (interval_start != -1 && BitIsSet(i, run->woy)) {
1911                if ((i - interval_start) > 1) {
1912                   Dmsg2(200, "found end of interval from w%02d to w%02d\n", interval_start, i);
1913                   Mmsg(interval, "-w%02d", i);
1914                   PmStrcat(temp, interval.c_str());
1915                }
1916             }
1917 
1918             PmStrcat(temp, " ");
1919             PmStrcat(run_str, temp.c_str() + 1); /* jump over first comma*/
1920          }
1921 
1922          /*
1923           * run->hour output is HH:MM for hour and minute though its a bitfield.
1924           * only "hourly" sets all bits.
1925           */
1926          PmStrcpy(temp, "");
1927          for (i = 0; i < 24; i++) {
1928             if BitIsSet(i, run->hour) {
1929                Mmsg(temp, "at %02d:%02d\n", i, run->minute);
1930                PmStrcat(run_str, temp.c_str());
1931             }
1932          }
1933 
1934          /*
1935           * run->minute output is smply the minute in HH:MM
1936           */
1937          PmStrcat(cfg_str, run_str.c_str());
1938 
1939          run = run->next;
1940       } /* loop over runs */
1941    }
1942 }
1943 
PrintConfig(PoolMem & buff,bool hide_sensitive_data,bool verbose)1944 bool FilesetResource::PrintConfig(PoolMem &buff, bool hide_sensitive_data, bool verbose)
1945 {
1946    PoolMem cfg_str;
1947    PoolMem temp;
1948    const char *p;
1949 
1950    Dmsg0(200,"FilesetResource::PrintConfig\n");
1951 
1952    Mmsg(temp, "FileSet {\n");
1953    PmStrcat(cfg_str, temp.c_str());
1954 
1955    Mmsg(temp, "Name = \"%s\"\n", this->name());
1956    IndentConfigItem(cfg_str, 1, temp.c_str());
1957 
1958    if (this->hdr.desc != NULL ) {
1959       Mmsg(temp, "Description = \"%s\"\n", this->hdr.desc);
1960       IndentConfigItem(cfg_str, 1, temp.c_str());
1961    }
1962 
1963    if (num_includes) {
1964       /*
1965        * Loop over all exclude blocks.
1966        */
1967       for (int i = 0;  i < num_includes; i++) {
1968          IncludeExcludeItem *incexe = include_items[i];
1969 
1970          IndentConfigItem(cfg_str, 1, "Include {\n");
1971 
1972          /*
1973           * Start options block
1974           */
1975          if (incexe->num_opts > 0) {
1976             for (int j = 0; j < incexe->num_opts; j++) {
1977                FileOptions *fo = incexe->opts_list[j];
1978 
1979                IndentConfigItem(cfg_str, 2, "Options {\n");
1980                for (p = &fo->opts[0]; *p; p++) {
1981                   switch (*p) {
1982                   case '0':                 /* no option */
1983                      break;
1984                   case 'a':                 /* alway replace */
1985                      IndentConfigItem(cfg_str, 3, "Replace = Always\n");
1986                      break;
1987                   case 'C':                 /* */
1988                      IndentConfigItem(cfg_str, 3, "Accurate = ");
1989                      p++;                   /* skip C */
1990                      for (; *p && *p != ':'; p++) {
1991                         Mmsg(temp, "%c", *p);
1992                         PmStrcat(cfg_str, temp.c_str());
1993                      }
1994                      PmStrcat(cfg_str, "\n");
1995                      break;
1996                   case 'c':
1997                      IndentConfigItem(cfg_str, 3, "CheckFileChanges = Yes\n");
1998                      break;
1999                   case 'd':
2000                      switch(*(p + 1)) {
2001                      case '1':
2002                         IndentConfigItem(cfg_str, 3, "Shadowing = LocalWarn\n");
2003                         p++;
2004                         break;
2005                      case '2':
2006                         IndentConfigItem(cfg_str, 3, "Shadowing = LocalRemove\n");
2007                         p++;
2008                         break;
2009                      case '3':
2010                         IndentConfigItem(cfg_str, 3, "Shadowing = GlobalWarn\n");
2011                         p++;
2012                         break;
2013                      case '4':
2014                         IndentConfigItem(cfg_str, 3, "Shadowing = GlobalRemove\n");
2015                         p++;
2016                         break;
2017                      }
2018                      break;
2019                   case 'e':
2020                      IndentConfigItem(cfg_str, 3, "Exclude = Yes\n");
2021                      break;
2022                   case 'f':
2023                      IndentConfigItem(cfg_str, 3, "OneFS = No\n");
2024                      break;
2025                   case 'h':                 /* no recursion */
2026                      IndentConfigItem(cfg_str, 3, "Recurse = No\n");
2027                      break;
2028                   case 'H':                 /* no hard link handling */
2029                      IndentConfigItem(cfg_str, 3, "Hardlinks = No\n");
2030                      break;
2031                   case 'i':
2032                      IndentConfigItem(cfg_str, 3, "IgnoreCase = Yes\n");
2033                      break;
2034                   case 'J':                 /* Base Job */
2035                      IndentConfigItem(cfg_str, 3, "BaseJob = ");
2036                      p++;                   /* skip J */
2037                      for (; *p && *p != ':'; p++) {
2038                         Mmsg(temp, "%c", *p);
2039                         PmStrcat(cfg_str, temp.c_str());
2040                      }
2041                      PmStrcat(cfg_str, "\n");
2042                      break;
2043                   case 'M':                 /* MD5 */
2044                      IndentConfigItem(cfg_str, 3, "Signature = MD5\n");
2045                      break;
2046                   case 'n':
2047                      IndentConfigItem(cfg_str, 3, "Replace = Never\n");
2048                      break;
2049                   case 'p':                 /* use portable data format */
2050                      IndentConfigItem(cfg_str, 3, "Portable = Yes\n");
2051                      break;
2052                   case 'P':                 /* strip path */
2053                      IndentConfigItem(cfg_str, 3, "Strip = ");
2054                      p++;                   /* skip P */
2055                      for (; *p && *p != ':'; p++) {
2056                         Mmsg(temp, "%c", *p);
2057                         PmStrcat(cfg_str, temp.c_str());
2058                      }
2059                      PmStrcat(cfg_str, "\n");
2060                      break;
2061                   case 'R':                 /* Resource forks and Finder Info */
2062                      IndentConfigItem(cfg_str, 3, "HFSPlusSupport = Yes\n");
2063                      break;
2064                   case 'r':                 /* read fifo */
2065                      IndentConfigItem(cfg_str, 3, "ReadFifo = Yes\n");
2066                      break;
2067                   case 'S':
2068                      switch(*(p + 1)) {
2069 #ifdef HAVE_SHA2
2070                      case '2':
2071                         IndentConfigItem(cfg_str, 3, "Signature = SHA256\n");
2072                         p++;
2073                         break;
2074                      case '3':
2075                         IndentConfigItem(cfg_str, 3, "Signature = SHA512\n");
2076                         p++;
2077                         break;
2078 #endif
2079                      default:
2080                         IndentConfigItem(cfg_str, 3, "Signature = SHA1\n");
2081                         break;
2082                      }
2083                      break;
2084                   case 's':
2085                      IndentConfigItem(cfg_str, 3, "Sparse = Yes\n");
2086                      break;
2087                   case 'm':
2088                      IndentConfigItem(cfg_str, 3, "MtimeOnly = Yes\n");
2089                      break;
2090                   case 'k':
2091                      IndentConfigItem(cfg_str, 3, "KeepAtime = Yes\n");
2092                      break;
2093                   case 'K':
2094                      IndentConfigItem(cfg_str, 3, "NoAtime = Yes\n");
2095                      break;
2096                   case 'A':
2097                      IndentConfigItem(cfg_str, 3, "AclSupport = Yes\n");
2098                      break;
2099                   case 'V':                  /* verify options */
2100                      IndentConfigItem(cfg_str, 3, "Verify = ");
2101                      p++;                   /* skip V */
2102                      for (; *p && *p != ':'; p++) {
2103                         Mmsg(temp, "%c", *p);
2104                         PmStrcat(cfg_str, temp.c_str());
2105                      }
2106                      PmStrcat(cfg_str, "\n");
2107                      break;
2108                   case 'w':
2109                      IndentConfigItem(cfg_str, 3, "Replace = IfNewer\n");
2110                      break;
2111                   case 'W':
2112                      IndentConfigItem(cfg_str, 3, "EnhancedWild = Yes\n");
2113                      break;
2114                   case 'z':                 /* size */
2115                      IndentConfigItem(cfg_str, 3, "Size = ");
2116                      p++;                   /* skip z */
2117                      for (; *p && *p != ':'; p++) {
2118                         Mmsg(temp, "%c", *p);
2119                         PmStrcat(cfg_str, temp.c_str());
2120                      }
2121                      PmStrcat(cfg_str, "\n");
2122                      break;
2123                   case 'Z':                 /* compression */
2124                      IndentConfigItem(cfg_str, 3, "Compression = ");
2125                      p++;                   /* skip Z */
2126                      switch (*p) {
2127                      case '0':
2128                      case '1':
2129                      case '2':
2130                      case '3':
2131                      case '4':
2132                      case '5':
2133                      case '6':
2134                      case '7':
2135                      case '8':
2136                      case '9':
2137                         Mmsg(temp, "GZIP%c\n", *p);
2138                         PmStrcat(cfg_str, temp.c_str());
2139                         break;
2140                      case 'o':
2141                         Mmsg(temp, "LZO\n");
2142                         PmStrcat(cfg_str, temp.c_str());
2143                         break;
2144                      case 'f':
2145                         p++;                /* skip f */
2146                         switch (*p) {
2147                         case 'f':
2148                            Mmsg(temp, "LZFAST\n");
2149                            PmStrcat(cfg_str, temp.c_str());
2150                            break;
2151                         case '4':
2152                            Mmsg(temp, "LZ4\n");
2153                            PmStrcat(cfg_str, temp.c_str());
2154                            break;
2155                         case 'h':
2156                            Mmsg(temp, "LZ4HC\n");
2157                            PmStrcat(cfg_str, temp.c_str());
2158                            break;
2159                         default:
2160                            Emsg1(M_ERROR, 0, _("Unknown compression include/exclude option: %c\n"), *p);
2161                            break;
2162                         }
2163                         break;
2164                      default:
2165                         Emsg1(M_ERROR, 0, _("Unknown compression include/exclude option: %c\n"), *p);
2166                         break;
2167                      }
2168                      break;
2169                   case 'X':
2170                      IndentConfigItem(cfg_str, 3, "XattrSupport = Yes\n");
2171                      break;
2172                   case 'x':
2173                      IndentConfigItem(cfg_str, 3, "AutoExclude = No\n");
2174                      break;
2175                   default:
2176                      Emsg1(M_ERROR, 0, _("Unknown include/exclude option: %c\n"), *p);
2177                      break;
2178                   }
2179                }
2180 
2181                for (int k = 0; k < fo->regex.size(); k++) {
2182                   Mmsg(temp, "Regex = \"%s\"\n", fo->regex.get(k));
2183                   IndentConfigItem(cfg_str, 3, temp.c_str());
2184                }
2185 
2186                for (int k = 0; k < fo->regexdir.size(); k++) {
2187                   Mmsg(temp, "Regex Dir = \"%s\"\n", fo->regexdir.get(k));
2188                   IndentConfigItem(cfg_str, 3, temp.c_str());
2189                }
2190 
2191                for (int k = 0; k < fo->regexfile.size(); k++) {
2192                   Mmsg(temp, "Regex File = \"%s\"\n", fo->regexfile.get(k));
2193                   IndentConfigItem(cfg_str, 3, temp.c_str());
2194                }
2195 
2196                for (int k = 0; k < fo->wild.size(); k++) {
2197                   Mmsg(temp, "Wild = \"%s\"\n", fo->wild.get(k));
2198                   IndentConfigItem(cfg_str, 3, temp.c_str());
2199                }
2200 
2201                for (int k = 0; k < fo->wilddir.size(); k++) {
2202                   Mmsg(temp, "Wild Dir = \"%s\"\n", fo->wilddir.get(k));
2203                   IndentConfigItem(cfg_str, 3, temp.c_str());
2204                }
2205 
2206                for (int k = 0; k < fo->wildfile.size(); k++) {
2207                   Mmsg(temp, "Wild File = \"%s\"\n", fo->wildfile.get(k));
2208                   IndentConfigItem(cfg_str, 3, temp.c_str());
2209                }
2210 
2211                /*
2212                 *  Wildbase is WildFile not containing a / or \\
2213                 *  see  void StoreWild() in inc_conf.c
2214                 *  so we need to translate it back to a Wild File entry
2215                 */
2216                for (int k = 0; k < fo->wildbase.size(); k++) {
2217                   Mmsg(temp, "Wild File = \"%s\"\n", fo->wildbase.get(k));
2218                   IndentConfigItem(cfg_str, 3, temp.c_str());
2219                }
2220 
2221                for (int k = 0; k < fo->base.size(); k++) {
2222                   Mmsg(temp, "Base = \"%s\"\n", fo->base.get(k));
2223                   IndentConfigItem(cfg_str, 3, temp.c_str());
2224                }
2225 
2226                for (int k = 0; k < fo->fstype.size(); k++) {
2227                   Mmsg(temp, "Fs Type = \"%s\"\n", fo->fstype.get(k));
2228                   IndentConfigItem(cfg_str, 3, temp.c_str());
2229                }
2230 
2231                for (int k = 0; k < fo->Drivetype.size(); k++) {
2232                   Mmsg(temp, "Drive Type = \"%s\"\n", fo->Drivetype.get(k));
2233                   IndentConfigItem(cfg_str, 3, temp.c_str());
2234                }
2235 
2236                for (int k = 0; k < fo->meta.size(); k++) {
2237                   Mmsg(temp, "Meta = \"%s\"\n", fo->meta.get(k));
2238                   IndentConfigItem(cfg_str, 3, temp.c_str());
2239                }
2240 
2241                if (fo->plugin) {
2242                   Mmsg(temp, "Plugin = \"%s\"\n", fo->plugin);
2243                   IndentConfigItem(cfg_str, 3, temp.c_str());
2244                }
2245 
2246                if (fo->reader) {
2247                   Mmsg(temp, "Reader = \"%s\"\n", fo->reader);
2248                   IndentConfigItem(cfg_str, 3, temp.c_str());
2249                }
2250 
2251                if (fo->writer) {
2252                   Mmsg(temp, "Writer = \"%s\"\n", fo->writer);
2253                   IndentConfigItem(cfg_str, 3, temp.c_str());
2254                }
2255 
2256                IndentConfigItem(cfg_str, 2, "}\n");
2257             }
2258          } /* end options block */
2259 
2260          /*
2261           * File = entries.
2262           */
2263          if (incexe->name_list.size()) {
2264             char *entry;
2265             PoolMem esc;
2266 
2267             for (int l = 0; l < incexe->name_list.size(); l++) {
2268                entry = (char *)incexe->name_list.get(l);
2269                EscapeString(esc, entry, strlen(entry));
2270                Mmsg(temp, "File = \"%s\"\n", esc.c_str());
2271                IndentConfigItem(cfg_str, 2, temp.c_str());
2272             }
2273          }
2274 
2275          /*
2276           * Plugin = entries.
2277           */
2278          if (incexe->plugin_list.size()) {
2279             char *entry;
2280             PoolMem esc;
2281 
2282             for (int l = 0; l < incexe->plugin_list.size(); l++) {
2283                entry = (char *)incexe->plugin_list.get(l);
2284                EscapeString(esc, entry, strlen(entry));
2285                Mmsg(temp, "Plugin = \"%s\"\n", esc.c_str());
2286                IndentConfigItem(cfg_str, 2, temp.c_str());
2287             }
2288          }
2289 
2290          /*
2291           * Exclude Dir Containing = entry.
2292           */
2293          if (incexe->ignoredir.size()) {
2294             for (int l = 0; l < incexe->ignoredir.size(); l++) {
2295                Mmsg(temp, "Exclude Dir Containing = \"%s\"\n", incexe->ignoredir.get(l));
2296                IndentConfigItem(cfg_str, 2, temp.c_str());
2297             }
2298          }
2299 
2300          IndentConfigItem(cfg_str, 1, "}\n");
2301 
2302          /*
2303           * End Include block
2304           */
2305       } /* loop over all include blocks */
2306    }
2307 
2308    if (num_excludes) {
2309       /*
2310        * Loop over all exclude blocks.
2311        */
2312       for (int j = 0; j < num_excludes; j++) {
2313          IncludeExcludeItem *incexe = exclude_items[j];
2314 
2315          if (incexe->name_list.size()) {
2316             char *entry;
2317             PoolMem esc;
2318 
2319             IndentConfigItem(cfg_str, 1, "Exclude {\n");
2320             for (int k = 0; k < incexe->name_list.size(); k++) {
2321                entry = (char *)incexe->name_list.get(k);
2322                EscapeString(esc, entry, strlen(entry));
2323                Mmsg(temp, "File = \"%s\"\n", esc.c_str());
2324                IndentConfigItem(cfg_str, 2, temp.c_str());
2325             }
2326 
2327             IndentConfigItem(cfg_str, 1, "}\n");
2328          }
2329       } /* loop over all exclude blocks */
2330    }
2331 
2332    PmStrcat(cfg_str, "}\n\n");
2333    PmStrcat(buff, cfg_str.c_str());
2334 
2335    return true;
2336 }
2337 
auth_protocol_to_str(uint32_t auth_protocol)2338 const char *auth_protocol_to_str(uint32_t auth_protocol)
2339 {
2340    for (int i = 0; authprotocols[i].name; i++) {
2341       if (authprotocols[i].token == auth_protocol) {
2342          return authprotocols[i].name;
2343       }
2344    }
2345 
2346    return "Unknown";
2347 }
2348 
level_to_str(int level)2349 const char *level_to_str(int level)
2350 {
2351    static char level_no[30];
2352    const char *str = level_no;
2353 
2354    Bsnprintf(level_no, sizeof(level_no), "%c (%d)", level, level); /* default if not found */
2355    for (int i = 0; joblevels[i].level_name; i++) {
2356       if (level == (int)joblevels[i].level) {
2357          str = joblevels[i].level_name;
2358          break;
2359       }
2360    }
2361 
2362    return str;
2363 }
2364 
2365 /**
2366  * Free all the members of an IncludeExcludeItem structure
2367  */
FreeIncexe(IncludeExcludeItem * incexe)2368 static void FreeIncexe(IncludeExcludeItem *incexe)
2369 {
2370    incexe->name_list.destroy();
2371    incexe->plugin_list.destroy();
2372    for (int i = 0; i < incexe->num_opts; i++) {
2373       FileOptions *fopt = incexe->opts_list[i];
2374       fopt->regex.destroy();
2375       fopt->regexdir.destroy();
2376       fopt->regexfile.destroy();
2377       fopt->wild.destroy();
2378       fopt->wilddir.destroy();
2379       fopt->wildfile.destroy();
2380       fopt->wildbase.destroy();
2381       fopt->base.destroy();
2382       fopt->fstype.destroy();
2383       fopt->Drivetype.destroy();
2384       fopt->meta.destroy();
2385       if (fopt->plugin) {
2386          free(fopt->plugin);
2387       }
2388       if (fopt->reader) {
2389          free(fopt->reader);
2390       }
2391       if (fopt->writer) {
2392          free(fopt->writer);
2393       }
2394       free(fopt);
2395    }
2396    if (incexe->opts_list) {
2397       free(incexe->opts_list);
2398    }
2399    incexe->ignoredir.destroy();
2400    free(incexe);
2401 }
2402 
UpdateResourcePointer(int type,ResourceItem * items)2403 static bool UpdateResourcePointer(int type, ResourceItem *items)
2404 {
2405    UnionOfResources *res;
2406    bool result = true;
2407 
2408    switch (type) {
2409    case R_PROFILE:
2410    case R_CATALOG:
2411    case R_MSGS:
2412    case R_FILESET:
2413    case R_DEVICE:
2414       /*
2415        * Resources not containing a resource
2416        */
2417       break;
2418    case R_POOL:
2419       /*
2420        * Resources containing another resource or alist. First
2421        * look up the resource which contains another resource. It
2422        * was written during pass 1.  Then stuff in the pointers to
2423        * the resources it contains, which were inserted this pass.
2424        * Finally, it will all be stored back.
2425        *
2426        * Find resource saved in pass 1
2427        */
2428       if (!(res = (UnionOfResources *)my_config->GetResWithName(R_POOL, res_all.res_pool.name()))) {
2429          Emsg1(M_ERROR, 0, _("Cannot find Pool resource %s\n"), res_all.res_pool.name());
2430          return false;
2431       } else {
2432          /*
2433           * Explicitly copy resource pointers from this pass (res_all)
2434           */
2435          res->res_pool.NextPool = res_all.res_pool.NextPool;
2436          res->res_pool.RecyclePool = res_all.res_pool.RecyclePool;
2437          res->res_pool.ScratchPool = res_all.res_pool.ScratchPool;
2438          res->res_pool.storage = res_all.res_pool.storage;
2439          if (res_all.res_pool.catalog || !res->res_pool.use_catalog) {
2440             res->res_pool.catalog = res_all.res_pool.catalog;
2441          }
2442       }
2443       break;
2444    case R_CONSOLE:
2445       if (!(res = (UnionOfResources *)my_config->GetResWithName(R_CONSOLE, res_all.res_con.name()))) {
2446          Emsg1(M_ERROR, 0, _("Cannot find Console resource %s\n"), res_all.res_con.name());
2447          return false;
2448       } else {
2449         res->res_con.tls_cert_.allowed_certificate_common_names_ =
2450             res_all.res_con.tls_cert_.allowed_certificate_common_names_;
2451          res->res_con.profiles = res_all.res_con.profiles;
2452       }
2453       break;
2454    case R_DIRECTOR:
2455       if (!(res = (UnionOfResources *)my_config->GetResWithName(R_DIRECTOR, res_all.res_dir.name()))) {
2456          Emsg1(M_ERROR, 0, _("Cannot find Director resource %s\n"), res_all.res_dir.name());
2457          return false;
2458       } else {
2459          res->res_dir.plugin_names = res_all.res_dir.plugin_names;
2460          res->res_dir.messages = res_all.res_dir.messages;
2461          res->res_dir.backend_directories = res_all.res_dir.backend_directories;
2462         res->res_dir.tls_cert_.allowed_certificate_common_names_ =
2463             res_all.res_dir.tls_cert_.allowed_certificate_common_names_;
2464       }
2465       break;
2466    case R_STORAGE:
2467       if (!(res = (UnionOfResources *)my_config->GetResWithName(type, res_all.res_store.name()))) {
2468          Emsg1(M_ERROR, 0, _("Cannot find Storage resource %s\n"), res_all.res_dir.name());
2469          return false;
2470       } else {
2471          int status;
2472 
2473          res->res_store.paired_storage = res_all.res_store.paired_storage;
2474         res->res_store.tls_cert_.allowed_certificate_common_names_ =
2475             res_all.res_store.tls_cert_.allowed_certificate_common_names_;
2476 
2477          /*
2478           * We must explicitly copy the device alist pointer
2479           */
2480          res->res_store.device = res_all.res_store.device;
2481 
2482          res->res_store.rss = (runtime_storage_status_t *)malloc(sizeof(runtime_storage_status_t));
2483          memset(res->res_store.rss, 0, sizeof(runtime_storage_status_t));
2484          if ((status = pthread_mutex_init(&res->res_store.rss->changer_lock, NULL)) != 0) {
2485             BErrNo be;
2486 
2487             Emsg1(M_ERROR_TERM, 0, _("pthread_mutex_init: ERR=%s\n"), be.bstrerror(status));
2488          }
2489          if ((status = pthread_mutex_init(&res->res_store.rss->ndmp_deviceinfo_lock, NULL)) != 0) {
2490             BErrNo be;
2491 
2492             Emsg1(M_ERROR_TERM, 0, _("pthread_mutex_init: ERR=%s\n"), be.bstrerror(status));
2493          }
2494 
2495       }
2496       break;
2497    case R_JOBDEFS:
2498    case R_JOB:
2499       if (!(res = (UnionOfResources *)my_config->GetResWithName(type, res_all.res_job.name()))) {
2500          Emsg1(M_ERROR, 0, _("Cannot find Job resource %s\n"), res_all.res_job.name());
2501          return false;
2502       } else {
2503          res->res_job.messages = res_all.res_job.messages;
2504          res->res_job.schedule = res_all.res_job.schedule;
2505          res->res_job.client = res_all.res_job.client;
2506          res->res_job.fileset = res_all.res_job.fileset;
2507          res->res_job.storage = res_all.res_job.storage;
2508          res->res_job.catalog = res_all.res_job.catalog;
2509          res->res_job.FdPluginOptions = res_all.res_job.FdPluginOptions;
2510          res->res_job.SdPluginOptions = res_all.res_job.SdPluginOptions;
2511          res->res_job.DirPluginOptions = res_all.res_job.DirPluginOptions;
2512          res->res_job.base = res_all.res_job.base;
2513          res->res_job.pool = res_all.res_job.pool;
2514          res->res_job.full_pool = res_all.res_job.full_pool;
2515          res->res_job.vfull_pool = res_all.res_job.vfull_pool;
2516          res->res_job.inc_pool = res_all.res_job.inc_pool;
2517          res->res_job.diff_pool = res_all.res_job.diff_pool;
2518          res->res_job.next_pool = res_all.res_job.next_pool;
2519          res->res_job.verify_job = res_all.res_job.verify_job;
2520          res->res_job.jobdefs = res_all.res_job.jobdefs;
2521          res->res_job.run_cmds = res_all.res_job.run_cmds;
2522          res->res_job.RunScripts = res_all.res_job.RunScripts;
2523 
2524          /*
2525           * TODO: JobDefs where/regexwhere doesn't work well (but this is not very useful)
2526           * We have to SetBit(index, res_all.hdr.item_present); or something like that
2527           *
2528           * We take RegexWhere before all other options
2529           */
2530          if (!res->res_job.RegexWhere &&
2531                (res->res_job.strip_prefix ||
2532                 res->res_job.add_suffix ||
2533                 res->res_job.add_prefix)) {
2534             int len = BregexpGetBuildWhereSize(res->res_job.strip_prefix,
2535                   res->res_job.add_prefix,
2536                   res->res_job.add_suffix);
2537             res->res_job.RegexWhere = (char *) bmalloc (len * sizeof(char));
2538             bregexp_build_where(res->res_job.RegexWhere, len,
2539                   res->res_job.strip_prefix,
2540                   res->res_job.add_prefix,
2541                   res->res_job.add_suffix);
2542             /*
2543              * TODO: test bregexp
2544              */
2545          }
2546 
2547          if (res->res_job.RegexWhere && res->res_job.RestoreWhere) {
2548             free(res->res_job.RestoreWhere);
2549             res->res_job.RestoreWhere = NULL;
2550          }
2551 
2552          if (type == R_JOB) {
2553             res->res_job.rjs = (runtime_job_status_t *)malloc(sizeof(runtime_job_status_t));
2554             memset(res->res_job.rjs, 0, sizeof(runtime_job_status_t));
2555          }
2556       }
2557       break;
2558    case R_COUNTER:
2559       if (!(res = (UnionOfResources *)my_config->GetResWithName(R_COUNTER, res_all.res_counter.name()))) {
2560          Emsg1(M_ERROR, 0, _("Cannot find Counter resource %s\n"), res_all.res_counter.name());
2561          return false;
2562       } else {
2563          res->res_counter.Catalog = res_all.res_counter.Catalog;
2564          res->res_counter.WrapCounter = res_all.res_counter.WrapCounter;
2565       }
2566       break;
2567    case R_CLIENT:
2568       if (!(res = (UnionOfResources *)my_config->GetResWithName(R_CLIENT, res_all.res_client.name()))) {
2569          Emsg1(M_ERROR, 0, _("Cannot find Client resource %s\n"), res_all.res_client.name());
2570          return false;
2571       } else {
2572          if (res_all.res_client.catalog) {
2573             res->res_client.catalog = res_all.res_client.catalog;
2574          } else {
2575             /*
2576              * No catalog overwrite given use the first catalog definition.
2577              */
2578           res->res_client.catalog = (CatalogResource *)my_config->GetNextRes(R_CATALOG, NULL);
2579          }
2580         res->res_client.tls_cert_.allowed_certificate_common_names_ =
2581             res_all.res_client.tls_cert_.allowed_certificate_common_names_;
2582 
2583          res->res_client.rcs = (runtime_client_status_t *)malloc(sizeof(runtime_client_status_t));
2584          memset(res->res_client.rcs, 0, sizeof(runtime_client_status_t));
2585       }
2586       break;
2587    case R_SCHEDULE:
2588       /*
2589        * Schedule is a bit different in that it contains a RunResource record
2590        * chain which isn't a "named" resource. This chain was linked
2591        * in by run_conf.c during pass 2, so here we jam the pointer
2592        * into the Schedule resource.
2593        */
2594       if (!(res = (UnionOfResources *)my_config->GetResWithName(R_SCHEDULE, res_all.res_client.name()))) {
2595          Emsg1(M_ERROR, 0, _("Cannot find Schedule resource %s\n"), res_all.res_client.name());
2596          return false;
2597       } else {
2598          res->res_sch.run = res_all.res_sch.run;
2599       }
2600       break;
2601    default:
2602       Emsg1(M_ERROR, 0, _("Unknown resource type %d in SaveResource.\n"), type);
2603       result = false;
2604       break;
2605    }
2606 
2607    /*
2608     * Note, the resource name was already saved during pass 1,
2609     * so here, we can just release it.
2610     */
2611    if (res_all.res_dir.hdr.name) {
2612       free(res_all.res_dir.hdr.name);
2613       res_all.res_dir.hdr.name = NULL;
2614    }
2615 
2616    if (res_all.res_dir.hdr.desc) {
2617       free(res_all.res_dir.hdr.desc);
2618       res_all.res_dir.hdr.desc = NULL;
2619    }
2620 
2621    return result;
2622 }
2623 
PropagateJobdefs(int res_type,JobResource * res)2624 bool PropagateJobdefs(int res_type, JobResource *res)
2625 {
2626    JobResource *jobdefs = NULL;
2627 
2628    if (!res->jobdefs) {
2629       return true;
2630    }
2631 
2632    /*
2633     * Don't allow the JobDefs pointing to itself.
2634     */
2635    if (res->jobdefs == res) {
2636       return false;
2637    }
2638 
2639    if (res_type == R_JOB) {
2640       jobdefs = res->jobdefs;
2641 
2642       /*
2643        * Handle RunScripts alists specifically
2644        */
2645       if (jobdefs->RunScripts) {
2646          RunScript *rs = nullptr, *elt;
2647 
2648          if (!res->RunScripts) {
2649             res->RunScripts = New(alist(10, not_owned_by_alist));
2650          }
2651 
2652          foreach_alist(rs, jobdefs->RunScripts) {
2653             elt = copy_runscript(rs);
2654             elt->from_jobdef = true;
2655             res->RunScripts->append(elt); /* we have to free it */
2656          }
2657       }
2658    }
2659 
2660    /*
2661     * Transfer default items from JobDefs Resource
2662     */
2663    PropagateResource(job_items, res->jobdefs, res);
2664 
2665    return true;
2666 }
2667 
2668 /**
2669  * Populate Job Defaults (e.g. JobDefs)
2670  */
populate_jobdefs()2671 static inline bool populate_jobdefs()
2672 {
2673    JobResource *job, *jobdefs;
2674    bool retval = true;
2675 
2676    /*
2677     * Propagate the content of a JobDefs to another.
2678     */
2679    foreach_res(jobdefs, R_JOBDEFS) {
2680       PropagateJobdefs(R_JOBDEFS, jobdefs);
2681    }
2682 
2683    /*
2684     * Propagate the content of the JobDefs to the actual Job.
2685     */
2686    foreach_res(job, R_JOB) {
2687       PropagateJobdefs(R_JOB, job);
2688 
2689       /*
2690        * Ensure that all required items are present
2691        */
2692       if (!ValidateResource(R_JOB, job_items, job)) {
2693          retval = false;
2694          goto bail_out;
2695       }
2696 
2697    } /* End loop over Job res */
2698 
2699 bail_out:
2700    return retval;
2701 }
2702 
PopulateDefs()2703 bool PopulateDefs()
2704 {
2705    return populate_jobdefs();
2706 }
2707 
StorePooltype(LEX * lc,ResourceItem * item,int index,int pass)2708 static void StorePooltype(LEX *lc, ResourceItem *item, int index, int pass)
2709 {
2710    int i;
2711 
2712    LexGetToken(lc, BCT_NAME);
2713    if (pass == 1) {
2714       for (i = 0; PoolTypes[i].name; i++) {
2715          if (Bstrcasecmp(lc->str, PoolTypes[i].name)) {
2716             /*
2717              * If a default was set free it first.
2718              */
2719             if (*(item->value)) {
2720                free(*(item->value));
2721             }
2722             *(item->value) = bstrdup(PoolTypes[i].name);
2723             i = 0;
2724             break;
2725          }
2726       }
2727 
2728       if (i != 0) {
2729          scan_err1(lc, _("Expected a Pool Type option, got: %s"), lc->str);
2730       }
2731    }
2732 
2733    ScanToEol(lc);
2734    SetBit(index, res_all.hdr.item_present);
2735    ClearBit(index, res_all.hdr.inherit_content);
2736 }
2737 
StoreActiononpurge(LEX * lc,ResourceItem * item,int index,int pass)2738 static void StoreActiononpurge(LEX *lc, ResourceItem *item, int index, int pass)
2739 {
2740    int i;
2741    uint32_t *destination = item->ui32value;
2742 
2743    LexGetToken(lc, BCT_NAME);
2744    /*
2745     * Store the type both in pass 1 and pass 2
2746     * Scan ActionOnPurge options
2747     */
2748    for (i = 0; ActionOnPurgeOptions[i].name; i++) {
2749       if (Bstrcasecmp(lc->str, ActionOnPurgeOptions[i].name)) {
2750          *destination = (*destination) | ActionOnPurgeOptions[i].token;
2751          i = 0;
2752          break;
2753       }
2754    }
2755 
2756    if (i != 0) {
2757       scan_err1(lc, _("Expected an Action On Purge option, got: %s"), lc->str);
2758    }
2759 
2760    ScanToEol(lc);
2761    SetBit(index, res_all.hdr.item_present);
2762    ClearBit(index, res_all.hdr.inherit_content);
2763 }
2764 
2765 /**
2766  * Store Device. Note, the resource is created upon the
2767  * first reference. The details of the resource are obtained
2768  * later from the SD.
2769  */
StoreDevice(LEX * lc,ResourceItem * item,int index,int pass)2770 static void StoreDevice(LEX *lc, ResourceItem *item, int index, int pass)
2771 {
2772    UnionOfResources *res;
2773    int rindex = R_DEVICE - R_FIRST;
2774    bool found = false;
2775 
2776    if (pass == 1) {
2777       LexGetToken(lc, BCT_NAME);
2778       if (!res_head[rindex]) {
2779          res = (UnionOfResources *)malloc(resources[rindex].size);
2780          memset(res, 0, resources[rindex].size);
2781          res->res_dev.hdr.name = bstrdup(lc->str);
2782          res_head[rindex] = (CommonResourceHeader *)res; /* store first entry */
2783       Dmsg3(900, "Inserting first %s res: %s index=%d\n", my_config->res_to_str(R_DEVICE), res->res_dir.name(),
2784             rindex);
2785       } else {
2786          CommonResourceHeader *next;
2787          /*
2788           * See if it is already defined
2789           */
2790          for (next = res_head[rindex]; next->next; next = next->next) {
2791             if (bstrcmp(next->name, lc->str)) {
2792                found = true;
2793                break;
2794             }
2795          }
2796          if (!found) {
2797             res = (UnionOfResources *)malloc(resources[rindex].size);
2798             memset(res, 0, resources[rindex].size);
2799             res->res_dev.hdr.name = bstrdup(lc->str);
2800             next->next = (CommonResourceHeader *)res;
2801         Dmsg4(900, "Inserting %s res: %s index=%d pass=%d\n", my_config->res_to_str(R_DEVICE),
2802               res->res_dir.name(), rindex, pass);
2803          }
2804       }
2805 
2806       ScanToEol(lc);
2807       SetBit(index, res_all.hdr.item_present);
2808       ClearBit(index, res_all.hdr.inherit_content);
2809    } else {
2810     my_config->StoreResource(CFG_TYPE_ALIST_RES, lc, item, index, pass);
2811    }
2812 }
2813 
2814 /**
2815  * Store Migration/Copy type
2816  */
StoreMigtype(LEX * lc,ResourceItem * item,int index,int pass)2817 static void StoreMigtype(LEX *lc, ResourceItem *item, int index, int pass)
2818 {
2819    int i;
2820 
2821    LexGetToken(lc, BCT_NAME);
2822    /*
2823     * Store the type both in pass 1 and pass 2
2824     */
2825    for (i = 0; migtypes[i].type_name; i++) {
2826       if (Bstrcasecmp(lc->str, migtypes[i].type_name)) {
2827          *(item->ui32value) = migtypes[i].job_type;
2828          i = 0;
2829          break;
2830       }
2831    }
2832 
2833    if (i != 0) {
2834       scan_err1(lc, _("Expected a Migration Job Type keyword, got: %s"), lc->str);
2835    }
2836 
2837    ScanToEol(lc);
2838    SetBit(index, res_all.hdr.item_present);
2839    ClearBit(index, res_all.hdr.inherit_content);
2840 }
2841 
2842 /**
2843  * Store JobType (backup, verify, restore)
2844  */
StoreJobtype(LEX * lc,ResourceItem * item,int index,int pass)2845 static void StoreJobtype(LEX *lc, ResourceItem *item, int index, int pass)
2846 {
2847    int i;
2848 
2849    LexGetToken(lc, BCT_NAME);
2850    /*
2851     * Store the type both in pass 1 and pass 2
2852     */
2853    for (i = 0; jobtypes[i].type_name; i++) {
2854       if (Bstrcasecmp(lc->str, jobtypes[i].type_name)) {
2855          *(item->ui32value) = jobtypes[i].job_type;
2856          i = 0;
2857          break;
2858       }
2859    }
2860 
2861    if (i != 0) {
2862       scan_err1(lc, _("Expected a Job Type keyword, got: %s"), lc->str);
2863    }
2864 
2865    ScanToEol(lc);
2866    SetBit(index, res_all.hdr.item_present);
2867    ClearBit(index, res_all.hdr.inherit_content);
2868 }
2869 
2870 /**
2871  * Store Protocol (Native, NDMP/NDMP_BAREOS, NDMP_NATIVE)
2872  */
StoreProtocoltype(LEX * lc,ResourceItem * item,int index,int pass)2873 static void StoreProtocoltype(LEX *lc, ResourceItem *item, int index, int pass)
2874 {
2875    int i;
2876 
2877    LexGetToken(lc, BCT_NAME);
2878    /*
2879     * Store the type both in pass 1 and pass 2
2880     */
2881    for (i = 0; backupprotocols[i].name; i++) {
2882       if (Bstrcasecmp(lc->str, backupprotocols[i].name)) {
2883          *(item->ui32value) = backupprotocols[i].token;
2884          i = 0;
2885          break;
2886       }
2887    }
2888 
2889    if (i != 0) {
2890       scan_err1(lc, _("Expected a Protocol Type keyword, got: %s"), lc->str);
2891    }
2892 
2893    ScanToEol(lc);
2894    SetBit(index, res_all.hdr.item_present);
2895    ClearBit(index, res_all.hdr.inherit_content);
2896 }
2897 
StoreReplace(LEX * lc,ResourceItem * item,int index,int pass)2898 static void StoreReplace(LEX *lc, ResourceItem *item, int index, int pass)
2899 {
2900    int i;
2901 
2902    LexGetToken(lc, BCT_NAME);
2903    /*
2904     * Scan Replacement options
2905     */
2906    for (i = 0; ReplaceOptions[i].name; i++) {
2907       if (Bstrcasecmp(lc->str, ReplaceOptions[i].name)) {
2908          *(item->ui32value) = ReplaceOptions[i].token;
2909          i = 0;
2910          break;
2911       }
2912    }
2913 
2914    if (i != 0) {
2915       scan_err1(lc, _("Expected a Restore replacement option, got: %s"), lc->str);
2916    }
2917 
2918    ScanToEol(lc);
2919    SetBit(index, res_all.hdr.item_present);
2920    ClearBit(index, res_all.hdr.inherit_content);
2921 }
2922 
2923 /**
2924  * Store Auth Protocol (Native, NDMPv2, NDMPv3, NDMPv4)
2925  */
StoreAuthprotocoltype(LEX * lc,ResourceItem * item,int index,int pass)2926 static void StoreAuthprotocoltype(LEX *lc, ResourceItem *item, int index, int pass)
2927 {
2928    int i;
2929 
2930    LexGetToken(lc, BCT_NAME);
2931    /*
2932     * Store the type both in pass 1 and pass 2
2933     */
2934    for (i = 0; authprotocols[i].name; i++) {
2935       if (Bstrcasecmp(lc->str, authprotocols[i].name)) {
2936          *(item->ui32value) = authprotocols[i].token;
2937          i = 0;
2938          break;
2939       }
2940    }
2941 
2942    if (i != 0) {
2943       scan_err1(lc, _("Expected a Auth Protocol Type keyword, got: %s"), lc->str);
2944    }
2945    ScanToEol(lc);
2946    SetBit(index, res_all.hdr.item_present);
2947    ClearBit(index, res_all.hdr.inherit_content);
2948 }
2949 
2950 /**
2951  * Store authentication type (Mostly for NDMP like clear or MD5).
2952  */
StoreAuthtype(LEX * lc,ResourceItem * item,int index,int pass)2953 static void StoreAuthtype(LEX *lc, ResourceItem *item, int index, int pass)
2954 {
2955    int i;
2956 
2957    LexGetToken(lc, BCT_NAME);
2958    /*
2959     * Store the type both in pass 1 and pass 2
2960     */
2961    for (i = 0; authmethods[i].name; i++) {
2962       if (Bstrcasecmp(lc->str, authmethods[i].name)) {
2963          *(item->ui32value) = authmethods[i].token;
2964          i = 0;
2965          break;
2966       }
2967    }
2968 
2969    if (i != 0) {
2970       scan_err1(lc, _("Expected a Authentication Type keyword, got: %s"), lc->str);
2971    }
2972 
2973    ScanToEol(lc);
2974    SetBit(index, res_all.hdr.item_present);
2975    ClearBit(index, res_all.hdr.inherit_content);
2976 }
2977 
2978 /**
2979  * Store Job Level (Full, Incremental, ...)
2980  */
StoreLevel(LEX * lc,ResourceItem * item,int index,int pass)2981 static void StoreLevel(LEX *lc, ResourceItem *item, int index, int pass)
2982 {
2983    int i;
2984 
2985    LexGetToken(lc, BCT_NAME);
2986    /*
2987     * Store the level pass 2 so that type is defined
2988     */
2989    for (i = 0; joblevels[i].level_name; i++) {
2990       if (Bstrcasecmp(lc->str, joblevels[i].level_name)) {
2991          *(item->ui32value) = joblevels[i].level;
2992          i = 0;
2993          break;
2994       }
2995    }
2996 
2997    if (i != 0) {
2998       scan_err1(lc, _("Expected a Job Level keyword, got: %s"), lc->str);
2999    }
3000 
3001    ScanToEol(lc);
3002    SetBit(index, res_all.hdr.item_present);
3003    ClearBit(index, res_all.hdr.inherit_content);
3004 }
3005 
3006 /**
3007  * Store password either clear if for NDMP and catalog or MD5 hashed for native.
3008  */
StoreAutopassword(LEX * lc,ResourceItem * item,int index,int pass)3009 static void StoreAutopassword(LEX *lc, ResourceItem *item, int index, int pass)
3010 {
3011    switch (res_all.hdr.rcode) {
3012    case R_DIRECTOR:
3013       /*
3014        * As we need to store both clear and MD5 hashed within the same
3015        * resource class we use the item->code as a hint default is 0
3016        * and for clear we need a code of 1.
3017        */
3018       switch (item->code) {
3019       case 1:
3020           my_config->StoreResource(CFG_TYPE_CLEARPASSWORD, lc, item, index, pass);
3021          break;
3022       default:
3023           my_config->StoreResource(CFG_TYPE_MD5PASSWORD, lc, item, index, pass);
3024          break;
3025       }
3026       break;
3027    case R_CLIENT:
3028       switch (res_all.res_client.Protocol) {
3029       case APT_NDMPV2:
3030       case APT_NDMPV3:
3031       case APT_NDMPV4:
3032           my_config->StoreResource(CFG_TYPE_CLEARPASSWORD, lc, item, index, pass);
3033          break;
3034       default:
3035           my_config->StoreResource(CFG_TYPE_MD5PASSWORD, lc, item, index, pass);
3036          break;
3037       }
3038       break;
3039    case R_STORAGE:
3040       switch (res_all.res_store.Protocol) {
3041       case APT_NDMPV2:
3042       case APT_NDMPV3:
3043       case APT_NDMPV4:
3044           my_config->StoreResource(CFG_TYPE_CLEARPASSWORD, lc, item, index, pass);
3045          break;
3046       default:
3047           my_config->StoreResource(CFG_TYPE_MD5PASSWORD, lc, item, index, pass);
3048          break;
3049       }
3050       break;
3051    case R_CATALOG:
3052       my_config->StoreResource(CFG_TYPE_CLEARPASSWORD, lc, item, index, pass);
3053       break;
3054    default:
3055       my_config->StoreResource(CFG_TYPE_MD5PASSWORD, lc, item, index, pass);
3056       break;
3057    }
3058 }
3059 
3060 /**
3061  * Store ACL (access control list)
3062  */
StoreAcl(LEX * lc,ResourceItem * item,int index,int pass)3063 static void StoreAcl(LEX *lc, ResourceItem *item, int index, int pass)
3064 {
3065    int token;
3066    alist *list;
3067 
3068    if (pass == 1) {
3069       if (!item->alistvalue[item->code]) {
3070          item->alistvalue[item->code] = New(alist(10, owned_by_alist));
3071          Dmsg1(900, "Defined new ACL alist at %d\n", item->code);
3072       }
3073    }
3074    list = item->alistvalue[item->code];
3075 
3076    for (;;) {
3077       LexGetToken(lc, BCT_STRING);
3078       if (pass == 1) {
3079          list->append(bstrdup(lc->str));
3080          Dmsg2(900, "Appended to %d %s\n", item->code, lc->str);
3081       }
3082       token = LexGetToken(lc, BCT_ALL);
3083       if (token == BCT_COMMA) {
3084          continue; /* get another ACL */
3085       }
3086       break;
3087    }
3088    SetBit(index, res_all.hdr.item_present);
3089    ClearBit(index, res_all.hdr.inherit_content);
3090 }
3091 
3092 /**
3093  * Store Audit event.
3094  */
StoreAudit(LEX * lc,ResourceItem * item,int index,int pass)3095 static void StoreAudit(LEX *lc, ResourceItem *item, int index, int pass)
3096 {
3097    int token;
3098    alist *list;
3099 
3100    if (pass == 1) {
3101       if (!*item->alistvalue) {
3102          *(item->alistvalue) = New(alist(10, owned_by_alist));
3103       }
3104    }
3105    list = *item->alistvalue;
3106 
3107    for (;;) {
3108       LexGetToken(lc, BCT_STRING);
3109       if (pass == 1) {
3110          list->append(bstrdup(lc->str));
3111       }
3112       token = LexGetToken(lc, BCT_ALL);
3113       if (token == BCT_COMMA) {
3114          continue;
3115       }
3116       break;
3117    }
3118    SetBit(index, res_all.hdr.item_present);
3119    ClearBit(index, res_all.hdr.inherit_content);
3120 }
3121 /**
3122  * Store a runscript->when in a bit field
3123  */
StoreRunscriptWhen(LEX * lc,ResourceItem * item,int index,int pass)3124 static void StoreRunscriptWhen(LEX *lc, ResourceItem *item, int index, int pass)
3125 {
3126    LexGetToken(lc, BCT_NAME);
3127 
3128    if (Bstrcasecmp(lc->str, "before")) {
3129       *(item->ui32value) = SCRIPT_Before;
3130    } else if (Bstrcasecmp(lc->str, "after")) {
3131       *(item->ui32value) = SCRIPT_After;
3132    } else if (Bstrcasecmp(lc->str, "aftervss")) {
3133       *(item->ui32value) = SCRIPT_AfterVSS;
3134    } else if (Bstrcasecmp(lc->str, "always")) {
3135       *(item->ui32value) = SCRIPT_Any;
3136    } else {
3137       scan_err2(lc, _("Expect %s, got: %s"), "Before, After, AfterVSS or Always", lc->str);
3138    }
3139    ScanToEol(lc);
3140 }
3141 
3142 /**
3143  * Store a runscript->target
3144  */
StoreRunscriptTarget(LEX * lc,ResourceItem * item,int index,int pass)3145 static void StoreRunscriptTarget(LEX *lc, ResourceItem *item, int index, int pass)
3146 {
3147    LexGetToken(lc, BCT_STRING);
3148 
3149    if (pass == 2) {
3150       if (bstrcmp(lc->str, "%c")) {
3151          ((RunScript *)item->value)->SetTarget(lc->str);
3152       } else if (Bstrcasecmp(lc->str, "yes")) {
3153          ((RunScript *)item->value)->SetTarget("%c");
3154       } else if (Bstrcasecmp(lc->str, "no")) {
3155          ((RunScript *)item->value)->SetTarget("");
3156       } else {
3157          CommonResourceHeader *res;
3158 
3159       if (!(res = my_config->GetResWithName(R_CLIENT, lc->str))) {
3160         scan_err3(lc, _("Could not find config Resource %s referenced on line %d : %s\n"), lc->str,
3161                   lc->line_no, lc->line);
3162          }
3163 
3164          ((RunScript *)item->value)->SetTarget(lc->str);
3165       }
3166    }
3167    ScanToEol(lc);
3168 }
3169 
3170 /**
3171  * Store a runscript->command as a string and runscript->cmd_type as a pointer
3172  */
StoreRunscriptCmd(LEX * lc,ResourceItem * item,int index,int pass)3173 static void StoreRunscriptCmd(LEX *lc, ResourceItem *item, int index, int pass)
3174 {
3175    LexGetToken(lc, BCT_STRING);
3176 
3177    if (pass == 2) {
3178       Dmsg2(1, "runscript cmd=%s type=%c\n", lc->str, item->code);
3179       POOLMEM *c = GetPoolMemory(PM_FNAME);
3180       /*
3181        * Each runscript command takes 2 entries in commands list
3182        */
3183       PmStrcpy(c, lc->str);
3184       ((RunScript *)item->value)->commands->prepend(c); /* command line */
3185       ((RunScript *)item->value)->commands->prepend((void *)(intptr_t)item->code); /* command type */
3186    }
3187    ScanToEol(lc);
3188 }
3189 
StoreShortRunscript(LEX * lc,ResourceItem * item,int index,int pass)3190 static void StoreShortRunscript(LEX *lc, ResourceItem *item, int index, int pass)
3191 {
3192    LexGetToken(lc, BCT_STRING);
3193    alist **runscripts = item->alistvalue;
3194 
3195    if (pass == 2) {
3196       RunScript *script = NewRunscript();
3197       script->SetJobCodeCallback(job_code_callback_director);
3198 
3199       script->SetCommand(lc->str);
3200       if (Bstrcasecmp(item->name, "runbeforejob")) {
3201          script->when = SCRIPT_Before;
3202          script->SetTarget("");
3203       } else if (Bstrcasecmp(item->name, "runafterjob")) {
3204          script->when = SCRIPT_After;
3205          script->on_success = true;
3206          script->on_failure = false;
3207          script->fail_on_error = false;
3208          script->SetTarget("");
3209       } else if (Bstrcasecmp(item->name, "clientrunafterjob")) {
3210          script->when = SCRIPT_After;
3211          script->on_success = true;
3212          script->on_failure = false;
3213          script->fail_on_error = false;
3214          script->SetTarget("%c");
3215       } else if (Bstrcasecmp(item->name, "clientrunbeforejob")) {
3216          script->when = SCRIPT_Before;
3217          script->SetTarget("%c");
3218       } else if (Bstrcasecmp(item->name, "runafterfailedjob")) {
3219          script->when = SCRIPT_After;
3220          script->on_failure = true;
3221          script->on_success = false;
3222          script->fail_on_error = false;
3223          script->SetTarget("");
3224       }
3225 
3226       /*
3227        * Remember that the entry was configured in the short runscript form.
3228        */
3229       script->short_form = true;
3230 
3231       if (!*runscripts) {
3232         *runscripts = New(alist(10, not_owned_by_alist));
3233       }
3234 
3235       (*runscripts)->append(script);
3236       script->debug();
3237    }
3238 
3239    ScanToEol(lc);
3240 }
3241 
3242 /**
3243  * Store a bool in a bit field without modifing res_all.hdr
3244  * We can also add an option to StoreBool to skip res_all.hdr
3245  */
StoreRunscriptBool(LEX * lc,ResourceItem * item,int index,int pass)3246 static void StoreRunscriptBool(LEX *lc, ResourceItem *item, int index, int pass)
3247 {
3248    LexGetToken(lc, BCT_NAME);
3249    if (Bstrcasecmp(lc->str, "yes") || Bstrcasecmp(lc->str, "true")) {
3250       *(item->boolvalue) = true;
3251    } else if (Bstrcasecmp(lc->str, "no") || Bstrcasecmp(lc->str, "false")) {
3252       *(item->boolvalue) = false;
3253    } else {
3254       scan_err2(lc, _("Expect %s, got: %s"), "YES, NO, TRUE, or FALSE", lc->str); /* YES and NO must not be translated */
3255    }
3256    ScanToEol(lc);
3257 }
3258 
3259 /**
3260  * Store RunScript info
3261  *
3262  * Note, when this routine is called, we are inside a Job
3263  * resource.  We treat the RunScript like a sort of
3264  * mini-resource within the Job resource.
3265  */
StoreRunscript(LEX * lc,ResourceItem * item,int index,int pass)3266 static void StoreRunscript(LEX *lc, ResourceItem *item, int index, int pass)
3267 {
3268    char *c;
3269    int token, i, t;
3270    alist **runscripts = item->alistvalue;
3271 
3272    Dmsg1(200, "StoreRunscript: begin StoreRunscript pass=%i\n", pass);
3273 
3274    token = LexGetToken(lc, BCT_SKIP_EOL);
3275 
3276    if (token != BCT_BOB) {
3277       scan_err1(lc, _("Expecting open brace. Got %s"), lc->str);
3278    }
3279    /*
3280     * Setting on_success, on_failure, fail_on_error
3281     */
3282    res_runscript.ResetDefault();
3283 
3284    if (pass == 2) {
3285       res_runscript.commands = New(alist(10, not_owned_by_alist));
3286    }
3287 
3288    while ((token = LexGetToken(lc, BCT_SKIP_EOL)) != BCT_EOF) {
3289       if (token == BCT_EOB) {
3290         break;
3291       }
3292 
3293       if (token != BCT_IDENTIFIER) {
3294         scan_err1(lc, _("Expecting keyword, got: %s\n"), lc->str);
3295       }
3296 
3297       for (i = 0; runscript_items[i].name; i++) {
3298         if (Bstrcasecmp(runscript_items[i].name, lc->str)) {
3299            token = LexGetToken(lc, BCT_SKIP_EOL);
3300            if (token != BCT_EQUALS) {
3301               scan_err1(lc, _("Expected an equals, got: %s"), lc->str);
3302            }
3303 
3304            /*
3305             * Call item handler
3306             */
3307            switch (runscript_items[i].type) {
3308            case CFG_TYPE_RUNSCRIPT_CMD:
3309               StoreRunscriptCmd(lc, &runscript_items[i], i, pass);
3310               break;
3311            case CFG_TYPE_RUNSCRIPT_TARGET:
3312               StoreRunscriptTarget(lc, &runscript_items[i], i, pass);
3313               break;
3314            case CFG_TYPE_RUNSCRIPT_BOOL:
3315               StoreRunscriptBool(lc, &runscript_items[i], i, pass);
3316               break;
3317            case CFG_TYPE_RUNSCRIPT_WHEN:
3318               StoreRunscriptWhen(lc, &runscript_items[i], i, pass);
3319               break;
3320            default:
3321               break;
3322            }
3323            i = -1;
3324            break;
3325         }
3326       }
3327 
3328       if (i >=0) {
3329         scan_err1(lc, _("Keyword %s not permitted in this resource"), lc->str);
3330       }
3331    }
3332 
3333    if (pass == 2) {
3334       /*
3335        * Run on client by default
3336        */
3337       if (!res_runscript.target) {
3338          res_runscript.SetTarget("%c");
3339       }
3340       if (!*runscripts) {
3341          *runscripts = New(alist(10, not_owned_by_alist));
3342       }
3343       /*
3344        * commands list contains 2 values per command
3345        * - POOLMEM command string (ex: /bin/true)
3346        * - int command type (ex: SHELL_CMD)
3347        */
3348       res_runscript.SetJobCodeCallback(job_code_callback_director);
3349       while ((c = (char *)res_runscript.commands->pop()) != NULL) {
3350          t = (intptr_t)res_runscript.commands->pop();
3351          RunScript *script = NewRunscript();
3352          memcpy(script, &res_runscript, sizeof(RunScript));
3353          script->command = c;
3354          script->cmd_type = t;
3355          /*
3356           * target is taken from res_runscript,
3357           * each runscript object have a copy
3358           */
3359          script->target = NULL;
3360          script->SetTarget(res_runscript.target);
3361 
3362          /*
3363           * Remember that the entry was configured in the short runscript form.
3364           */
3365          script->short_form = false;
3366 
3367          (*runscripts)->append(script);
3368          script->debug();
3369       }
3370       delete res_runscript.commands;
3371       /*
3372        * setting on_success, on_failure... cleanup target field
3373        */
3374       res_runscript.ResetDefault(true);
3375    }
3376 
3377    ScanToEol(lc);
3378    SetBit(index, res_all.hdr.item_present);
3379    ClearBit(index, res_all.hdr.inherit_content);
3380 }
3381 
3382 /**
3383  * callback function for edit_job_codes
3384  * See ../lib/util.c, function edit_job_codes, for more remaining codes
3385  *
3386  * %f = Filesetname
3387  * %h = Client Address
3388  * %p = Poolname
3389  * %w = Write storage
3390  * %x = Spooling (yes/no)
3391  * %C = Cloning (yes/no)
3392  * %D = Director name
3393  * %V = Volume name(s) (Destination)
3394  */
job_code_callback_director(JobControlRecord * jcr,const char * param)3395 extern "C" char *job_code_callback_director(JobControlRecord *jcr, const char *param)
3396 {
3397    static char yes[] = "yes";
3398    static char no[] = "no";
3399 
3400    switch (param[0]) {
3401    case 'f':
3402       if (jcr->res.fileset) {
3403          return jcr->res.fileset->name();
3404       }
3405       break;
3406    case 'h':
3407       if (jcr->res.client) {
3408          return jcr->res.client->address;
3409       }
3410       break;
3411    case 'p':
3412       if (jcr->res.pool) {
3413          return jcr->res.pool->name();
3414       }
3415       break;
3416    case 'w':
3417       if (jcr->res.write_storage) {
3418          return jcr->res.write_storage->name();
3419       }
3420       break;
3421    case 'x':
3422       return jcr->spool_data ? yes : no;
3423    case 'C':
3424       return jcr->cloned ? yes : no;
3425    case 'D':
3426       return my_name;
3427    case 'V':
3428       if (jcr) {
3429          /*
3430           * If this is a migration/copy we need the volume name from the mig_jcr.
3431           */
3432          if (jcr->mig_jcr) {
3433             jcr = jcr->mig_jcr;
3434          }
3435 
3436          if (jcr->VolumeName) {
3437             return jcr->VolumeName;
3438          } else {
3439             return (char *)_("*None*");
3440          }
3441       } else {
3442          return (char *)_("*None*");
3443       }
3444       break;
3445    }
3446 
3447    return NULL;
3448 }
3449 
3450 /**
3451  * callback function for init_resource
3452  * See ../lib/parse_conf.c, function InitResource, for more generic handling.
3453  */
InitResourceCb(ResourceItem * item,int pass)3454 static void InitResourceCb(ResourceItem *item, int pass)
3455 {
3456    switch (pass) {
3457    case 1:
3458       switch (item->type) {
3459       case CFG_TYPE_REPLACE:
3460          for (int i = 0; ReplaceOptions[i].name; i++) {
3461             if (Bstrcasecmp(item->default_value, ReplaceOptions[i].name)) {
3462                *(item->ui32value) = ReplaceOptions[i].token;
3463             }
3464          }
3465          break;
3466       case CFG_TYPE_AUTHPROTOCOLTYPE:
3467          for (int i = 0; authprotocols[i].name; i++) {
3468             if (Bstrcasecmp(item->default_value, authprotocols[i].name)) {
3469                *(item->ui32value) = authprotocols[i].token;
3470             }
3471          }
3472          break;
3473       case CFG_TYPE_AUTHTYPE:
3474          for (int i = 0; authmethods[i].name; i++) {
3475             if (Bstrcasecmp(item->default_value, authmethods[i].name)) {
3476                *(item->ui32value) = authmethods[i].token;
3477             }
3478          }
3479          break;
3480       case CFG_TYPE_POOLTYPE:
3481          *(item->value) = bstrdup(item->default_value);
3482          break;
3483       default:
3484          break;
3485       }
3486       break;
3487    default:
3488       break;
3489    }
3490 }
3491 
3492 /**
3493  * callback function for parse_config
3494  * See ../lib/parse_conf.c, function ParseConfig, for more generic handling.
3495  */
ParseConfigCb(LEX * lc,ResourceItem * item,int index,int pass)3496 static void ParseConfigCb(LEX *lc, ResourceItem *item, int index, int pass)
3497 {
3498    switch (item->type) {
3499    case CFG_TYPE_AUTOPASSWORD:
3500       StoreAutopassword(lc, item, index, pass);
3501       break;
3502    case CFG_TYPE_ACL:
3503       StoreAcl(lc, item, index, pass);
3504       break;
3505    case CFG_TYPE_AUDIT:
3506       StoreAudit(lc, item, index, pass);
3507       break;
3508    case CFG_TYPE_AUTHPROTOCOLTYPE:
3509       StoreAuthprotocoltype(lc, item, index, pass);
3510       break;
3511    case CFG_TYPE_AUTHTYPE:
3512       StoreAuthtype(lc, item, index, pass);
3513       break;
3514    case CFG_TYPE_DEVICE:
3515       StoreDevice(lc, item, index, pass);
3516       break;
3517    case CFG_TYPE_JOBTYPE:
3518       StoreJobtype(lc, item, index, pass);
3519       break;
3520    case CFG_TYPE_PROTOCOLTYPE:
3521       StoreProtocoltype(lc, item, index, pass);
3522       break;
3523    case CFG_TYPE_LEVEL:
3524       StoreLevel(lc, item, index, pass);
3525       break;
3526    case CFG_TYPE_REPLACE:
3527       StoreReplace(lc, item, index, pass);
3528       break;
3529    case CFG_TYPE_SHRTRUNSCRIPT:
3530       StoreShortRunscript(lc, item, index, pass);
3531       break;
3532    case CFG_TYPE_RUNSCRIPT:
3533       StoreRunscript(lc, item, index, pass);
3534       break;
3535    case CFG_TYPE_MIGTYPE:
3536       StoreMigtype(lc, item, index, pass);
3537       break;
3538    case CFG_TYPE_INCEXC:
3539       StoreInc(lc, item, index, pass);
3540       break;
3541    case CFG_TYPE_RUN:
3542       StoreRun(lc, item, index, pass);
3543       break;
3544    case CFG_TYPE_ACTIONONPURGE:
3545       StoreActiononpurge(lc, item, index, pass);
3546       break;
3547    case CFG_TYPE_POOLTYPE:
3548       StorePooltype(lc, item, index, pass);
3549       break;
3550    default:
3551       break;
3552    }
3553 }
3554 
3555 /**
3556  * callback function for print_config
3557  * See ../lib/res.c, function BareosResource::PrintConfig, for more generic handling.
3558  */
PrintConfigCb(ResourceItem * items,int i,PoolMem & cfg_str,bool hide_sensitive_data,bool inherited)3559 static void PrintConfigCb(ResourceItem *items, int i, PoolMem &cfg_str, bool hide_sensitive_data, bool inherited)
3560 {
3561    PoolMem temp;
3562 
3563    switch (items[i].type) {
3564    case CFG_TYPE_DEVICE: {
3565       /*
3566        * Each member of the list is comma-separated
3567        */
3568       int cnt = 0;
3569       CommonResourceHeader *res = nullptr;
3570       alist *list;
3571       PoolMem res_names;
3572 
3573       list = *(items[i].alistvalue);
3574       if (list != NULL) {
3575          Mmsg(temp, "%s = ", items[i].name);
3576          IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3577 
3578          PmStrcpy(res_names, "");
3579          foreach_alist(res, list) {
3580             if (cnt) {
3581                Mmsg(temp, ",\"%s\"", res->name);
3582             } else {
3583                Mmsg(temp, "\"%s\"", res->name);
3584             }
3585             PmStrcat(res_names, temp.c_str());
3586             cnt++;
3587          }
3588 
3589          PmStrcat(cfg_str, res_names.c_str());
3590          PmStrcat(cfg_str, "\n");
3591       }
3592       break;
3593    }
3594    case CFG_TYPE_RUNSCRIPT:
3595       Dmsg0(200, "CFG_TYPE_RUNSCRIPT\n");
3596       PrintConfigRunscript(&items[i], cfg_str);
3597       break;
3598    case CFG_TYPE_SHRTRUNSCRIPT:
3599       /*
3600        * We don't get here as this type is converted to a CFG_TYPE_RUNSCRIPT when parsed
3601        */
3602       break;
3603    case CFG_TYPE_ACL: {
3604       int cnt = 0;
3605       char *value = nullptr;
3606       alist *list;
3607       PoolMem acl;
3608 
3609       list = items[i].alistvalue[items[i].code];
3610       if (list != NULL) {
3611          Mmsg(temp, "%s = ", items[i].name);
3612          IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3613          foreach_alist(value, list) {
3614             if (cnt) {
3615                Mmsg(temp, ",\"%s\"", value);
3616             } else {
3617                Mmsg(temp, "\"%s\"", value);
3618             }
3619             PmStrcat(acl, temp.c_str());
3620             cnt++;
3621          }
3622 
3623          PmStrcat(cfg_str, acl.c_str());
3624          PmStrcat(cfg_str, "\n");
3625       }
3626       break;
3627    }
3628    case CFG_TYPE_RUN:
3629       PrintConfigRun(&items[i], cfg_str);
3630       break;
3631    case CFG_TYPE_JOBTYPE: {
3632       int32_t jobtype = *(items[i].ui32value);
3633 
3634       if (jobtype) {
3635          for (int j = 0; jobtypes[j].type_name; j++) {
3636             if (jobtypes[j].job_type == jobtype) {
3637                Mmsg(temp, "%s = %s\n", items[i].name, jobtypes[j].type_name);
3638                IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3639                break;
3640             }
3641          }
3642       }
3643       break;
3644    }
3645    case CFG_TYPE_PROTOCOLTYPE: {
3646       uint32_t protocol = *(items[i].ui32value);
3647 
3648       if (protocol) {
3649          for (int j = 0; backupprotocols[j].name; j++) {
3650             if (backupprotocols[j].token == protocol) {
3651                /*
3652                 * Suppress printing default value.
3653                 */
3654                if (items[i].flags & CFG_ITEM_DEFAULT) {
3655                   if (Bstrcasecmp(items[i].default_value, backupprotocols[j].name)) {
3656                      break;
3657                   }
3658                }
3659 
3660                Mmsg(temp, "%s = %s\n", items[i].name, backupprotocols[j].name);
3661                IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3662                break;
3663             }
3664          }
3665       }
3666       break;
3667    }
3668    case CFG_TYPE_MIGTYPE: {
3669       int32_t migtype = *(items[i].ui32value);
3670 
3671       if (migtype) {
3672          for (int j = 0; migtypes[j].type_name; j++) {
3673             if (migtypes[j].job_type == migtype) {
3674                Mmsg(temp, "%s = %s\n", items[i].name, migtypes[j].type_name);
3675                IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3676                break;
3677             }
3678          }
3679       }
3680       break;
3681    }
3682    case CFG_TYPE_REPLACE: {
3683       uint32_t replace = *(items[i].ui32value);
3684 
3685       if (replace) {
3686          for (int j = 0; ReplaceOptions[j].name; j++) {
3687             if (ReplaceOptions[j].token == replace) {
3688                /*
3689                 * Supress printing default value.
3690                 */
3691                if (items[i].flags & CFG_ITEM_DEFAULT) {
3692                   if (Bstrcasecmp(items[i].default_value, ReplaceOptions[j].name)) {
3693                      break;
3694                   }
3695                }
3696 
3697                Mmsg(temp, "%s = %s\n", items[i].name, ReplaceOptions[j].name);
3698                IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3699                break;
3700             }
3701          }
3702       }
3703       break;
3704    }
3705    case CFG_TYPE_LEVEL: {
3706       uint32_t level = *(items[i].ui32value);
3707 
3708       if (level) {
3709          for (int j = 0; joblevels[j].level_name; j++) {
3710             if (joblevels[j].level == level) {
3711                Mmsg(temp, "%s = %s\n", items[i].name, joblevels[j].level_name);
3712                IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3713                break;
3714             }
3715          }
3716       }
3717       break;
3718    }
3719    case CFG_TYPE_ACTIONONPURGE: {
3720       uint32_t action = *(items[i].ui32value);
3721 
3722       if (action) {
3723          for (int j = 0; ActionOnPurgeOptions[j].name; j++) {
3724             if (ActionOnPurgeOptions[j].token == action) {
3725                Mmsg(temp, "%s = %s\n", items[i].name, ActionOnPurgeOptions[j].name);
3726                IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3727                break;
3728             }
3729          }
3730       }
3731       break;
3732    }
3733    case CFG_TYPE_AUTHPROTOCOLTYPE: {
3734       uint32_t authprotocol = *(items[i].ui32value);
3735 
3736       if (authprotocol) {
3737          for (int j = 0; authprotocols[j].name; j++) {
3738             if (authprotocols[j].token == authprotocol) {
3739                Mmsg(temp, "%s = %s\n", items[i].name, authprotocols[j].name);
3740                IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3741                break;
3742             }
3743          }
3744       }
3745       break;
3746    }
3747    case CFG_TYPE_AUTHTYPE: {
3748       uint32_t authtype = *(items[i].ui32value);
3749 
3750       if (authtype) {
3751          for (int j = 0; authmethods[j].name; j++) {
3752             if (authprotocols[j].token == authtype) {
3753                Mmsg(temp, "%s = %s\n", items[i].name, authmethods[j].name);
3754                IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3755                break;
3756             }
3757          }
3758       }
3759       break;
3760    }
3761    case CFG_TYPE_AUDIT: {
3762       /*
3763        * Each member of the list is comma-separated
3764        */
3765       int cnt = 0;
3766       char *audit_event = nullptr;
3767       alist *list;
3768       PoolMem audit_events;
3769 
3770       list = *(items[i].alistvalue);
3771       if (list != NULL) {
3772          Mmsg(temp, "%s = ", items[i].name);
3773          IndentConfigItem(cfg_str, 1, temp.c_str(), inherited);
3774 
3775          PmStrcpy(audit_events, "");
3776          foreach_alist(audit_event, list) {
3777             if (cnt) {
3778                Mmsg(temp, ",\"%s\"", audit_event);
3779             } else {
3780                Mmsg(temp, "\"%s\"", audit_event);
3781             }
3782             PmStrcat(audit_events, temp.c_str());
3783             cnt++;
3784          }
3785 
3786          PmStrcat(cfg_str, audit_events.c_str());
3787          PmStrcat(cfg_str, "\n");
3788       }
3789       break;
3790    }
3791    case CFG_TYPE_POOLTYPE:
3792       Mmsg(temp, "%s = %s\n", items[i].name, *(items[i].value));
3793       IndentConfigItem(cfg_str, 1, temp.c_str());
3794       break;
3795    default:
3796       Dmsg2(200, "%s is UNSUPPORTED TYPE: %d\n", items[i].name, items[i].type);
3797       break;
3798    }
3799 }
3800 
ResetAllClientConnectionHandshakeModes(ConfigurationParser & my_config)3801 static void ResetAllClientConnectionHandshakeModes(ConfigurationParser &my_config)
3802 {
3803   CommonResourceHeader *header = nullptr;
3804   header = my_config.GetNextRes(R_CLIENT, header);
3805   while(header) {
3806     ClientResource *client = reinterpret_cast<ClientResource*>(header);
3807     if (client) {
3808       client->connection_successful_handshake_ = ClientConnectionHandshakeMode::kUndefined;
3809     }
3810     header = my_config.GetNextRes(R_CLIENT, header);
3811   };
3812 }
3813 
ConfigReadyCallback(ConfigurationParser & my_config)3814 static void ConfigReadyCallback(ConfigurationParser &my_config)
3815 {
3816   CreateAndAddUserAgentConsoleResource(my_config);
3817 
3818   std::map<int, std::string> map{
3819       {R_DIRECTOR, "R_DIRECTOR"}, {R_CLIENT, "R_CLIENT"},   {R_JOBDEFS, "R_JOBDEFS"},
3820       {R_JOB, "R_JOB"},           {R_STORAGE, "R_STORAGE"}, {R_CATALOG, "R_CATALOG"},
3821       {R_SCHEDULE, "R_SCHEDULE"}, {R_FILESET, "R_FILESET"}, {R_POOL, "R_POOL"},
3822       {R_MSGS, "R_MSGS"},         {R_COUNTER, "R_COUNTER"}, {R_PROFILE, "R_PROFILE"},
3823       {R_CONSOLE, "R_CONSOLE"},   {R_DEVICE, "R_DEVICE"}};
3824   my_config.InitializeQualifiedResourceNameTypeConverter(map);
3825 
3826   ResetAllClientConnectionHandshakeModes(my_config);
3827 }
3828 
AddResourceCopyToEndOfChain(UnionOfResources * res_to_add,int type)3829 static bool AddResourceCopyToEndOfChain(UnionOfResources *res_to_add, int type)
3830 {
3831   int rindex            = type - R_FIRST;
3832   UnionOfResources *res = (UnionOfResources *)malloc(resources[rindex].size);
3833   memcpy(res, res_to_add, resources[rindex].size);
3834   if (!res_head[rindex]) {
3835     res_head[rindex] = (CommonResourceHeader *)res; /* store first entry */
3836     Dmsg3(900, "Inserting first %s res: %s index=%d\n", my_config->res_to_str(type), res->res_dir.name(),
3837           rindex);
3838   } else {
3839     CommonResourceHeader *next, *last;
3840     if (!res->res_dir.name()) {
3841       Emsg1(M_ERROR, 0, _("Name item is required in %s resource, but not found.\n"), resources[rindex].name);
3842       return false;
3843     }
3844     /*
3845      * Add new res to end of chain
3846      */
3847     for (last = next = res_head[rindex]; next; next = next->next) {
3848       last = next;
3849       if (bstrcmp(next->name, res->res_dir.name())) {
3850         Emsg2(M_ERROR, 0, _("Attempt to define second %s resource named \"%s\" is not permitted.\n"),
3851               resources[rindex].name, res->res_dir.name());
3852         return false;
3853       }
3854     }
3855     last->next = (CommonResourceHeader *)res;
3856     Dmsg3(900, _("Inserting %s res: %s index=%d\n"), my_config->res_to_str(type), res->res_dir.name(),rindex);
3857   }
3858   return true;
3859 }
3860 
3861 /*
3862  * Create a special Console named "*UserAgent*" with
3863  * root console password so that incoming console
3864  * connections can be handled in unique way
3865  *
3866  */
CreateAndAddUserAgentConsoleResource(ConfigurationParser & my_config)3867 static void CreateAndAddUserAgentConsoleResource(ConfigurationParser &my_config)
3868 {
3869   DirectorResource *dir_resource = (DirectorResource *)my_config.GetNextRes(R_DIRECTOR, NULL);
3870   if (!dir_resource) { return; }
3871 
3872   ConsoleResource console;
3873   memset(&console, 0, sizeof(console));
3874   console.password_.encoding = dir_resource->password_.encoding;
3875   console.password_.value   = bstrdup(dir_resource->password_.value);
3876   console.tls_enable_           = true;
3877   console.hdr.name          = bstrdup("*UserAgent*");
3878   console.hdr.desc          = bstrdup("root console definition");
3879   console.hdr.rcode         = 1013;
3880   console.hdr.refcnt        = 1;
3881 
3882   AddResourceCopyToEndOfChain((UnionOfResources *)&console, R_CONSOLE);
3883 }
3884 
InitDirConfig(const char * configfile,int exit_code)3885 ConfigurationParser *InitDirConfig(const char *configfile, int exit_code)
3886 {
3887   ConfigurationParser *config =
3888       new ConfigurationParser(configfile, nullptr, nullptr, InitResourceCb, ParseConfigCb, PrintConfigCb,
3889                               exit_code, (void *)&res_all, res_all_size, R_FIRST, R_LAST, resources, res_head,
3890                               default_config_filename.c_str(), "bareos-dir.d", ConfigReadyCallback,
3891                               SaveResource, DumpResource, FreeResource);
3892   if (config) { config->r_own_ = R_DIRECTOR; }
3893   return config;
3894 }
3895 
3896 
3897 /**
3898  * Dump contents of resource
3899  */
DumpResource(int type,CommonResourceHeader * ures,void sendit (void * sock,const char * fmt,...),void * sock,bool hide_sensitive_data,bool verbose)3900 static void DumpResource(int type,
3901                   CommonResourceHeader *ures,
3902                   void sendit(void *sock, const char *fmt, ...),
3903                   void *sock,
3904                   bool hide_sensitive_data,
3905                   bool verbose)
3906 {
3907   PoolMem buf;
3908   bool recurse          = true;
3909   UnionOfResources *res = (UnionOfResources *)ures;
3910   UaContext *ua         = (UaContext *)sock;
3911 
3912   if (!res) {
3913     sendit(sock, _("No %s resource defined\n"), my_config->res_to_str(type));
3914     return;
3915   }
3916 
3917   if (type < 0) { /* no recursion */
3918     type    = -type;
3919     recurse = false;
3920   }
3921 
3922   if (ua && !ua->IsResAllowed(ures)) { goto bail_out; }
3923 
3924   switch (type) {
3925     case R_DIRECTOR:
3926       res->res_dir.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3927       sendit(sock, "%s", buf.c_str());
3928       break;
3929     case R_PROFILE:
3930       res->res_profile.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3931       sendit(sock, "%s", buf.c_str());
3932       break;
3933     case R_CONSOLE:
3934       res->res_con.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3935       sendit(sock, "%s", buf.c_str());
3936       break;
3937     case R_COUNTER:
3938       res->res_counter.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3939       sendit(sock, "%s", buf.c_str());
3940       break;
3941     case R_CLIENT:
3942       res->res_client.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3943       sendit(sock, "%s", buf.c_str());
3944       break;
3945     case R_DEVICE:
3946       res->res_dev.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3947       sendit(sock, "%s", buf.c_str());
3948       break;
3949     case R_STORAGE:
3950       res->res_store.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3951       sendit(sock, "%s", buf.c_str());
3952       break;
3953     case R_CATALOG:
3954       res->res_cat.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3955       sendit(sock, "%s", buf.c_str());
3956       break;
3957     case R_JOBDEFS:
3958     case R_JOB:
3959       res->res_job.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3960       sendit(sock, "%s", buf.c_str());
3961       break;
3962     case R_FILESET:
3963       res->res_fs.PrintConfig(buf, hide_sensitive_data, verbose);
3964       sendit(sock, "%s", buf.c_str());
3965       break;
3966     case R_SCHEDULE:
3967       res->res_sch.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3968       sendit(sock, "%s", buf.c_str());
3969       break;
3970     case R_POOL:
3971       res->res_pool.PrintConfig(buf, *my_config, hide_sensitive_data, verbose);
3972       sendit(sock, "%s", buf.c_str());
3973       break;
3974     case R_MSGS:
3975       res->res_msgs.PrintConfig(buf, hide_sensitive_data, verbose);
3976       sendit(sock, "%s", buf.c_str());
3977       break;
3978     default:
3979       sendit(sock, _("Unknown resource type %d in DumpResource.\n"), type);
3980       break;
3981   }
3982 
3983 bail_out:
3984   if (recurse && res->res_dir.hdr.next) {
3985     my_config->DumpResourceCb_(type, res->res_dir.hdr.next, sendit, sock, hide_sensitive_data, verbose);
3986   }
3987 }
3988 
3989 /**
3990  * Free memory of resource -- called when daemon terminates.
3991  * NB, we don't need to worry about freeing any references
3992  * to other resources as they will be freed when that
3993  * resource chain is traversed.  Mainly we worry about freeing
3994  * allocated strings (names).
3995  */
FreeResource(CommonResourceHeader * sres,int type)3996 static void FreeResource(CommonResourceHeader *sres, int type)
3997 {
3998   int num;
3999   CommonResourceHeader *nres; /* next resource if linked */
4000   UnionOfResources *res = (UnionOfResources *)sres;
4001 
4002   if (!res) return;
4003 
4004   /*
4005    * Common stuff -- free the resource name and description
4006    */
4007   nres = (CommonResourceHeader *)res->res_dir.hdr.next;
4008   if (res->res_dir.hdr.name) { free(res->res_dir.hdr.name); }
4009   if (res->res_dir.hdr.desc) { free(res->res_dir.hdr.desc); }
4010 
4011   switch (type) {
4012     case R_DIRECTOR:
4013       if (res->res_dir.working_directory) { free(res->res_dir.working_directory); }
4014       if (res->res_dir.scripts_directory) { free(res->res_dir.scripts_directory); }
4015       if (res->res_dir.plugin_directory) { free(res->res_dir.plugin_directory); }
4016       if (res->res_dir.plugin_names) { delete res->res_dir.plugin_names; }
4017       if (res->res_dir.pid_directory) { free(res->res_dir.pid_directory); }
4018       if (res->res_dir.subsys_directory) { free(res->res_dir.subsys_directory); }
4019       if (res->res_dir.backend_directories) { delete res->res_dir.backend_directories; }
4020       if (res->res_dir.password_.value) { free(res->res_dir.password_.value); }
4021       if (res->res_dir.query_file) { free(res->res_dir.query_file); }
4022       if (res->res_dir.DIRaddrs) { FreeAddresses(res->res_dir.DIRaddrs); }
4023       if (res->res_dir.DIRsrc_addr) { FreeAddresses(res->res_dir.DIRsrc_addr); }
4024       if (res->res_dir.verid) { free(res->res_dir.verid); }
4025       if (res->res_dir.keyencrkey.value) { free(res->res_dir.keyencrkey.value); }
4026       if (res->res_dir.audit_events) { delete res->res_dir.audit_events; }
4027       if (res->res_dir.secure_erase_cmdline) { free(res->res_dir.secure_erase_cmdline); }
4028       if (res->res_dir.log_timestamp_format) { free(res->res_dir.log_timestamp_format); }
4029       if (res->res_dir.tls_cert_.allowed_certificate_common_names_) {
4030         res->res_dir.tls_cert_.allowed_certificate_common_names_->destroy();
4031         free(res->res_dir.tls_cert_.allowed_certificate_common_names_);
4032       }
4033       if (res->res_dir.tls_cert_.ca_certfile_) { delete res->res_dir.tls_cert_.ca_certfile_; }
4034       if (res->res_dir.tls_cert_.ca_certdir_) { delete res->res_dir.tls_cert_.ca_certdir_; }
4035       if (res->res_dir.tls_cert_.crlfile_) { delete res->res_dir.tls_cert_.crlfile_; }
4036       if (res->res_dir.tls_cert_.certfile_) { delete res->res_dir.tls_cert_.certfile_; }
4037       if (res->res_dir.tls_cert_.keyfile_) { delete res->res_dir.tls_cert_.keyfile_; }
4038       if (res->res_dir.cipherlist_) { delete res->res_dir.cipherlist_; }
4039       if (res->res_dir.tls_cert_.dhfile_) { delete res->res_dir.tls_cert_.dhfile_; }
4040       if (res->res_dir.tls_cert_.pem_message_) { delete res->res_dir.tls_cert_.pem_message_; }
4041       break;
4042     case R_DEVICE:
4043     case R_COUNTER:
4044       break;
4045     case R_PROFILE:
4046       for (int i = 0; i < Num_ACL; i++) {
4047         if (res->res_profile.ACL_lists[i]) {
4048           delete res->res_profile.ACL_lists[i];
4049           res->res_profile.ACL_lists[i] = NULL;
4050         }
4051       }
4052       break;
4053     case R_CONSOLE:
4054       if (res->res_con.password_.value) { free(res->res_con.password_.value); }
4055       if (res->res_con.profiles) { delete res->res_con.profiles; }
4056       for (int i = 0; i < Num_ACL; i++) {
4057         if (res->res_con.ACL_lists[i]) {
4058           delete res->res_con.ACL_lists[i];
4059           res->res_con.ACL_lists[i] = NULL;
4060         }
4061       }
4062       if (res->res_con.tls_cert_.allowed_certificate_common_names_) {
4063         res->res_con.tls_cert_.allowed_certificate_common_names_->destroy();
4064         free(res->res_con.tls_cert_.allowed_certificate_common_names_);
4065       }
4066       if (res->res_con.tls_cert_.ca_certfile_) { delete res->res_con.tls_cert_.ca_certfile_; }
4067       if (res->res_con.tls_cert_.ca_certdir_) { delete res->res_con.tls_cert_.ca_certdir_; }
4068       if (res->res_con.tls_cert_.crlfile_) { delete res->res_con.tls_cert_.crlfile_; }
4069       if (res->res_con.tls_cert_.certfile_) { delete res->res_con.tls_cert_.certfile_; }
4070       if (res->res_con.tls_cert_.keyfile_) { delete res->res_con.tls_cert_.keyfile_; }
4071       if (res->res_con.cipherlist_) { delete res->res_con.cipherlist_; }
4072       if (res->res_con.tls_cert_.dhfile_) { delete res->res_con.tls_cert_.dhfile_; }
4073       if (res->res_con.tls_cert_.pem_message_) { delete res->res_con.tls_cert_.pem_message_; }
4074       break;
4075     case R_CLIENT:
4076       if (res->res_client.address) { free(res->res_client.address); }
4077       if (res->res_client.lanaddress) { free(res->res_client.lanaddress); }
4078       if (res->res_client.username) { free(res->res_client.username); }
4079       if (res->res_client.password_.value) { free(res->res_client.password_.value); }
4080       if (res->res_client.rcs) { free(res->res_client.rcs); }
4081       if (res->res_client.tls_cert_.allowed_certificate_common_names_) {
4082         res->res_client.tls_cert_.allowed_certificate_common_names_->destroy();
4083         free(res->res_client.tls_cert_.allowed_certificate_common_names_);
4084       }
4085       if (res->res_client.tls_cert_.ca_certfile_) { delete res->res_client.tls_cert_.ca_certfile_; }
4086       if (res->res_client.tls_cert_.ca_certdir_) { delete res->res_client.tls_cert_.ca_certdir_; }
4087       if (res->res_client.tls_cert_.crlfile_) { delete res->res_client.tls_cert_.crlfile_; }
4088       if (res->res_client.tls_cert_.certfile_) { delete res->res_client.tls_cert_.certfile_; }
4089       if (res->res_client.tls_cert_.keyfile_) { delete res->res_client.tls_cert_.keyfile_; }
4090       if (res->res_client.cipherlist_) { delete res->res_client.cipherlist_; }
4091       if (res->res_client.tls_cert_.dhfile_) { delete res->res_client.tls_cert_.dhfile_; }
4092       if (res->res_client.tls_cert_.pem_message_) { delete res->res_client.tls_cert_.pem_message_; }
4093       break;
4094     case R_STORAGE:
4095       if (res->res_store.address) { free(res->res_store.address); }
4096       if (res->res_store.lanaddress) { free(res->res_store.lanaddress); }
4097       if (res->res_store.username) { free(res->res_store.username); }
4098       if (res->res_store.password_.value) { free(res->res_store.password_.value); }
4099       if (res->res_store.media_type) { free(res->res_store.media_type); }
4100       if (res->res_store.ndmp_changer_device) { free(res->res_store.ndmp_changer_device); }
4101       if (res->res_store.device) { delete res->res_store.device; }
4102       if (res->res_store.rss) {
4103         if (res->res_store.rss->vol_list) {
4104           if (res->res_store.rss->vol_list->contents) {
4105             vol_list_t *vl;
4106 
4107             foreach_dlist (vl, res->res_store.rss->vol_list->contents) {
4108               if (vl->VolName) { free(vl->VolName); }
4109             }
4110             res->res_store.rss->vol_list->contents->destroy();
4111             delete res->res_store.rss->vol_list->contents;
4112           }
4113           free(res->res_store.rss->vol_list);
4114         }
4115         pthread_mutex_destroy(&res->res_store.rss->changer_lock);
4116         pthread_mutex_destroy(&res->res_store.rss->ndmp_deviceinfo_lock);
4117         free(res->res_store.rss);
4118       }
4119       if (res->res_store.tls_cert_.allowed_certificate_common_names_) {
4120         res->res_store.tls_cert_.allowed_certificate_common_names_->destroy();
4121         free(res->res_store.tls_cert_.allowed_certificate_common_names_);
4122       }
4123       if (res->res_store.tls_cert_.ca_certfile_) { delete res->res_store.tls_cert_.ca_certfile_; }
4124       if (res->res_store.tls_cert_.ca_certdir_) { delete res->res_store.tls_cert_.ca_certdir_; }
4125       if (res->res_store.tls_cert_.crlfile_) { delete res->res_store.tls_cert_.crlfile_; }
4126       if (res->res_store.tls_cert_.certfile_) { delete res->res_store.tls_cert_.certfile_; }
4127       if (res->res_store.tls_cert_.keyfile_) { delete res->res_store.tls_cert_.keyfile_; }
4128       if (res->res_store.cipherlist_) { delete res->res_store.cipherlist_; }
4129       if (res->res_store.tls_cert_.dhfile_) { delete res->res_store.tls_cert_.dhfile_; }
4130       if (res->res_store.tls_cert_.pem_message_) { delete res->res_store.tls_cert_.pem_message_; }
4131       break;
4132     case R_CATALOG:
4133       if (res->res_cat.db_address) { free(res->res_cat.db_address); }
4134       if (res->res_cat.db_socket) { free(res->res_cat.db_socket); }
4135       if (res->res_cat.db_user) { free(res->res_cat.db_user); }
4136       if (res->res_cat.db_name) { free(res->res_cat.db_name); }
4137       if (res->res_cat.db_driver) { free(res->res_cat.db_driver); }
4138       if (res->res_cat.db_password.value) { free(res->res_cat.db_password.value); }
4139       break;
4140     case R_FILESET:
4141       if ((num = res->res_fs.num_includes)) {
4142         while (--num >= 0) { FreeIncexe(res->res_fs.include_items[num]); }
4143         free(res->res_fs.include_items);
4144       }
4145       res->res_fs.num_includes = 0;
4146       if ((num = res->res_fs.num_excludes)) {
4147         while (--num >= 0) { FreeIncexe(res->res_fs.exclude_items[num]); }
4148         free(res->res_fs.exclude_items);
4149       }
4150       res->res_fs.num_excludes = 0;
4151       break;
4152     case R_POOL:
4153       if (res->res_pool.pool_type) { free(res->res_pool.pool_type); }
4154       if (res->res_pool.label_format) { free(res->res_pool.label_format); }
4155       if (res->res_pool.cleaning_prefix) { free(res->res_pool.cleaning_prefix); }
4156       if (res->res_pool.storage) { delete res->res_pool.storage; }
4157       break;
4158     case R_SCHEDULE:
4159       if (res->res_sch.run) {
4160         RunResource *nrun, *next;
4161         nrun = res->res_sch.run;
4162         while (nrun) {
4163           next = nrun->next;
4164           free(nrun);
4165           nrun = next;
4166         }
4167       }
4168       break;
4169     case R_JOBDEFS:
4170     case R_JOB:
4171       if (res->res_job.backup_format) { free(res->res_job.backup_format); }
4172       if (res->res_job.RestoreWhere) { free(res->res_job.RestoreWhere); }
4173       if (res->res_job.RegexWhere) { free(res->res_job.RegexWhere); }
4174       if (res->res_job.strip_prefix) { free(res->res_job.strip_prefix); }
4175       if (res->res_job.add_prefix) { free(res->res_job.add_prefix); }
4176       if (res->res_job.add_suffix) { free(res->res_job.add_suffix); }
4177       if (res->res_job.RestoreBootstrap) { free(res->res_job.RestoreBootstrap); }
4178       if (res->res_job.WriteBootstrap) { free(res->res_job.WriteBootstrap); }
4179       if (res->res_job.WriteVerifyList) { free(res->res_job.WriteVerifyList); }
4180       if (res->res_job.selection_pattern) { free(res->res_job.selection_pattern); }
4181       if (res->res_job.run_cmds) { delete res->res_job.run_cmds; }
4182       if (res->res_job.storage) { delete res->res_job.storage; }
4183       if (res->res_job.FdPluginOptions) { delete res->res_job.FdPluginOptions; }
4184       if (res->res_job.SdPluginOptions) { delete res->res_job.SdPluginOptions; }
4185       if (res->res_job.DirPluginOptions) { delete res->res_job.DirPluginOptions; }
4186       if (res->res_job.base) { delete res->res_job.base; }
4187       if (res->res_job.RunScripts) {
4188         FreeRunscripts(res->res_job.RunScripts);
4189         delete res->res_job.RunScripts;
4190       }
4191       if (res->res_job.rjs) { free(res->res_job.rjs); }
4192       break;
4193     case R_MSGS:
4194       if (res->res_msgs.mail_cmd) { free(res->res_msgs.mail_cmd); }
4195       if (res->res_msgs.operator_cmd) { free(res->res_msgs.operator_cmd); }
4196       if (res->res_msgs.timestamp_format) { free(res->res_msgs.timestamp_format); }
4197       FreeMsgsRes((MessagesResource *)res); /* free message resource */
4198       res = NULL;
4199       break;
4200     default:
4201       printf(_("Unknown resource type %d in FreeResource.\n"), type);
4202   }
4203   /*
4204    * Common stuff again -- free the resource, recurse to next one
4205    */
4206   if (res) { free(res); }
4207   if (nres) { my_config->FreeResourceCb_(nres, type); }
4208 }
4209 
4210 /**
4211  * Save the new resource by chaining it into the head list for
4212  * the resource. If this is pass 2, we update any resource
4213  * pointers because they may not have been defined until
4214  * later in pass 1.
4215  */
SaveResource(int type,ResourceItem * items,int pass)4216 static bool SaveResource(int type, ResourceItem *items, int pass)
4217 {
4218   int rindex = type - R_FIRST;
4219 
4220   switch (type) {
4221     case R_JOBDEFS:
4222       break;
4223     case R_JOB:
4224       /*
4225        * Check Job requirements after applying JobDefs
4226        * Ensure that the name item is present however.
4227        */
4228       if (items[0].flags & CFG_ITEM_REQUIRED) {
4229         if (!BitIsSet(0, res_all.res_dir.hdr.item_present)) {
4230           Emsg2(M_ERROR, 0, _("%s item is required in %s resource, but not found.\n"), items[0].name,
4231                 resources[rindex].name);
4232           return false;
4233         }
4234       }
4235       break;
4236     default:
4237       /*
4238        * Ensure that all required items are present
4239        */
4240       if (!ValidateResource(type, items, &res_all.res_dir)) { return false; }
4241   }
4242 
4243   /*
4244    * During pass 2 in each "store" routine, we looked up pointers
4245    * to all the resources referenced in the current resource, now we
4246    * must copy their addresses from the static record to the allocated
4247    * record.
4248    */
4249   if (pass == 2) { return UpdateResourcePointer(type, items); }
4250 
4251   if (!AddResourceCopyToEndOfChain(&res_all, type)) { return false; }
4252   return true;
4253 }
4254 
4255 } /* namespace directordaemon */
4256