mirror of
				https://github.com/qemu/qemu.git
				synced 2025-10-26 03:33:28 +00:00 
			
		
		
		
	 0fdc1f2f5e
			
		
	
	
		0fdc1f2f5e
		
	
	
	
	
		
			
			Fixes the following compiling error:
../tests/test-util-filemonitor.c: In function 'test_file_monitor_events':
../tests/test-util-filemonitor.c:620:17: error: too many arguments to function 'mkdir'
  620 |             if (mkdir(pathsrc, 0700) < 0) {
      |                 ^~~~~
In file included from C:/CI-Tools/msys64/mingw64/x86_64-w64-mingw32/include/unistd.h:10,
                 from C:/work/xemu/qemu/include/qemu/osdep.h:93,
                 from ../tests/test-util-filemonitor.c:21:
C:/CI-Tools/msys64/mingw64/x86_64-w64-mingw32/include/io.h:282:15: note: declared here
  282 |   int __cdecl mkdir (const char *) __MINGW_ATTRIB_DEPRECATED_MSVC2005;
      |               ^~~~~
Signed-off-by: Yonggang Luo <luoyonggang@gmail.com>
Message-Id: <20200905203425.1470-1-luoyonggang@gmail.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Thomas Huth <thuth@redhat.com>
		
	
			
		
			
				
	
	
		
			721 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			721 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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>
 | |
| 
 | |
| 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 upto 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 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 },
 | |
|         { .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 (!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();
 | |
| }
 |