• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2   FUSE fsel: FUSE select example
3   Copyright (C) 2008       SUSE Linux Products GmbH
4   Copyright (C) 2008       Tejun Heo <teheo@suse.de>
5 
6   This program can be distributed under the terms of the GNU GPLv2.
7   See the file COPYING.
8 */
9 
10 /** @file
11  *
12  * This example illustrates how to write a FUSE file system that
13  * supports polling for changes that don't come through the kernel. It
14  * can be tested with the poll_client.c program.
15  *
16  * Compile with:
17  *
18  *     gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll
19  *
20  * ## Source code ##
21  * \include poll.c
22  */
23 
24 #define FUSE_USE_VERSION 31
25 
26 #include <fuse.h>
27 #include <unistd.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <time.h>
34 #include <pthread.h>
35 #include <poll.h>
36 
37 /*
38  * fsel_open_mask is used to limit the number of opens to 1 per file.
39  * This is to use file index (0-F) as fh as poll support requires
40  * unique fh per open file.  Lifting this would require proper open
41  * file management.
42  */
43 static unsigned fsel_open_mask;
44 static const char fsel_hex_map[] = "0123456789ABCDEF";
45 static struct fuse *fsel_fuse;	/* needed for poll notification */
46 
47 #define FSEL_CNT_MAX	10	/* each file can store up to 10 chars */
48 #define FSEL_FILES	16
49 
50 static pthread_mutex_t fsel_mutex;	/* protects notify_mask and cnt array */
51 static unsigned fsel_poll_notify_mask;	/* poll notification scheduled? */
52 static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
53 static unsigned fsel_cnt[FSEL_FILES];	/* nbytes stored in each file */
54 
fsel_path_index(const char * path)55 static int fsel_path_index(const char *path)
56 {
57 	char ch = path[1];
58 
59 	if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
60 		return -1;
61 	return ch <= '9' ? ch - '0' : ch - 'A' + 10;
62 }
63 
fsel_getattr(const char * path,struct stat * stbuf,struct fuse_file_info * fi)64 static int fsel_getattr(const char *path, struct stat *stbuf,
65 			struct fuse_file_info *fi)
66 {
67 	(void) fi;
68 	int idx;
69 
70 	memset(stbuf, 0, sizeof(struct stat));
71 
72 	if (strcmp(path, "/") == 0) {
73 		stbuf->st_mode = S_IFDIR | 0555;
74 		stbuf->st_nlink = 2;
75 		return 0;
76 	}
77 
78 	idx = fsel_path_index(path);
79 	if (idx < 0)
80 		return -ENOENT;
81 
82 	stbuf->st_mode = S_IFREG | 0444;
83 	stbuf->st_nlink = 1;
84 	stbuf->st_size = fsel_cnt[idx];
85 	return 0;
86 }
87 
fsel_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi,enum fuse_readdir_flags flags)88 static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
89 			off_t offset, struct fuse_file_info *fi,
90 			enum fuse_readdir_flags flags)
91 {
92 	char name[2] = { };
93 	int i;
94 
95 	(void) offset;
96 	(void) fi;
97 	(void) flags;
98 
99 	if (strcmp(path, "/") != 0)
100 		return -ENOENT;
101 
102 	for (i = 0; i < FSEL_FILES; i++) {
103 		name[0] = fsel_hex_map[i];
104 		filler(buf, name, NULL, 0, 0);
105 	}
106 
107 	return 0;
108 }
109 
fsel_open(const char * path,struct fuse_file_info * fi)110 static int fsel_open(const char *path, struct fuse_file_info *fi)
111 {
112 	int idx = fsel_path_index(path);
113 
114 	if (idx < 0)
115 		return -ENOENT;
116 	if ((fi->flags & O_ACCMODE) != O_RDONLY)
117 		return -EACCES;
118 	if (fsel_open_mask & (1 << idx))
119 		return -EBUSY;
120 	fsel_open_mask |= (1 << idx);
121 
122 	/*
123 	 * fsel files are nonseekable somewhat pipe-like files which
124 	 * gets filled up periodically by producer thread and consumed
125 	 * on read.  Tell FUSE as such.
126 	 */
127 	fi->fh = idx;
128 	fi->direct_io = 1;
129 	fi->nonseekable = 1;
130 
131 	return 0;
132 }
133 
fsel_release(const char * path,struct fuse_file_info * fi)134 static int fsel_release(const char *path, struct fuse_file_info *fi)
135 {
136 	int idx = fi->fh;
137 
138 	(void) path;
139 
140 	fsel_open_mask &= ~(1 << idx);
141 	return 0;
142 }
143 
fsel_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)144 static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
145 		     struct fuse_file_info *fi)
146 {
147 	int idx = fi->fh;
148 
149 	(void) path;
150 	(void) offset;
151 
152 	pthread_mutex_lock(&fsel_mutex);
153 	if (fsel_cnt[idx] < size)
154 		size = fsel_cnt[idx];
155 	printf("READ   %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
156 	fsel_cnt[idx] -= size;
157 	pthread_mutex_unlock(&fsel_mutex);
158 
159 	memset(buf, fsel_hex_map[idx], size);
160 	return size;
161 }
162 
fsel_poll(const char * path,struct fuse_file_info * fi,struct fuse_pollhandle * ph,unsigned * reventsp)163 static int fsel_poll(const char *path, struct fuse_file_info *fi,
164 		     struct fuse_pollhandle *ph, unsigned *reventsp)
165 {
166 	static unsigned polled_zero;
167 	int idx = fi->fh;
168 
169 	(void) path;
170 
171 	/*
172 	 * Poll notification requires pointer to struct fuse which
173 	 * can't be obtained when using fuse_main().  As notification
174 	 * happens only after poll is called, fill it here from
175 	 * fuse_context.
176 	 */
177 	if (!fsel_fuse) {
178 		struct fuse_context *cxt = fuse_get_context();
179 		if (cxt)
180 			fsel_fuse = cxt->fuse;
181 	}
182 
183 	pthread_mutex_lock(&fsel_mutex);
184 
185 	if (ph != NULL) {
186 		struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
187 
188 		if (oldph)
189 			fuse_pollhandle_destroy(oldph);
190 
191 		fsel_poll_notify_mask |= (1 << idx);
192 		fsel_poll_handle[idx] = ph;
193 	}
194 
195 	if (fsel_cnt[idx]) {
196 		*reventsp |= POLLIN;
197 		printf("POLL   %X cnt=%u polled_zero=%u\n",
198 		       idx, fsel_cnt[idx], polled_zero);
199 		polled_zero = 0;
200 	} else
201 		polled_zero++;
202 
203 	pthread_mutex_unlock(&fsel_mutex);
204 	return 0;
205 }
206 
207 static const struct fuse_operations fsel_oper = {
208 	.getattr	= fsel_getattr,
209 	.readdir	= fsel_readdir,
210 	.open		= fsel_open,
211 	.release	= fsel_release,
212 	.read		= fsel_read,
213 	.poll		= fsel_poll,
214 };
215 
fsel_producer(void * data)216 static void *fsel_producer(void *data)
217 {
218 	const struct timespec interval = { 0, 250000000 };
219 	unsigned idx = 0, nr = 1;
220 
221 	(void) data;
222 
223 	while (1) {
224 		int i, t;
225 
226 		pthread_mutex_lock(&fsel_mutex);
227 
228 		/*
229 		 * This is the main producer loop which is executed
230 		 * ever 500ms.  On each iteration, it fills one byte
231 		 * to 1, 2 or 4 files and sends poll notification if
232 		 * requested.
233 		 */
234 		for (i = 0, t = idx; i < nr;
235 		     i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
236 			if (fsel_cnt[t] == FSEL_CNT_MAX)
237 				continue;
238 
239 			fsel_cnt[t]++;
240 			if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
241 				struct fuse_pollhandle *ph;
242 
243 				printf("NOTIFY %X\n", t);
244 				ph = fsel_poll_handle[t];
245 				fuse_notify_poll(ph);
246 				fuse_pollhandle_destroy(ph);
247 				fsel_poll_notify_mask &= ~(1 << t);
248 				fsel_poll_handle[t] = NULL;
249 			}
250 		}
251 
252 		idx = (idx + 1) % FSEL_FILES;
253 		if (idx == 0)
254 			nr = (nr * 2) % 7;	/* cycle through 1, 2 and 4 */
255 
256 		pthread_mutex_unlock(&fsel_mutex);
257 
258 		nanosleep(&interval, NULL);
259 	}
260 
261 	return NULL;
262 }
263 
main(int argc,char * argv[])264 int main(int argc, char *argv[])
265 {
266 	pthread_t producer;
267 	pthread_attr_t attr;
268 	int ret;
269 
270 	errno = pthread_mutex_init(&fsel_mutex, NULL);
271 	if (errno) {
272 		perror("pthread_mutex_init");
273 		return 1;
274 	}
275 
276 	errno = pthread_attr_init(&attr);
277 	if (errno) {
278 		perror("pthread_attr_init");
279 		return 1;
280 	}
281 
282 	errno = pthread_create(&producer, &attr, fsel_producer, NULL);
283 	if (errno) {
284 		perror("pthread_create");
285 		return 1;
286 	}
287 
288 	ret = fuse_main(argc, argv, &fsel_oper, NULL);
289 
290 	pthread_cancel(producer);
291 	pthread_join(producer, NULL);
292 
293 	return ret;
294 }
295