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"},     {B_TAPE_DEV, "tape"},
53     {B_GFAPI_DEV, "gfapi"},   {B_DROPLET_DEV, "droplet"},
54     {B_RADOS_DEV, "rados"},   {B_CEPHFS_DEV, "cephfs"},
55     {B_ELASTO_DEV, "elasto"}, {0, NULL}};
56 
57 
58 /**
59  * All loaded backends.
60  */
61 static alist* loaded_backends = NULL;
62 static std::vector<std::string> backend_dirs;
63 
SdSetBackendDirs(std::vector<std::string> && new_backend_dirs)64 void SdSetBackendDirs(std::vector<std::string>&& new_backend_dirs)
65 {
66   backend_dirs = new_backend_dirs;
67 }
68 
lookup_backend_interface_mapping(int device_type)69 static inline backend_interface_mapping_t* lookup_backend_interface_mapping(
70     int device_type)
71 {
72   backend_interface_mapping_t* backend_interface_mapping;
73 
74   for (backend_interface_mapping = backend_interface_mappings;
75        backend_interface_mapping->interface_name != NULL;
76        backend_interface_mapping++) {
77     /*
78      * See if this is a match.
79      */
80     if (backend_interface_mapping->interface_type_id == device_type) {
81       return backend_interface_mapping;
82     }
83   }
84 
85   return NULL;
86 }
87 
init_backend_dev(JobControlRecord * jcr,int device_type)88 Device* init_backend_dev(JobControlRecord* jcr, int device_type)
89 {
90   struct stat st;
91   void* dl_handle = NULL;
92   PoolMem shared_library_name(PM_FNAME);
93   PoolMem error(PM_FNAME);
94   backend_interface_mapping_t* backend_interface_mapping;
95   backend_shared_library_t* backend_shared_library = nullptr;
96   t_backend_instantiate backend_instantiate;
97   t_flush_backend flush_backend;
98 
99   /*
100    * For dynamic loading storage backends there must be a list of backend dirs
101    * set.
102    */
103   if (backend_dirs.empty()) {
104     Jmsg(jcr, M_ERROR_TERM, 0, _("Catalog Backends Dir not configured.\n"));
105   }
106 
107   backend_interface_mapping = lookup_backend_interface_mapping(device_type);
108   if (backend_interface_mapping == NULL) { return (Device*)NULL; }
109 
110   /*
111    * See if the backend is already loaded.
112    */
113   if (loaded_backends) {
114     foreach_alist (backend_shared_library, loaded_backends) {
115       if (backend_shared_library->interface_type_id ==
116           backend_interface_mapping->interface_type_id) {
117         return backend_shared_library->backend_instantiate(jcr, device_type);
118       }
119     }
120   }
121 
122   /*
123    * This is a new backend try to use dynamic loading to load the backend
124    * library.
125    */
126   for (const auto& backend_dir : backend_dirs) {
127     Mmsg(shared_library_name, "%s/libbareossd-%s%s", backend_dir.c_str(),
128          backend_interface_mapping->interface_name, DYN_LIB_EXTENSION);
129     Dmsg3(100, "init_backend_dev: testing backend %s/libbareossd-%s%s\n",
130           backend_dir.c_str(), backend_interface_mapping->interface_name,
131           DYN_LIB_EXTENSION);
132 
133     /*
134      * Make sure the shared library with this name exists.
135      */
136     if (stat(shared_library_name.c_str(), &st) == 0) {
137       dl_handle = dlopen(shared_library_name.c_str(), RTLD_NOW);
138       if (!dl_handle) {
139         PmStrcpy(error, dlerror());
140         Jmsg(jcr, M_ERROR, 0, _("Unable to load shared library: %s ERR=%s\n"),
141              shared_library_name.c_str(), error.c_str());
142         Dmsg2(100, _("Unable to load shared library: %s ERR=%s\n"),
143               shared_library_name.c_str(), error.c_str());
144         continue;
145       }
146 
147       /*
148        * Lookup the backend_instantiate function.
149        */
150       backend_instantiate =
151           (t_backend_instantiate)dlsym(dl_handle, "backend_instantiate");
152       if (backend_instantiate == NULL) {
153         PmStrcpy(error, dlerror());
154         Jmsg(jcr, M_ERROR, 0,
155              _("Lookup of backend_instantiate in shared library %s failed: "
156                "ERR=%s\n"),
157              shared_library_name.c_str(), error.c_str());
158         Dmsg2(100,
159               _("Lookup of backend_instantiate in shared library %s failed: "
160                 "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,
174              _("Lookup of flush_backend in shared library %s failed: ERR=%s\n"),
175              shared_library_name.c_str(), error.c_str());
176         Dmsg2(
177             100,
178             _("Lookup of flush_backend in shared library %s failed: ERR=%s\n"),
179             shared_library_name.c_str(), error.c_str());
180         dlclose(dl_handle);
181         dl_handle = NULL;
182         continue;
183       }
184 
185       /*
186        * We found the shared library and it has the right entry points.
187        */
188       break;
189     }
190   }
191 
192   if (dl_handle) {
193     /*
194      * Create a new loaded shared library entry and tack it onto the list of
195      * loaded backend shared libs.
196      */
197     backend_shared_library =
198         (backend_shared_library_t*)malloc(sizeof(backend_shared_library_t));
199     backend_shared_library->interface_type_id =
200         backend_interface_mapping->interface_type_id;
201     backend_shared_library->handle = dl_handle;
202     backend_shared_library->backend_instantiate = backend_instantiate;
203     backend_shared_library->flush_backend = flush_backend;
204 
205     if (loaded_backends == NULL) {
206       loaded_backends = new alist(10, not_owned_by_alist);
207     }
208     loaded_backends->append(backend_shared_library);
209 
210     return backend_shared_library->backend_instantiate(jcr, device_type);
211   } else {
212     Jmsg(jcr, M_ERROR_TERM, 0,
213          _("Unable to load any shared library for libbareossd-%s%s\n"),
214          backend_interface_mapping->interface_name, DYN_LIB_EXTENSION);
215     return (Device*)NULL;
216   }
217 }
218 
DevFlushBackends()219 void DevFlushBackends()
220 {
221   backend_shared_library_t* backend_shared_library = nullptr;
222 
223   if (loaded_backends) {
224     foreach_alist (backend_shared_library, loaded_backends) {
225       /*
226        * Call the flush entry point in the lib.
227        */
228       backend_shared_library->flush_backend();
229 
230       /*
231        * Close the shared library and unload it.
232        */
233       dlclose(backend_shared_library->handle);
234       free(backend_shared_library);
235     }
236 
237     delete loaded_backends;
238     loaded_backends = NULL;
239   }
240 }
241 
242 } /* namespace storagedaemon */
243 
244 #endif /* HAVE_DYNAMIC_SD_BACKENDS */
245