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