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