xref: /openbmc/qemu/util/filemonitor-inotify.c (revision f0dbe427)
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