1da668aa1SThomas Huth /*
2da668aa1SThomas Huth * Tests for util/filemonitor-*.c
3da668aa1SThomas Huth *
4da668aa1SThomas Huth * Copyright 2018 Red Hat, Inc.
5da668aa1SThomas Huth *
6da668aa1SThomas Huth * This program is free software; you can redistribute it and/or modify
7da668aa1SThomas Huth * it under the terms of the GNU General Public License as published by
8da668aa1SThomas Huth * the Free Software Foundation; either version 2 of the License, or
9da668aa1SThomas Huth * (at your option) any later version.
10da668aa1SThomas Huth *
11da668aa1SThomas Huth * This program is distributed in the hope that it will be useful,
12da668aa1SThomas Huth * but WITHOUT ANY WARRANTY; without even the implied warranty of
13da668aa1SThomas Huth * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14da668aa1SThomas Huth * GNU General Public License for more details.
15da668aa1SThomas Huth *
16da668aa1SThomas Huth * You should have received a copy of the GNU General Public License
17da668aa1SThomas Huth * along with this library; if not, see <http://www.gnu.org/licenses/>.
18da668aa1SThomas Huth *
19da668aa1SThomas Huth */
20da668aa1SThomas Huth
21da668aa1SThomas Huth #include "qemu/osdep.h"
22da668aa1SThomas Huth #include "qemu/main-loop.h"
23da668aa1SThomas Huth #include "qapi/error.h"
24da668aa1SThomas Huth #include "qemu/filemonitor.h"
25da668aa1SThomas Huth
26da668aa1SThomas Huth #include <glib/gstdio.h>
27da668aa1SThomas Huth
28da668aa1SThomas Huth #include <utime.h>
29da668aa1SThomas Huth
30da668aa1SThomas Huth enum {
31da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_ADD_WATCH,
32da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_DEL_WATCH,
33da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_EVENT,
34da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_CREATE,
35da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_APPEND,
36da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_TRUNC,
37da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_RENAME,
38da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_TOUCH,
39da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_UNLINK,
40da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_MKDIR,
41da668aa1SThomas Huth QFILE_MONITOR_TEST_OP_RMDIR,
42da668aa1SThomas Huth };
43da668aa1SThomas Huth
44da668aa1SThomas Huth typedef struct {
45da668aa1SThomas Huth int type;
46da668aa1SThomas Huth const char *filesrc;
47da668aa1SThomas Huth const char *filedst;
48da668aa1SThomas Huth int64_t *watchid;
49da668aa1SThomas Huth int eventid;
50da668aa1SThomas Huth /*
51da668aa1SThomas Huth * Only valid with OP_EVENT - this event might be
52da668aa1SThomas Huth * swapped with the next OP_EVENT
53da668aa1SThomas Huth */
54da668aa1SThomas Huth bool swapnext;
55da668aa1SThomas Huth } QFileMonitorTestOp;
56da668aa1SThomas Huth
57da668aa1SThomas Huth typedef struct {
58da668aa1SThomas Huth int64_t id;
59da668aa1SThomas Huth QFileMonitorEvent event;
60da668aa1SThomas Huth char *filename;
61da668aa1SThomas Huth } QFileMonitorTestRecord;
62da668aa1SThomas Huth
63da668aa1SThomas Huth
64da668aa1SThomas Huth typedef struct {
65da668aa1SThomas Huth QemuMutex lock;
66da668aa1SThomas Huth GList *records;
67da668aa1SThomas Huth } QFileMonitorTestData;
68da668aa1SThomas Huth
69da668aa1SThomas Huth static QemuMutex evlock;
70da668aa1SThomas Huth static bool evstopping;
71da668aa1SThomas Huth static bool evrunning;
72da668aa1SThomas Huth static bool debug;
73da668aa1SThomas Huth
74da668aa1SThomas Huth /*
75da668aa1SThomas Huth * Main function for a background thread that is
76da668aa1SThomas Huth * running the event loop during the test
77da668aa1SThomas Huth */
78da668aa1SThomas Huth static void *
qemu_file_monitor_test_event_loop(void * opaque G_GNUC_UNUSED)79da668aa1SThomas Huth qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED)
80da668aa1SThomas Huth {
81da668aa1SThomas Huth qemu_mutex_lock(&evlock);
82da668aa1SThomas Huth
83da668aa1SThomas Huth while (!evstopping) {
84da668aa1SThomas Huth qemu_mutex_unlock(&evlock);
85da668aa1SThomas Huth main_loop_wait(true);
86da668aa1SThomas Huth qemu_mutex_lock(&evlock);
87da668aa1SThomas Huth }
88da668aa1SThomas Huth
89da668aa1SThomas Huth evrunning = false;
90da668aa1SThomas Huth qemu_mutex_unlock(&evlock);
91da668aa1SThomas Huth return NULL;
92da668aa1SThomas Huth }
93da668aa1SThomas Huth
94da668aa1SThomas Huth
95da668aa1SThomas Huth /*
96da668aa1SThomas Huth * File monitor event handler which simply maintains
97da668aa1SThomas Huth * an ordered list of all events that it receives
98da668aa1SThomas Huth */
99da668aa1SThomas Huth static void
qemu_file_monitor_test_handler(int64_t id,QFileMonitorEvent event,const char * filename,void * opaque)100da668aa1SThomas Huth qemu_file_monitor_test_handler(int64_t id,
101da668aa1SThomas Huth QFileMonitorEvent event,
102da668aa1SThomas Huth const char *filename,
103da668aa1SThomas Huth void *opaque)
104da668aa1SThomas Huth {
105da668aa1SThomas Huth QFileMonitorTestData *data = opaque;
106da668aa1SThomas Huth QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1);
107da668aa1SThomas Huth
108da668aa1SThomas Huth if (debug) {
109da668aa1SThomas Huth g_printerr("Queue event id %" PRIx64 " event %d file %s\n",
110da668aa1SThomas Huth id, event, filename);
111da668aa1SThomas Huth }
112da668aa1SThomas Huth rec->id = id;
113da668aa1SThomas Huth rec->event = event;
114da668aa1SThomas Huth rec->filename = g_strdup(filename);
115da668aa1SThomas Huth
116da668aa1SThomas Huth qemu_mutex_lock(&data->lock);
117da668aa1SThomas Huth data->records = g_list_append(data->records, rec);
118da668aa1SThomas Huth qemu_mutex_unlock(&data->lock);
119da668aa1SThomas Huth }
120da668aa1SThomas Huth
121da668aa1SThomas Huth
122da668aa1SThomas Huth static void
qemu_file_monitor_test_record_free(QFileMonitorTestRecord * rec)123da668aa1SThomas Huth qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec)
124da668aa1SThomas Huth {
125da668aa1SThomas Huth g_free(rec->filename);
126da668aa1SThomas Huth g_free(rec);
127da668aa1SThomas Huth }
128da668aa1SThomas Huth
129da668aa1SThomas Huth
130da668aa1SThomas Huth /*
131da668aa1SThomas Huth * Get the next event record that has been received by
132da668aa1SThomas Huth * the file monitor event handler. Since events are
133da668aa1SThomas Huth * emitted in the background thread running the event
134da668aa1SThomas Huth * loop, we can't assume there is a record available
135da668aa1SThomas Huth * immediately. Thus we will sleep for up to 5 seconds
136da668aa1SThomas Huth * to wait for the event to be queued for us.
137da668aa1SThomas Huth */
138da668aa1SThomas Huth static QFileMonitorTestRecord *
qemu_file_monitor_test_next_record(QFileMonitorTestData * data,QFileMonitorTestRecord * pushback)139da668aa1SThomas Huth qemu_file_monitor_test_next_record(QFileMonitorTestData *data,
140da668aa1SThomas Huth QFileMonitorTestRecord *pushback)
141da668aa1SThomas Huth {
142da668aa1SThomas Huth GTimer *timer = g_timer_new();
143da668aa1SThomas Huth QFileMonitorTestRecord *record = NULL;
144da668aa1SThomas Huth GList *tmp;
145da668aa1SThomas Huth
146da668aa1SThomas Huth qemu_mutex_lock(&data->lock);
147da668aa1SThomas Huth while (!data->records && g_timer_elapsed(timer, NULL) < 5) {
148da668aa1SThomas Huth qemu_mutex_unlock(&data->lock);
149da668aa1SThomas Huth usleep(10 * 1000);
150da668aa1SThomas Huth qemu_mutex_lock(&data->lock);
151da668aa1SThomas Huth }
152da668aa1SThomas Huth if (data->records) {
153da668aa1SThomas Huth record = data->records->data;
154da668aa1SThomas Huth if (pushback) {
155da668aa1SThomas Huth data->records->data = pushback;
156da668aa1SThomas Huth } else {
157da668aa1SThomas Huth tmp = data->records;
158da668aa1SThomas Huth data->records = g_list_remove_link(data->records, tmp);
159da668aa1SThomas Huth g_list_free(tmp);
160da668aa1SThomas Huth }
161da668aa1SThomas Huth } else if (pushback) {
162da668aa1SThomas Huth qemu_file_monitor_test_record_free(pushback);
163da668aa1SThomas Huth }
164da668aa1SThomas Huth qemu_mutex_unlock(&data->lock);
165da668aa1SThomas Huth
166da668aa1SThomas Huth g_timer_destroy(timer);
167da668aa1SThomas Huth return record;
168da668aa1SThomas Huth }
169da668aa1SThomas Huth
170da668aa1SThomas Huth
171da668aa1SThomas Huth /*
172da668aa1SThomas Huth * Check whether the event record we retrieved matches
173da668aa1SThomas Huth * data we were expecting to see for the event
174da668aa1SThomas Huth */
175da668aa1SThomas Huth static bool
qemu_file_monitor_test_expect(QFileMonitorTestData * data,int64_t id,QFileMonitorEvent event,const char * filename,bool swapnext)176da668aa1SThomas Huth qemu_file_monitor_test_expect(QFileMonitorTestData *data,
177da668aa1SThomas Huth int64_t id,
178da668aa1SThomas Huth QFileMonitorEvent event,
179da668aa1SThomas Huth const char *filename,
180da668aa1SThomas Huth bool swapnext)
181da668aa1SThomas Huth {
182da668aa1SThomas Huth QFileMonitorTestRecord *rec;
183da668aa1SThomas Huth bool ret = false;
184da668aa1SThomas Huth
185da668aa1SThomas Huth rec = qemu_file_monitor_test_next_record(data, NULL);
186da668aa1SThomas Huth
187da668aa1SThomas Huth retry:
188da668aa1SThomas Huth if (!rec) {
189da668aa1SThomas Huth g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n",
190da668aa1SThomas Huth id, event, filename);
191da668aa1SThomas Huth return false;
192da668aa1SThomas Huth }
193da668aa1SThomas Huth
194da668aa1SThomas Huth if (id != rec->id) {
195da668aa1SThomas Huth if (swapnext) {
196da668aa1SThomas Huth rec = qemu_file_monitor_test_next_record(data, rec);
197da668aa1SThomas Huth swapnext = false;
198da668aa1SThomas Huth goto retry;
199da668aa1SThomas Huth }
200da668aa1SThomas Huth g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n",
201da668aa1SThomas Huth id, rec->id);
202da668aa1SThomas Huth goto cleanup;
203da668aa1SThomas Huth }
204da668aa1SThomas Huth
205da668aa1SThomas Huth if (event != rec->event) {
206da668aa1SThomas Huth g_printerr("Expected event %d but got %d\n", event, rec->event);
207da668aa1SThomas Huth goto cleanup;
208da668aa1SThomas Huth }
209da668aa1SThomas Huth
210da668aa1SThomas Huth if (!g_str_equal(filename, rec->filename)) {
211da668aa1SThomas Huth g_printerr("Expected filename %s but got %s\n",
212da668aa1SThomas Huth filename, rec->filename);
213da668aa1SThomas Huth goto cleanup;
214da668aa1SThomas Huth }
215da668aa1SThomas Huth
216da668aa1SThomas Huth ret = true;
217da668aa1SThomas Huth
218da668aa1SThomas Huth cleanup:
219da668aa1SThomas Huth qemu_file_monitor_test_record_free(rec);
220da668aa1SThomas Huth return ret;
221da668aa1SThomas Huth }
222da668aa1SThomas Huth
223da668aa1SThomas Huth
224da668aa1SThomas Huth static void
test_file_monitor_events(void)225da668aa1SThomas Huth test_file_monitor_events(void)
226da668aa1SThomas Huth {
227da668aa1SThomas Huth int64_t watch0 = 0;
228da668aa1SThomas Huth int64_t watch1 = 0;
229da668aa1SThomas Huth int64_t watch2 = 0;
230da668aa1SThomas Huth int64_t watch3 = 0;
231da668aa1SThomas Huth int64_t watch4 = 0;
232da668aa1SThomas Huth int64_t watch5 = 0;
233da668aa1SThomas Huth QFileMonitorTestOp ops[] = {
234da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
235da668aa1SThomas Huth .filesrc = NULL, .watchid = &watch0 },
236da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
237da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch1 },
238da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
239da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch2 },
240da668aa1SThomas Huth
241da668aa1SThomas Huth
242da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_CREATE,
243da668aa1SThomas Huth .filesrc = "one.txt", },
244da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
245da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch0,
246da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
247da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
248da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch1,
249da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
250da668aa1SThomas Huth
251da668aa1SThomas Huth
252da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_CREATE,
253da668aa1SThomas Huth .filesrc = "two.txt", },
254da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
255da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch0,
256da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
257da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
258da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch2,
259da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
260da668aa1SThomas Huth
261da668aa1SThomas Huth
262da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_CREATE,
263da668aa1SThomas Huth .filesrc = "three.txt", },
264da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
265da668aa1SThomas Huth .filesrc = "three.txt", .watchid = &watch0,
266da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
267da668aa1SThomas Huth
268da668aa1SThomas Huth
269da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_UNLINK,
270da668aa1SThomas Huth .filesrc = "three.txt", },
271da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
272da668aa1SThomas Huth .filesrc = "three.txt", .watchid = &watch0,
273da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_DELETED },
274da668aa1SThomas Huth
275da668aa1SThomas Huth
276da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_RENAME,
277da668aa1SThomas Huth .filesrc = "one.txt", .filedst = "two.txt" },
278da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
279da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch0,
280da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_DELETED },
281da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
282da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch1,
283da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_DELETED },
284da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
285da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch0,
286da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
287da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
288da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch2,
289da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
290da668aa1SThomas Huth
291da668aa1SThomas Huth
292da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_APPEND,
293da668aa1SThomas Huth .filesrc = "two.txt", },
294da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
295da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch0,
296da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_MODIFIED },
297da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
298da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch2,
299da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_MODIFIED },
300da668aa1SThomas Huth
301da668aa1SThomas Huth
302da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_TOUCH,
303da668aa1SThomas Huth .filesrc = "two.txt", },
304da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
305da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch0,
306da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
307da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
308da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch2,
309da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES },
310da668aa1SThomas Huth
311da668aa1SThomas Huth
312da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
313da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch1 },
314da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
315da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch3 },
316da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_CREATE,
317da668aa1SThomas Huth .filesrc = "one.txt", },
318da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
319da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch0,
320da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
321da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
322da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch3,
323da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
324da668aa1SThomas Huth
325da668aa1SThomas Huth
326da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
327da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch3 },
328da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_UNLINK,
329da668aa1SThomas Huth .filesrc = "one.txt", },
330da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
331da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch0,
332da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_DELETED },
333da668aa1SThomas Huth
334da668aa1SThomas Huth
335da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_MKDIR,
336da668aa1SThomas Huth .filesrc = "fish", },
337da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
338da668aa1SThomas Huth .filesrc = "fish", .watchid = &watch0,
339da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
340da668aa1SThomas Huth
341da668aa1SThomas Huth
342da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
343da668aa1SThomas Huth .filesrc = "fish/", .watchid = &watch4 },
344da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH,
345da668aa1SThomas Huth .filesrc = "fish/one.txt", .watchid = &watch5 },
346da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_CREATE,
347da668aa1SThomas Huth .filesrc = "fish/one.txt", },
348da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
349da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch4,
350da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
351da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
352da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch5,
353da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
354da668aa1SThomas Huth
355da668aa1SThomas Huth
356da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
357da668aa1SThomas Huth .filesrc = "fish/one.txt", .watchid = &watch5 },
358da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_RENAME,
359da668aa1SThomas Huth .filesrc = "fish/one.txt", .filedst = "two.txt", },
360da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
361da668aa1SThomas Huth .filesrc = "one.txt", .watchid = &watch4,
362da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_DELETED },
363*3faebbcdSIlya Leoshkevich #ifdef __FreeBSD__
364*3faebbcdSIlya Leoshkevich { .type = QFILE_MONITOR_TEST_OP_EVENT,
365*3faebbcdSIlya Leoshkevich .filesrc = "two.txt", .watchid = &watch0,
366*3faebbcdSIlya Leoshkevich .eventid = QFILE_MONITOR_EVENT_DELETED },
367*3faebbcdSIlya Leoshkevich { .type = QFILE_MONITOR_TEST_OP_EVENT,
368*3faebbcdSIlya Leoshkevich .filesrc = "two.txt", .watchid = &watch2,
369*3faebbcdSIlya Leoshkevich .eventid = QFILE_MONITOR_EVENT_DELETED },
370*3faebbcdSIlya Leoshkevich #endif
371da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
372da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch0,
373da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
374da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
375da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch2,
376da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_CREATED },
377da668aa1SThomas Huth
378da668aa1SThomas Huth
379da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_RMDIR,
380da668aa1SThomas Huth .filesrc = "fish", },
381da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
382da668aa1SThomas Huth .filesrc = "", .watchid = &watch4,
383da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_IGNORED,
384da668aa1SThomas Huth .swapnext = true },
385da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
386da668aa1SThomas Huth .filesrc = "fish", .watchid = &watch0,
387da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_DELETED },
388da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
389da668aa1SThomas Huth .filesrc = "fish", .watchid = &watch4 },
390da668aa1SThomas Huth
391da668aa1SThomas Huth
392da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_UNLINK,
393da668aa1SThomas Huth .filesrc = "two.txt", },
394da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
395da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch0,
396da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_DELETED },
397da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_EVENT,
398da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch2,
399da668aa1SThomas Huth .eventid = QFILE_MONITOR_EVENT_DELETED },
400da668aa1SThomas Huth
401da668aa1SThomas Huth
402da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
403da668aa1SThomas Huth .filesrc = "two.txt", .watchid = &watch2 },
404da668aa1SThomas Huth { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
405da668aa1SThomas Huth .filesrc = NULL, .watchid = &watch0 },
406da668aa1SThomas Huth };
407da668aa1SThomas Huth Error *local_err = NULL;
408da668aa1SThomas Huth GError *gerr = NULL;
409da668aa1SThomas Huth QFileMonitor *mon = qemu_file_monitor_new(&local_err);
410da668aa1SThomas Huth QemuThread th;
411da668aa1SThomas Huth GTimer *timer;
412da668aa1SThomas Huth gchar *dir = NULL;
413da668aa1SThomas Huth int err = -1;
414da668aa1SThomas Huth gsize i;
415da668aa1SThomas Huth char *pathsrc = NULL;
416da668aa1SThomas Huth char *pathdst = NULL;
417da668aa1SThomas Huth QFileMonitorTestData data;
418da668aa1SThomas Huth GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal);
419da668aa1SThomas Huth char *travis_arch;
420da668aa1SThomas Huth
421da668aa1SThomas Huth qemu_mutex_init(&data.lock);
422da668aa1SThomas Huth data.records = NULL;
423da668aa1SThomas Huth
424da668aa1SThomas Huth /*
425da668aa1SThomas Huth * This test does not work on Travis LXD containers since some
426da668aa1SThomas Huth * syscalls are blocked in that environment.
427da668aa1SThomas Huth */
428da668aa1SThomas Huth travis_arch = getenv("TRAVIS_ARCH");
429da668aa1SThomas Huth if (travis_arch && !g_str_equal(travis_arch, "x86_64")) {
430da668aa1SThomas Huth g_test_skip("Test does not work on non-x86 Travis containers.");
431da668aa1SThomas Huth return;
432da668aa1SThomas Huth }
433da668aa1SThomas Huth
434da668aa1SThomas Huth /*
435da668aa1SThomas Huth * The file monitor needs the main loop running in
436da668aa1SThomas Huth * order to receive events from inotify. We must
437da668aa1SThomas Huth * thus spawn a background thread to run an event
438da668aa1SThomas Huth * loop impl, while this thread triggers the
439da668aa1SThomas Huth * actual file operations we're testing
440da668aa1SThomas Huth */
441da668aa1SThomas Huth evrunning = 1;
442da668aa1SThomas Huth evstopping = 0;
443da668aa1SThomas Huth qemu_thread_create(&th, "event-loop",
444da668aa1SThomas Huth qemu_file_monitor_test_event_loop, NULL,
445da668aa1SThomas Huth QEMU_THREAD_JOINABLE);
446da668aa1SThomas Huth
447da668aa1SThomas Huth if (local_err) {
448da668aa1SThomas Huth g_printerr("File monitoring not available: %s",
449da668aa1SThomas Huth error_get_pretty(local_err));
450da668aa1SThomas Huth error_free(local_err);
451da668aa1SThomas Huth return;
452da668aa1SThomas Huth }
453da668aa1SThomas Huth
454da668aa1SThomas Huth dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX",
455da668aa1SThomas Huth &gerr);
456da668aa1SThomas Huth if (!dir) {
457da668aa1SThomas Huth g_printerr("Unable to create tmp dir %s",
458da668aa1SThomas Huth gerr->message);
459da668aa1SThomas Huth g_error_free(gerr);
460da668aa1SThomas Huth abort();
461da668aa1SThomas Huth }
462da668aa1SThomas Huth
463da668aa1SThomas Huth /*
464da668aa1SThomas Huth * Run through the operation sequence validating events
465da668aa1SThomas Huth * as we go
466da668aa1SThomas Huth */
467da668aa1SThomas Huth for (i = 0; i < G_N_ELEMENTS(ops); i++) {
468da668aa1SThomas Huth const QFileMonitorTestOp *op = &(ops[i]);
469da668aa1SThomas Huth int fd;
470da668aa1SThomas Huth struct utimbuf ubuf;
471da668aa1SThomas Huth char *watchdir;
472da668aa1SThomas Huth const char *watchfile;
473da668aa1SThomas Huth
474da668aa1SThomas Huth pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
475da668aa1SThomas Huth if (op->filedst) {
476da668aa1SThomas Huth pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
477da668aa1SThomas Huth }
478da668aa1SThomas Huth
479da668aa1SThomas Huth switch (op->type) {
480da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_ADD_WATCH:
481da668aa1SThomas Huth if (debug) {
482da668aa1SThomas Huth g_printerr("Add watch %s %s\n",
483da668aa1SThomas Huth dir, op->filesrc);
484da668aa1SThomas Huth }
485da668aa1SThomas Huth if (op->filesrc && strchr(op->filesrc, '/')) {
486da668aa1SThomas Huth watchdir = g_strdup_printf("%s/%s", dir, op->filesrc);
487da668aa1SThomas Huth watchfile = strrchr(watchdir, '/');
488da668aa1SThomas Huth *(char *)watchfile = '\0';
489da668aa1SThomas Huth watchfile++;
490da668aa1SThomas Huth if (*watchfile == '\0') {
491da668aa1SThomas Huth watchfile = NULL;
492da668aa1SThomas Huth }
493da668aa1SThomas Huth } else {
494da668aa1SThomas Huth watchdir = g_strdup(dir);
495da668aa1SThomas Huth watchfile = op->filesrc;
496da668aa1SThomas Huth }
497da668aa1SThomas Huth *op->watchid =
498da668aa1SThomas Huth qemu_file_monitor_add_watch(mon,
499da668aa1SThomas Huth watchdir,
500da668aa1SThomas Huth watchfile,
501da668aa1SThomas Huth qemu_file_monitor_test_handler,
502da668aa1SThomas Huth &data,
503da668aa1SThomas Huth &local_err);
504da668aa1SThomas Huth g_free(watchdir);
505da668aa1SThomas Huth if (*op->watchid < 0) {
506da668aa1SThomas Huth g_printerr("Unable to add watch %s",
507da668aa1SThomas Huth error_get_pretty(local_err));
508da668aa1SThomas Huth error_free(local_err);
509da668aa1SThomas Huth goto cleanup;
510da668aa1SThomas Huth }
511da668aa1SThomas Huth if (debug) {
512da668aa1SThomas Huth g_printerr("Watch ID %" PRIx64 "\n", *op->watchid);
513da668aa1SThomas Huth }
514da668aa1SThomas Huth if (g_hash_table_contains(ids, op->watchid)) {
515da668aa1SThomas Huth g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid);
516da668aa1SThomas Huth goto cleanup;
517da668aa1SThomas Huth }
518da668aa1SThomas Huth g_hash_table_add(ids, op->watchid);
519da668aa1SThomas Huth break;
520da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_DEL_WATCH:
521da668aa1SThomas Huth if (debug) {
522da668aa1SThomas Huth g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid);
523da668aa1SThomas Huth }
524da668aa1SThomas Huth if (op->filesrc && strchr(op->filesrc, '/')) {
525da668aa1SThomas Huth watchdir = g_strdup_printf("%s/%s", dir, op->filesrc);
526da668aa1SThomas Huth watchfile = strrchr(watchdir, '/');
527da668aa1SThomas Huth *(char *)watchfile = '\0';
528da668aa1SThomas Huth } else {
529da668aa1SThomas Huth watchdir = g_strdup(dir);
530da668aa1SThomas Huth }
531da668aa1SThomas Huth g_hash_table_remove(ids, op->watchid);
532da668aa1SThomas Huth qemu_file_monitor_remove_watch(mon,
533da668aa1SThomas Huth watchdir,
534da668aa1SThomas Huth *op->watchid);
535da668aa1SThomas Huth g_free(watchdir);
536da668aa1SThomas Huth break;
537da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_EVENT:
538da668aa1SThomas Huth if (debug) {
539da668aa1SThomas Huth g_printerr("Event id=%" PRIx64 " event=%d file=%s\n",
540da668aa1SThomas Huth *op->watchid, op->eventid, op->filesrc);
541da668aa1SThomas Huth }
542da668aa1SThomas Huth if (!qemu_file_monitor_test_expect(&data, *op->watchid,
543da668aa1SThomas Huth op->eventid, op->filesrc,
544da668aa1SThomas Huth op->swapnext))
545da668aa1SThomas Huth goto cleanup;
546da668aa1SThomas Huth break;
547da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_CREATE:
548da668aa1SThomas Huth if (debug) {
549da668aa1SThomas Huth g_printerr("Create %s\n", pathsrc);
550da668aa1SThomas Huth }
551da668aa1SThomas Huth fd = open(pathsrc, O_WRONLY | O_CREAT, 0700);
552da668aa1SThomas Huth if (fd < 0) {
553da668aa1SThomas Huth g_printerr("Unable to create %s: %s",
554da668aa1SThomas Huth pathsrc, strerror(errno));
555da668aa1SThomas Huth goto cleanup;
556da668aa1SThomas Huth }
557da668aa1SThomas Huth close(fd);
558da668aa1SThomas Huth break;
559da668aa1SThomas Huth
560da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_APPEND:
561da668aa1SThomas Huth if (debug) {
562da668aa1SThomas Huth g_printerr("Append %s\n", pathsrc);
563da668aa1SThomas Huth }
564da668aa1SThomas Huth fd = open(pathsrc, O_WRONLY | O_APPEND, 0700);
565da668aa1SThomas Huth if (fd < 0) {
566da668aa1SThomas Huth g_printerr("Unable to open %s: %s",
567da668aa1SThomas Huth pathsrc, strerror(errno));
568da668aa1SThomas Huth goto cleanup;
569da668aa1SThomas Huth }
570da668aa1SThomas Huth
571da668aa1SThomas Huth if (write(fd, "Hello World", 10) != 10) {
572da668aa1SThomas Huth g_printerr("Unable to write %s: %s",
573da668aa1SThomas Huth pathsrc, strerror(errno));
574da668aa1SThomas Huth close(fd);
575da668aa1SThomas Huth goto cleanup;
576da668aa1SThomas Huth }
577da668aa1SThomas Huth close(fd);
578da668aa1SThomas Huth break;
579da668aa1SThomas Huth
580da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_TRUNC:
581da668aa1SThomas Huth if (debug) {
582da668aa1SThomas Huth g_printerr("Truncate %s\n", pathsrc);
583da668aa1SThomas Huth }
584da668aa1SThomas Huth if (truncate(pathsrc, 4) < 0) {
585da668aa1SThomas Huth g_printerr("Unable to truncate %s: %s",
586da668aa1SThomas Huth pathsrc, strerror(errno));
587da668aa1SThomas Huth goto cleanup;
588da668aa1SThomas Huth }
589da668aa1SThomas Huth break;
590da668aa1SThomas Huth
591da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_RENAME:
592da668aa1SThomas Huth if (debug) {
593da668aa1SThomas Huth g_printerr("Rename %s -> %s\n", pathsrc, pathdst);
594da668aa1SThomas Huth }
595da668aa1SThomas Huth if (rename(pathsrc, pathdst) < 0) {
596da668aa1SThomas Huth g_printerr("Unable to rename %s to %s: %s",
597da668aa1SThomas Huth pathsrc, pathdst, strerror(errno));
598da668aa1SThomas Huth goto cleanup;
599da668aa1SThomas Huth }
600da668aa1SThomas Huth break;
601da668aa1SThomas Huth
602da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_UNLINK:
603da668aa1SThomas Huth if (debug) {
604da668aa1SThomas Huth g_printerr("Unlink %s\n", pathsrc);
605da668aa1SThomas Huth }
606da668aa1SThomas Huth if (unlink(pathsrc) < 0) {
607da668aa1SThomas Huth g_printerr("Unable to unlink %s: %s",
608da668aa1SThomas Huth pathsrc, strerror(errno));
609da668aa1SThomas Huth goto cleanup;
610da668aa1SThomas Huth }
611da668aa1SThomas Huth break;
612da668aa1SThomas Huth
613da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_TOUCH:
614da668aa1SThomas Huth if (debug) {
615da668aa1SThomas Huth g_printerr("Touch %s\n", pathsrc);
616da668aa1SThomas Huth }
617da668aa1SThomas Huth ubuf.actime = 1024;
618da668aa1SThomas Huth ubuf.modtime = 1025;
619da668aa1SThomas Huth if (utime(pathsrc, &ubuf) < 0) {
620da668aa1SThomas Huth g_printerr("Unable to touch %s: %s",
621da668aa1SThomas Huth pathsrc, strerror(errno));
622da668aa1SThomas Huth goto cleanup;
623da668aa1SThomas Huth }
624da668aa1SThomas Huth break;
625da668aa1SThomas Huth
626da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_MKDIR:
627da668aa1SThomas Huth if (debug) {
628da668aa1SThomas Huth g_printerr("Mkdir %s\n", pathsrc);
629da668aa1SThomas Huth }
630da668aa1SThomas Huth if (g_mkdir_with_parents(pathsrc, 0700) < 0) {
631da668aa1SThomas Huth g_printerr("Unable to mkdir %s: %s",
632da668aa1SThomas Huth pathsrc, strerror(errno));
633da668aa1SThomas Huth goto cleanup;
634da668aa1SThomas Huth }
635da668aa1SThomas Huth break;
636da668aa1SThomas Huth
637da668aa1SThomas Huth case QFILE_MONITOR_TEST_OP_RMDIR:
638da668aa1SThomas Huth if (debug) {
639da668aa1SThomas Huth g_printerr("Rmdir %s\n", pathsrc);
640da668aa1SThomas Huth }
641da668aa1SThomas Huth if (rmdir(pathsrc) < 0) {
642da668aa1SThomas Huth g_printerr("Unable to rmdir %s: %s",
643da668aa1SThomas Huth pathsrc, strerror(errno));
644da668aa1SThomas Huth goto cleanup;
645da668aa1SThomas Huth }
646da668aa1SThomas Huth break;
647da668aa1SThomas Huth
648da668aa1SThomas Huth default:
649da668aa1SThomas Huth g_assert_not_reached();
650da668aa1SThomas Huth }
651da668aa1SThomas Huth
652da668aa1SThomas Huth g_free(pathsrc);
653da668aa1SThomas Huth g_free(pathdst);
654da668aa1SThomas Huth pathsrc = pathdst = NULL;
655da668aa1SThomas Huth }
656da668aa1SThomas Huth
657da668aa1SThomas Huth g_assert_cmpint(g_hash_table_size(ids), ==, 0);
658da668aa1SThomas Huth
659da668aa1SThomas Huth err = 0;
660da668aa1SThomas Huth
661da668aa1SThomas Huth cleanup:
662da668aa1SThomas Huth g_free(pathsrc);
663da668aa1SThomas Huth g_free(pathdst);
664da668aa1SThomas Huth
665da668aa1SThomas Huth qemu_mutex_lock(&evlock);
666da668aa1SThomas Huth evstopping = 1;
667da668aa1SThomas Huth timer = g_timer_new();
668da668aa1SThomas Huth while (evrunning && g_timer_elapsed(timer, NULL) < 5) {
669da668aa1SThomas Huth qemu_mutex_unlock(&evlock);
670da668aa1SThomas Huth usleep(10 * 1000);
671da668aa1SThomas Huth qemu_mutex_lock(&evlock);
672da668aa1SThomas Huth }
673da668aa1SThomas Huth qemu_mutex_unlock(&evlock);
674da668aa1SThomas Huth
675da668aa1SThomas Huth if (g_timer_elapsed(timer, NULL) >= 5) {
676da668aa1SThomas Huth g_printerr("Event loop failed to quit after 5 seconds\n");
677da668aa1SThomas Huth }
678da668aa1SThomas Huth g_timer_destroy(timer);
679da668aa1SThomas Huth
680da668aa1SThomas Huth qemu_file_monitor_free(mon);
681da668aa1SThomas Huth g_list_foreach(data.records,
682da668aa1SThomas Huth (GFunc)qemu_file_monitor_test_record_free, NULL);
683da668aa1SThomas Huth g_list_free(data.records);
684da668aa1SThomas Huth qemu_mutex_destroy(&data.lock);
685da668aa1SThomas Huth if (dir) {
686da668aa1SThomas Huth for (i = 0; i < G_N_ELEMENTS(ops); i++) {
687da668aa1SThomas Huth const QFileMonitorTestOp *op = &(ops[i]);
688da668aa1SThomas Huth char *path = g_strdup_printf("%s/%s",
689da668aa1SThomas Huth dir, op->filesrc);
690da668aa1SThomas Huth if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) {
691da668aa1SThomas Huth rmdir(path);
692da668aa1SThomas Huth g_free(path);
693da668aa1SThomas Huth } else {
694da668aa1SThomas Huth unlink(path);
695da668aa1SThomas Huth g_free(path);
696da668aa1SThomas Huth if (op->filedst) {
697da668aa1SThomas Huth path = g_strdup_printf("%s/%s",
698da668aa1SThomas Huth dir, op->filedst);
699da668aa1SThomas Huth unlink(path);
700da668aa1SThomas Huth g_free(path);
701da668aa1SThomas Huth }
702da668aa1SThomas Huth }
703da668aa1SThomas Huth }
704da668aa1SThomas Huth if (rmdir(dir) < 0) {
705da668aa1SThomas Huth g_printerr("Failed to remove %s: %s\n",
706da668aa1SThomas Huth dir, strerror(errno));
707da668aa1SThomas Huth abort();
708da668aa1SThomas Huth }
709da668aa1SThomas Huth }
710da668aa1SThomas Huth g_hash_table_unref(ids);
711da668aa1SThomas Huth g_free(dir);
712da668aa1SThomas Huth g_assert(err == 0);
713da668aa1SThomas Huth }
714da668aa1SThomas Huth
715da668aa1SThomas Huth
main(int argc,char ** argv)716da668aa1SThomas Huth int main(int argc, char **argv)
717da668aa1SThomas Huth {
718da668aa1SThomas Huth g_test_init(&argc, &argv, NULL);
719da668aa1SThomas Huth
720da668aa1SThomas Huth qemu_init_main_loop(&error_abort);
721da668aa1SThomas Huth
722da668aa1SThomas Huth qemu_mutex_init(&evlock);
723da668aa1SThomas Huth
724da668aa1SThomas Huth debug = getenv("FILEMONITOR_DEBUG") != NULL;
725da668aa1SThomas Huth g_test_add_func("/util/filemonitor", test_file_monitor_events);
726da668aa1SThomas Huth
727da668aa1SThomas Huth return g_test_run();
728da668aa1SThomas Huth }
729