1 /*
2    BAREOS® - Backup Archiving REcovery Open Sourced
3 
4    Copyright (C) 2014-2014 Planets Communications B.V.
5    Copyright (C) 2014-2016 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, June 2014
24  */
25 /**
26  * @file
27  * Dynamic loading of SD backend plugins.
28  */
29 
30 
31 #include "include/bareos.h"
32 #include "stored/stored.h"
33 
34 #if defined(HAVE_DYNAMIC_SD_BACKENDS)
35 
36 #include "sd_backends.h"
37 #include <dlfcn.h>
38 
39 #ifndef RTLD_NOW
40 #define RTLD_NOW 2
41 #endif
42 
43 namespace storagedaemon {
44 
45 /**
46  * Known backend to interface mappings.
47  */
48 static struct backend_interface_mapping_t {
49    int interface_type_id;
50    const char *interface_name;
51 } backend_interface_mappings[] = {
52    { B_FIFO_DEV, "fifo" },
53    { B_TAPE_DEV, "tape" },
54    { B_GFAPI_DEV, "gfapi" },
55    { B_DROPLET_DEV, "droplet" },
56    { B_RADOS_DEV, "rados" },
57    { B_CEPHFS_DEV, "cephfs" },
58    { B_ELASTO_DEV, "elasto" },
59    { 0, NULL }
60 };
61 
62 
63 /**
64  * All loaded backends.
65  */
66 static alist *loaded_backends = NULL;
67 static alist *backend_dirs = NULL;
68 
SdSetBackendDirs(alist * new_backend_dirs)69 void SdSetBackendDirs(alist *new_backend_dirs)
70 {
71    backend_dirs = new_backend_dirs;
72 }
73 
lookup_backend_interface_mapping(int device_type)74 static inline backend_interface_mapping_t *lookup_backend_interface_mapping(int device_type)
75 {
76    backend_interface_mapping_t *backend_interface_mapping;
77 
78    for (backend_interface_mapping = backend_interface_mappings;
79         backend_interface_mapping->interface_name != NULL;
80         backend_interface_mapping++) {
81 
82       /*
83        * See if this is a match.
84        */
85       if (backend_interface_mapping->interface_type_id == device_type) {
86          return backend_interface_mapping;
87       }
88    }
89 
90    return NULL;
91 }
92 
init_backend_dev(JobControlRecord * jcr,int device_type)93 Device *init_backend_dev(JobControlRecord *jcr, int device_type)
94 {
95    struct stat st;
96    char *backend_dir = nullptr;
97    void *dl_handle = NULL;
98    PoolMem shared_library_name(PM_FNAME);
99    PoolMem error(PM_FNAME);
100    backend_interface_mapping_t *backend_interface_mapping;
101    backend_shared_library_t *backend_shared_library = nullptr;
102    t_backend_instantiate backend_instantiate;
103    t_flush_backend flush_backend;
104 
105    /*
106     * For dynamic loading storage backends there must be a list of backend dirs set.
107     */
108    if (!backend_dirs) {
109       Jmsg(jcr, M_ERROR_TERM, 0, _("Catalog Backends Dir not configured.\n"));
110    }
111 
112    backend_interface_mapping = lookup_backend_interface_mapping(device_type);
113    if (backend_interface_mapping == NULL) {
114       return (Device *)NULL;
115    }
116 
117    /*
118     * See if the backend is already loaded.
119     */
120    if (loaded_backends) {
121       foreach_alist(backend_shared_library, loaded_backends) {
122          if (backend_shared_library->interface_type_id ==
123              backend_interface_mapping->interface_type_id) {
124             return backend_shared_library->backend_instantiate(jcr, device_type);
125          }
126       }
127    }
128 
129    /*
130     * This is a new backend try to use dynamic loading to load the backend library.
131     */
132    foreach_alist(backend_dir, backend_dirs) {
133       Mmsg(shared_library_name, "%s/libbareossd-%s%s", backend_dir,
134            backend_interface_mapping->interface_name, DYN_LIB_EXTENSION);
135       Dmsg3(100, "init_backend_dev: testing backend %s/libbareossd-%s%s\n",
136             backend_dir, backend_interface_mapping->interface_name, DYN_LIB_EXTENSION);
137 
138       /*
139        * Make sure the shared library with this name exists.
140        */
141       if (stat(shared_library_name.c_str(), &st) == 0) {
142          dl_handle = dlopen(shared_library_name.c_str(), RTLD_NOW);
143          if (!dl_handle) {
144             PmStrcpy(error, dlerror());
145             Jmsg(jcr, M_ERROR, 0, _("Unable to load shared library: %s ERR=%s\n"),
146                  shared_library_name.c_str(), error.c_str());
147             Dmsg2(100, _("Unable to load shared library: %s ERR=%s\n"),
148                   shared_library_name.c_str(), error.c_str());
149             continue;
150          }
151 
152          /*
153           * Lookup the backend_instantiate function.
154           */
155          backend_instantiate = (t_backend_instantiate)dlsym(dl_handle, "backend_instantiate");
156          if (backend_instantiate == NULL) {
157             PmStrcpy(error, dlerror());
158             Jmsg(jcr, M_ERROR, 0, _("Lookup of backend_instantiate in shared library %s failed: ERR=%s\n"),
159                  shared_library_name.c_str(), error.c_str());
160             Dmsg2(100, _("Lookup of backend_instantiate in shared library %s failed: ERR=%s\n"),
161                   shared_library_name.c_str(), error.c_str());
162             dlclose(dl_handle);
163             dl_handle = NULL;
164             continue;
165          }
166 
167          /*
168           * Lookup the flush_backend function.
169           */
170          flush_backend = (t_flush_backend)dlsym(dl_handle, "flush_backend");
171          if (flush_backend == NULL) {
172             PmStrcpy(error, dlerror());
173             Jmsg(jcr, M_ERROR, 0, _("Lookup of flush_backend in shared library %s failed: ERR=%s\n"),
174                  shared_library_name.c_str(), error.c_str());
175             Dmsg2(100, _("Lookup of flush_backend in shared library %s failed: ERR=%s\n"),
176                   shared_library_name.c_str(), error.c_str());
177             dlclose(dl_handle);
178             dl_handle = NULL;
179             continue;
180          }
181 
182          /*
183           * We found the shared library and it has the right entry points.
184           */
185          break;
186       }
187    }
188 
189    if (dl_handle) {
190       /*
191        * Create a new loaded shared library entry and tack it onto the list of loaded backend shared libs.
192        */
193       backend_shared_library = (backend_shared_library_t *)malloc(sizeof(backend_shared_library_t));
194       backend_shared_library->interface_type_id = backend_interface_mapping->interface_type_id;
195       backend_shared_library->handle = dl_handle;
196       backend_shared_library->backend_instantiate = backend_instantiate;
197       backend_shared_library->flush_backend = flush_backend;
198 
199       if (loaded_backends == NULL) {
200          loaded_backends = New(alist(10, not_owned_by_alist));
201       }
202       loaded_backends->append(backend_shared_library);
203 
204       return backend_shared_library->backend_instantiate(jcr, device_type);
205    } else {
206       Jmsg(jcr, M_ERROR_TERM, 0, _("Unable to load any shared library for libbareossd-%s%s\n"),
207            backend_interface_mapping->interface_name, DYN_LIB_EXTENSION);
208       return (Device *)NULL;
209    }
210 }
211 
DevFlushBackends()212 void DevFlushBackends()
213 {
214    backend_shared_library_t *backend_shared_library = nullptr;
215 
216    if (loaded_backends) {
217       foreach_alist(backend_shared_library, loaded_backends) {
218          /*
219           * Call the flush entry point in the lib.
220           */
221          backend_shared_library->flush_backend();
222 
223          /*
224           * Close the shared library and unload it.
225           */
226          dlclose(backend_shared_library->handle);
227          free(backend_shared_library);
228       }
229 
230       delete loaded_backends;
231       loaded_backends = NULL;
232    }
233 }
234 
235 } /* namespace storagedaemon */
236 
237 #endif /* HAVE_DYNAMIC_SD_BACKENDS */
238