1 /*
2  * lock_manager.c: Implements the internal lock manager API
3  *
4  * Copyright (C) 2010-2011 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library.  If not, see
18  * <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #include <config.h>
23 
24 #include "lock_manager.h"
25 #include "lock_driver_nop.h"
26 #include "virerror.h"
27 #include "virfile.h"
28 #include "virlog.h"
29 #include "viralloc.h"
30 #include "viruuid.h"
31 #include "virstring.h"
32 
33 #if WITH_DLFCN_H
34 # include <dlfcn.h>
35 #endif
36 #include <unistd.h>
37 
38 #include "configmake.h"
39 
40 #define VIR_FROM_THIS VIR_FROM_LOCKING
41 
42 VIR_LOG_INIT("locking.lock_manager");
43 
44 #define CHECK_DRIVER(field, errret) \
45     if (!driver->field) { \
46         virReportError(VIR_ERR_INTERNAL_ERROR, \
47                      _("Missing '%s' field in lock manager driver"), \
48                      #field); \
49         return errret; \
50     }
51 
52 #define CHECK_MANAGER(field, errret) \
53     if (!lock->driver->field) { \
54         virReportError(VIR_ERR_INTERNAL_ERROR, \
55                        _("Missing '%s' field in lock manager driver"), \
56                        #field); \
57         return errret; \
58     }
59 
60 struct _virLockManagerPlugin {
61     char *name;
62     virLockDriver *driver;
63     void *handle;
64     int refs;
65 };
66 
virLockManagerLogParams(size_t nparams,virLockManagerParam * params)67 static void virLockManagerLogParams(size_t nparams,
68                                     virLockManagerParam *params)
69 {
70     size_t i;
71     char uuidstr[VIR_UUID_STRING_BUFLEN];
72     for (i = 0; i < nparams; i++) {
73         switch (params[i].type) {
74         case VIR_LOCK_MANAGER_PARAM_TYPE_INT:
75             VIR_DEBUG("  key=%s type=int value=%d", params[i].key, params[i].value.iv);
76             break;
77         case VIR_LOCK_MANAGER_PARAM_TYPE_UINT:
78             VIR_DEBUG("  key=%s type=uint value=%u", params[i].key, params[i].value.ui);
79             break;
80         case VIR_LOCK_MANAGER_PARAM_TYPE_LONG:
81             VIR_DEBUG("  key=%s type=long value=%lld", params[i].key, params[i].value.l);
82             break;
83         case VIR_LOCK_MANAGER_PARAM_TYPE_ULONG:
84             VIR_DEBUG("  key=%s type=ulong value=%llu", params[i].key, params[i].value.ul);
85             break;
86         case VIR_LOCK_MANAGER_PARAM_TYPE_DOUBLE:
87             VIR_DEBUG("  key=%s type=double value=%lf", params[i].key, params[i].value.d);
88             break;
89         case VIR_LOCK_MANAGER_PARAM_TYPE_STRING:
90             VIR_DEBUG("  key=%s type=string value=%s", params[i].key, params[i].value.str);
91             break;
92         case VIR_LOCK_MANAGER_PARAM_TYPE_CSTRING:
93             VIR_DEBUG("  key=%s type=cstring value=%s", params[i].key, params[i].value.cstr);
94             break;
95         case VIR_LOCK_MANAGER_PARAM_TYPE_UUID:
96             virUUIDFormat(params[i].value.uuid, uuidstr);
97             VIR_DEBUG("  key=%s type=uuid value=%s", params[i].key, uuidstr);
98             break;
99         }
100     }
101 }
102 
103 
104 /**
105  * virLockManagerPluginNew:
106  * @name: the name of the plugin
107  * @flag: optional plugin flags
108  *
109  * Attempt to load the plugin $(libdir)/libvirt/lock-driver/@name.so
110  * The plugin driver entry point will be resolved & invoked to obtain
111  * the lock manager driver.
112  *
113  * Even if the loading of the plugin succeeded, this may still
114  * return NULL if the plugin impl decided that we (libvirtd)
115  * are too old to support a feature it requires
116  *
117  * Returns a plugin object, or NULL if loading failed.
118  */
119 #if WITH_DLFCN_H
virLockManagerPluginNew(const char * name,const char * driverName,const char * configDir,unsigned int flags)120 virLockManagerPlugin *virLockManagerPluginNew(const char *name,
121                                                 const char *driverName,
122                                                 const char *configDir,
123                                                 unsigned int flags)
124 {
125     void *handle = NULL;
126     virLockDriver *driver;
127     virLockManagerPlugin *plugin = NULL;
128     char *modfile = NULL;
129     char *configFile = NULL;
130 
131     VIR_DEBUG("name=%s driverName=%s configDir=%s flags=0x%x",
132               name, driverName, configDir, flags);
133 
134     configFile = g_strdup_printf("%s/%s-%s.conf", configDir, driverName, name);
135 
136     if (STREQ(name, "nop")) {
137         driver = &virLockDriverNop;
138     } else {
139         if (!(modfile = virFileFindResourceFull(name,
140                                                 NULL,
141                                                 VIR_FILE_MODULE_EXT,
142                                                 abs_top_builddir "/src",
143                                                 LIBDIR "/libvirt/lock-driver",
144                                                 "LIBVIRT_LOCK_MANAGER_PLUGIN_DIR")))
145             goto cleanup;
146 
147         VIR_DEBUG("Module load %s from %s", name, modfile);
148 
149         if (access(modfile, R_OK) < 0) {
150             virReportSystemError(errno,
151                                  _("Plugin %s not accessible"),
152                                  modfile);
153             goto cleanup;
154         }
155 
156         handle = dlopen(modfile, RTLD_NOW | RTLD_LOCAL);
157         if (!handle) {
158             virReportError(VIR_ERR_SYSTEM_ERROR,
159                            _("Failed to load plugin %s: %s"),
160                            modfile, dlerror());
161             goto cleanup;
162         }
163 
164         if (!(driver = dlsym(handle, "virLockDriverImpl"))) {
165             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
166                            _("Missing plugin initialization symbol 'virLockDriverImpl'"));
167             goto cleanup;
168         }
169     }
170 
171     if (driver->drvInit(VIR_LOCK_MANAGER_VERSION, configFile, flags) < 0)
172         goto cleanup;
173 
174     plugin = g_new0(virLockManagerPlugin, 1);
175 
176     plugin->driver = driver;
177     plugin->handle = handle;
178     plugin->refs = 1;
179     plugin->name = g_strdup(name);
180 
181     VIR_FREE(configFile);
182     VIR_FREE(modfile);
183     return plugin;
184 
185  cleanup:
186     VIR_FREE(configFile);
187     VIR_FREE(plugin);
188     VIR_FREE(modfile);
189     if (handle)
190         dlclose(handle);
191     return NULL;
192 }
193 #else /* !WITH_DLFCN_H */
194 virLockManagerPlugin *
virLockManagerPluginNew(const char * name G_GNUC_UNUSED,const char * driverName G_GNUC_UNUSED,const char * configDir G_GNUC_UNUSED,unsigned int flags_unused G_GNUC_UNUSED)195 virLockManagerPluginNew(const char *name G_GNUC_UNUSED,
196                         const char *driverName G_GNUC_UNUSED,
197                         const char *configDir G_GNUC_UNUSED,
198                         unsigned int flags_unused G_GNUC_UNUSED)
199 {
200     virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
201                    _("this platform is missing dlopen"));
202     return NULL;
203 }
204 #endif /* !WITH_DLFCN_H */
205 
206 
207 /**
208  * virLockManagerPluginRef:
209  * @plugin: the plugin implementation to ref
210  *
211  * Acquires an additional reference on the plugin.
212  */
virLockManagerPluginRef(virLockManagerPlugin * plugin)213 void virLockManagerPluginRef(virLockManagerPlugin *plugin)
214 {
215     plugin->refs++;
216 }
217 
218 
219 /**
220  * virLockManagerPluginUnref:
221  * @plugin: the plugin implementation to unref
222  *
223  * Releases a reference on the plugin. When the last reference
224  * is released, it will attempt to unload the plugin from memory.
225  * The plugin may refuse to allow unloading if this would
226  * result in an unsafe scenario.
227  *
228  */
229 #if WITH_DLFCN_H
virLockManagerPluginUnref(virLockManagerPlugin * plugin)230 void virLockManagerPluginUnref(virLockManagerPlugin *plugin)
231 {
232     if (!plugin)
233         return;
234 
235     plugin->refs--;
236 
237     if (plugin->refs > 0)
238         return;
239 
240     if (plugin->driver->drvDeinit() >= 0) {
241         if (plugin->handle)
242             dlclose(plugin->handle);
243     } else {
244         VIR_WARN("Unable to unload lock maanger plugin from memory");
245         return;
246     }
247 
248     g_free(plugin->name);
249     g_free(plugin);
250 }
251 #else /* !WITH_DLFCN_H */
virLockManagerPluginUnref(virLockManagerPlugin * plugin G_GNUC_UNUSED)252 void virLockManagerPluginUnref(virLockManagerPlugin *plugin G_GNUC_UNUSED)
253 {
254 }
255 #endif /* !WITH_DLFCN_H */
256 
257 
virLockManagerPluginGetName(virLockManagerPlugin * plugin)258 const char *virLockManagerPluginGetName(virLockManagerPlugin *plugin)
259 {
260     VIR_DEBUG("plugin=%p", plugin);
261 
262     return plugin->name;
263 }
264 
265 
virLockManagerPluginUsesState(virLockManagerPlugin * plugin)266 bool virLockManagerPluginUsesState(virLockManagerPlugin *plugin)
267 {
268     VIR_DEBUG("plugin=%p", plugin);
269 
270     return plugin->driver->flags & VIR_LOCK_MANAGER_USES_STATE;
271 }
272 
273 
virLockManagerPluginGetDriver(virLockManagerPlugin * plugin)274 virLockDriver *virLockManagerPluginGetDriver(virLockManagerPlugin *plugin)
275 {
276     VIR_DEBUG("plugin=%p", plugin);
277 
278     return plugin->driver;
279 }
280 
281 /**
282  * virLockManagerNew:
283  * @driver: the lock manager implementation to use
284  * @type: the type of process to be supervised
285  * @flags: bitwise-OR of virLockManagerNewFlags
286  *
287  * Create a new context to supervise a process, usually
288  * a virtual machine.
289  *
290  * Returns a new lock manager context
291  */
virLockManagerNew(virLockDriver * driver,unsigned int type,size_t nparams,virLockManagerParam * params,unsigned int flags)292 virLockManager *virLockManagerNew(virLockDriver *driver,
293                                     unsigned int type,
294                                     size_t nparams,
295                                     virLockManagerParam *params,
296                                     unsigned int flags)
297 {
298     virLockManager *lock;
299     VIR_DEBUG("driver=%p type=%u nparams=%zu params=%p flags=0x%x",
300               driver, type, nparams, params, flags);
301     virLockManagerLogParams(nparams, params);
302 
303     CHECK_DRIVER(drvNew, NULL);
304 
305     lock = g_new0(virLockManager, 1);
306 
307     lock->driver = driver;
308 
309     if (driver->drvNew(lock, type, nparams, params, flags) < 0) {
310         VIR_FREE(lock);
311         return NULL;
312     }
313 
314     return lock;
315 }
316 
317 
virLockManagerAddResource(virLockManager * lock,unsigned int type,const char * name,size_t nparams,virLockManagerParam * params,unsigned int flags)318 int virLockManagerAddResource(virLockManager *lock,
319                               unsigned int type,
320                               const char *name,
321                               size_t nparams,
322                               virLockManagerParam *params,
323                               unsigned int flags)
324 {
325     VIR_DEBUG("lock=%p type=%u name=%s nparams=%zu params=%p flags=0x%x",
326               lock, type, name, nparams, params, flags);
327     virLockManagerLogParams(nparams, params);
328 
329     CHECK_MANAGER(drvAddResource, -1);
330 
331     return lock->driver->drvAddResource(lock,
332                                         type, name,
333                                         nparams, params,
334                                         flags);
335 }
336 
virLockManagerAcquire(virLockManager * lock,const char * state,unsigned int flags,virDomainLockFailureAction action,int * fd)337 int virLockManagerAcquire(virLockManager *lock,
338                           const char *state,
339                           unsigned int flags,
340                           virDomainLockFailureAction action,
341                           int *fd)
342 {
343     VIR_DEBUG("lock=%p state='%s' flags=0x%x action=%d fd=%p",
344               lock, NULLSTR(state), flags, action, fd);
345 
346     CHECK_MANAGER(drvAcquire, -1);
347 
348     if (fd)
349         *fd = -1;
350 
351     return lock->driver->drvAcquire(lock, state, flags, action, fd);
352 }
353 
354 
virLockManagerRelease(virLockManager * lock,char ** state,unsigned int flags)355 int virLockManagerRelease(virLockManager *lock,
356                           char **state,
357                           unsigned int flags)
358 {
359     VIR_DEBUG("lock=%p state=%p flags=0x%x", lock, state, flags);
360 
361     CHECK_MANAGER(drvRelease, -1);
362 
363     return lock->driver->drvRelease(lock, state, flags);
364 }
365 
366 
virLockManagerInquire(virLockManager * lock,char ** state,unsigned int flags)367 int virLockManagerInquire(virLockManager *lock,
368                           char **state,
369                           unsigned int flags)
370 {
371     VIR_DEBUG("lock=%p state=%p flags=0x%x", lock, state, flags);
372 
373     CHECK_MANAGER(drvInquire, -1);
374 
375     return lock->driver->drvInquire(lock, state, flags);
376 }
377 
378 
virLockManagerFree(virLockManager * lock)379 int virLockManagerFree(virLockManager *lock)
380 {
381     VIR_DEBUG("lock=%p", lock);
382 
383     if (!lock)
384         return 0;
385 
386     CHECK_MANAGER(drvFree, -1);
387 
388     lock->driver->drvFree(lock);
389 
390     g_free(lock);
391 
392     return 0;
393 }
394