190e33dfeSDaniel P. Berrangé /*
290e33dfeSDaniel P. Berrangé * QEMU file monitor Linux inotify impl
390e33dfeSDaniel P. Berrangé *
490e33dfeSDaniel P. Berrangé * Copyright (c) 2018 Red Hat, Inc.
590e33dfeSDaniel P. Berrangé *
690e33dfeSDaniel P. Berrangé * This library is free software; you can redistribute it and/or
790e33dfeSDaniel P. Berrangé * modify it under the terms of the GNU Lesser General Public
890e33dfeSDaniel P. Berrangé * License as published by the Free Software Foundation; either
961f3c91aSChetan Pant * version 2.1 of the License, or (at your option) any later version.
1090e33dfeSDaniel P. Berrangé *
1190e33dfeSDaniel P. Berrangé * This library is distributed in the hope that it will be useful,
1290e33dfeSDaniel P. Berrangé * but WITHOUT ANY WARRANTY; without even the implied warranty of
1390e33dfeSDaniel P. Berrangé * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1490e33dfeSDaniel P. Berrangé * Lesser General Public License for more details.
1590e33dfeSDaniel P. Berrangé *
1690e33dfeSDaniel P. Berrangé * You should have received a copy of the GNU Lesser General Public
1790e33dfeSDaniel P. Berrangé * License along with this library; if not, see <http://www.gnu.org/licenses/>.
1890e33dfeSDaniel P. Berrangé *
1990e33dfeSDaniel P. Berrangé */
2090e33dfeSDaniel P. Berrangé
2190e33dfeSDaniel P. Berrangé #include "qemu/osdep.h"
2290e33dfeSDaniel P. Berrangé #include "qemu/filemonitor.h"
2390e33dfeSDaniel P. Berrangé #include "qemu/main-loop.h"
2490e33dfeSDaniel P. Berrangé #include "qemu/error-report.h"
2590e33dfeSDaniel P. Berrangé #include "qapi/error.h"
2690e33dfeSDaniel P. Berrangé #include "trace.h"
2790e33dfeSDaniel P. Berrangé
2890e33dfeSDaniel P. Berrangé #include <sys/inotify.h>
2990e33dfeSDaniel P. Berrangé
3090e33dfeSDaniel P. Berrangé struct QFileMonitor {
3190e33dfeSDaniel P. Berrangé int fd;
3290e33dfeSDaniel P. Berrangé QemuMutex lock; /* protects dirs & idmap */
3390e33dfeSDaniel P. Berrangé GHashTable *dirs; /* dirname => QFileMonitorDir */
3490e33dfeSDaniel P. Berrangé GHashTable *idmap; /* inotify ID => dirname */
3590e33dfeSDaniel P. Berrangé };
3690e33dfeSDaniel P. Berrangé
3790e33dfeSDaniel P. Berrangé
3890e33dfeSDaniel P. Berrangé typedef struct {
39b4682a63SDaniel P. Berrangé int64_t id; /* watch ID */
4090e33dfeSDaniel P. Berrangé char *filename; /* optional filter */
4190e33dfeSDaniel P. Berrangé QFileMonitorHandler cb;
4290e33dfeSDaniel P. Berrangé void *opaque;
4390e33dfeSDaniel P. Berrangé } QFileMonitorWatch;
4490e33dfeSDaniel P. Berrangé
4590e33dfeSDaniel P. Berrangé
4690e33dfeSDaniel P. Berrangé typedef struct {
4790e33dfeSDaniel P. Berrangé char *path;
48b4682a63SDaniel P. Berrangé int inotify_id; /* inotify ID */
49b4682a63SDaniel P. Berrangé int next_file_id; /* file ID counter */
5090e33dfeSDaniel P. Berrangé GArray *watches; /* QFileMonitorWatch elements */
5190e33dfeSDaniel P. Berrangé } QFileMonitorDir;
5290e33dfeSDaniel P. Berrangé
5390e33dfeSDaniel P. Berrangé
qemu_file_monitor_watch(void * arg)5490e33dfeSDaniel P. Berrangé static void qemu_file_monitor_watch(void *arg)
5590e33dfeSDaniel P. Berrangé {
5690e33dfeSDaniel P. Berrangé QFileMonitor *mon = arg;
5790e33dfeSDaniel P. Berrangé char buf[4096]
5890e33dfeSDaniel P. Berrangé __attribute__ ((aligned(__alignof__(struct inotify_event))));
5990e33dfeSDaniel P. Berrangé int used = 0;
6090e33dfeSDaniel P. Berrangé int len;
6190e33dfeSDaniel P. Berrangé
6290e33dfeSDaniel P. Berrangé qemu_mutex_lock(&mon->lock);
6390e33dfeSDaniel P. Berrangé
6490e33dfeSDaniel P. Berrangé if (mon->fd == -1) {
6590e33dfeSDaniel P. Berrangé qemu_mutex_unlock(&mon->lock);
6690e33dfeSDaniel P. Berrangé return;
6790e33dfeSDaniel P. Berrangé }
6890e33dfeSDaniel P. Berrangé
6990e33dfeSDaniel P. Berrangé len = read(mon->fd, buf, sizeof(buf));
7090e33dfeSDaniel P. Berrangé
7190e33dfeSDaniel P. Berrangé if (len < 0) {
7290e33dfeSDaniel P. Berrangé if (errno != EAGAIN) {
7390e33dfeSDaniel P. Berrangé error_report("Failure monitoring inotify FD '%s',"
7490e33dfeSDaniel P. Berrangé "disabling events", strerror(errno));
7590e33dfeSDaniel P. Berrangé goto cleanup;
7690e33dfeSDaniel P. Berrangé }
7790e33dfeSDaniel P. Berrangé
7890e33dfeSDaniel P. Berrangé /* no more events right now */
7990e33dfeSDaniel P. Berrangé goto cleanup;
8090e33dfeSDaniel P. Berrangé }
8190e33dfeSDaniel P. Berrangé
8290e33dfeSDaniel P. Berrangé /* Loop over all events in the buffer */
8390e33dfeSDaniel P. Berrangé while (used < len) {
842e12dd40SVladimir Sementsov-Ogievskiy const char *name;
852e12dd40SVladimir Sementsov-Ogievskiy QFileMonitorDir *dir;
862e12dd40SVladimir Sementsov-Ogievskiy uint32_t iev;
8790e33dfeSDaniel P. Berrangé int qev;
8890e33dfeSDaniel P. Berrangé gsize i;
892e12dd40SVladimir Sementsov-Ogievskiy struct inotify_event *ev = (struct inotify_event *)(buf + used);
902e12dd40SVladimir Sementsov-Ogievskiy
912e12dd40SVladimir Sementsov-Ogievskiy /*
92*f0dbe427SMichael Tokarev * We trust the kernel to provide valid buffer with complete event
932e12dd40SVladimir Sementsov-Ogievskiy * records.
942e12dd40SVladimir Sementsov-Ogievskiy */
952e12dd40SVladimir Sementsov-Ogievskiy assert(len - used >= sizeof(struct inotify_event));
962e12dd40SVladimir Sementsov-Ogievskiy assert(len - used - sizeof(struct inotify_event) >= ev->len);
972e12dd40SVladimir Sementsov-Ogievskiy
982e12dd40SVladimir Sementsov-Ogievskiy name = ev->len ? ev->name : "";
992e12dd40SVladimir Sementsov-Ogievskiy dir = g_hash_table_lookup(mon->idmap, GINT_TO_POINTER(ev->wd));
1002e12dd40SVladimir Sementsov-Ogievskiy iev = ev->mask &
1012e12dd40SVladimir Sementsov-Ogievskiy (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED |
1022e12dd40SVladimir Sementsov-Ogievskiy IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB);
10390e33dfeSDaniel P. Berrangé
10490e33dfeSDaniel P. Berrangé used += sizeof(struct inotify_event) + ev->len;
10590e33dfeSDaniel P. Berrangé
10690e33dfeSDaniel P. Berrangé if (!dir) {
10790e33dfeSDaniel P. Berrangé continue;
10890e33dfeSDaniel P. Berrangé }
10990e33dfeSDaniel P. Berrangé
11090e33dfeSDaniel P. Berrangé /*
11190e33dfeSDaniel P. Berrangé * During a rename operation, the old name gets
11290e33dfeSDaniel P. Berrangé * IN_MOVED_FROM and the new name gets IN_MOVED_TO.
11390e33dfeSDaniel P. Berrangé * To simplify life for callers, we turn these into
11490e33dfeSDaniel P. Berrangé * DELETED and CREATED events
11590e33dfeSDaniel P. Berrangé */
11690e33dfeSDaniel P. Berrangé switch (iev) {
11790e33dfeSDaniel P. Berrangé case IN_CREATE:
11890e33dfeSDaniel P. Berrangé case IN_MOVED_TO:
11990e33dfeSDaniel P. Berrangé qev = QFILE_MONITOR_EVENT_CREATED;
12090e33dfeSDaniel P. Berrangé break;
12190e33dfeSDaniel P. Berrangé case IN_MODIFY:
12290e33dfeSDaniel P. Berrangé qev = QFILE_MONITOR_EVENT_MODIFIED;
12390e33dfeSDaniel P. Berrangé break;
12490e33dfeSDaniel P. Berrangé case IN_DELETE:
12590e33dfeSDaniel P. Berrangé case IN_MOVED_FROM:
12690e33dfeSDaniel P. Berrangé qev = QFILE_MONITOR_EVENT_DELETED;
12790e33dfeSDaniel P. Berrangé break;
12890e33dfeSDaniel P. Berrangé case IN_ATTRIB:
12990e33dfeSDaniel P. Berrangé qev = QFILE_MONITOR_EVENT_ATTRIBUTES;
13090e33dfeSDaniel P. Berrangé break;
13190e33dfeSDaniel P. Berrangé case IN_IGNORED:
13290e33dfeSDaniel P. Berrangé qev = QFILE_MONITOR_EVENT_IGNORED;
13390e33dfeSDaniel P. Berrangé break;
13490e33dfeSDaniel P. Berrangé default:
13590e33dfeSDaniel P. Berrangé g_assert_not_reached();
13690e33dfeSDaniel P. Berrangé }
13790e33dfeSDaniel P. Berrangé
138b4682a63SDaniel P. Berrangé trace_qemu_file_monitor_event(mon, dir->path, name, ev->mask,
139b4682a63SDaniel P. Berrangé dir->inotify_id);
14090e33dfeSDaniel P. Berrangé for (i = 0; i < dir->watches->len; i++) {
14190e33dfeSDaniel P. Berrangé QFileMonitorWatch *watch = &g_array_index(dir->watches,
14290e33dfeSDaniel P. Berrangé QFileMonitorWatch,
14390e33dfeSDaniel P. Berrangé i);
14490e33dfeSDaniel P. Berrangé
14590e33dfeSDaniel P. Berrangé if (watch->filename == NULL ||
14690e33dfeSDaniel P. Berrangé (name && g_str_equal(watch->filename, name))) {
14790e33dfeSDaniel P. Berrangé trace_qemu_file_monitor_dispatch(mon, dir->path, name,
14890e33dfeSDaniel P. Berrangé qev, watch->cb,
14990e33dfeSDaniel P. Berrangé watch->opaque, watch->id);
15090e33dfeSDaniel P. Berrangé watch->cb(watch->id, qev, name, watch->opaque);
15190e33dfeSDaniel P. Berrangé }
15290e33dfeSDaniel P. Berrangé }
15390e33dfeSDaniel P. Berrangé }
15490e33dfeSDaniel P. Berrangé
15590e33dfeSDaniel P. Berrangé cleanup:
15690e33dfeSDaniel P. Berrangé qemu_mutex_unlock(&mon->lock);
15790e33dfeSDaniel P. Berrangé }
15890e33dfeSDaniel P. Berrangé
15990e33dfeSDaniel P. Berrangé
16090e33dfeSDaniel P. Berrangé static void
qemu_file_monitor_dir_free(void * data)16190e33dfeSDaniel P. Berrangé qemu_file_monitor_dir_free(void *data)
16290e33dfeSDaniel P. Berrangé {
16390e33dfeSDaniel P. Berrangé QFileMonitorDir *dir = data;
16490e33dfeSDaniel P. Berrangé gsize i;
16590e33dfeSDaniel P. Berrangé
16690e33dfeSDaniel P. Berrangé for (i = 0; i < dir->watches->len; i++) {
16790e33dfeSDaniel P. Berrangé QFileMonitorWatch *watch = &g_array_index(dir->watches,
16890e33dfeSDaniel P. Berrangé QFileMonitorWatch, i);
16990e33dfeSDaniel P. Berrangé g_free(watch->filename);
17090e33dfeSDaniel P. Berrangé }
17190e33dfeSDaniel P. Berrangé g_array_unref(dir->watches);
17290e33dfeSDaniel P. Berrangé g_free(dir->path);
17390e33dfeSDaniel P. Berrangé g_free(dir);
17490e33dfeSDaniel P. Berrangé }
17590e33dfeSDaniel P. Berrangé
17690e33dfeSDaniel P. Berrangé
17790e33dfeSDaniel P. Berrangé QFileMonitor *
qemu_file_monitor_new(Error ** errp)17890e33dfeSDaniel P. Berrangé qemu_file_monitor_new(Error **errp)
17990e33dfeSDaniel P. Berrangé {
18090e33dfeSDaniel P. Berrangé int fd;
18190e33dfeSDaniel P. Berrangé QFileMonitor *mon;
18290e33dfeSDaniel P. Berrangé
18390e33dfeSDaniel P. Berrangé fd = inotify_init1(IN_NONBLOCK);
18490e33dfeSDaniel P. Berrangé if (fd < 0) {
18590e33dfeSDaniel P. Berrangé error_setg_errno(errp, errno,
18690e33dfeSDaniel P. Berrangé "Unable to initialize inotify");
18790e33dfeSDaniel P. Berrangé return NULL;
18890e33dfeSDaniel P. Berrangé }
18990e33dfeSDaniel P. Berrangé
19090e33dfeSDaniel P. Berrangé mon = g_new0(QFileMonitor, 1);
19190e33dfeSDaniel P. Berrangé qemu_mutex_init(&mon->lock);
19290e33dfeSDaniel P. Berrangé mon->fd = fd;
19390e33dfeSDaniel P. Berrangé
19490e33dfeSDaniel P. Berrangé mon->dirs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
19590e33dfeSDaniel P. Berrangé qemu_file_monitor_dir_free);
19690e33dfeSDaniel P. Berrangé mon->idmap = g_hash_table_new(g_direct_hash, g_direct_equal);
19790e33dfeSDaniel P. Berrangé
19890e33dfeSDaniel P. Berrangé trace_qemu_file_monitor_new(mon, mon->fd);
19990e33dfeSDaniel P. Berrangé
20090e33dfeSDaniel P. Berrangé return mon;
20190e33dfeSDaniel P. Berrangé }
20290e33dfeSDaniel P. Berrangé
20390e33dfeSDaniel P. Berrangé static gboolean
qemu_file_monitor_free_idle(void * opaque)20490e33dfeSDaniel P. Berrangé qemu_file_monitor_free_idle(void *opaque)
20590e33dfeSDaniel P. Berrangé {
20690e33dfeSDaniel P. Berrangé QFileMonitor *mon = opaque;
20790e33dfeSDaniel P. Berrangé
20890e33dfeSDaniel P. Berrangé if (!mon) {
20990e33dfeSDaniel P. Berrangé return G_SOURCE_REMOVE;
21090e33dfeSDaniel P. Berrangé }
21190e33dfeSDaniel P. Berrangé
21290e33dfeSDaniel P. Berrangé qemu_mutex_lock(&mon->lock);
21390e33dfeSDaniel P. Berrangé
21490e33dfeSDaniel P. Berrangé g_hash_table_unref(mon->idmap);
21590e33dfeSDaniel P. Berrangé g_hash_table_unref(mon->dirs);
21690e33dfeSDaniel P. Berrangé
21790e33dfeSDaniel P. Berrangé qemu_mutex_unlock(&mon->lock);
21890e33dfeSDaniel P. Berrangé
21990e33dfeSDaniel P. Berrangé qemu_mutex_destroy(&mon->lock);
22090e33dfeSDaniel P. Berrangé g_free(mon);
22190e33dfeSDaniel P. Berrangé
22290e33dfeSDaniel P. Berrangé return G_SOURCE_REMOVE;
22390e33dfeSDaniel P. Berrangé }
22490e33dfeSDaniel P. Berrangé
22590e33dfeSDaniel P. Berrangé void
qemu_file_monitor_free(QFileMonitor * mon)22690e33dfeSDaniel P. Berrangé qemu_file_monitor_free(QFileMonitor *mon)
22790e33dfeSDaniel P. Berrangé {
22890e33dfeSDaniel P. Berrangé if (!mon) {
22990e33dfeSDaniel P. Berrangé return;
23090e33dfeSDaniel P. Berrangé }
23190e33dfeSDaniel P. Berrangé
23290e33dfeSDaniel P. Berrangé qemu_mutex_lock(&mon->lock);
23390e33dfeSDaniel P. Berrangé if (mon->fd != -1) {
23490e33dfeSDaniel P. Berrangé qemu_set_fd_handler(mon->fd, NULL, NULL, NULL);
23590e33dfeSDaniel P. Berrangé close(mon->fd);
23690e33dfeSDaniel P. Berrangé mon->fd = -1;
23790e33dfeSDaniel P. Berrangé }
23890e33dfeSDaniel P. Berrangé qemu_mutex_unlock(&mon->lock);
23990e33dfeSDaniel P. Berrangé
24090e33dfeSDaniel P. Berrangé /*
24190e33dfeSDaniel P. Berrangé * Can't free it yet, because another thread
24290e33dfeSDaniel P. Berrangé * may be running event loop, so the inotify
24390e33dfeSDaniel P. Berrangé * callback might be pending. Using an idle
24490e33dfeSDaniel P. Berrangé * source ensures we'll only free after the
24590e33dfeSDaniel P. Berrangé * pending callback is done
24690e33dfeSDaniel P. Berrangé */
24790e33dfeSDaniel P. Berrangé g_idle_add((GSourceFunc)qemu_file_monitor_free_idle, mon);
24890e33dfeSDaniel P. Berrangé }
24990e33dfeSDaniel P. Berrangé
250b4682a63SDaniel P. Berrangé int64_t
qemu_file_monitor_add_watch(QFileMonitor * mon,const char * dirpath,const char * filename,QFileMonitorHandler cb,void * opaque,Error ** errp)25190e33dfeSDaniel P. Berrangé qemu_file_monitor_add_watch(QFileMonitor *mon,
25290e33dfeSDaniel P. Berrangé const char *dirpath,
25390e33dfeSDaniel P. Berrangé const char *filename,
25490e33dfeSDaniel P. Berrangé QFileMonitorHandler cb,
25590e33dfeSDaniel P. Berrangé void *opaque,
25690e33dfeSDaniel P. Berrangé Error **errp)
25790e33dfeSDaniel P. Berrangé {
25890e33dfeSDaniel P. Berrangé QFileMonitorDir *dir;
25990e33dfeSDaniel P. Berrangé QFileMonitorWatch watch;
260b4682a63SDaniel P. Berrangé int64_t ret = -1;
26190e33dfeSDaniel P. Berrangé
26290e33dfeSDaniel P. Berrangé qemu_mutex_lock(&mon->lock);
26390e33dfeSDaniel P. Berrangé dir = g_hash_table_lookup(mon->dirs, dirpath);
26490e33dfeSDaniel P. Berrangé if (!dir) {
26590e33dfeSDaniel P. Berrangé int rv = inotify_add_watch(mon->fd, dirpath,
26690e33dfeSDaniel P. Berrangé IN_CREATE | IN_DELETE | IN_MODIFY |
26790e33dfeSDaniel P. Berrangé IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB);
26890e33dfeSDaniel P. Berrangé
26990e33dfeSDaniel P. Berrangé if (rv < 0) {
27090e33dfeSDaniel P. Berrangé error_setg_errno(errp, errno, "Unable to watch '%s'", dirpath);
27190e33dfeSDaniel P. Berrangé goto cleanup;
27290e33dfeSDaniel P. Berrangé }
27390e33dfeSDaniel P. Berrangé
27490e33dfeSDaniel P. Berrangé trace_qemu_file_monitor_enable_watch(mon, dirpath, rv);
27590e33dfeSDaniel P. Berrangé
27690e33dfeSDaniel P. Berrangé dir = g_new0(QFileMonitorDir, 1);
27790e33dfeSDaniel P. Berrangé dir->path = g_strdup(dirpath);
278b4682a63SDaniel P. Berrangé dir->inotify_id = rv;
27990e33dfeSDaniel P. Berrangé dir->watches = g_array_new(FALSE, TRUE, sizeof(QFileMonitorWatch));
28090e33dfeSDaniel P. Berrangé
28190e33dfeSDaniel P. Berrangé g_hash_table_insert(mon->dirs, dir->path, dir);
28290e33dfeSDaniel P. Berrangé g_hash_table_insert(mon->idmap, GINT_TO_POINTER(rv), dir);
28390e33dfeSDaniel P. Berrangé
28490e33dfeSDaniel P. Berrangé if (g_hash_table_size(mon->dirs) == 1) {
28590e33dfeSDaniel P. Berrangé qemu_set_fd_handler(mon->fd, qemu_file_monitor_watch, NULL, mon);
28690e33dfeSDaniel P. Berrangé }
28790e33dfeSDaniel P. Berrangé }
28890e33dfeSDaniel P. Berrangé
289b4682a63SDaniel P. Berrangé watch.id = (((int64_t)dir->inotify_id) << 32) | dir->next_file_id++;
29090e33dfeSDaniel P. Berrangé watch.filename = g_strdup(filename);
29190e33dfeSDaniel P. Berrangé watch.cb = cb;
29290e33dfeSDaniel P. Berrangé watch.opaque = opaque;
29390e33dfeSDaniel P. Berrangé
29490e33dfeSDaniel P. Berrangé g_array_append_val(dir->watches, watch);
29590e33dfeSDaniel P. Berrangé
29690e33dfeSDaniel P. Berrangé trace_qemu_file_monitor_add_watch(mon, dirpath,
29790e33dfeSDaniel P. Berrangé filename ? filename : "<none>",
29890e33dfeSDaniel P. Berrangé cb, opaque, watch.id);
29990e33dfeSDaniel P. Berrangé
30090e33dfeSDaniel P. Berrangé ret = watch.id;
30190e33dfeSDaniel P. Berrangé
30290e33dfeSDaniel P. Berrangé cleanup:
30390e33dfeSDaniel P. Berrangé qemu_mutex_unlock(&mon->lock);
30490e33dfeSDaniel P. Berrangé return ret;
30590e33dfeSDaniel P. Berrangé }
30690e33dfeSDaniel P. Berrangé
30790e33dfeSDaniel P. Berrangé
qemu_file_monitor_remove_watch(QFileMonitor * mon,const char * dirpath,int64_t id)30890e33dfeSDaniel P. Berrangé void qemu_file_monitor_remove_watch(QFileMonitor *mon,
30990e33dfeSDaniel P. Berrangé const char *dirpath,
310b4682a63SDaniel P. Berrangé int64_t id)
31190e33dfeSDaniel P. Berrangé {
31290e33dfeSDaniel P. Berrangé QFileMonitorDir *dir;
31390e33dfeSDaniel P. Berrangé gsize i;
31490e33dfeSDaniel P. Berrangé
31590e33dfeSDaniel P. Berrangé qemu_mutex_lock(&mon->lock);
31690e33dfeSDaniel P. Berrangé
31790e33dfeSDaniel P. Berrangé trace_qemu_file_monitor_remove_watch(mon, dirpath, id);
31890e33dfeSDaniel P. Berrangé
31990e33dfeSDaniel P. Berrangé dir = g_hash_table_lookup(mon->dirs, dirpath);
32090e33dfeSDaniel P. Berrangé if (!dir) {
32190e33dfeSDaniel P. Berrangé goto cleanup;
32290e33dfeSDaniel P. Berrangé }
32390e33dfeSDaniel P. Berrangé
32490e33dfeSDaniel P. Berrangé for (i = 0; i < dir->watches->len; i++) {
32590e33dfeSDaniel P. Berrangé QFileMonitorWatch *watch = &g_array_index(dir->watches,
32690e33dfeSDaniel P. Berrangé QFileMonitorWatch, i);
32790e33dfeSDaniel P. Berrangé if (watch->id == id) {
32890e33dfeSDaniel P. Berrangé g_free(watch->filename);
32990e33dfeSDaniel P. Berrangé g_array_remove_index(dir->watches, i);
33090e33dfeSDaniel P. Berrangé break;
33190e33dfeSDaniel P. Berrangé }
33290e33dfeSDaniel P. Berrangé }
33390e33dfeSDaniel P. Berrangé
33490e33dfeSDaniel P. Berrangé if (dir->watches->len == 0) {
335b4682a63SDaniel P. Berrangé inotify_rm_watch(mon->fd, dir->inotify_id);
336b4682a63SDaniel P. Berrangé trace_qemu_file_monitor_disable_watch(mon, dir->path, dir->inotify_id);
33790e33dfeSDaniel P. Berrangé
338b4682a63SDaniel P. Berrangé g_hash_table_remove(mon->idmap, GINT_TO_POINTER(dir->inotify_id));
33990e33dfeSDaniel P. Berrangé g_hash_table_remove(mon->dirs, dir->path);
34090e33dfeSDaniel P. Berrangé
34190e33dfeSDaniel P. Berrangé if (g_hash_table_size(mon->dirs) == 0) {
34290e33dfeSDaniel P. Berrangé qemu_set_fd_handler(mon->fd, NULL, NULL, NULL);
34390e33dfeSDaniel P. Berrangé }
34490e33dfeSDaniel P. Berrangé }
34590e33dfeSDaniel P. Berrangé
34690e33dfeSDaniel P. Berrangé cleanup:
34790e33dfeSDaniel P. Berrangé qemu_mutex_unlock(&mon->lock);
34890e33dfeSDaniel P. Berrangé }
349