qapi: Introduce add-fd, remove-fd, query-fdsets

This patch adds support that enables passing of file descriptors
to the QEMU monitor where they will be stored in specified file
descriptor sets.

A file descriptor set can be used by a client like libvirt to
store file descriptors for the same file.  This allows the
client to open a file with different access modes (O_RDWR,
O_WRONLY, O_RDONLY) and add/remove the passed fds to/from an fd
set as needed.  This will allow QEMU to (in a later patch in this
series) "open" and "reopen" the same file by dup()ing the fd in
the fd set that corresponds to the file, where the fd has the
matching access mode flag that QEMU requests.

The new QMP commands are:
  add-fd: Add a file descriptor to an fd set
  remove-fd: Remove a file descriptor from an fd set
  query-fdsets: Return information describing all fd sets

Note: These commands are not compatible with the existing getfd
and closefd QMP commands.

Signed-off-by: Corey Bryant <coreyb@linux.vnet.ibm.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Corey Bryant 2012-08-14 16:43:43 -04:00 committed by Kevin Wolf
parent 06138651f3
commit ba1c048a8f
3 changed files with 409 additions and 0 deletions

189
monitor.c
View File

@ -140,6 +140,23 @@ struct mon_fd_t {
QLIST_ENTRY(mon_fd_t) next;
};
/* file descriptor associated with a file descriptor set */
typedef struct MonFdsetFd MonFdsetFd;
struct MonFdsetFd {
int fd;
bool removed;
char *opaque;
QLIST_ENTRY(MonFdsetFd) next;
};
/* file descriptor set containing fds passed via SCM_RIGHTS */
typedef struct MonFdset MonFdset;
struct MonFdset {
int64_t id;
QLIST_HEAD(, MonFdsetFd) fds;
QLIST_ENTRY(MonFdset) next;
};
typedef struct MonitorControl {
QObject *id;
JSONMessageParser parser;
@ -181,6 +198,7 @@ struct Monitor {
#define QMP_ACCEPT_UNKNOWNS 1
static QLIST_HEAD(mon_list, Monitor) mon_list;
static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets;
static mon_cmd_t mon_cmds[];
static mon_cmd_t info_cmds[];
@ -2366,6 +2384,177 @@ int monitor_get_fd(Monitor *mon, const char *fdname)
return -1;
}
static void monitor_fdset_cleanup(MonFdset *mon_fdset)
{
MonFdsetFd *mon_fdset_fd;
MonFdsetFd *mon_fdset_fd_next;
QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) {
if (mon_fdset_fd->removed) {
close(mon_fdset_fd->fd);
g_free(mon_fdset_fd->opaque);
QLIST_REMOVE(mon_fdset_fd, next);
g_free(mon_fdset_fd);
}
}
if (QLIST_EMPTY(&mon_fdset->fds)) {
QLIST_REMOVE(mon_fdset, next);
g_free(mon_fdset);
}
}
AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, bool has_opaque,
const char *opaque, Error **errp)
{
int fd;
Monitor *mon = cur_mon;
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd;
AddfdInfo *fdinfo;
fd = qemu_chr_fe_get_msgfd(mon->chr);
if (fd == -1) {
error_set(errp, QERR_FD_NOT_SUPPLIED);
goto error;
}
if (has_fdset_id) {
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
if (mon_fdset->id == fdset_id) {
break;
}
}
if (mon_fdset == NULL) {
error_set(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id",
"an existing fdset-id");
goto error;
}
} else {
int64_t fdset_id_prev = -1;
MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets);
/* Use first available fdset ID */
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
mon_fdset_cur = mon_fdset;
if (fdset_id_prev == mon_fdset_cur->id - 1) {
fdset_id_prev = mon_fdset_cur->id;
continue;
}
break;
}
mon_fdset = g_malloc0(sizeof(*mon_fdset));
mon_fdset->id = fdset_id_prev + 1;
/* The fdset list is ordered by fdset ID */
if (mon_fdset->id == 0) {
QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next);
} else if (mon_fdset->id < mon_fdset_cur->id) {
QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next);
} else {
QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next);
}
}
mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd));
mon_fdset_fd->fd = fd;
mon_fdset_fd->removed = false;
if (has_opaque) {
mon_fdset_fd->opaque = g_strdup(opaque);
}
QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next);
fdinfo = g_malloc0(sizeof(*fdinfo));
fdinfo->fdset_id = mon_fdset->id;
fdinfo->fd = mon_fdset_fd->fd;
return fdinfo;
error:
if (fd != -1) {
close(fd);
}
return NULL;
}
void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp)
{
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd;
char fd_str[60];
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
if (mon_fdset->id != fdset_id) {
continue;
}
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
if (has_fd) {
if (mon_fdset_fd->fd != fd) {
continue;
}
mon_fdset_fd->removed = true;
break;
} else {
mon_fdset_fd->removed = true;
}
}
if (has_fd && !mon_fdset_fd) {
goto error;
}
monitor_fdset_cleanup(mon_fdset);
return;
}
error:
if (has_fd) {
snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64,
fdset_id, fd);
} else {
snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id);
}
error_set(errp, QERR_FD_NOT_FOUND, fd_str);
}
FdsetInfoList *qmp_query_fdsets(Error **errp)
{
MonFdset *mon_fdset;
MonFdsetFd *mon_fdset_fd;
FdsetInfoList *fdset_list = NULL;
QLIST_FOREACH(mon_fdset, &mon_fdsets, next) {
FdsetInfoList *fdset_info = g_malloc0(sizeof(*fdset_info));
FdsetFdInfoList *fdsetfd_list = NULL;
fdset_info->value = g_malloc0(sizeof(*fdset_info->value));
fdset_info->value->fdset_id = mon_fdset->id;
QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) {
FdsetFdInfoList *fdsetfd_info;
fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info));
fdsetfd_info->value = g_malloc0(sizeof(*fdsetfd_info->value));
fdsetfd_info->value->fd = mon_fdset_fd->fd;
if (mon_fdset_fd->opaque) {
fdsetfd_info->value->has_opaque = true;
fdsetfd_info->value->opaque = g_strdup(mon_fdset_fd->opaque);
} else {
fdsetfd_info->value->has_opaque = false;
}
fdsetfd_info->next = fdsetfd_list;
fdsetfd_list = fdsetfd_info;
}
fdset_info->value->fds = fdsetfd_list;
fdset_info->next = fdset_list;
fdset_list = fdset_info;
}
return fdset_list;
}
/* mon_cmds and info_cmds would be sorted at runtime */
static mon_cmd_t mon_cmds[] = {
#include "hmp-commands.h"

View File

@ -2356,3 +2356,101 @@
# Since: 1.2.0
##
{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] }
# @AddfdInfo:
#
# Information about a file descriptor that was added to an fd set.
#
# @fdset-id: The ID of the fd set that @fd was added to.
#
# @fd: The file descriptor that was received via SCM rights and
# added to the fd set.
#
# Since: 1.2.0
##
{ 'type': 'AddfdInfo', 'data': {'fdset-id': 'int', 'fd': 'int'} }
##
# @add-fd:
#
# Add a file descriptor, that was passed via SCM rights, to an fd set.
#
# @fdset-id: #optional The ID of the fd set to add the file descriptor to.
#
# @opaque: #optional A free-form string that can be used to describe the fd.
#
# Returns: @AddfdInfo on success
# If file descriptor was not received, FdNotSupplied
# If @fdset-id does not exist, InvalidParameterValue
#
# Notes: The list of fd sets is shared by all monitor connections.
#
# If @fdset-id is not specified, a new fd set will be created.
#
# Since: 1.2.0
##
{ 'command': 'add-fd', 'data': {'*fdset-id': 'int', '*opaque': 'str'},
'returns': 'AddfdInfo' }
##
# @remove-fd:
#
# Remove a file descriptor from an fd set.
#
# @fdset-id: The ID of the fd set that the file descriptor belongs to.
#
# @fd: #optional The file descriptor that is to be removed.
#
# Returns: Nothing on success
# If @fdset-id or @fd is not found, FdNotFound
#
# Since: 1.2.0
#
# Notes: The list of fd sets is shared by all monitor connections.
#
# If @fd is not specified, all file descriptors in @fdset-id
# will be removed.
##
{ 'command': 'remove-fd', 'data': {'fdset-id': 'int', '*fd': 'int'} }
##
# @FdsetFdInfo:
#
# Information about a file descriptor that belongs to an fd set.
#
# @fd: The file descriptor value.
#
# @opaque: #optional A free-form string that can be used to describe the fd.
#
# Since: 1.2.0
##
{ 'type': 'FdsetFdInfo',
'data': {'fd': 'int', '*opaque': 'str'} }
##
# @FdsetInfo:
#
# Information about an fd set.
#
# @fdset-id: The ID of the fd set.
#
# @fds: A list of file descriptors that belong to this fd set.
#
# Since: 1.2.0
##
{ 'type': 'FdsetInfo',
'data': {'fdset-id': 'int', 'fds': ['FdsetFdInfo']} }
##
# @query-fdsets:
#
# Return information describing all fd sets.
#
# Returns: A list of @FdsetInfo
#
# Since: 1.2.0
#
# Note: The list of fd sets is shared by all monitor connections.
#
##
{ 'command': 'query-fdsets', 'returns': ['FdsetInfo'] }

