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