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