qemu/tests/unit/test-util-filemonitor.c
<<
>>
Prefs
   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
  30enum {
  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
  44typedef 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
  57typedef struct {
  58    int64_t id;
  59    QFileMonitorEvent event;
  60    char *filename;
  61} QFileMonitorTestRecord;
  62
  63
  64typedef struct {
  65    QemuMutex lock;
  66    GList *records;
  67} QFileMonitorTestData;
  68
  69static QemuMutex evlock;
  70static bool evstopping;
  71static bool evrunning;
  72static bool debug;
  73
  74/*
  75 * Main function for a background thread that is
  76 * running the event loop during the test
  77 */
  78static void *
  79qemu_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 */
  99static void
 100qemu_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
 122static void
 123qemu_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 upto 5 seconds
 136 * to wait for the event to be queued for us.
 137 */
 138static QFileMonitorTestRecord *
 139qemu_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 */
 175static bool
 176qemu_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
 224static void
 225test_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        { .type = QFILE_MONITOR_TEST_OP_EVENT,
 364          .filesrc = "two.txt", .watchid = &watch0,
 365          .eventid = QFILE_MONITOR_EVENT_CREATED },
 366        { .type = QFILE_MONITOR_TEST_OP_EVENT,
 367          .filesrc = "two.txt", .watchid = &watch2,
 368          .eventid = QFILE_MONITOR_EVENT_CREATED },
 369
 370
 371        { .type = QFILE_MONITOR_TEST_OP_RMDIR,
 372          .filesrc = "fish", },
 373        { .type = QFILE_MONITOR_TEST_OP_EVENT,
 374          .filesrc = "", .watchid = &watch4,
 375          .eventid = QFILE_MONITOR_EVENT_IGNORED,
 376          .swapnext = true },
 377        { .type = QFILE_MONITOR_TEST_OP_EVENT,
 378          .filesrc = "fish", .watchid = &watch0,
 379          .eventid = QFILE_MONITOR_EVENT_DELETED },
 380        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
 381          .filesrc = "fish", .watchid = &watch4 },
 382
 383
 384        { .type = QFILE_MONITOR_TEST_OP_UNLINK,
 385          .filesrc = "two.txt", },
 386        { .type = QFILE_MONITOR_TEST_OP_EVENT,
 387          .filesrc = "two.txt", .watchid = &watch0,
 388          .eventid = QFILE_MONITOR_EVENT_DELETED },
 389        { .type = QFILE_MONITOR_TEST_OP_EVENT,
 390          .filesrc = "two.txt", .watchid = &watch2,
 391          .eventid = QFILE_MONITOR_EVENT_DELETED },
 392
 393
 394        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
 395          .filesrc = "two.txt", .watchid = &watch2 },
 396        { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH,
 397          .filesrc = NULL, .watchid = &watch0 },
 398    };
 399    Error *local_err = NULL;
 400    GError *gerr = NULL;
 401    QFileMonitor *mon = qemu_file_monitor_new(&local_err);
 402    QemuThread th;
 403    GTimer *timer;
 404    gchar *dir = NULL;
 405    int err = -1;
 406    gsize i;
 407    char *pathsrc = NULL;
 408    char *pathdst = NULL;
 409    QFileMonitorTestData data;
 410    GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal);
 411    char *travis_arch;
 412
 413    qemu_mutex_init(&data.lock);
 414    data.records = NULL;
 415
 416    /*
 417     * This test does not work on Travis LXD containers since some
 418     * syscalls are blocked in that environment.
 419     */
 420    travis_arch = getenv("TRAVIS_ARCH");
 421    if (travis_arch && !g_str_equal(travis_arch, "x86_64")) {
 422        g_test_skip("Test does not work on non-x86 Travis containers.");
 423        return;
 424    }
 425
 426    /*
 427     * The file monitor needs the main loop running in
 428     * order to receive events from inotify. We must
 429     * thus spawn a background thread to run an event
 430     * loop impl, while this thread triggers the
 431     * actual file operations we're testing
 432     */
 433    evrunning = 1;
 434    evstopping = 0;
 435    qemu_thread_create(&th, "event-loop",
 436                       qemu_file_monitor_test_event_loop, NULL,
 437                       QEMU_THREAD_JOINABLE);
 438
 439    if (local_err) {
 440        g_printerr("File monitoring not available: %s",
 441                   error_get_pretty(local_err));
 442        error_free(local_err);
 443        return;
 444    }
 445
 446    dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX",
 447                         &gerr);
 448    if (!dir) {
 449        g_printerr("Unable to create tmp dir %s",
 450                   gerr->message);
 451        g_error_free(gerr);
 452        abort();
 453    }
 454
 455    /*
 456     * Run through the operation sequence validating events
 457     * as we go
 458     */
 459    for (i = 0; i < G_N_ELEMENTS(ops); i++) {
 460        const QFileMonitorTestOp *op = &(ops[i]);
 461        int fd;
 462        struct utimbuf ubuf;
 463        char *watchdir;
 464        const char *watchfile;
 465
 466        pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc);
 467        if (op->filedst) {
 468            pathdst = g_strdup_printf("%s/%s", dir, op->filedst);
 469        }
 470
 471        switch (op->type) {
 472        case QFILE_MONITOR_TEST_OP_ADD_WATCH:
 473            if (debug) {
 474                g_printerr("Add watch %s %s\n",
 475                           dir, op->filesrc);
 476            }
 477            if (op->filesrc && strchr(op->filesrc, '/')) {
 478                watchdir = g_strdup_printf("%s/%s", dir, op->filesrc);
 479                watchfile = strrchr(watchdir, '/');
 480                *(char *)watchfile = '\0';
 481                watchfile++;
 482                if (*watchfile == '\0') {
 483                    watchfile = NULL;
 484                }
 485            } else {
 486                watchdir = g_strdup(dir);
 487                watchfile = op->filesrc;
 488            }
 489            *op->watchid =
 490                qemu_file_monitor_add_watch(mon,
 491                                            watchdir,
 492                                            watchfile,
 493                                            qemu_file_monitor_test_handler,
 494                                            &data,
 495                                            &local_err);
 496            g_free(watchdir);
 497            if (*op->watchid < 0) {
 498                g_printerr("Unable to add watch %s",
 499                           error_get_pretty(local_err));
 500                error_free(local_err);
 501                goto cleanup;
 502            }
 503            if (debug) {
 504                g_printerr("Watch ID %" PRIx64 "\n", *op->watchid);
 505            }
 506            if (g_hash_table_contains(ids, op->watchid)) {
 507                g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid);
 508                goto cleanup;
 509            }
 510            g_hash_table_add(ids, op->watchid);
 511            break;
 512        case QFILE_MONITOR_TEST_OP_DEL_WATCH:
 513            if (debug) {
 514                g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid);
 515            }
 516            if (op->filesrc && strchr(op->filesrc, '/')) {
 517                watchdir = g_strdup_printf("%s/%s", dir, op->filesrc);
 518                watchfile = strrchr(watchdir, '/');
 519                *(char *)watchfile = '\0';
 520            } else {
 521                watchdir = g_strdup(dir);
 522            }
 523            g_hash_table_remove(ids, op->watchid);
 524            qemu_file_monitor_remove_watch(mon,
 525                                           watchdir,
 526                                           *op->watchid);
 527            g_free(watchdir);
 528            break;
 529        case QFILE_MONITOR_TEST_OP_EVENT:
 530            if (debug) {
 531                g_printerr("Event id=%" PRIx64 " event=%d file=%s\n",
 532                           *op->watchid, op->eventid, op->filesrc);
 533            }
 534            if (!qemu_file_monitor_test_expect(&data, *op->watchid,
 535                                               op->eventid, op->filesrc,
 536                                               op->swapnext))
 537                goto cleanup;
 538            break;
 539        case QFILE_MONITOR_TEST_OP_CREATE:
 540            if (debug) {
 541                g_printerr("Create %s\n", pathsrc);
 542            }
 543            fd = open(pathsrc, O_WRONLY | O_CREAT, 0700);
 544            if (fd < 0) {
 545                g_printerr("Unable to create %s: %s",
 546                           pathsrc, strerror(errno));
 547                goto cleanup;
 548            }
 549            close(fd);
 550            break;
 551
 552        case QFILE_MONITOR_TEST_OP_APPEND:
 553            if (debug) {
 554                g_printerr("Append %s\n", pathsrc);
 555            }
 556            fd = open(pathsrc, O_WRONLY | O_APPEND, 0700);
 557            if (fd < 0) {
 558                g_printerr("Unable to open %s: %s",
 559                           pathsrc, strerror(errno));
 560                goto cleanup;
 561            }
 562
 563            if (write(fd, "Hello World", 10) != 10) {
 564                g_printerr("Unable to write %s: %s",
 565                           pathsrc, strerror(errno));
 566                close(fd);
 567                goto cleanup;
 568            }
 569            close(fd);
 570            break;
 571
 572        case QFILE_MONITOR_TEST_OP_TRUNC:
 573            if (debug) {
 574                g_printerr("Truncate %s\n", pathsrc);
 575            }
 576            if (truncate(pathsrc, 4) < 0) {
 577                g_printerr("Unable to truncate %s: %s",
 578                           pathsrc, strerror(errno));
 579                goto cleanup;
 580            }
 581            break;
 582
 583        case QFILE_MONITOR_TEST_OP_RENAME:
 584            if (debug) {
 585                g_printerr("Rename %s -> %s\n", pathsrc, pathdst);
 586            }
 587            if (rename(pathsrc, pathdst) < 0) {
 588                g_printerr("Unable to rename %s to %s: %s",
 589                           pathsrc, pathdst, strerror(errno));
 590                goto cleanup;
 591            }
 592            break;
 593
 594        case QFILE_MONITOR_TEST_OP_UNLINK:
 595            if (debug) {
 596                g_printerr("Unlink %s\n", pathsrc);
 597            }
 598            if (unlink(pathsrc) < 0) {
 599                g_printerr("Unable to unlink %s: %s",
 600                           pathsrc, strerror(errno));
 601                goto cleanup;
 602            }
 603            break;
 604
 605        case QFILE_MONITOR_TEST_OP_TOUCH:
 606            if (debug) {
 607                g_printerr("Touch %s\n", pathsrc);
 608            }
 609            ubuf.actime = 1024;
 610            ubuf.modtime = 1025;
 611            if (utime(pathsrc, &ubuf) < 0) {
 612                g_printerr("Unable to touch %s: %s",
 613                           pathsrc, strerror(errno));
 614                goto cleanup;
 615            }
 616            break;
 617
 618        case QFILE_MONITOR_TEST_OP_MKDIR:
 619            if (debug) {
 620                g_printerr("Mkdir %s\n", pathsrc);
 621            }
 622            if (g_mkdir_with_parents(pathsrc, 0700) < 0) {
 623                g_printerr("Unable to mkdir %s: %s",
 624                           pathsrc, strerror(errno));
 625                goto cleanup;
 626            }
 627            break;
 628
 629        case QFILE_MONITOR_TEST_OP_RMDIR:
 630            if (debug) {
 631                g_printerr("Rmdir %s\n", pathsrc);
 632            }
 633            if (rmdir(pathsrc) < 0) {
 634                g_printerr("Unable to rmdir %s: %s",
 635                           pathsrc, strerror(errno));
 636                goto cleanup;
 637            }
 638            break;
 639
 640        default:
 641            g_assert_not_reached();
 642        }
 643
 644        g_free(pathsrc);
 645        g_free(pathdst);
 646        pathsrc = pathdst = NULL;
 647    }
 648
 649    g_assert_cmpint(g_hash_table_size(ids), ==, 0);
 650
 651    err = 0;
 652
 653 cleanup:
 654    g_free(pathsrc);
 655    g_free(pathdst);
 656
 657    qemu_mutex_lock(&evlock);
 658    evstopping = 1;
 659    timer = g_timer_new();
 660    while (evrunning && g_timer_elapsed(timer, NULL) < 5) {
 661        qemu_mutex_unlock(&evlock);
 662        usleep(10 * 1000);
 663        qemu_mutex_lock(&evlock);
 664    }
 665    qemu_mutex_unlock(&evlock);
 666
 667    if (g_timer_elapsed(timer, NULL) >= 5) {
 668        g_printerr("Event loop failed to quit after 5 seconds\n");
 669    }
 670    g_timer_destroy(timer);
 671
 672    qemu_file_monitor_free(mon);
 673    g_list_foreach(data.records,
 674                   (GFunc)qemu_file_monitor_test_record_free, NULL);
 675    g_list_free(data.records);
 676    qemu_mutex_destroy(&data.lock);
 677    if (dir) {
 678        for (i = 0; i < G_N_ELEMENTS(ops); i++) {
 679            const QFileMonitorTestOp *op = &(ops[i]);
 680            char *path = g_strdup_printf("%s/%s",
 681                                         dir, op->filesrc);
 682            if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) {
 683                rmdir(path);
 684                g_free(path);
 685            } else {
 686                unlink(path);
 687                g_free(path);
 688                if (op->filedst) {
 689                    path = g_strdup_printf("%s/%s",
 690                                           dir, op->filedst);
 691                    unlink(path);
 692                    g_free(path);
 693                }
 694            }
 695        }
 696        if (rmdir(dir) < 0) {
 697            g_printerr("Failed to remove %s: %s\n",
 698                       dir, strerror(errno));
 699            abort();
 700        }
 701    }
 702    g_hash_table_unref(ids);
 703    g_free(dir);
 704    g_assert(err == 0);
 705}
 706
 707
 708int main(int argc, char **argv)
 709{
 710    g_test_init(&argc, &argv, NULL);
 711
 712    qemu_init_main_loop(&error_abort);
 713
 714    qemu_mutex_init(&evlock);
 715
 716    debug = getenv("FILEMONITOR_DEBUG") != NULL;
 717    g_test_add_func("/util/filemonitor", test_file_monitor_events);
 718
 719    return g_test_run();
 720}
 721