1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2011-2016 Planets Communications B.V.
5    Copyright (C) 2013-2020 Bareos GmbH & Co. KG
6 
7    This program is Free Software; you can redistribute it and/or
8    modify it under the terms of version three of the GNU Affero General Public
9    License as published by the Free Software Foundation and included
10    in the file LICENSE.
11 
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15    Affero General Public License for more details.
16 
17    You should have received a copy of the GNU Affero General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301, USA.
21 */
22 /*
23  * Marco van Wieringen, November 2010
24  */
25 /**
26  * @file
27  * Dynamic loading of catalog plugins.
28  */
29 
30 #include "include/bareos.h"
31 #include "lib/edit.h"
32 
33 #if HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || HAVE_DBI
34 
35 #  include "cats.h"
36 
37 #  if defined(HAVE_DYNAMIC_CATS_BACKENDS)
38 
39 /**
40  * Known backend to interface mappings.
41  */
42 static struct backend_interface_mapping_t {
43   const char* interface_name;
44   bool partly_compare;
45   int interface_type_id;
46 } backend_interface_mappings[]
47     = {{"dbi", TRUE, SQL_INTERFACE_TYPE_DBI},
48        {"mysql", FALSE, SQL_INTERFACE_TYPE_MYSQL},
49        {"postgresql", FALSE, SQL_INTERFACE_TYPE_POSTGRESQL},
50        {"sqlite3", FALSE, SQL_INTERFACE_TYPE_SQLITE3},
51        {NULL, FALSE, 0}};
52 
53 #    include "cats_backends.h"
54 #    include <dlfcn.h>
55 
56 #    ifndef RTLD_NOW
57 #      define RTLD_NOW 2
58 #    endif
59 
60 /**
61  * All loaded backends.
62  */
63 static alist* loaded_backends = NULL;
64 static std::vector<std::string> backend_dirs;
65 
DbSetBackendDirs(std::vector<std::string> & new_backend_dirs)66 void DbSetBackendDirs(std::vector<std::string>& new_backend_dirs)
67 {
68   backend_dirs = new_backend_dirs;
69 }
70 
lookup_backend_interface_mapping(const char * interface_name)71 static inline backend_interface_mapping_t* lookup_backend_interface_mapping(
72     const char* interface_name)
73 {
74   backend_interface_mapping_t* backend_interface_mapping;
75 
76   for (backend_interface_mapping = backend_interface_mappings;
77        backend_interface_mapping->interface_name != NULL;
78        backend_interface_mapping++) {
79     Dmsg3(100,
80           "db_init_database: Trying to find mapping of given interfacename %s "
81           "to mapping interfacename %s, partly_compare = %s\n",
82           interface_name, backend_interface_mapping->interface_name,
83           (backend_interface_mapping->partly_compare) ? "true" : "false");
84 
85     /*
86      * See if this is a match.
87      */
88     if (backend_interface_mapping->partly_compare) {
89       if (bstrncasecmp(interface_name,
90                        backend_interface_mapping->interface_name,
91                        strlen(backend_interface_mapping->interface_name))) {
92         return backend_interface_mapping;
93       }
94     } else {
95       if (Bstrcasecmp(interface_name,
96                       backend_interface_mapping->interface_name)) {
97         return backend_interface_mapping;
98       }
99     }
100   }
101 
102   return NULL;
103 }
104 
db_init_database(JobControlRecord * jcr,const char * db_driver,const char * db_name,const char * db_user,const char * db_password,const char * db_address,int db_port,const char * db_socket,bool mult_db_connections,bool disable_batch_insert,bool try_reconnect,bool exit_on_fatal,bool need_private)105 BareosDb* db_init_database(JobControlRecord* jcr,
106                            const char* db_driver,
107                            const char* db_name,
108                            const char* db_user,
109                            const char* db_password,
110                            const char* db_address,
111                            int db_port,
112                            const char* db_socket,
113                            bool mult_db_connections,
114                            bool disable_batch_insert,
115                            bool try_reconnect,
116                            bool exit_on_fatal,
117                            bool need_private)
118 {
119   void* dl_handle = NULL;
120   PoolMem shared_library_name(PM_FNAME);
121   PoolMem error(PM_FNAME);
122   backend_interface_mapping_t* backend_interface_mapping;
123   backend_shared_library_t* backend_shared_library = nullptr;
124   t_backend_instantiate backend_instantiate;
125   t_flush_backend flush_backend;
126 
127   /*
128    * For dynamic loading catalog backends there must be a list of backend dirs
129    * set.
130    */
131   if (backend_dirs.empty()) {
132     Jmsg(jcr, M_ERROR_TERM, 0, _("Catalog Backends Dir not configured.\n"));
133   }
134 
135   /*
136    * A db_driver is mandatory for dynamic loading of backends to work.
137    */
138   if (!db_driver) {
139     Jmsg(jcr, M_ERROR_TERM, 0,
140          _("Driver type not specified in Catalog resource.\n"));
141   }
142 
143   /*
144    * If we didn't find a mapping its fatal because we don't know what database
145    * backend to use.
146    */
147   backend_interface_mapping = lookup_backend_interface_mapping(db_driver);
148   if (backend_interface_mapping == NULL) {
149     Jmsg(jcr, M_ERROR_TERM, 0, _("Unknown database driver: %s\n"), db_driver);
150     return (BareosDb*)NULL;
151   }
152 
153   /*
154    * See if the backend is already loaded.
155    */
156   if (loaded_backends) {
157     foreach_alist (backend_shared_library, loaded_backends) {
158       if (backend_shared_library->interface_type_id
159           == backend_interface_mapping->interface_type_id) {
160         return backend_shared_library->backend_instantiate(
161             jcr, db_driver, db_name, db_user, db_password, db_address, db_port,
162             db_socket, mult_db_connections, disable_batch_insert, try_reconnect,
163             exit_on_fatal, need_private);
164       }
165     }
166   }
167 
168   /*
169    * This is a new backend try to use dynamic loading to load the backend
170    * library.
171    */
172   for (const auto& backend_dir : backend_dirs) {
173 #    ifndef HAVE_WIN32
174     Mmsg(shared_library_name, "%s/libbareoscats-%s%s", backend_dir.c_str(),
175          backend_interface_mapping->interface_name, DYN_LIB_EXTENSION);
176     Dmsg3(100, "db_init_database: checking backend %s/libbareoscats-%s%s\n",
177           backend_dir.c_str(), backend_interface_mapping->interface_name,
178           DYN_LIB_EXTENSION);
179 
180     /*
181      * Make sure the shared library with this name exists.
182      */
183     struct stat st;
184     if (stat(shared_library_name.c_str(), &st) == 0) {
185 #    else
186     Mmsg(shared_library_name, "libbareoscats-%s%s",
187          backend_interface_mapping->interface_name, DYN_LIB_EXTENSION);
188     {
189 #    endif
190       dl_handle = dlopen(shared_library_name.c_str(), RTLD_NOW);
191       if (!dl_handle) {
192         PmStrcpy(error, dlerror());
193         Jmsg(jcr, M_ERROR, 0, _("Unable to load shared library: %s ERR=%s\n"),
194              shared_library_name.c_str(), error.c_str());
195         Dmsg2(100, _("Unable to load shared library: %s ERR=%s\n"),
196               shared_library_name.c_str(), error.c_str());
197         continue;
198       }
199 
200       /*
201        * Lookup the backend_instantiate function.
202        */
203       backend_instantiate
204           = (t_backend_instantiate)dlsym(dl_handle, "backend_instantiate");
205       if (backend_instantiate == NULL) {
206         PmStrcpy(error, dlerror());
207         Jmsg(jcr, M_ERROR, 0,
208              _("Lookup of backend_instantiate in shared library %s failed: "
209                "ERR=%s\n"),
210              shared_library_name.c_str(), error.c_str());
211         Dmsg2(100,
212               _("Lookup of backend_instantiate in shared library %s failed: "
213                 "ERR=%s\n"),
214               shared_library_name.c_str(), error.c_str());
215         dlclose(dl_handle);
216         dl_handle = NULL;
217         continue;
218       }
219 
220       /*
221        * Lookup the flush_backend function.
222        */
223       flush_backend = (t_flush_backend)dlsym(dl_handle, "flush_backend");
224       if (flush_backend == NULL) {
225         PmStrcpy(error, dlerror());
226         Jmsg(jcr, M_ERROR, 0,
227              _("Lookup of flush_backend in shared library %s failed: ERR=%s\n"),
228              shared_library_name.c_str(), error.c_str());
229         Dmsg2(
230             100,
231             _("Lookup of flush_backend in shared library %s failed: ERR=%s\n"),
232             shared_library_name.c_str(), error.c_str());
233         dlclose(dl_handle);
234         dl_handle = NULL;
235         continue;
236       }
237 
238       /*
239        * We found the shared library and it has the right entry points.
240        */
241       break;
242     }
243   }
244 
245   if (dl_handle) {
246     /*
247      * Create a new loaded shared library entry and tack it onto the list of
248      * loaded backend shared libs.
249      */
250     backend_shared_library
251         = (backend_shared_library_t*)malloc(sizeof(backend_shared_library_t));
252     backend_shared_library->interface_type_id
253         = backend_interface_mapping->interface_type_id;
254     backend_shared_library->handle = dl_handle;
255     backend_shared_library->backend_instantiate = backend_instantiate;
256     backend_shared_library->flush_backend = flush_backend;
257 
258     if (loaded_backends == NULL) {
259       loaded_backends = new alist(10, not_owned_by_alist);
260     }
261     loaded_backends->append(backend_shared_library);
262 
263     Dmsg1(100, "db_init_database: loaded backend %s\n",
264           shared_library_name.c_str());
265 
266     return backend_shared_library->backend_instantiate(
267         jcr, db_driver, db_name, db_user, db_password, db_address, db_port,
268         db_socket, mult_db_connections, disable_batch_insert, try_reconnect,
269         exit_on_fatal, need_private);
270   } else {
271     Jmsg(jcr, M_ABORT, 0,
272          _("Unable to load any shared library for libbareoscats-%s%s\n"),
273          backend_interface_mapping->interface_name, DYN_LIB_EXTENSION);
274     return (BareosDb*)NULL;
275   }
276 }
277 
278 void DbFlushBackends(void)
279 {
280   backend_shared_library_t* backend_shared_library = nullptr;
281 
282   if (loaded_backends) {
283     foreach_alist (backend_shared_library, loaded_backends) {
284       /*
285        * Call the flush entry point in the lib.
286        */
287       backend_shared_library->flush_backend();
288 
289       /*
290        * Close the shared library and unload it.
291        */
292       dlclose(backend_shared_library->handle);
293       free(backend_shared_library);
294     }
295 
296     delete loaded_backends;
297     loaded_backends = NULL;
298   }
299 }
300 #  else
301 /**
302  * Dummy bareos backend function replaced with the correct one at install time.
303  */
304 BareosDb* db_init_database(JobControlRecord* jcr,
305                            const char* db_driver,
306                            const char* db_name,
307                            const char* db_user,
308                            const char* db_password,
309                            const char* db_address,
310                            int db_port,
311                            const char* db_socket,
312                            bool mult_db_connections,
313                            bool disable_batch_insert,
314                            bool try_reconnect,
315                            bool exit_on_fatal,
316                            bool need_private)
317 {
318   Jmsg(jcr, M_FATAL, 0,
319        _("Please replace this dummy libbareoscats library with a proper "
320          "one.\n"));
321   Dmsg0(0, _("Please replace this dummy libbareoscats library with a proper "
322              "one.\n"));
323   return NULL;
324 }
325 
326 void DbFlushBackends(void) {}
327 #  endif /* HAVE_DYNAMIC_CATS_BACKENDS */
328 #endif   /* HAVE_SQLITE3 || HAVE_MYSQL || HAVE_POSTGRESQL || HAVE_INGRES || \
329             HAVE_DBI */
330