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