xref: /qemu/tests/unit/test-util-filemonitor.c (revision 3faebbcd)
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