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