1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2018 Joyent, Inc. 14 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. 15 */ 16 17 #include <errno.h> 18 #include <fcntl.h> 19 #include <pthread.h> 20 #include <signal.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <strings.h> 24 #include <unistd.h> 25 26 #include <sys/types.h> 27 #include <sys/stat.h> 28 29 #include "testlib.h" 30 #include "mevent.h" 31 32 static char *cookie = "Shortcake"; 33 34 static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; 35 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER; 36 37 static void 38 callback(int fd, enum ev_type ev, void *arg) 39 { 40 static off_t size = 0; 41 struct stat st; 42 43 ASSERT_INT_EQ(("bad event"), ev, EVF_VNODE); 44 ASSERT_PTR_EQ(("bad cookie"), arg, cookie); 45 46 if (fstat(fd, &st) != 0) 47 FAIL_ERRNO("fstat failed"); 48 49 ASSERT_INT64_NEQ(("File size has not changed"), size, st.st_size); 50 size = st.st_size; 51 52 pthread_mutex_lock(&mtx); 53 pthread_cond_signal(&cv); 54 VERBOSE(("wakeup")); 55 pthread_mutex_unlock(&mtx); 56 } 57 58 static void 59 test_fd(int fd, char *tag) 60 { 61 struct mevent *evp; 62 int err; 63 64 evp = mevent_add_flags(fd, EVF_VNODE, EVFF_ATTRIB, callback, cookie); 65 ASSERT_PTR_NEQ(("%s: mevent_add", tag), evp, NULL); 66 67 for (uint_t i = 0; cookie[i] != '\0'; i++) { 68 ssize_t written; 69 70 if (i > 0) { 71 /* 72 * Check that no events are emitted for writes which do 73 * not alter the size. 74 */ 75 if (lseek(fd, -1, SEEK_CUR) == -1) 76 FAIL_ERRNO("lseek"); 77 if (write(fd, "X", 1) == -1) 78 FAIL_ERRNO("write"); 79 /* 80 * Allow time for the callback to fire if it is going 81 * to. 82 */ 83 VERBOSE(("Write within")); 84 usleep(100); 85 } 86 87 pthread_mutex_lock(&mtx); 88 89 written = write(fd, cookie + i, 1); 90 if (written < 0) 91 FAIL_ERRNO("bad write"); 92 ASSERT_INT64_EQ(("write byte %d of cookie", i), written, 1); 93 VERBOSE(("Write extend")); 94 95 /* Wait for the size change to be processed */ 96 pthread_cond_wait(&cv, &mtx); 97 pthread_mutex_unlock(&mtx); 98 /* 99 * This is a bit unsatisfactory but we need to allow time 100 * for mevent to re-associate the port or the next write could 101 * be missed. 102 */ 103 usleep(100); 104 } 105 106 err = mevent_disable(evp); 107 ASSERT_INT_EQ(("%s: mevent_disable: %s", tag, strerror(err)), err, 0); 108 109 (void) printf("PASS %s - %s\n", testlib_prog, tag); 110 } 111 112 int 113 main(int argc, const char **argv) 114 { 115 int fd; 116 117 start_test(argv[0], 20); 118 set_mevent_file_poll_interval_ms(500); 119 start_event_thread(); 120 121 /* Test with a temporary file in /tmp */ 122 char *template = strdup("/tmp/mevent.vnode.XXXXXX"); 123 ASSERT_PTR_NEQ(("strdup"), template, NULL); 124 fd = mkstemp(template); 125 if (fd == -1) 126 FAIL_ERRNO("Couldn't create temporary file with mkstemp"); 127 128 VERBOSE(("Opened temporary file at '%s'", template)); 129 130 test_fd(fd, "temporary file"); 131 132 /* Test with a file which is unlinked from the filesystem */ 133 FILE *fp = tmpfile(); 134 ASSERT_PTR_NEQ(("tmpfile"), fp, NULL); 135 136 fd = fileno(fp); 137 if (fd == -1) 138 FAIL_ERRNO("Couldn't get file descriptor for temporary file"); 139 140 test_fd(fd, "anon file"); 141 142 /* 143 * Defer to here to avoid generating a new event before the disable has 144 * been processed and the port deassociated. 145 */ 146 unlink(template); 147 free(template); 148 149 PASS(); 150 } 151