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