| /* | |
| * Tests for util/filemonitor-*.c | |
| * | |
| * Copyright 2018 Red Hat, Inc. | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this library; if not, see <http://www.gnu.org/licenses/>. | |
| * | |
| */ | |
| #include "qemu/osdep.h" | |
| #include "qemu/main-loop.h" | |
| #include "qapi/error.h" | |
| #include "qemu/filemonitor.h" | |
| #include <glib/gstdio.h> | |
| #include <utime.h> | |
| #ifdef __FreeBSD__ | |
| #include <osreldate.h> | |
| #endif | |
| enum { | |
| QFILE_MONITOR_TEST_OP_ADD_WATCH, | |
| QFILE_MONITOR_TEST_OP_DEL_WATCH, | |
| QFILE_MONITOR_TEST_OP_EVENT, | |
| QFILE_MONITOR_TEST_OP_CREATE, | |
| QFILE_MONITOR_TEST_OP_APPEND, | |
| QFILE_MONITOR_TEST_OP_TRUNC, | |
| QFILE_MONITOR_TEST_OP_RENAME, | |
| QFILE_MONITOR_TEST_OP_TOUCH, | |
| QFILE_MONITOR_TEST_OP_UNLINK, | |
| QFILE_MONITOR_TEST_OP_MKDIR, | |
| QFILE_MONITOR_TEST_OP_RMDIR, | |
| }; | |
| typedef struct { | |
| int type; | |
| const char *filesrc; | |
| const char *filedst; | |
| int64_t *watchid; | |
| int eventid; | |
| /* | |
| * Only valid with OP_EVENT - this event might be | |
| * swapped with the next OP_EVENT | |
| */ | |
| bool swapnext; | |
| } QFileMonitorTestOp; | |
| typedef struct { | |
| int64_t id; | |
| QFileMonitorEvent event; | |
| char *filename; | |
| } QFileMonitorTestRecord; | |
| typedef struct { | |
| QemuMutex lock; | |
| GList *records; | |
| } QFileMonitorTestData; | |
| static QemuMutex evlock; | |
| static bool evstopping; | |
| static bool evrunning; | |
| static bool debug; | |
| /* | |
| * Main function for a background thread that is | |
| * running the event loop during the test | |
| */ | |
| static void * | |
| qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED) | |
| { | |
| qemu_mutex_lock(&evlock); | |
| while (!evstopping) { | |
| qemu_mutex_unlock(&evlock); | |
| main_loop_wait(true); | |
| qemu_mutex_lock(&evlock); | |
| } | |
| evrunning = false; | |
| qemu_mutex_unlock(&evlock); | |
| return NULL; | |
| } | |
| /* | |
| * File monitor event handler which simply maintains | |
| * an ordered list of all events that it receives | |
| */ | |
| static void | |
| qemu_file_monitor_test_handler(int64_t id, | |
| QFileMonitorEvent event, | |
| const char *filename, | |
| void *opaque) | |
| { | |
| QFileMonitorTestData *data = opaque; | |
| QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1); | |
| if (debug) { | |
| g_printerr("Queue event id %" PRIx64 " event %d file %s\n", | |
| id, event, filename); | |
| } | |
| rec->id = id; | |
| rec->event = event; | |
| rec->filename = g_strdup(filename); | |
| qemu_mutex_lock(&data->lock); | |
| data->records = g_list_append(data->records, rec); | |
| qemu_mutex_unlock(&data->lock); | |
| } | |
| static void | |
| qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec) | |
| { | |
| g_free(rec->filename); | |
| g_free(rec); | |
| } | |
| /* | |
| * Get the next event record that has been received by | |
| * the file monitor event handler. Since events are | |
| * emitted in the background thread running the event | |
| * loop, we can't assume there is a record available | |
| * immediately. Thus we will sleep for up to 5 seconds | |
| * to wait for the event to be queued for us. | |
| */ | |
| static QFileMonitorTestRecord * | |
| qemu_file_monitor_test_next_record(QFileMonitorTestData *data, | |
| QFileMonitorTestRecord *pushback) | |
| { | |
| GTimer *timer = g_timer_new(); | |
| QFileMonitorTestRecord *record = NULL; | |
| GList *tmp; | |
| qemu_mutex_lock(&data->lock); | |
| while (!data->records && g_timer_elapsed(timer, NULL) < 5) { | |
| qemu_mutex_unlock(&data->lock); | |
| usleep(10 * 1000); | |
| qemu_mutex_lock(&data->lock); | |
| } | |
| if (data->records) { | |
| record = data->records->data; | |
| if (pushback) { | |
| data->records->data = pushback; | |
| } else { | |
| tmp = data->records; | |
| data->records = g_list_remove_link(data->records, tmp); | |
| g_list_free(tmp); | |
| } | |
| } else if (pushback) { | |
| qemu_file_monitor_test_record_free(pushback); | |
| } | |
| qemu_mutex_unlock(&data->lock); | |
| g_timer_destroy(timer); | |
| return record; | |
| } | |
| /* | |
| * Check whether the event record we retrieved matches | |
| * data we were expecting to see for the event | |
| */ | |
| static bool | |
| qemu_file_monitor_test_expect(QFileMonitorTestData *data, | |
| int64_t id, | |
| QFileMonitorEvent event, | |
| const char *filename, | |
| bool swapnext) | |
| { | |
| QFileMonitorTestRecord *rec; | |
| bool ret = false; | |
| rec = qemu_file_monitor_test_next_record(data, NULL); | |
| retry: | |
| if (!rec) { | |
| g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n", | |
| id, event, filename); | |
| return false; | |
| } | |
| if (id != rec->id) { | |
| if (swapnext) { | |
| rec = qemu_file_monitor_test_next_record(data, rec); | |
| swapnext = false; | |
| goto retry; | |
| } | |
| g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n", | |
| id, rec->id); | |
| goto cleanup; | |
| } | |
| if (event != rec->event) { | |
| g_printerr("Expected event %d but got %d\n", event, rec->event); | |
| goto cleanup; | |
| } | |
| if (!g_str_equal(filename, rec->filename)) { | |
| g_printerr("Expected filename %s but got %s\n", | |
| filename, rec->filename); | |
| goto cleanup; | |
| } | |
| ret = true; | |
| cleanup: | |
| qemu_file_monitor_test_record_free(rec); | |
| return ret; | |
| } | |
| static bool | |
| expect_broken_ignored(void) | |
| { | |
| #if defined(__FreeBSD__) && __FreeBSD_version >= 1500051 | |
| int osreldate; | |
| osreldate = getosreldate(); | |
| if (osreldate == -1) { | |
| g_printerr("Unable to call getosreldate: %s\n", strerror(errno)); | |
| abort(); | |
| } | |
| return osreldate < 1501501 || (osreldate >= 1600000 && osreldate < 1600019); | |
| #else | |
| return false; | |
| #endif | |
| } | |
| static void | |
| test_file_monitor_events(void) | |
| { | |
| int64_t watch0 = 0; | |
| int64_t watch1 = 0; | |
| int64_t watch2 = 0; | |
| int64_t watch3 = 0; | |
| int64_t watch4 = 0; | |
| int64_t watch5 = 0; | |
| QFileMonitorTestOp ops[] = { | |
| { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, | |
| .filesrc = NULL, .watchid = &watch0 }, | |
| { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, | |
| .filesrc = "one.txt", .watchid = &watch1 }, | |
| { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, | |
| .filesrc = "two.txt", .watchid = &watch2 }, | |
| { .type = QFILE_MONITOR_TEST_OP_CREATE, | |
| .filesrc = "one.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch1, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_CREATE, | |
| .filesrc = "two.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch2, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_CREATE, | |
| .filesrc = "three.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "three.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_UNLINK, | |
| .filesrc = "three.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "three.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| { .type = QFILE_MONITOR_TEST_OP_RENAME, | |
| .filesrc = "one.txt", .filedst = "two.txt" }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch1, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch2, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_APPEND, | |
| .filesrc = "two.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_MODIFIED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch2, | |
| .eventid = QFILE_MONITOR_EVENT_MODIFIED }, | |
| { .type = QFILE_MONITOR_TEST_OP_TOUCH, | |
| .filesrc = "two.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch2, | |
| .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, | |
| { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, | |
| .filesrc = "one.txt", .watchid = &watch1 }, | |
| { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, | |
| .filesrc = "one.txt", .watchid = &watch3 }, | |
| { .type = QFILE_MONITOR_TEST_OP_CREATE, | |
| .filesrc = "one.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch3, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, | |
| .filesrc = "one.txt", .watchid = &watch3 }, | |
| { .type = QFILE_MONITOR_TEST_OP_UNLINK, | |
| .filesrc = "one.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| { .type = QFILE_MONITOR_TEST_OP_MKDIR, | |
| .filesrc = "fish", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "fish", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, | |
| .filesrc = "fish/", .watchid = &watch4 }, | |
| { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, | |
| .filesrc = "fish/one.txt", .watchid = &watch5 }, | |
| { .type = QFILE_MONITOR_TEST_OP_CREATE, | |
| .filesrc = "fish/one.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch4, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch5, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, | |
| .filesrc = "fish/one.txt", .watchid = &watch5 }, | |
| { .type = QFILE_MONITOR_TEST_OP_RENAME, | |
| .filesrc = "fish/one.txt", .filedst = "two.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "one.txt", .watchid = &watch4, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| #if defined(__FreeBSD__) && __FreeBSD_version < 1500051 | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch2, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| #endif | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch2, | |
| .eventid = QFILE_MONITOR_EVENT_CREATED }, | |
| { .type = QFILE_MONITOR_TEST_OP_RMDIR, | |
| .filesrc = "fish", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "", .watchid = &watch4, | |
| .eventid = QFILE_MONITOR_EVENT_IGNORED, | |
| .swapnext = true }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "fish", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, | |
| .filesrc = "fish", .watchid = &watch4 }, | |
| { .type = QFILE_MONITOR_TEST_OP_UNLINK, | |
| .filesrc = "two.txt", }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch0, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| { .type = QFILE_MONITOR_TEST_OP_EVENT, | |
| .filesrc = "two.txt", .watchid = &watch2, | |
| .eventid = QFILE_MONITOR_EVENT_DELETED }, | |
| { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, | |
| .filesrc = "two.txt", .watchid = &watch2 }, | |
| { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, | |
| .filesrc = NULL, .watchid = &watch0 }, | |
| }; | |
| Error *local_err = NULL; | |
| GError *gerr = NULL; | |
| QFileMonitor *mon = qemu_file_monitor_new(&local_err); | |
| QemuThread th; | |
| GTimer *timer; | |
| gchar *dir = NULL; | |
| int err = -1; | |
| gsize i; | |
| char *pathsrc = NULL; | |
| char *pathdst = NULL; | |
| QFileMonitorTestData data; | |
| GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal); | |
| char *travis_arch; | |
| qemu_mutex_init(&data.lock); | |
| data.records = NULL; | |
| /* | |
| * This test does not work on Travis LXD containers since some | |
| * syscalls are blocked in that environment. | |
| */ | |
| travis_arch = getenv("TRAVIS_ARCH"); | |
| if (travis_arch && !g_str_equal(travis_arch, "x86_64")) { | |
| g_test_skip("Test does not work on non-x86 Travis containers."); | |
| return; | |
| } | |
| /* | |
| * The file monitor needs the main loop running in | |
| * order to receive events from inotify. We must | |
| * thus spawn a background thread to run an event | |
| * loop impl, while this thread triggers the | |
| * actual file operations we're testing | |
| */ | |
| evrunning = 1; | |
| evstopping = 0; | |
| qemu_thread_create(&th, "event-loop", | |
| qemu_file_monitor_test_event_loop, NULL, | |
| QEMU_THREAD_JOINABLE); | |
| if (local_err) { | |
| g_printerr("File monitoring not available: %s", | |
| error_get_pretty(local_err)); | |
| error_free(local_err); | |
| return; | |
| } | |
| dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX", | |
| &gerr); | |
| if (!dir) { | |
| g_printerr("Unable to create tmp dir %s", | |
| gerr->message); | |
| g_error_free(gerr); | |
| abort(); | |
| } | |
| /* | |
| * Run through the operation sequence validating events | |
| * as we go | |
| */ | |
| for (i = 0; i < G_N_ELEMENTS(ops); i++) { | |
| const QFileMonitorTestOp *op = &(ops[i]); | |
| int fd; | |
| struct utimbuf ubuf; | |
| char *watchdir; | |
| const char *watchfile; | |
| pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc); | |
| if (op->filedst) { | |
| pathdst = g_strdup_printf("%s/%s", dir, op->filedst); | |
| } | |
| switch (op->type) { | |
| case QFILE_MONITOR_TEST_OP_ADD_WATCH: | |
| if (debug) { | |
| g_printerr("Add watch %s %s\n", | |
| dir, op->filesrc); | |
| } | |
| if (op->filesrc && strchr(op->filesrc, '/')) { | |
| watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); | |
| watchfile = strrchr(watchdir, '/'); | |
| *(char *)watchfile = '\0'; | |
| watchfile++; | |
| if (*watchfile == '\0') { | |
| watchfile = NULL; | |
| } | |
| } else { | |
| watchdir = g_strdup(dir); | |
| watchfile = op->filesrc; | |
| } | |
| *op->watchid = | |
| qemu_file_monitor_add_watch(mon, | |
| watchdir, | |
| watchfile, | |
| qemu_file_monitor_test_handler, | |
| &data, | |
| &local_err); | |
| g_free(watchdir); | |
| if (*op->watchid < 0) { | |
| g_printerr("Unable to add watch %s", | |
| error_get_pretty(local_err)); | |
| error_free(local_err); | |
| goto cleanup; | |
| } | |
| if (debug) { | |
| g_printerr("Watch ID %" PRIx64 "\n", *op->watchid); | |
| } | |
| if (g_hash_table_contains(ids, op->watchid)) { | |
| g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid); | |
| goto cleanup; | |
| } | |
| g_hash_table_add(ids, op->watchid); | |
| break; | |
| case QFILE_MONITOR_TEST_OP_DEL_WATCH: | |
| if (debug) { | |
| g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid); | |
| } | |
| if (op->filesrc && strchr(op->filesrc, '/')) { | |
| watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); | |
| watchfile = strrchr(watchdir, '/'); | |
| *(char *)watchfile = '\0'; | |
| } else { | |
| watchdir = g_strdup(dir); | |
| } | |
| g_hash_table_remove(ids, op->watchid); | |
| qemu_file_monitor_remove_watch(mon, | |
| watchdir, | |
| *op->watchid); | |
| g_free(watchdir); | |
| break; | |
| case QFILE_MONITOR_TEST_OP_EVENT: | |
| if (debug) { | |
| g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", | |
| *op->watchid, op->eventid, op->filesrc); | |
| } | |
| if (op->eventid == QFILE_MONITOR_EVENT_IGNORED && | |
| expect_broken_ignored()) { | |
| g_printerr("Expect ignored event to be broken, skipping\n"); | |
| break; | |
| } | |
| if (!qemu_file_monitor_test_expect(&data, *op->watchid, | |
| op->eventid, op->filesrc, | |
| op->swapnext)) | |
| goto cleanup; | |
| break; | |
| case QFILE_MONITOR_TEST_OP_CREATE: | |
| if (debug) { | |
| g_printerr("Create %s\n", pathsrc); | |
| } | |
| fd = open(pathsrc, O_WRONLY | O_CREAT, 0700); | |
| if (fd < 0) { | |
| g_printerr("Unable to create %s: %s", | |
| pathsrc, strerror(errno)); | |
| goto cleanup; | |
| } | |
| close(fd); | |
| break; | |
| case QFILE_MONITOR_TEST_OP_APPEND: | |
| if (debug) { | |
| g_printerr("Append %s\n", pathsrc); | |
| } | |
| fd = open(pathsrc, O_WRONLY | O_APPEND, 0700); | |
| if (fd < 0) { | |
| g_printerr("Unable to open %s: %s", | |
| pathsrc, strerror(errno)); | |
| goto cleanup; | |
| } | |
| if (write(fd, "Hello World", 10) != 10) { | |
| g_printerr("Unable to write %s: %s", | |
| pathsrc, strerror(errno)); | |
| close(fd); | |
| goto cleanup; | |
| } | |
| close(fd); | |
| break; | |
| case QFILE_MONITOR_TEST_OP_TRUNC: | |
| if (debug) { | |
| g_printerr("Truncate %s\n", pathsrc); | |
| } | |
| if (truncate(pathsrc, 4) < 0) { | |
| g_printerr("Unable to truncate %s: %s", | |
| pathsrc, strerror(errno)); | |
| goto cleanup; | |
| } | |
| break; | |
| case QFILE_MONITOR_TEST_OP_RENAME: | |
| if (debug) { | |
| g_printerr("Rename %s -> %s\n", pathsrc, pathdst); | |
| } | |
| if (rename(pathsrc, pathdst) < 0) { | |
| g_printerr("Unable to rename %s to %s: %s", | |
| pathsrc, pathdst, strerror(errno)); | |
| goto cleanup; | |
| } | |
| break; | |
| case QFILE_MONITOR_TEST_OP_UNLINK: | |
| if (debug) { | |
| g_printerr("Unlink %s\n", pathsrc); | |
| } | |
| if (unlink(pathsrc) < 0) { | |
| g_printerr("Unable to unlink %s: %s", | |
| pathsrc, strerror(errno)); | |
| goto cleanup; | |
| } | |
| break; | |
| case QFILE_MONITOR_TEST_OP_TOUCH: | |
| if (debug) { | |
| g_printerr("Touch %s\n", pathsrc); | |
| } | |
| ubuf.actime = 1024; | |
| ubuf.modtime = 1025; | |
| if (utime(pathsrc, &ubuf) < 0) { | |
| g_printerr("Unable to touch %s: %s", | |
| pathsrc, strerror(errno)); | |
| goto cleanup; | |
| } | |
| break; | |
| case QFILE_MONITOR_TEST_OP_MKDIR: | |
| if (debug) { | |
| g_printerr("Mkdir %s\n", pathsrc); | |
| } | |
| if (g_mkdir_with_parents(pathsrc, 0700) < 0) { | |
| g_printerr("Unable to mkdir %s: %s", | |
| pathsrc, strerror(errno)); | |
| goto cleanup; | |
| } | |
| break; | |
| case QFILE_MONITOR_TEST_OP_RMDIR: | |
| if (debug) { | |
| g_printerr("Rmdir %s\n", pathsrc); | |
| } | |
| if (rmdir(pathsrc) < 0) { | |
| g_printerr("Unable to rmdir %s: %s", | |
| pathsrc, strerror(errno)); | |
| goto cleanup; | |
| } | |
| break; | |
| default: | |
| g_assert_not_reached(); | |
| } | |
| g_free(pathsrc); | |
| g_free(pathdst); | |
| pathsrc = pathdst = NULL; | |
| } | |
| g_assert_cmpint(g_hash_table_size(ids), ==, 0); | |
| err = 0; | |
| cleanup: | |
| g_free(pathsrc); | |
| g_free(pathdst); | |
| qemu_mutex_lock(&evlock); | |
| evstopping = 1; | |
| timer = g_timer_new(); | |
| while (evrunning && g_timer_elapsed(timer, NULL) < 5) { | |
| qemu_mutex_unlock(&evlock); | |
| usleep(10 * 1000); | |
| qemu_mutex_lock(&evlock); | |
| } | |
| qemu_mutex_unlock(&evlock); | |
| if (g_timer_elapsed(timer, NULL) >= 5) { | |
| g_printerr("Event loop failed to quit after 5 seconds\n"); | |
| } | |
| g_timer_destroy(timer); | |
| qemu_file_monitor_free(mon); | |
| g_list_foreach(data.records, | |
| (GFunc)qemu_file_monitor_test_record_free, NULL); | |
| g_list_free(data.records); | |
| qemu_mutex_destroy(&data.lock); | |
| if (dir) { | |
| for (i = 0; i < G_N_ELEMENTS(ops); i++) { | |
| const QFileMonitorTestOp *op = &(ops[i]); | |
| char *path = g_strdup_printf("%s/%s", | |
| dir, op->filesrc); | |
| if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) { | |
| rmdir(path); | |
| g_free(path); | |
| } else { | |
| unlink(path); | |
| g_free(path); | |
| if (op->filedst) { | |
| path = g_strdup_printf("%s/%s", | |
| dir, op->filedst); | |
| unlink(path); | |
| g_free(path); | |
| } | |
| } | |
| } | |
| if (rmdir(dir) < 0) { | |
| g_printerr("Failed to remove %s: %s\n", | |
| dir, strerror(errno)); | |
| abort(); | |
| } | |
| } | |
| g_hash_table_unref(ids); | |
| g_free(dir); | |
| g_assert(err == 0); | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| g_test_init(&argc, &argv, NULL); | |
| qemu_init_main_loop(&error_abort); | |
| qemu_mutex_init(&evlock); | |
| debug = getenv("FILEMONITOR_DEBUG") != NULL; | |
| g_test_add_func("/util/filemonitor", test_file_monitor_events); | |
| return g_test_run(); | |
| } |