diff options
Diffstat (limited to 'util')
-rw-r--r-- | util/Makefile.objs | 4 | ||||
-rw-r--r-- | util/acl.c | 179 | ||||
-rw-r--r-- | util/filemonitor-inotify.c | 339 | ||||
-rw-r--r-- | util/filemonitor-stub.c | 59 | ||||
-rw-r--r-- | util/trace-events | 9 |
5 files changed, 410 insertions, 180 deletions
diff --git a/util/Makefile.objs b/util/Makefile.objs index 0820923c18..0808575e3e 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -20,7 +20,6 @@ util-obj-y += envlist.o path.o module.o util-obj-y += host-utils.o util-obj-y += bitmap.o bitops.o hbitmap.o util-obj-y += fifo8.o -util-obj-y += acl.o util-obj-y += cacheinfo.o util-obj-y += error.o qemu-error.o util-obj-y += id.o @@ -50,5 +49,8 @@ util-obj-y += range.o util-obj-y += stats64.o util-obj-y += systemd.o util-obj-y += iova-tree.o +util-obj-$(CONFIG_INOTIFY1) += filemonitor-inotify.o util-obj-$(CONFIG_LINUX) += vfio-helpers.o util-obj-$(CONFIG_OPENGL) += drm.o + +stub-obj-y += filemonitor-stub.o diff --git a/util/acl.c b/util/acl.c deleted file mode 100644 index c105addadc..0000000000 --- a/util/acl.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * QEMU access control list management - * - * Copyright (C) 2009 Red Hat, Inc - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/acl.h" - -#ifdef CONFIG_FNMATCH -#include <fnmatch.h> -#endif - - -static unsigned int nacls = 0; -static qemu_acl **acls = NULL; - - - -qemu_acl *qemu_acl_find(const char *aclname) -{ - int i; - for (i = 0 ; i < nacls ; i++) { - if (strcmp(acls[i]->aclname, aclname) == 0) - return acls[i]; - } - - return NULL; -} - -qemu_acl *qemu_acl_init(const char *aclname) -{ - qemu_acl *acl; - - acl = qemu_acl_find(aclname); - if (acl) - return acl; - - acl = g_malloc(sizeof(*acl)); - acl->aclname = g_strdup(aclname); - /* Deny by default, so there is no window of "open - * access" between QEMU starting, and the user setting - * up ACLs in the monitor */ - acl->defaultDeny = 1; - - acl->nentries = 0; - QTAILQ_INIT(&acl->entries); - - acls = g_realloc(acls, sizeof(*acls) * (nacls +1)); - acls[nacls] = acl; - nacls++; - - return acl; -} - -int qemu_acl_party_is_allowed(qemu_acl *acl, - const char *party) -{ - qemu_acl_entry *entry; - - QTAILQ_FOREACH(entry, &acl->entries, next) { -#ifdef CONFIG_FNMATCH - if (fnmatch(entry->match, party, 0) == 0) - return entry->deny ? 0 : 1; -#else - /* No fnmatch, so fallback to exact string matching - * instead of allowing wildcards */ - if (strcmp(entry->match, party) == 0) - return entry->deny ? 0 : 1; -#endif - } - - return acl->defaultDeny ? 0 : 1; -} - - -void qemu_acl_reset(qemu_acl *acl) -{ - qemu_acl_entry *entry, *next_entry; - - /* Put back to deny by default, so there is no window - * of "open access" while the user re-initializes the - * access control list */ - acl->defaultDeny = 1; - QTAILQ_FOREACH_SAFE(entry, &acl->entries, next, next_entry) { - QTAILQ_REMOVE(&acl->entries, entry, next); - g_free(entry->match); - g_free(entry); - } - acl->nentries = 0; -} - - -int qemu_acl_append(qemu_acl *acl, - int deny, - const char *match) -{ - qemu_acl_entry *entry; - - entry = g_malloc(sizeof(*entry)); - entry->match = g_strdup(match); - entry->deny = deny; - - QTAILQ_INSERT_TAIL(&acl->entries, entry, next); - acl->nentries++; - - return acl->nentries; -} - - -int qemu_acl_insert(qemu_acl *acl, - int deny, - const char *match, - int index) -{ - qemu_acl_entry *tmp; - int i = 0; - - if (index <= 0) - return -1; - if (index > acl->nentries) { - return qemu_acl_append(acl, deny, match); - } - - QTAILQ_FOREACH(tmp, &acl->entries, next) { - i++; - if (i == index) { - qemu_acl_entry *entry; - entry = g_malloc(sizeof(*entry)); - entry->match = g_strdup(match); - entry->deny = deny; - - QTAILQ_INSERT_BEFORE(tmp, entry, next); - acl->nentries++; - break; - } - } - - return i; -} - -int qemu_acl_remove(qemu_acl *acl, - const char *match) -{ - qemu_acl_entry *entry; - int i = 0; - - QTAILQ_FOREACH(entry, &acl->entries, next) { - i++; - if (strcmp(entry->match, match) == 0) { - QTAILQ_REMOVE(&acl->entries, entry, next); - acl->nentries--; - g_free(entry->match); - g_free(entry); - return i; - } - } - return -1; -} diff --git a/util/filemonitor-inotify.c b/util/filemonitor-inotify.c new file mode 100644 index 0000000000..3a72be037f --- /dev/null +++ b/util/filemonitor-inotify.c @@ -0,0 +1,339 @@ +/* + * QEMU file monitor Linux inotify impl + * + * Copyright (c) 2018 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 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 + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu/filemonitor.h" +#include "qemu/main-loop.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "trace.h" + +#include <sys/inotify.h> + +struct QFileMonitor { + int fd; + + QemuMutex lock; /* protects dirs & idmap */ + GHashTable *dirs; /* dirname => QFileMonitorDir */ + GHashTable *idmap; /* inotify ID => dirname */ +}; + + +typedef struct { + int id; /* watch ID */ + char *filename; /* optional filter */ + QFileMonitorHandler cb; + void *opaque; +} QFileMonitorWatch; + + +typedef struct { + char *path; + int id; /* inotify ID */ + int nextid; /* watch ID counter */ + GArray *watches; /* QFileMonitorWatch elements */ +} QFileMonitorDir; + + +static void qemu_file_monitor_watch(void *arg) +{ + QFileMonitor *mon = arg; + char buf[4096] + __attribute__ ((aligned(__alignof__(struct inotify_event)))); + int used = 0; + int len; + + qemu_mutex_lock(&mon->lock); + + if (mon->fd == -1) { + qemu_mutex_unlock(&mon->lock); + return; + } + + len = read(mon->fd, buf, sizeof(buf)); + + if (len < 0) { + if (errno != EAGAIN) { + error_report("Failure monitoring inotify FD '%s'," + "disabling events", strerror(errno)); + goto cleanup; + } + + /* no more events right now */ + goto cleanup; + } + + /* Loop over all events in the buffer */ + while (used < len) { + struct inotify_event *ev = + (struct inotify_event *)(buf + used); + const char *name = ev->len ? ev->name : ""; + QFileMonitorDir *dir = g_hash_table_lookup(mon->idmap, + GINT_TO_POINTER(ev->wd)); + uint32_t iev = ev->mask & + (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED | + IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB); + int qev; + gsize i; + + used += sizeof(struct inotify_event) + ev->len; + + if (!dir) { + continue; + } + + /* + * During a rename operation, the old name gets + * IN_MOVED_FROM and the new name gets IN_MOVED_TO. + * To simplify life for callers, we turn these into + * DELETED and CREATED events + */ + switch (iev) { + case IN_CREATE: + case IN_MOVED_TO: + qev = QFILE_MONITOR_EVENT_CREATED; + break; + case IN_MODIFY: + qev = QFILE_MONITOR_EVENT_MODIFIED; + break; + case IN_DELETE: + case IN_MOVED_FROM: + qev = QFILE_MONITOR_EVENT_DELETED; + break; + case IN_ATTRIB: + qev = QFILE_MONITOR_EVENT_ATTRIBUTES; + break; + case IN_IGNORED: + qev = QFILE_MONITOR_EVENT_IGNORED; + break; + default: + g_assert_not_reached(); + } + + trace_qemu_file_monitor_event(mon, dir->path, name, ev->mask, dir->id); + for (i = 0; i < dir->watches->len; i++) { + QFileMonitorWatch *watch = &g_array_index(dir->watches, + QFileMonitorWatch, + i); + + if (watch->filename == NULL || + (name && g_str_equal(watch->filename, name))) { + trace_qemu_file_monitor_dispatch(mon, dir->path, name, + qev, watch->cb, + watch->opaque, watch->id); + watch->cb(watch->id, qev, name, watch->opaque); + } + } + } + + cleanup: + qemu_mutex_unlock(&mon->lock); +} + + +static void +qemu_file_monitor_dir_free(void *data) +{ + QFileMonitorDir *dir = data; + gsize i; + + for (i = 0; i < dir->watches->len; i++) { + QFileMonitorWatch *watch = &g_array_index(dir->watches, + QFileMonitorWatch, i); + g_free(watch->filename); + } + g_array_unref(dir->watches); + g_free(dir->path); + g_free(dir); +} + + +QFileMonitor * +qemu_file_monitor_new(Error **errp) +{ + int fd; + QFileMonitor *mon; + + fd = inotify_init1(IN_NONBLOCK); + if (fd < 0) { + error_setg_errno(errp, errno, + "Unable to initialize inotify"); + return NULL; + } + + mon = g_new0(QFileMonitor, 1); + qemu_mutex_init(&mon->lock); + mon->fd = fd; + + mon->dirs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + qemu_file_monitor_dir_free); + mon->idmap = g_hash_table_new(g_direct_hash, g_direct_equal); + + trace_qemu_file_monitor_new(mon, mon->fd); + + return mon; +} + +static gboolean +qemu_file_monitor_free_idle(void *opaque) +{ + QFileMonitor *mon = opaque; + + if (!mon) { + return G_SOURCE_REMOVE; + } + + qemu_mutex_lock(&mon->lock); + + g_hash_table_unref(mon->idmap); + g_hash_table_unref(mon->dirs); + + qemu_mutex_unlock(&mon->lock); + + qemu_mutex_destroy(&mon->lock); + g_free(mon); + + return G_SOURCE_REMOVE; +} + +void +qemu_file_monitor_free(QFileMonitor *mon) +{ + if (!mon) { + return; + } + + qemu_mutex_lock(&mon->lock); + if (mon->fd != -1) { + qemu_set_fd_handler(mon->fd, NULL, NULL, NULL); + close(mon->fd); + mon->fd = -1; + } + qemu_mutex_unlock(&mon->lock); + + /* + * Can't free it yet, because another thread + * may be running event loop, so the inotify + * callback might be pending. Using an idle + * source ensures we'll only free after the + * pending callback is done + */ + g_idle_add((GSourceFunc)qemu_file_monitor_free_idle, mon); +} + +int +qemu_file_monitor_add_watch(QFileMonitor *mon, + const char *dirpath, + const char *filename, + QFileMonitorHandler cb, + void *opaque, + Error **errp) +{ + QFileMonitorDir *dir; + QFileMonitorWatch watch; + int ret = -1; + + qemu_mutex_lock(&mon->lock); + dir = g_hash_table_lookup(mon->dirs, dirpath); + if (!dir) { + int rv = inotify_add_watch(mon->fd, dirpath, + IN_CREATE | IN_DELETE | IN_MODIFY | + IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB); + + if (rv < 0) { + error_setg_errno(errp, errno, "Unable to watch '%s'", dirpath); + goto cleanup; + } + + trace_qemu_file_monitor_enable_watch(mon, dirpath, rv); + + dir = g_new0(QFileMonitorDir, 1); + dir->path = g_strdup(dirpath); + dir->id = rv; + dir->watches = g_array_new(FALSE, TRUE, sizeof(QFileMonitorWatch)); + + g_hash_table_insert(mon->dirs, dir->path, dir); + g_hash_table_insert(mon->idmap, GINT_TO_POINTER(rv), dir); + + if (g_hash_table_size(mon->dirs) == 1) { + qemu_set_fd_handler(mon->fd, qemu_file_monitor_watch, NULL, mon); + } + } + + watch.id = dir->nextid++; + watch.filename = g_strdup(filename); + watch.cb = cb; + watch.opaque = opaque; + + g_array_append_val(dir->watches, watch); + + trace_qemu_file_monitor_add_watch(mon, dirpath, + filename ? filename : "<none>", + cb, opaque, watch.id); + + ret = watch.id; + + cleanup: + qemu_mutex_unlock(&mon->lock); + return ret; +} + + +void qemu_file_monitor_remove_watch(QFileMonitor *mon, + const char *dirpath, + int id) +{ + QFileMonitorDir *dir; + gsize i; + + qemu_mutex_lock(&mon->lock); + + trace_qemu_file_monitor_remove_watch(mon, dirpath, id); + + dir = g_hash_table_lookup(mon->dirs, dirpath); + if (!dir) { + goto cleanup; + } + + for (i = 0; i < dir->watches->len; i++) { + QFileMonitorWatch *watch = &g_array_index(dir->watches, + QFileMonitorWatch, i); + if (watch->id == id) { + g_free(watch->filename); + g_array_remove_index(dir->watches, i); + break; + } + } + + if (dir->watches->len == 0) { + inotify_rm_watch(mon->fd, dir->id); + trace_qemu_file_monitor_disable_watch(mon, dir->path, dir->id); + + g_hash_table_remove(mon->idmap, GINT_TO_POINTER(dir->id)); + g_hash_table_remove(mon->dirs, dir->path); + + if (g_hash_table_size(mon->dirs) == 0) { + qemu_set_fd_handler(mon->fd, NULL, NULL, NULL); + } + } + + cleanup: + qemu_mutex_unlock(&mon->lock); +} diff --git a/util/filemonitor-stub.c b/util/filemonitor-stub.c new file mode 100644 index 0000000000..48268b2bb6 --- /dev/null +++ b/util/filemonitor-stub.c @@ -0,0 +1,59 @@ +/* + * QEMU file monitor stub impl + * + * Copyright (c) 2018 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 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 + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "qemu/filemonitor.h" +#include "qemu/error-report.h" +#include "qapi/error.h" + + +QFileMonitor * +qemu_file_monitor_new(Error **errp) +{ + error_setg(errp, "File monitoring not available on this platform"); + return NULL; +} + + +void +qemu_file_monitor_free(QFileMonitor *mon G_GNUC_UNUSED) +{ +} + + +int +qemu_file_monitor_add_watch(QFileMonitor *mon G_GNUC_UNUSED, + const char *dirpath G_GNUC_UNUSED, + const char *filename G_GNUC_UNUSED, + QFileMonitorHandler cb G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "File monitoring not available on this platform"); + return -1; +} + + +void +qemu_file_monitor_remove_watch(QFileMonitor *mon G_GNUC_UNUSED, + const char *dirpath G_GNUC_UNUSED, + int id G_GNUC_UNUSED) +{ +} diff --git a/util/trace-events b/util/trace-events index 79569b7fdf..ff19b253e2 100644 --- a/util/trace-events +++ b/util/trace-events @@ -21,6 +21,15 @@ buffer_move_empty(const char *buf, size_t len, const char *from) "%s: %zd bytes buffer_move(const char *buf, size_t len, const char *from) "%s: %zd bytes from %s" buffer_free(const char *buf, size_t len) "%s: capacity %zd" +# util/filemonitor.c +qemu_file_monitor_add_watch(void *mon, const char *dirpath, const char *filename, void *cb, void *opaque, int id) "File monitor %p add watch dir='%s' file='%s' cb=%p opaque=%p id=%u" +qemu_file_monitor_remove_watch(void *mon, const char *dirpath, int id) "File monitor %p remove watch dir='%s' id=%u" +qemu_file_monitor_new(void *mon, int fd) "File monitor %p created fd=%d" +qemu_file_monitor_enable_watch(void *mon, const char *dirpath, int id) "File monitor %p enable watch dir='%s' id=%u" +qemu_file_monitor_disable_watch(void *mon, const char *dirpath, int id) "Fle monitor %p disable watch dir='%s' id=%u" +qemu_file_monitor_event(void *mon, const char *dirpath, const char *filename, int mask, unsigned int id) "File monitor %p event dir='%s' file='%s' mask=0x%x id=%u" +qemu_file_monitor_dispatch(void *mon, const char *dirpath, const char *filename, int ev, void *cb, void *opaque, unsigned int id) "File monitor %p dispatch dir='%s' file='%s' ev=%d cb=%p opaque=%p id=%u" + # util/qemu-coroutine.c qemu_aio_coroutine_enter(void *ctx, void *from, void *to, void *opaque) "ctx %p from %p to %p opaque %p" qemu_coroutine_yield(void *from, void *to) "from %p to %p" |