2 * QEMU file monitor Linux inotify impl
4 * Copyright (c) 2018 Red Hat, Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 #include "qemu/osdep.h"
22 #include "qemu/filemonitor.h"
23 #include "qemu/main-loop.h"
24 #include "qemu/error-report.h"
25 #include "qapi/error.h"
28 #include <sys/inotify.h>
32 QemuMutex lock
; /* protects dirs & idmap */
33 GHashTable
*dirs
; /* dirname => QFileMonitorDir */
34 GHashTable
*idmap
; /* inotify ID => dirname */
39 int64_t id
; /* watch ID */
40 char *filename
; /* optional filter */
41 QFileMonitorHandler cb
;
48 int inotify_id
; /* inotify ID */
49 int next_file_id
; /* file ID counter */
50 GArray
*watches
; /* QFileMonitorWatch elements */
54 static void qemu_file_monitor_watch(void *arg
)
56 QFileMonitor
*mon
= arg
;
58 __attribute__ ((aligned(__alignof__(struct inotify_event
))));
62 qemu_mutex_lock(&mon
->lock
);
65 qemu_mutex_unlock(&mon
->lock
);
69 len
= read(mon
->fd
, buf
, sizeof(buf
));
72 if (errno
!= EAGAIN
) {
73 error_report("Failure monitoring inotify FD '%s',"
74 "disabling events", strerror(errno
));
78 /* no more events right now */
82 /* Loop over all events in the buffer */
89 struct inotify_event
*ev
= (struct inotify_event
*)(buf
+ used
);
92 * We trust the kernel to provide valid buffer with complete event
95 assert(len
- used
>= sizeof(struct inotify_event
));
96 assert(len
- used
- sizeof(struct inotify_event
) >= ev
->len
);
98 name
= ev
->len
? ev
->name
: "";
99 dir
= g_hash_table_lookup(mon
->idmap
, GINT_TO_POINTER(ev
->wd
));
101 (IN_CREATE
| IN_MODIFY
| IN_DELETE
| IN_IGNORED
|
102 IN_MOVED_TO
| IN_MOVED_FROM
| IN_ATTRIB
);
104 used
+= sizeof(struct inotify_event
) + ev
->len
;
111 * During a rename operation, the old name gets
112 * IN_MOVED_FROM and the new name gets IN_MOVED_TO.
113 * To simplify life for callers, we turn these into
114 * DELETED and CREATED events
119 qev
= QFILE_MONITOR_EVENT_CREATED
;
122 qev
= QFILE_MONITOR_EVENT_MODIFIED
;
126 qev
= QFILE_MONITOR_EVENT_DELETED
;
129 qev
= QFILE_MONITOR_EVENT_ATTRIBUTES
;
132 qev
= QFILE_MONITOR_EVENT_IGNORED
;
135 g_assert_not_reached();
138 trace_qemu_file_monitor_event(mon
, dir
->path
, name
, ev
->mask
,
140 for (i
= 0; i
< dir
->watches
->len
; i
++) {
141 QFileMonitorWatch
*watch
= &g_array_index(dir
->watches
,
145 if (watch
->filename
== NULL
||
146 (name
&& g_str_equal(watch
->filename
, name
))) {
147 trace_qemu_file_monitor_dispatch(mon
, dir
->path
, name
,
149 watch
->opaque
, watch
->id
);
150 watch
->cb(watch
->id
, qev
, name
, watch
->opaque
);
156 qemu_mutex_unlock(&mon
->lock
);
161 qemu_file_monitor_dir_free(void *data
)
163 QFileMonitorDir
*dir
= data
;
166 for (i
= 0; i
< dir
->watches
->len
; i
++) {
167 QFileMonitorWatch
*watch
= &g_array_index(dir
->watches
,
168 QFileMonitorWatch
, i
);
169 g_free(watch
->filename
);
171 g_array_unref(dir
->watches
);
178 qemu_file_monitor_new(Error
**errp
)
183 fd
= inotify_init1(IN_NONBLOCK
);
185 error_setg_errno(errp
, errno
,
186 "Unable to initialize inotify");
190 mon
= g_new0(QFileMonitor
, 1);
191 qemu_mutex_init(&mon
->lock
);
194 mon
->dirs
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
,
195 qemu_file_monitor_dir_free
);
196 mon
->idmap
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
198 trace_qemu_file_monitor_new(mon
, mon
->fd
);
204 qemu_file_monitor_free_idle(void *opaque
)
206 QFileMonitor
*mon
= opaque
;
209 return G_SOURCE_REMOVE
;
212 qemu_mutex_lock(&mon
->lock
);
214 g_hash_table_unref(mon
->idmap
);
215 g_hash_table_unref(mon
->dirs
);
217 qemu_mutex_unlock(&mon
->lock
);
219 qemu_mutex_destroy(&mon
->lock
);
222 return G_SOURCE_REMOVE
;
226 qemu_file_monitor_free(QFileMonitor
*mon
)
232 qemu_mutex_lock(&mon
->lock
);
234 qemu_set_fd_handler(mon
->fd
, NULL
, NULL
, NULL
);
238 qemu_mutex_unlock(&mon
->lock
);
241 * Can't free it yet, because another thread
242 * may be running event loop, so the inotify
243 * callback might be pending. Using an idle
244 * source ensures we'll only free after the
245 * pending callback is done
247 g_idle_add((GSourceFunc
)qemu_file_monitor_free_idle
, mon
);
251 qemu_file_monitor_add_watch(QFileMonitor
*mon
,
253 const char *filename
,
254 QFileMonitorHandler cb
,
258 QFileMonitorDir
*dir
;
259 QFileMonitorWatch watch
;
262 qemu_mutex_lock(&mon
->lock
);
263 dir
= g_hash_table_lookup(mon
->dirs
, dirpath
);
265 int rv
= inotify_add_watch(mon
->fd
, dirpath
,
266 IN_CREATE
| IN_DELETE
| IN_MODIFY
|
267 IN_MOVED_TO
| IN_MOVED_FROM
| IN_ATTRIB
);
270 error_setg_errno(errp
, errno
, "Unable to watch '%s'", dirpath
);
274 trace_qemu_file_monitor_enable_watch(mon
, dirpath
, rv
);
276 dir
= g_new0(QFileMonitorDir
, 1);
277 dir
->path
= g_strdup(dirpath
);
278 dir
->inotify_id
= rv
;
279 dir
->watches
= g_array_new(FALSE
, TRUE
, sizeof(QFileMonitorWatch
));
281 g_hash_table_insert(mon
->dirs
, dir
->path
, dir
);
282 g_hash_table_insert(mon
->idmap
, GINT_TO_POINTER(rv
), dir
);
284 if (g_hash_table_size(mon
->dirs
) == 1) {
285 qemu_set_fd_handler(mon
->fd
, qemu_file_monitor_watch
, NULL
, mon
);
289 watch
.id
= (((int64_t)dir
->inotify_id
) << 32) | dir
->next_file_id
++;
290 watch
.filename
= g_strdup(filename
);
292 watch
.opaque
= opaque
;
294 g_array_append_val(dir
->watches
, watch
);
296 trace_qemu_file_monitor_add_watch(mon
, dirpath
,
297 filename
? filename
: "<none>",
298 cb
, opaque
, watch
.id
);
303 qemu_mutex_unlock(&mon
->lock
);
308 void qemu_file_monitor_remove_watch(QFileMonitor
*mon
,
312 QFileMonitorDir
*dir
;
315 qemu_mutex_lock(&mon
->lock
);
317 trace_qemu_file_monitor_remove_watch(mon
, dirpath
, id
);
319 dir
= g_hash_table_lookup(mon
->dirs
, dirpath
);
324 for (i
= 0; i
< dir
->watches
->len
; i
++) {
325 QFileMonitorWatch
*watch
= &g_array_index(dir
->watches
,
326 QFileMonitorWatch
, i
);
327 if (watch
->id
== id
) {
328 g_free(watch
->filename
);
329 g_array_remove_index(dir
->watches
, i
);
334 if (dir
->watches
->len
== 0) {
335 inotify_rm_watch(mon
->fd
, dir
->inotify_id
);
336 trace_qemu_file_monitor_disable_watch(mon
, dir
->path
, dir
->inotify_id
);
338 g_hash_table_remove(mon
->idmap
, GINT_TO_POINTER(dir
->inotify_id
));
339 g_hash_table_remove(mon
->dirs
, dir
->path
);
341 if (g_hash_table_size(mon
->dirs
) == 0) {
342 qemu_set_fd_handler(mon
->fd
, NULL
, NULL
, NULL
);
347 qemu_mutex_unlock(&mon
->lock
);