1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2004-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  * Main configuration file parser for Bareos Tray Monitor.
25  * Adapted from dird_conf.c
26  *
27  * Note, the configuration file parser consists of three parts
28  *
29  * 1. The generic lexical scanner in lib/lex.c and lib/lex.h
30  *
31  * 2. The generic config  scanner in lib/parse_config.c and
32  *    lib/parse_config.h. These files contain the parser code,
33  *    some utility routines, and the common store routines
34  *    (name, int, string).
35  *
36  * 3. The daemon specific file, which contains the Resource
37  *    definitions as well as any specific store routines
38  *    for the resource records.
39  *
40  * Nicolas Boichat, August MMIV
41  */
42 
43 #include "include/bareos.h"
44 #define NEED_JANSSON_NAMESPACE 1
45 #include "lib/output_formatter.h"
46 #include "tray_conf.h"
47 
48 #include "lib/parse_conf.h"
49 #include "lib/resource_item.h"
50 #include "lib/tls_resource_items.h"
51 #include "lib/output_formatter.h"
52 #include "lib/output_formatter_resource.h"
53 
54 #include <cassert>
55 
56 static const std::string default_config_filename("tray-monitor.conf");
57 
58 static BareosResource* sres_head[R_LAST - R_FIRST + 1];
59 static BareosResource** res_head = sres_head;
60 
61 static bool SaveResource(int type, ResourceItem* items, int pass);
62 static void FreeResource(BareosResource* sres, int type);
63 static void DumpResource(int type,
64                          BareosResource* reshdr,
65                          bool sendit(void* sock, const char* fmt, ...),
66                          void* sock,
67                          bool hide_sensitive_data,
68                          bool verbose);
69 /*
70  * We build the current resource here as we are
71  * scanning the resource configuration definition,
72  * then move it to allocated memory when the resource
73  * scan is complete.
74  */
75 static MonitorResource* res_monitor;
76 static DirectorResource* res_dir;
77 static ClientResource* res_client;
78 static StorageResource* res_store;
79 static ConsoleFontResource* res_font;
80 
81 /* clang-format off */
82 
83 /*
84  * Monitor Resource
85  *
86  * name handler value code flags default_value
87  */
88 static ResourceItem mon_items[] = {
89   {"Name", CFG_TYPE_NAME, ITEM(res_monitor,resource_name_), 0, CFG_ITEM_REQUIRED, 0, NULL, NULL},
90   {"Description", CFG_TYPE_STR, ITEM(res_monitor,description_), 0, 0, 0, NULL, NULL},
91   {"Password", CFG_TYPE_MD5PASSWORD, ITEM(res_monitor,password), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
92   {"RefreshInterval", CFG_TYPE_TIME, ITEM(res_monitor,RefreshInterval), 0, CFG_ITEM_DEFAULT, "60", NULL, NULL},
93   {"FdConnectTimeout", CFG_TYPE_TIME, ITEM(res_monitor,FDConnectTimeout), 0, CFG_ITEM_DEFAULT, "10", NULL, NULL},
94   {"SdConnectTimeout", CFG_TYPE_TIME, ITEM(res_monitor,SDConnectTimeout), 0, CFG_ITEM_DEFAULT, "10", NULL, NULL},
95   {"DirConnectTimeout", CFG_TYPE_TIME, ITEM(res_monitor,DIRConnectTimeout), 0, CFG_ITEM_DEFAULT, "10", NULL, NULL},
96     TLS_COMMON_CONFIG(res_monitor),
97     TLS_CERT_CONFIG(res_monitor),
98   {nullptr, 0, 0, nullptr, 0, 0, nullptr, nullptr, nullptr}
99 };
100 
101 /*
102  * Director's that we can contact
103  *
104  * name handler value code flags default_value
105  */
106 static ResourceItem dir_items[] = {
107   {"Name", CFG_TYPE_NAME, ITEM(res_dir,resource_name_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
108   {"Description", CFG_TYPE_STR, ITEM(res_dir,description_), 0, 0, NULL, NULL, NULL},
109   {"DirPort", CFG_TYPE_PINT32, ITEM(res_dir,DIRport), 0, CFG_ITEM_DEFAULT, DIR_DEFAULT_PORT, NULL, NULL},
110   {"Address", CFG_TYPE_STR, ITEM(res_dir,address), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
111     TLS_COMMON_CONFIG(res_dir),
112     TLS_CERT_CONFIG(res_dir),
113   {nullptr, 0, 0, nullptr, 0, 0, nullptr, nullptr, nullptr}
114 };
115 
116 /*
117  * Client or File daemon resource
118  *
119  * name handler value code flags default_value
120  */
121 static ResourceItem cli_items[] = {
122   {"Name", CFG_TYPE_NAME, ITEM(res_client,resource_name_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
123   {"Description", CFG_TYPE_STR, ITEM(res_client,description_), 0, 0, NULL, NULL, NULL},
124   {"Address", CFG_TYPE_STR, ITEM(res_client,address), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
125   {"FdPort", CFG_TYPE_PINT32, ITEM(res_client,FDport), 0, CFG_ITEM_DEFAULT, FD_DEFAULT_PORT, NULL, NULL},
126   {"Password", CFG_TYPE_MD5PASSWORD, ITEM(res_client,password), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
127     TLS_COMMON_CONFIG(res_client),
128     TLS_CERT_CONFIG(res_client),
129   {nullptr, 0, 0, nullptr, 0, 0, nullptr, nullptr, nullptr}
130 };
131 
132 /*
133  * Storage daemon resource
134  *
135  * name handler value code flags default_value
136  */
137 static ResourceItem store_items[] = {
138   {"Name", CFG_TYPE_NAME, ITEM(res_store,resource_name_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
139   {"Description", CFG_TYPE_STR, ITEM(res_store,description_), 0, 0, NULL, NULL, NULL},
140   {"SdPort", CFG_TYPE_PINT32, ITEM(res_store,SDport), 0, CFG_ITEM_DEFAULT, SD_DEFAULT_PORT, NULL, NULL},
141   {"Address", CFG_TYPE_STR, ITEM(res_store,address), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
142   {"SdAddress", CFG_TYPE_STR, ITEM(res_store,address), 0, 0, NULL, NULL, NULL},
143   {"Password", CFG_TYPE_MD5PASSWORD, ITEM(res_store,password), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
144   {"SdPassword", CFG_TYPE_MD5PASSWORD, ITEM(res_store,password), 0, 0, NULL, NULL, NULL},
145     TLS_COMMON_CONFIG(res_store),
146     TLS_CERT_CONFIG(res_store),
147   {nullptr, 0, 0, nullptr, 0, 0, nullptr, nullptr, nullptr}
148 };
149 
150 /*
151  * Font resource
152  *
153  * name handler value code flags default_value
154  */
155 static ResourceItem con_font_items[] = {
156   {"Name", CFG_TYPE_NAME, ITEM(res_font,resource_name_), 0, CFG_ITEM_REQUIRED, NULL, NULL, NULL},
157   {"Description", CFG_TYPE_STR, ITEM(res_font,description_), 0, 0, NULL, NULL, NULL},
158   {"Font", CFG_TYPE_STR, ITEM(res_font,fontface), 0, 0, NULL, NULL, NULL},
159   {nullptr, 0, 0, nullptr, 0, 0, nullptr, nullptr, nullptr}
160 };
161 
162 /*
163  * This is the master resource definition.
164  * It must have one item for each of the resources.
165  *
166  * NOTE!!! keep it in the same order as the R_codes
167  *   or eliminate all resources[rindex].name
168  *
169  *  name items rcode res_head
170  */
171 static ResourceTable resources[] = {
172   {"Monitor", "Monitors", mon_items, R_MONITOR, sizeof(MonitorResource),
__anon8c058a970102() 173       []() { res_monitor = new MonitorResource(); }, reinterpret_cast<BareosResource**>(&res_monitor)},
174   {"Director", "Directors", dir_items, R_DIRECTOR, sizeof(DirectorResource),
__anon8c058a970202() 175       []() { res_dir = new DirectorResource(); }, reinterpret_cast<BareosResource**>(&res_dir)},
176   {"Client", "Clients", cli_items, R_CLIENT, sizeof(ClientResource),
__anon8c058a970302() 177       []() { res_client = new ClientResource(); }, reinterpret_cast<BareosResource**>(&res_client)},
178   {"Storage", "Storages", store_items, R_STORAGE, sizeof(StorageResource),
__anon8c058a970402() 179       []() { res_store = new StorageResource(); }, reinterpret_cast<BareosResource**>(&res_store)},
180   {"ConsoleFont", "ConsoleFonts", con_font_items, R_CONSOLE_FONT, sizeof(ConsoleFontResource),
__anon8c058a970502() 181       []() { res_font = new ConsoleFontResource(); }, reinterpret_cast<BareosResource**>(&res_font)},
182   {nullptr, nullptr, nullptr, 0, 0, nullptr, nullptr}
183 };
184 
185 /* clang-format on */
186 
187 /*
188  * Dump contents of resource
189  */
DumpResource(int type,BareosResource * res,bool sendit (void * sock,const char * fmt,...),void * sock,bool hide_sensitive_data,bool verbose)190 static void DumpResource(int type,
191                          BareosResource* res,
192                          bool sendit(void* sock, const char* fmt, ...),
193                          void* sock,
194                          bool hide_sensitive_data,
195                          bool verbose)
196 {
197   PoolMem buf;
198   bool recurse = true;
199   OutputFormatter output_formatter
200       = OutputFormatter(sendit, sock, nullptr, nullptr);
201   OutputFormatterResource output_formatter_resource
202       = OutputFormatterResource(&output_formatter);
203 
204   if (res == NULL) {
205     sendit(sock, _("Warning: no \"%s\" resource (%d) defined.\n"),
206            my_config->ResToStr(type), type);
207     return;
208   }
209   if (type < 0) { /* no recursion */
210     type = -type;
211     recurse = false;
212   }
213   switch (type) {
214     default:
215       res->PrintConfig(output_formatter_resource, *my_config,
216                        hide_sensitive_data, verbose);
217       break;
218   }
219   sendit(sock, "%s", buf.c_str());
220 
221   if (recurse && res->next_) {
222     DumpResource(type, res->next_, sendit, sock, hide_sensitive_data, verbose);
223   }
224 }
225 
FreeResource(BareosResource * res,int type)226 static void FreeResource(BareosResource* res, int type)
227 {
228   if (res == NULL) return;
229 
230   BareosResource* next_resource = (BareosResource*)res->next_;
231 
232   if (res->resource_name_) { free(res->resource_name_); }
233   if (res->description_) { free(res->description_); }
234 
235   switch (type) {
236     case R_MONITOR:
237       break;
238     case R_DIRECTOR: {
239       DirectorResource* p = dynamic_cast<DirectorResource*>(res);
240       assert(p);
241       if (p->address) { free(p->address); }
242       break;
243     }
244     case R_CLIENT: {
245       ClientResource* p = dynamic_cast<ClientResource*>(res);
246       assert(p);
247       if (p->address) { free(p->address); }
248       if (p->password.value) { free(p->password.value); }
249       break;
250     }
251     case R_STORAGE: {
252       StorageResource* p = dynamic_cast<StorageResource*>(res);
253       assert(p);
254       if (p->address) { free(p->address); }
255       if (p->password.value) { free(p->password.value); }
256       break;
257     }
258     case R_CONSOLE_FONT: {
259       ConsoleFontResource* p = dynamic_cast<ConsoleFontResource*>(res);
260       assert(p);
261       if (p->fontface) { free(p->fontface); }
262       break;
263     }
264     default:
265       printf(_("Unknown resource type %d in FreeResource.\n"), type);
266       break;
267   }
268 
269   if (next_resource) { FreeResource(next_resource, type); }
270 }
271 
272 /*
273  * Save the new resource by chaining it into the head list for
274  * the resource. If this is pass 2, we update any resource
275  * pointers because they may not have been defined until
276  * later in pass 1.
277  */
SaveResource(int type,ResourceItem * items,int pass)278 static bool SaveResource(int type, ResourceItem* items, int pass)
279 {
280   int rindex = type - R_FIRST;
281   int i;
282   int error = 0;
283 
284   /*
285    * Ensure that all required items are present
286    */
287   for (i = 0; items[i].name; i++) {
288     if (items[i].flags & CFG_ITEM_REQUIRED) {
289       if (!BitIsSet(i, (*items[i].allocated_resource)->item_present_)) {
290         Emsg2(M_ERROR_TERM, 0,
291               _("%s item is required in %s resource, but not found.\n"),
292               items[i].name, resources[rindex].name);
293       }
294     }
295     /* If this triggers, take a look at lib/parse_conf.h */
296     if (i >= MAX_RES_ITEMS) {
297       Emsg1(M_ERROR_TERM, 0, _("Too many items in %s resource\n"),
298             resources[rindex].name);
299     }
300   }
301 
302   /*
303    * During pass 2 in each "store" routine, we looked up pointers
304    * to all the resources referrenced in the current resource, now we
305    * must copy their addresses from the static record to the allocated
306    * record.
307    */
308   if (pass == 2) {
309     switch (type) {
310       case R_MONITOR:
311       case R_CLIENT:
312       case R_STORAGE:
313       case R_DIRECTOR:
314       case R_CONSOLE_FONT:
315         // Resources not containing a resource
316         break;
317       default:
318         Emsg1(M_ERROR, 0, _("Unknown resource type %d in SaveResource.\n"),
319               type);
320         error = 1;
321         break;
322     }
323 
324     /* resource_name_ was already deep copied during 1. pass
325      * as matter of fact the remaining allocated memory is
326      * redundant and would not be freed in the dynamic resources;
327      *
328      * currently, this is the best place to free that */
329 
330     BareosResource* res = *items[0].allocated_resource;
331 
332     if (res) {
333       if (res->resource_name_) {
334         free(res->resource_name_);
335         res->resource_name_ = nullptr;
336       }
337       if (res->description_) {
338         free(res->description_);
339         res->description_ = nullptr;
340       }
341     }
342     return (error == 0);
343   }
344 
345   if (!error) {
346     BareosResource* new_resource = nullptr;
347     switch (type) {
348       case R_MONITOR:
349         new_resource = res_monitor;
350         res_monitor = nullptr;
351         break;
352       case R_CLIENT:
353         new_resource = res_client;
354         res_client = nullptr;
355         break;
356       case R_STORAGE:
357         new_resource = res_store;
358         res_store = nullptr;
359         break;
360       case R_DIRECTOR:
361         new_resource = res_dir;
362         res_dir = nullptr;
363         break;
364       case R_CONSOLE_FONT:
365         new_resource = res_font;
366         res_font = nullptr;
367         break;
368       default:
369         ASSERT(false);
370         break;
371     }
372     error = my_config->AppendToResourcesChain(new_resource, type) ? 0 : 1;
373   }
374   return (error == 0);
375 }
376 
ConfigBeforeCallback(ConfigurationParser & my_config)377 static void ConfigBeforeCallback(ConfigurationParser& my_config)
378 {
379   std::map<int, std::string> map{
380       {R_MONITOR, "R_MONITOR"}, {R_DIRECTOR, "R_DIRECTOR"},
381       {R_CLIENT, "R_CLIENT"},   {R_STORAGE, "R_STORAGE"},
382       {R_CONSOLE, "R_CONSOLE"}, {R_CONSOLE_FONT, "R_CONSOLE_FONT"}};
383   my_config.InitializeQualifiedResourceNameTypeConverter(map);
384 }
385 
ConfigReadyCallback(ConfigurationParser & my_config)386 static void ConfigReadyCallback(ConfigurationParser& my_config) {}
387 
InitTmonConfig(const char * configfile,int exit_code)388 ConfigurationParser* InitTmonConfig(const char* configfile, int exit_code)
389 {
390   ConfigurationParser* config = new ConfigurationParser(
391       configfile, nullptr, nullptr, nullptr, nullptr, nullptr, exit_code,
392       R_FIRST, R_LAST, resources, res_head, default_config_filename.c_str(),
393       "tray-monitor.d", ConfigBeforeCallback, ConfigReadyCallback, SaveResource,
394       DumpResource, FreeResource);
395   if (config) { config->r_own_ = R_MONITOR; }
396   return config;
397 }
398 
399 /*
400  * Print configuration file schema in json format
401  */
402 #ifdef HAVE_JANSSON
PrintConfigSchemaJson(PoolMem & buffer)403 bool PrintConfigSchemaJson(PoolMem& buffer)
404 {
405   ResourceTable* resources = my_config->resources_;
406 
407   InitializeJson();
408 
409   json_t* json = json_object();
410   json_object_set_new(json, "format-version", json_integer(2));
411   json_object_set_new(json, "component", json_string("bareos-tray-monitor"));
412   json_object_set_new(json, "version", json_string(kBareosVersionStrings.Full));
413 
414   /*
415    * Resources
416    */
417   json_t* resource = json_object();
418   json_object_set(json, "resource", resource);
419   json_t* bareos_tray_monitor = json_object();
420   json_object_set(resource, "bareos-tray-monitor", bareos_tray_monitor);
421 
422   for (int r = 0; resources[r].name; r++) {
423     ResourceTable resource = my_config->resources_[r];
424     json_object_set(bareos_tray_monitor, resource.name,
425                     json_items(resource.items));
426   }
427 
428   PmStrcat(buffer, json_dumps(json, JSON_INDENT(2)));
429   json_decref(json);
430 
431   return true;
432 }
433 #else
PrintConfigSchemaJson(PoolMem & buffer)434 bool PrintConfigSchemaJson(PoolMem& buffer)
435 {
436   PmStrcat(buffer, "{ \"success\": false, \"message\": \"not available\" }");
437   return false;
438 }
439 #endif
440