2011-11-25 15:25:14 +00:00
|
|
|
/**
|
2013-01-02 15:38:51 +00:00
|
|
|
* virchrdev.c: api to guarantee mutually exclusive
|
2013-01-02 15:38:52 +00:00
|
|
|
* access to domain's character devices
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
|
|
|
* Copyright (C) 2011-2012 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2011-11-25 15:25:14 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
2013-01-02 15:38:51 +00:00
|
|
|
#include "virchrdev.h"
|
2011-11-25 15:25:14 +00:00
|
|
|
#include "virhash.h"
|
2017-03-07 09:34:47 +00:00
|
|
|
#include "virfdstream.h"
|
2011-11-25 15:25:14 +00:00
|
|
|
#include "internal.h"
|
2012-12-13 15:49:48 +00:00
|
|
|
#include "virthread.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2011-11-25 15:25:14 +00:00
|
|
|
#include "virpidfile.h"
|
2012-12-12 17:59:27 +00:00
|
|
|
#include "virlog.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2011-11-25 15:25:14 +00:00
|
|
|
#include "virfile.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("conf.chrdev");
|
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
/* structure holding information about character devices
|
2011-11-25 15:25:14 +00:00
|
|
|
* open in a given domain */
|
2013-01-02 15:38:52 +00:00
|
|
|
struct _virChrdevs {
|
2011-11-25 15:25:14 +00:00
|
|
|
virMutex lock;
|
2020-10-22 17:04:18 +00:00
|
|
|
GHashTable *hash;
|
2011-11-25 15:25:14 +00:00
|
|
|
};
|
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
typedef struct _virChrdevStreamInfo virChrdevStreamInfo;
|
|
|
|
struct _virChrdevStreamInfo {
|
2021-03-11 07:16:13 +00:00
|
|
|
virChrdevs *devs;
|
2013-05-03 12:40:46 +00:00
|
|
|
char *path;
|
2011-11-25 15:25:14 +00:00
|
|
|
};
|
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
#ifdef VIR_CHRDEV_LOCK_FILE_PATH
|
2011-11-25 15:25:14 +00:00
|
|
|
/**
|
|
|
|
* Create a full filename with path to the lock file based on
|
2013-01-02 15:38:52 +00:00
|
|
|
* name/path of corresponding device
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
2013-01-02 15:38:52 +00:00
|
|
|
* @dev path of the character device
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
|
|
|
* Returns a modified name that the caller has to free, or NULL
|
|
|
|
* on error.
|
|
|
|
*/
|
2013-01-02 15:38:52 +00:00
|
|
|
static char *virChrdevLockFilePath(const char *dev)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
2020-01-08 08:18:31 +00:00
|
|
|
g_autofree char *path = NULL;
|
|
|
|
g_autofree char *sanitizedPath = NULL;
|
|
|
|
g_autofree char *devCopy = NULL;
|
2011-11-25 15:25:14 +00:00
|
|
|
char *filename;
|
|
|
|
char *p;
|
|
|
|
|
2019-10-20 11:49:46 +00:00
|
|
|
devCopy = g_strdup(dev);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
/* skip the leading "/dev/" */
|
2013-01-02 15:38:52 +00:00
|
|
|
filename = STRSKIP(devCopy, "/dev");
|
2011-11-25 15:25:14 +00:00
|
|
|
if (!filename)
|
2013-01-02 15:38:52 +00:00
|
|
|
filename = devCopy;
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
/* substitute path forward slashes for underscores */
|
|
|
|
p = filename;
|
|
|
|
while (*p) {
|
|
|
|
if (*p == '/')
|
|
|
|
*p = '_';
|
|
|
|
++p;
|
|
|
|
}
|
|
|
|
|
2019-10-22 13:26:14 +00:00
|
|
|
path = g_strdup_printf("%s/LCK..%s", VIR_CHRDEV_LOCK_FILE_PATH, filename);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
sanitizedPath = virFileSanitizePath(path);
|
|
|
|
|
2020-01-08 08:18:31 +00:00
|
|
|
return g_steal_pointer(&sanitizedPath);
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-01-02 15:38:52 +00:00
|
|
|
* Verify and create a lock file for a character device
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
2013-01-02 15:38:52 +00:00
|
|
|
* @dev Path of the character device
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
|
|
|
* Returns 0 on success, -1 on error
|
|
|
|
*/
|
2013-01-02 15:38:52 +00:00
|
|
|
static int virChrdevLockFileCreate(const char *dev)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
2020-01-08 08:18:31 +00:00
|
|
|
g_autofree char *path = NULL;
|
|
|
|
g_autofree char *pidStr = NULL;
|
|
|
|
VIR_AUTOCLOSE lockfd = -1;
|
2011-11-25 15:25:14 +00:00
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
/* build lock file path */
|
2013-01-02 15:38:52 +00:00
|
|
|
if (!(path = virChrdevLockFilePath(dev)))
|
2020-01-08 08:18:32 +00:00
|
|
|
return -1;
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
/* check if a log file and process holding the lock still exists */
|
|
|
|
if (virPidFileReadPathIfAlive(path, &pid, NULL) == 0 && pid >= 0) {
|
|
|
|
/* the process exists, the lockfile is valid */
|
2012-07-18 10:50:44 +00:00
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED,
|
2013-01-02 15:38:52 +00:00
|
|
|
_("Requested device '%s' is locked by "
|
2012-07-18 10:50:44 +00:00
|
|
|
"lock file '%s' held by process %lld"),
|
2013-01-02 15:38:52 +00:00
|
|
|
dev, path, (long long) pid);
|
2020-01-08 08:18:32 +00:00
|
|
|
return -1;
|
2011-11-25 15:25:14 +00:00
|
|
|
} else {
|
|
|
|
/* clean up the stale/corrupted/nonexistent lockfile */
|
|
|
|
unlink(path);
|
|
|
|
}
|
|
|
|
/* lockfile doesn't (shouldn't) exist */
|
|
|
|
|
|
|
|
/* ensure correct format according to filesystem hierarchy standard */
|
2020-08-25 22:44:00 +00:00
|
|
|
/* https://www.pathname.com/fhs/pub/fhs-2.3.html#VARLOCKLOCKFILES */
|
2019-10-22 13:26:14 +00:00
|
|
|
pidStr = g_strdup_printf("%10lld\n", (long long)getpid());
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
/* create the lock file */
|
|
|
|
if ((lockfd = open(path, O_WRONLY | O_CREAT | O_EXCL, 00644)) < 0) {
|
|
|
|
/* If we run in session mode, we might have no access to the lock
|
|
|
|
* file directory. We have to check for an permission denied error
|
|
|
|
* and see if we can reach it. This should cause an error only if
|
|
|
|
* we run in daemon mode and thus privileged.
|
|
|
|
*/
|
|
|
|
if (errno == EACCES && geteuid() != 0) {
|
2013-01-02 15:38:52 +00:00
|
|
|
VIR_DEBUG("Skipping lock file creation for device '%s in path '%s'.",
|
|
|
|
dev, path);
|
2020-01-08 08:18:32 +00:00
|
|
|
return 0;
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Couldn't create lock file for "
|
2013-01-02 15:38:52 +00:00
|
|
|
"device '%s' in path '%s'"),
|
|
|
|
dev, path);
|
2020-01-08 08:18:32 +00:00
|
|
|
return -1;
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* write the pid to the file */
|
|
|
|
if (safewrite(lockfd, pidStr, strlen(pidStr)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Couldn't write to lock file for "
|
2013-01-02 15:38:52 +00:00
|
|
|
"device '%s' in path '%s'"),
|
|
|
|
dev, path);
|
2011-11-25 15:25:14 +00:00
|
|
|
unlink(path);
|
2020-01-08 08:18:32 +00:00
|
|
|
return -1;
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* we hold the lock */
|
2020-01-08 08:18:32 +00:00
|
|
|
return 0;
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-01-02 15:38:52 +00:00
|
|
|
* Remove a lock file for a device
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
2013-01-02 15:38:52 +00:00
|
|
|
* @dev Path of the device
|
2011-11-25 15:25:14 +00:00
|
|
|
*/
|
2013-01-02 15:38:52 +00:00
|
|
|
static void virChrdevLockFileRemove(const char *dev)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
2020-01-08 08:18:31 +00:00
|
|
|
g_autofree char *path = virChrdevLockFilePath(dev);
|
|
|
|
unlink(path);
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
2013-01-02 15:38:52 +00:00
|
|
|
#else /* #ifdef VIR_CHRDEV_LOCK_FILE_PATH */
|
|
|
|
/* file locking for character devices is disabled */
|
2019-10-14 12:45:33 +00:00
|
|
|
static int virChrdevLockFileCreate(const char *dev G_GNUC_UNUSED)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-10-14 12:45:33 +00:00
|
|
|
static void virChrdevLockFileRemove(const char *dev G_GNUC_UNUSED)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2013-01-02 15:38:52 +00:00
|
|
|
#endif /* #ifdef VIR_CHRDEV_LOCK_FILE_PATH */
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2019-11-22 10:49:27 +00:00
|
|
|
typedef struct {
|
|
|
|
char *dev;
|
|
|
|
virStreamPtr st;
|
|
|
|
} virChrdevHashEntry;
|
|
|
|
|
2011-11-25 15:25:14 +00:00
|
|
|
/**
|
2013-01-02 15:38:52 +00:00
|
|
|
* Frees an entry from the hash containing domain's active devices
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
2013-01-02 15:38:52 +00:00
|
|
|
* @data Opaque data, struct holding information about the device
|
2011-11-25 15:25:14 +00:00
|
|
|
*/
|
2019-11-21 19:27:58 +00:00
|
|
|
static void virChrdevHashEntryFree(void *data)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
2019-11-22 10:49:27 +00:00
|
|
|
virChrdevHashEntry *ent = data;
|
|
|
|
|
|
|
|
if (!ent)
|
|
|
|
return;
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
/* free stream reference */
|
2019-11-22 10:49:27 +00:00
|
|
|
virObjectUnref(ent->st);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
/* delete lock file */
|
2019-11-22 10:49:27 +00:00
|
|
|
virChrdevLockFileRemove(ent->dev);
|
|
|
|
|
2020-01-08 08:18:29 +00:00
|
|
|
g_free(ent->dev);
|
2019-11-22 10:49:27 +00:00
|
|
|
g_free(ent);
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Frees opaque data provided for the stream closing callback
|
|
|
|
*
|
|
|
|
* @opaque Data to be freed.
|
|
|
|
*/
|
2013-01-02 15:38:52 +00:00
|
|
|
static void virChrdevFDStreamCloseCbFree(void *opaque)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virChrdevStreamInfo *priv = opaque;
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2021-02-03 19:34:14 +00:00
|
|
|
g_free(priv->path);
|
|
|
|
g_free(priv);
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-01-02 15:38:52 +00:00
|
|
|
* Callback being called if a FDstream is closed. Frees device entries
|
2011-11-25 15:25:14 +00:00
|
|
|
* from data structures and removes lockfiles.
|
|
|
|
*
|
|
|
|
* @st Pointer to stream being closed.
|
2013-01-02 15:38:52 +00:00
|
|
|
* @opaque Domain's device information structure.
|
2011-11-25 15:25:14 +00:00
|
|
|
*/
|
2019-10-14 12:45:33 +00:00
|
|
|
static void virChrdevFDStreamCloseCb(virStreamPtr st G_GNUC_UNUSED,
|
2011-11-25 15:25:14 +00:00
|
|
|
void *opaque)
|
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virChrdevStreamInfo *priv = opaque;
|
2013-01-02 15:38:52 +00:00
|
|
|
virMutexLock(&priv->devs->lock);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
/* remove entry from hash */
|
2013-01-02 15:38:52 +00:00
|
|
|
virHashRemoveEntry(priv->devs->hash, priv->path);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
virMutexUnlock(&priv->devs->lock);
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-01-02 15:38:52 +00:00
|
|
|
* Allocate structures for storing information about active device streams
|
2011-11-25 15:25:14 +00:00
|
|
|
* in domain's private data section.
|
|
|
|
*
|
|
|
|
* Returns pointer to the allocated structure or NULL on error
|
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
virChrdevs *virChrdevAlloc(void)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virChrdevs *devs;
|
2020-10-07 19:15:50 +00:00
|
|
|
devs = g_new0(virChrdevs, 1);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
if (virMutexInit(&devs->lock) < 0) {
|
2013-07-04 10:02:00 +00:00
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Unable to init device stream mutex"));
|
2013-01-02 15:38:52 +00:00
|
|
|
VIR_FREE(devs);
|
2011-11-25 15:25:14 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
/* there will hardly be any devices most of the time, the hash
|
2011-11-25 15:25:14 +00:00
|
|
|
* does not have to be huge */
|
2020-10-20 16:43:20 +00:00
|
|
|
if (!(devs->hash = virHashNew(virChrdevHashEntryFree)))
|
2011-11-25 15:25:14 +00:00
|
|
|
goto error;
|
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
return devs;
|
2014-03-25 06:48:31 +00:00
|
|
|
error:
|
2013-01-02 15:38:52 +00:00
|
|
|
virChrdevFree(devs);
|
2011-11-25 15:25:14 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-08-03 09:20:29 +00:00
|
|
|
/**
|
|
|
|
* Helper to clear stream callbacks when freeing the hash
|
|
|
|
*/
|
2016-02-12 09:03:50 +00:00
|
|
|
static int virChrdevFreeClearCallbacks(void *payload,
|
2020-10-21 11:31:16 +00:00
|
|
|
const char *name G_GNUC_UNUSED,
|
2019-10-14 12:45:33 +00:00
|
|
|
void *data G_GNUC_UNUSED)
|
2012-08-03 09:20:29 +00:00
|
|
|
{
|
2019-11-22 10:49:27 +00:00
|
|
|
virChrdevHashEntry *ent = payload;
|
2012-08-03 09:20:29 +00:00
|
|
|
|
2019-11-22 10:49:27 +00:00
|
|
|
virFDStreamSetInternalCloseCb(ent->st, NULL, NULL, NULL);
|
2016-02-12 09:03:50 +00:00
|
|
|
return 0;
|
2012-08-03 09:20:29 +00:00
|
|
|
}
|
|
|
|
|
2011-11-25 15:25:14 +00:00
|
|
|
/**
|
2013-01-02 15:38:52 +00:00
|
|
|
* Free structures for handling open device streams.
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
2013-01-02 15:38:52 +00:00
|
|
|
* @devs Pointer to the private structure.
|
2011-11-25 15:25:14 +00:00
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
void virChrdevFree(virChrdevs *devs)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
2020-01-08 08:18:30 +00:00
|
|
|
if (!devs)
|
2011-11-25 15:25:14 +00:00
|
|
|
return;
|
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
virMutexLock(&devs->lock);
|
2020-10-23 07:49:36 +00:00
|
|
|
virHashForEachSafe(devs->hash, virChrdevFreeClearCallbacks, NULL);
|
2013-01-02 15:38:52 +00:00
|
|
|
virHashFree(devs->hash);
|
|
|
|
virMutexUnlock(&devs->lock);
|
|
|
|
virMutexDestroy(&devs->lock);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2021-02-03 19:34:14 +00:00
|
|
|
g_free(devs);
|
2011-11-25 15:25:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-01-02 15:38:52 +00:00
|
|
|
* Open a device stream for a domain ensuring that other streams are
|
|
|
|
* not using the device, nor any lockfiles exist. This ensures that
|
|
|
|
* the device stream does not get corrupted due to a race on reading
|
2011-11-25 15:25:14 +00:00
|
|
|
* same FD by two processes.
|
|
|
|
*
|
2013-01-02 15:38:52 +00:00
|
|
|
* @devs Pointer to private structure holding data about device streams.
|
2013-01-02 15:38:53 +00:00
|
|
|
* @source Pointer to private structure holding data about device source.
|
2013-01-02 15:38:52 +00:00
|
|
|
* @st Stream the client wishes to use for the device connection.
|
|
|
|
* @force On true, close active device streams for the selected character
|
|
|
|
* device before opening this connection.
|
2011-11-25 15:25:14 +00:00
|
|
|
*
|
2013-01-02 15:38:52 +00:00
|
|
|
* Returns 0 on success and st is connected to the selected device and
|
2011-11-25 15:25:14 +00:00
|
|
|
* corresponding lock file is created (if configured). Returns -1 on
|
2013-01-02 15:38:52 +00:00
|
|
|
* error and 1 if the device stream is open and busy.
|
2011-11-25 15:25:14 +00:00
|
|
|
*/
|
2021-03-11 07:16:13 +00:00
|
|
|
int virChrdevOpen(virChrdevs *devs,
|
|
|
|
virDomainChrSourceDef *source,
|
2013-01-02 15:38:53 +00:00
|
|
|
virStreamPtr st,
|
|
|
|
bool force)
|
2011-11-25 15:25:14 +00:00
|
|
|
{
|
2021-03-11 07:16:13 +00:00
|
|
|
virChrdevStreamInfo *cbdata = NULL;
|
2019-11-22 10:49:27 +00:00
|
|
|
virChrdevHashEntry *ent;
|
2013-05-03 12:40:46 +00:00
|
|
|
char *path;
|
2011-11-25 15:25:14 +00:00
|
|
|
int ret;
|
2013-05-22 10:56:23 +00:00
|
|
|
bool added = false;
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2013-01-02 15:38:53 +00:00
|
|
|
switch (source->type) {
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_PTY:
|
|
|
|
path = source->data.file.path;
|
2015-05-31 15:55:59 +00:00
|
|
|
if (!path) {
|
|
|
|
virReportError(VIR_ERR_OPERATION_FAILED, "%s",
|
|
|
|
_("PTY device is not yet assigned"));
|
|
|
|
return -1;
|
|
|
|
}
|
2013-01-02 15:38:53 +00:00
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UNIX:
|
|
|
|
path = source->data.nix.path;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("Unsupported device type '%s'"),
|
|
|
|
virDomainChrTypeToString(source->type));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
virMutexLock(&devs->lock);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2019-11-22 10:49:27 +00:00
|
|
|
if ((ent = virHashLookup(devs->hash, path))) {
|
2011-11-25 15:25:14 +00:00
|
|
|
if (!force) {
|
2013-01-02 15:38:52 +00:00
|
|
|
/* entry found, device is busy */
|
|
|
|
virMutexUnlock(&devs->lock);
|
2011-11-25 15:25:14 +00:00
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
/* terminate existing connection */
|
2013-01-02 15:38:52 +00:00
|
|
|
/* The internal close callback handler needs to lock devs->lock to
|
2011-11-25 15:25:14 +00:00
|
|
|
* remove the aborted stream from the hash. This would cause a
|
|
|
|
* deadlock as we would try to enter the lock twice from the very
|
|
|
|
* same thread. We need to unregister the callback and abort the
|
2013-01-02 15:38:52 +00:00
|
|
|
* stream manually before we create a new device connection.
|
2011-11-25 15:25:14 +00:00
|
|
|
*/
|
2019-11-22 10:49:27 +00:00
|
|
|
virFDStreamSetInternalCloseCb(ent->st, NULL, NULL, NULL);
|
|
|
|
virStreamAbort(ent->st);
|
2013-01-02 15:38:52 +00:00
|
|
|
virHashRemoveEntry(devs->hash, path);
|
2011-11-25 15:25:14 +00:00
|
|
|
/* continue adding a new stream connection */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* create the lock file */
|
2013-01-02 15:38:52 +00:00
|
|
|
if ((ret = virChrdevLockFileCreate(path)) < 0) {
|
|
|
|
virMutexUnlock(&devs->lock);
|
2011-11-25 15:25:14 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* obtain a reference to the stream */
|
|
|
|
if (virStreamRef(st) < 0) {
|
2013-01-02 15:38:52 +00:00
|
|
|
virMutexUnlock(&devs->lock);
|
2011-11-25 15:25:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-10-07 19:15:50 +00:00
|
|
|
cbdata = g_new0(virChrdevStreamInfo, 1);
|
|
|
|
ent = g_new0(virChrdevHashEntry, 1);
|
2019-11-22 10:49:27 +00:00
|
|
|
|
|
|
|
ent->st = st;
|
|
|
|
ent->dev = g_strdup(path);
|
|
|
|
|
|
|
|
if (virHashAddEntry(devs->hash, path, ent) < 0)
|
2011-11-25 15:25:14 +00:00
|
|
|
goto error;
|
2019-11-22 10:49:27 +00:00
|
|
|
ent = NULL;
|
2013-05-22 10:56:23 +00:00
|
|
|
added = true;
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
cbdata->devs = devs;
|
2019-10-20 11:49:46 +00:00
|
|
|
cbdata->path = g_strdup(path);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
/* open the character device */
|
2013-01-02 15:38:53 +00:00
|
|
|
switch (source->type) {
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_PTY:
|
2014-03-10 12:32:51 +00:00
|
|
|
if (virFDStreamOpenPTY(st, path, 0, 0, O_RDWR) < 0)
|
2013-01-02 15:38:53 +00:00
|
|
|
goto error;
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UNIX:
|
|
|
|
if (virFDStreamConnectUNIX(st, path, false) < 0)
|
|
|
|
goto error;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
|
|
_("Unsupported device type '%s'"),
|
|
|
|
virDomainChrTypeToString(source->type));
|
2011-11-25 15:25:14 +00:00
|
|
|
goto error;
|
2013-01-02 15:38:53 +00:00
|
|
|
}
|
2011-11-25 15:25:14 +00:00
|
|
|
|
|
|
|
/* add cleanup callback */
|
2012-07-27 12:48:27 +00:00
|
|
|
virFDStreamSetInternalCloseCb(st,
|
2013-01-02 15:38:52 +00:00
|
|
|
virChrdevFDStreamCloseCb,
|
2011-11-25 15:25:14 +00:00
|
|
|
cbdata,
|
2013-01-02 15:38:52 +00:00
|
|
|
virChrdevFDStreamCloseCbFree);
|
2011-11-25 15:25:14 +00:00
|
|
|
|
2013-01-02 15:38:52 +00:00
|
|
|
virMutexUnlock(&devs->lock);
|
2011-11-25 15:25:14 +00:00
|
|
|
return 0;
|
|
|
|
|
2014-03-25 06:48:31 +00:00
|
|
|
error:
|
2013-05-22 10:56:23 +00:00
|
|
|
if (added)
|
|
|
|
virHashRemoveEntry(devs->hash, path);
|
|
|
|
else
|
2014-11-30 15:19:38 +00:00
|
|
|
virObjectUnref(st);
|
2013-05-22 10:56:23 +00:00
|
|
|
|
2011-11-25 15:25:14 +00:00
|
|
|
if (cbdata)
|
2013-01-02 15:38:52 +00:00
|
|
|
VIR_FREE(cbdata->path);
|
2011-11-25 15:25:14 +00:00
|
|
|
VIR_FREE(cbdata);
|
2013-01-02 15:38:52 +00:00
|
|
|
virMutexUnlock(&devs->lock);
|
2019-11-21 19:27:58 +00:00
|
|
|
virChrdevHashEntryFree(ent);
|
2011-11-25 15:25:14 +00:00
|
|
|
return -1;
|
|
|
|
}
|