View File

@ -968,6 +968,128 @@ Example:
-> { "execute": "closefd", "arguments": { "fdname": "fd1" } }
<- { "return": {} }
EQMP
{
.name = "add-fd",
.args_type = "fdset-id:i?,opaque:s?",
.params = "add-fd fdset-id opaque",
.help = "Add a file descriptor, that was passed via SCM rights, to an fd set",
.mhandler.cmd_new = qmp_marshal_input_add_fd,
},
SQMP
add-fd
-------
Add a file descriptor, that was passed via SCM rights, to an fd set.
Arguments:
- "fdset-id": The ID of the fd set to add the file descriptor to.
(json-int, optional)
- "opaque": A free-form string that can be used to describe the fd.
(json-string, optional)
Return a json-object with the following information:
- "fdset-id": The ID of the fd set that the fd was added to. (json-int)
- "fd": The file descriptor that was received via SCM rights and added to the
fd set. (json-int)
Example:
-> { "execute": "add-fd", "arguments": { "fdset-id": 1 } }
<- { "return": { "fdset-id": 1, "fd": 3 } }
Notes:
(1) The list of fd sets is shared by all monitor connections.
(2) If "fdset-id" is not specified, a new fd set will be created.
EQMP
{
.name = "remove-fd",
.args_type = "fdset-id:i,fd:i?",
.params = "remove-fd fdset-id fd",
.help = "Remove a file descriptor from an fd set",
.mhandler.cmd_new = qmp_marshal_input_remove_fd,
},
SQMP
remove-fd
---------
Remove a file descriptor from an fd set.
Arguments:
- "fdset-id": The ID of the fd set that the file descriptor belongs to.
(json-int)
- "fd": The file descriptor that is to be removed. (json-int, optional)
Example:
-> { "execute": "remove-fd", "arguments": { "fdset-id": 1, "fd": 3 } }
<- { "return": {} }
Notes:
(1) The list of fd sets is shared by all monitor connections.
(2) If "fd" is not specified, all file descriptors in "fdset-id" will be
removed.
EQMP
{
.name = "query-fdsets",
.args_type = "",
.help = "Return information describing all fd sets",
.mhandler.cmd_new = qmp_marshal_input_query_fdsets,
},
SQMP
query-fdsets
-------------
Return information describing all fd sets.
Arguments: None
Example:
-> { "execute": "query-fdsets" }
<- { "return": [
{
"fds": [
{
"fd": 30,
"opaque": "rdonly:/path/to/file"
},
{
"fd": 24,
"opaque": "rdwr:/path/to/file"
}
],
"fdset-id": 1
},
{
"fds": [
{
"fd": 28
},
{
"fd": 29
}
],
"fdset-id": 0
}
]
}
Note: The list of fd sets is shared by all monitor connections.
EQMP
{