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