Adds a new driver type.
Changes since the second submission:
- Update for changed public API
- Add virSecretPtr handling
- s/secret_id/uuid/g
- use "unsigned char *" for secret value
---
include/libvirt/virterror.h | 1 +
src/datatypes.c | 153 +++++++++++++++++++++++++++++++++++++++++++
src/datatypes.h | 28 ++++++++
src/driver.h | 57 ++++++++++++++++
src/libvirt.c | 55 +++++++++++++++
src/libvirt_private.syms | 2 +
src/virterror.c | 6 ++
7 files changed, 302 insertions(+), 0 deletions(-)
diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h
index e4d013f..5cbb120 100644
--- a/include/libvirt/virterror.h
+++ b/include/libvirt/virterror.h
@@ -166,6 +166,7 @@ typedef enum {
VIR_ERR_NO_INTERFACE, /* interface driver not running */
VIR_ERR_INVALID_INTERFACE, /* invalid interface object */
VIR_ERR_MULTIPLE_INTERFACES, /* more than one matching interface found */
+ VIR_WAR_NO_SECRET, /* failed to start secret storage */
} virErrorNumber;
/**
diff --git a/src/datatypes.c b/src/datatypes.c
index 8b3f8c3..5db3899 100644
--- a/src/datatypes.c
+++ b/src/datatypes.c
@@ -109,6 +109,23 @@ virStorageVolFreeName(virStorageVolPtr vol, const char *name
ATTRIBUTE_UNUSED)
}
/**
+ * virSecretFreeName:
+ * @secret_: a secret object
+ *
+ * Destroy the vol object, this is just used by the vol hash callback.
+ *
+ * Returns 0 in case of success and -1 in case of failure.
+ */
+static void
+virSecretFreeName(void *secret_, const char *name ATTRIBUTE_UNUSED)
+{
+ virSecretPtr secret;
+
+ secret = secret_;
+ virUnrefSecret(secret);
+}
+
+/**
* virGetConnect:
*
* Allocates a new hypervisor connection structure
@@ -152,6 +169,9 @@ virGetConnect(void) {
ret->nodeDevices = virHashCreate(256);
if (ret->nodeDevices == NULL)
goto failed;
+ ret->secrets = virHashCreate(20);
+ if (ret->secrets == NULL)
+ goto failed;
ret->refs = 1;
return(ret);
@@ -170,6 +190,8 @@ failed:
virHashFree(ret->storageVols, (virHashDeallocator)
virStorageVolFreeName);
if (ret->nodeDevices != NULL)
virHashFree(ret->nodeDevices, (virHashDeallocator) virNodeDeviceFree);
+ if (ret->secrets != NULL)
+ virHashFree(ret->secrets, virSecretFreeName);
virMutexDestroy(&ret->lock);
VIR_FREE(ret);
@@ -201,6 +223,8 @@ virReleaseConnect(virConnectPtr conn) {
virHashFree(conn->storageVols, (virHashDeallocator) virStorageVolFreeName);
if (conn->nodeDevices != NULL)
virHashFree(conn->nodeDevices, (virHashDeallocator) virNodeDeviceFree);
+ if (conn->secrets != NULL)
+ virHashFree(conn->secrets, virSecretFreeName);
virResetError(&conn->err);
@@ -1109,3 +1133,132 @@ virUnrefNodeDevice(virNodeDevicePtr dev) {
virMutexUnlock(&dev->conn->lock);
return (refs);
}
+
+/**
+ * virGetSecret:
+ * @conn: the hypervisor connection
+ * @uuid: secret UUID
+ *
+ * Lookup if the secret is already registered for that connection, if so return
+ * a pointer to it, otherwise allocate a new structure, and register it in the
+ * table. In any case a corresponding call to virFreeSecret() is needed to not
+ * leak data.
+ *
+ * Returns a pointer to the secret, or NULL in case of failure
+ */
+virSecretPtr
+virGetSecret(virConnectPtr conn, const char *uuid)
+{
+ virSecretPtr ret = NULL;
+
+ if (!VIR_IS_CONNECT(conn) || uuid == NULL) {
+ virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+ return NULL;
+ }
+ virMutexLock(&conn->lock);
+
+ ret = virHashLookup(conn->secrets, uuid);
+ if (ret == NULL) {
+ if (VIR_ALLOC(ret) < 0) {
+ virMutexUnlock(&conn->lock);
+ virReportOOMError(conn);
+ goto error;
+ }
+ ret->magic = VIR_SECRET_MAGIC;
+ ret->conn = conn;
+ ret->uuid = strdup(uuid);
+ if (ret->uuid == NULL) {
+ virMutexUnlock(&conn->lock);
+ virReportOOMError(conn);
+ goto error;
+ }
+
+ if (virHashAddEntry(conn->secrets, uuid, ret) < 0) {
+ virMutexUnlock(&conn->lock);
+ virLibConnError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("failed to add secret to conn hash
table"));
+ goto error;
+ }
+ conn->refs++;
+ }
+ ret->refs++;
+ virMutexUnlock(&conn->lock);
+ return ret;
+
+error:
+ if (ret != NULL) {
+ VIR_FREE(ret->uuid);
+ VIR_FREE(ret);
+ }
+ return NULL;
+}
+
+/**
+ * virReleaseSecret:
+ * @secret: the secret to release
+ *
+ * Unconditionally release all memory associated with a secret. The conn.lock
+ * mutex must be held prior to calling this, and will be released prior to this
+ * returning. The secret obj must not be used once this method returns.
+ *
+ * It will also unreference the associated connection object, which may also be
+ * released if its ref count hits zero.
+ */
+static void
+virReleaseSecret(virSecretPtr secret) {
+ virConnectPtr conn = secret->conn;
+ DEBUG("release secret %p %s", secret, secret->uuid);
+
+ if (virHashRemoveEntry(conn->secrets, secret->uuid, NULL) < 0) {
+ virMutexUnlock(&conn->lock);
+ virLibConnError(conn, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("secret missing from connection hash
table"));
+ conn = NULL;
+ }
+
+ secret->magic = -1;
+ VIR_FREE(secret->uuid);
+ VIR_FREE(secret);
+
+ if (conn) {
+ DEBUG("unref connection %p %d", conn, conn->refs);
+ conn->refs--;
+ if (conn->refs == 0) {
+ virReleaseConnect(conn);
+ /* Already unlocked mutex */
+ return;
+ }
+ virMutexUnlock(&conn->lock);
+ }
+}
+
+/**
+ * virUnrefSecret:
+ * @secret: the secret to unreference
+ *
+ * Unreference the secret. If the use count drops to zero, the structure is
+ * actually freed.
+ *
+ * Returns the reference count or -1 in case of failure.
+ */
+int
+virUnrefSecret(virSecretPtr secret) {
+ int refs;
+
+ if (!VIR_IS_CONNECTED_SECRET(secret)) {
+ virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+ return -1;
+ }
+ virMutexLock(&secret->conn->lock);
+ DEBUG("unref secret %p %s %d", secret, secret->uuid, secret->refs);
+ secret->refs--;
+ refs = secret->refs;
+ if (refs == 0) {
+ virReleaseSecret(secret);
+ /* Already unlocked mutex */
+ return 0;
+ }
+
+ virMutexUnlock(&secret->conn->lock);
+ return refs;
+}
diff --git a/src/datatypes.h b/src/datatypes.h
index da83e02..56c3777 100644
--- a/src/datatypes.h
+++ b/src/datatypes.h
@@ -98,6 +98,16 @@
#define VIR_IS_NODE_DEVICE(obj) ((obj) &&
(obj)->magic==VIR_NODE_DEVICE_MAGIC)
#define VIR_IS_CONNECTED_NODE_DEVICE(obj) (VIR_IS_NODE_DEVICE(obj) &&
VIR_IS_CONNECT((obj)->conn))
+/**
+ * VIR_SECRET_MAGIC:
+ *
+ * magic value used to protect the API when pointers to secret structures are
+ * passed down by the users.
+ */
+#define VIR_SECRET_MAGIC 0x5678DEAD
+#define VIR_IS_SECRET(obj) ((obj) && (obj)->magic==VIR_SECRET_MAGIC)
+#define VIR_IS_CONNECTED_SECRET(obj) (VIR_IS_SECRET(obj) &&
VIR_IS_CONNECT((obj)->conn))
+
/**
* _virConnect:
@@ -119,6 +129,7 @@ struct _virConnect {
virInterfaceDriverPtr interfaceDriver;
virStorageDriverPtr storageDriver;
virDeviceMonitorPtr deviceMonitor;
+ virSecretDriverPtr secretDriver;
/* Private data pointer which can be used by driver and
* network driver as they wish.
@@ -149,6 +160,7 @@ struct _virConnect {
virHashTablePtr storagePools;/* hash table for known storage pools */
virHashTablePtr storageVols;/* hash table for known storage vols */
virHashTablePtr nodeDevices; /* hash table for known node devices */
+ virHashTablePtr secrets; /* hash taboe for known secrets */
int refs; /* reference count */
};
@@ -233,6 +245,18 @@ struct _virNodeDevice {
char *parent; /* parent device name */
};
+/**
+ * _virSecret:
+ *
+ * Internal structure associated with a secret
+ */
+struct _virSecret {
+ unsigned int magic; /* specific value to check */
+ int refs; /* reference count */
+ virConnectPtr conn; /* pointer back to the connection */
+ char *uuid; /* ID of the secret */
+};
+
/************************************************************************
* *
@@ -270,4 +294,8 @@ virNodeDevicePtr virGetNodeDevice(virConnectPtr conn,
const char *name);
int virUnrefNodeDevice(virNodeDevicePtr dev);
+virSecretPtr virGetSecret(virConnectPtr conn,
+ const char *uuid);
+int virUnrefSecret(virSecretPtr secret);
+
#endif
diff --git a/src/driver.h b/src/driver.h
index 79d46ff..9ed76bf 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -6,6 +6,9 @@
#ifndef __VIR_DRIVER_H__
#define __VIR_DRIVER_H__
+#include "config.h"
+#include <stdbool.h>
+
#include <libxml/uri.h>
#include "internal.h"
@@ -799,6 +802,59 @@ struct _virDeviceMonitor {
virDrvNodeDeviceDestroy deviceDestroy;
};
+typedef virSecretPtr
+ (*virDrvSecretLookupByUUIDString) (virConnectPtr conn,
+ const char *uuid);
+typedef virSecretPtr
+ (*virDrvSecretDefineXML) (virConnectPtr conn,
+ const char *xml);
+typedef char *
+ (*virDrvSecretGetXMLDesc) (virSecretPtr secret);
+typedef int
+ (*virDrvSecretSetValue) (virSecretPtr secret,
+ const unsigned char *value,
+ size_t value_size);
+typedef unsigned char *
+ (*virDrvSecretGetValue) (virSecretPtr secret,
+ size_t *value_size,
+ bool libvirt_internal_call);
+typedef int
+ (*virDrvSecretUndefine) (virSecretPtr secret);
+typedef int
+ (*virDrvSecretNumOfSecrets) (virConnectPtr conn);
+typedef int
+ (*virDrvSecretListSecrets) (virConnectPtr conn,
+ char **uuids,
+ int maxuuids);
+
+typedef struct _virSecretDriver virSecretDriver;
+typedef virSecretDriver *virSecretDriverPtr;
+
+/**
+ * _virSecretDriver:
+ *
+ * Structure associated to a driver for storing secrets, defining the various
+ * entry points for it.
+ *
+ * All drivers must support the following fields/methods:
+ * - open
+ * - close
+ */
+struct _virSecretDriver {
+ const char *name;
+ virDrvOpen open;
+ virDrvClose close;
+
+ virDrvSecretNumOfSecrets numOfSecrets;
+ virDrvSecretListSecrets listSecrets;
+ virDrvSecretLookupByUUIDString lookupByUUIDString;
+ virDrvSecretDefineXML defineXML;
+ virDrvSecretGetXMLDesc getXMLDesc;
+ virDrvSecretSetValue setValue;
+ virDrvSecretGetValue getValue;
+ virDrvSecretUndefine undefine;
+};
+
/*
* Registration
* TODO: also need ways to (des)activate a given driver
@@ -809,6 +865,7 @@ int virRegisterNetworkDriver(virNetworkDriverPtr);
int virRegisterInterfaceDriver(virInterfaceDriverPtr);
int virRegisterStorageDriver(virStorageDriverPtr);
int virRegisterDeviceMonitor(virDeviceMonitorPtr);
+int virRegisterSecretDriver(virSecretDriverPtr);
#ifdef WITH_LIBVIRTD
int virRegisterStateDriver(virStateDriverPtr);
#endif
diff --git a/src/libvirt.c b/src/libvirt.c
index e450ad9..d6a023f 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -86,6 +86,8 @@ static virStorageDriverPtr virStorageDriverTab[MAX_DRIVERS];
static int virStorageDriverTabCount = 0;
static virDeviceMonitorPtr virDeviceMonitorTab[MAX_DRIVERS];
static int virDeviceMonitorTabCount = 0;
+static virSecretDriverPtr virSecretDriverTab[MAX_DRIVERS];
+static int virSecretDriverTabCount = 0;
#ifdef WITH_LIBVIRTD
static virStateDriverPtr virStateDriverTab[MAX_DRIVERS];
static int virStateDriverTabCount = 0;
@@ -684,6 +686,37 @@ virRegisterDeviceMonitor(virDeviceMonitorPtr driver)
}
/**
+ * virRegisterSecretDriver:
+ * @driver: pointer to a secret driver block
+ *
+ * Register a secret driver
+ *
+ * Returns the driver priority or -1 in case of error.
+ */
+int
+virRegisterSecretDriver(virSecretDriverPtr driver)
+{
+ if (virInitialize() < 0)
+ return -1;
+
+ if (driver == NULL) {
+ virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+ return(-1);
+ }
+
+ if (virSecretDriverTabCount >= MAX_DRIVERS) {
+ virLibConnError(NULL, VIR_ERR_INVALID_ARG, __FUNCTION__);
+ return(-1);
+ }
+
+ DEBUG ("registering %s as secret driver %d",
+ driver->name, virSecretDriverTabCount);
+
+ virSecretDriverTab[virSecretDriverTabCount] = driver;
+ return virSecretDriverTabCount++;
+}
+
+/**
* virRegisterDriver:
* @driver: pointer to a driver block
*
@@ -1096,6 +1129,26 @@ do_open (const char *name,
}
}
+ /* Secret manipulation driver. Optional */
+ for (i = 0; i < virSecretDriverTabCount; i++) {
+ res = virSecretDriverTab[i]->open (ret, auth, flags);
+ DEBUG("secret driver %d %s returned %s",
+ i, virSecretDriverTab[i]->name,
+ res == VIR_DRV_OPEN_SUCCESS ? "SUCCESS" :
+ (res == VIR_DRV_OPEN_DECLINED ? "DECLINED" :
+ (res == VIR_DRV_OPEN_ERROR ? "ERROR" : "unknown
status")));
+ if (res == VIR_DRV_OPEN_ERROR) {
+ if (STREQ(virSecretDriverTab[i]->name, "remote")) {
+ virLibConnWarning (NULL, VIR_WAR_NO_SECRET,
+ "Is the daemon running ?");
+ }
+ break;
+ } else if (res == VIR_DRV_OPEN_SUCCESS) {
+ ret->secretDriver = virSecretDriverTab[i];
+ break;
+ }
+ }
+
return ret;
failed:
@@ -1229,6 +1282,8 @@ virConnectClose(virConnectPtr conn)
conn->storageDriver->close (conn);
if (conn->deviceMonitor)
conn->deviceMonitor->close (conn);
+ if (conn->secretDriver)
+ conn->secretDriver->close (conn);
conn->driver->close (conn);
if (virUnrefConnect(conn) < 0)
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 23fa01b..61f18e6 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -52,10 +52,12 @@ virGetInterface;
virGetNetwork;
virGetStoragePool;
virGetStorageVol;
+virGetSecret;
virUnrefStorageVol;
virGetNodeDevice;
virUnrefDomain;
virUnrefConnect;
+virUnrefSecret;
# domain_conf.h
diff --git a/src/virterror.c b/src/virterror.c
index 362d8ef..83a0830 100644
--- a/src/virterror.c
+++ b/src/virterror.c
@@ -1068,6 +1068,12 @@ virErrorMsg(virErrorNumber error, const char *info)
else
errmsg = _("multiple matching interfaces found: %s");
break;
+ case VIR_WAR_NO_SECRET:
+ if (info == NULL)
+ errmsg = _("Failed to find a secret storage driver");
+ else
+ errmsg = _("Failed to find a secret storage driver: %s");
+ break;
}
return (errmsg);
}
--
1.6.2.5