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
callback(int fd,enum ev_type ev,void * arg)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
test_fd(int fd,char * tag)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
main(int argc,const char ** argv)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