libfuse

poll.c
Go to the documentation of this file.
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
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#include <stdbool.h>
37
38/*
39 * fsel_open_mask is used to limit the number of opens to 1 per file.
40 * This is to use file index (0-F) as fh as poll support requires
41 * unique fh per open file. Lifting this would require proper open
42 * file management.
43 */
44 static unsigned fsel_open_mask;
45 static const char fsel_hex_map[] = "0123456789ABCDEF";
46 static struct fuse *fsel_fuse; /* needed for poll notification */
47
48#define FSEL_CNT_MAX 10 /* each file can store up to 10 chars */
49#define FSEL_FILES 16
50
51 static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
52 static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
53 static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
54 static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
55 static _Atomic bool fsel_stop = false;
56 static pthread_t fsel_producer_thread;
57
58
59 static int fsel_path_index(const char *path)
60{
61 char ch = path[1];
62
63 if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
64 return -1;
65 return ch <= '9' ? ch - '0' : ch - 'A' + 10;
66}
67
68 static void fsel_destroy(void *private_data)
69{
70 (void)private_data;
71
72 fsel_stop = true;
73
74 pthread_join(fsel_producer_thread, NULL);
75}
76
77 static int fsel_getattr(const char *path, struct stat *stbuf,
78 struct fuse_file_info *fi)
79{
80 (void) fi;
81 int idx;
82
83 memset(stbuf, 0, sizeof(struct stat));
84
85 if (strcmp(path, "/") == 0) {
86 stbuf->st_mode = S_IFDIR | 0555;
87 stbuf->st_nlink = 2;
88 return 0;
89 }
90
91 idx = fsel_path_index(path);
92 if (idx < 0)
93 return -ENOENT;
94
95 stbuf->st_mode = S_IFREG | 0444;
96 stbuf->st_nlink = 1;
97 stbuf->st_size = fsel_cnt[idx];
98 return 0;
99}
100
101 static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
102 off_t offset, struct fuse_file_info *fi,
103 enum fuse_readdir_flags flags)
104{
105 char name[2] = { };
106 int i;
107
108 (void) offset;
109 (void) fi;
110 (void) flags;
111
112 if (strcmp(path, "/") != 0)
113 return -ENOENT;
114
115 for (i = 0; i < FSEL_FILES; i++) {
116 name[0] = fsel_hex_map[i];
117 filler(buf, name, NULL, 0, FUSE_FILL_DIR_DEFAULTS);
118 }
119
120 return 0;
121}
122
123 static int fsel_open(const char *path, struct fuse_file_info *fi)
124{
125 int idx = fsel_path_index(path);
126
127 if (idx < 0)
128 return -ENOENT;
129 if ((fi->flags & O_ACCMODE) != O_RDONLY)
130 return -EACCES;
131 if (fsel_open_mask & (1 << idx))
132 return -EBUSY;
133 fsel_open_mask |= (1 << idx);
134
135 /*
136 * fsel files are nonseekable somewhat pipe-like files which
137 * gets filled up periodically by producer thread and consumed
138 * on read. Tell FUSE as such.
139 */
140 fi->fh = idx;
141 fi->direct_io = 1;
142 fi->nonseekable = 1;
143
144 return 0;
145}
146
147 static int fsel_release(const char *path, struct fuse_file_info *fi)
148{
149 int idx = fi->fh;
150
151 (void) path;
152
153 fsel_open_mask &= ~(1 << idx);
154 return 0;
155}
156
157 static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
158 struct fuse_file_info *fi)
159{
160 int idx = fi->fh;
161
162 (void) path;
163 (void) offset;
164
165 pthread_mutex_lock(&fsel_mutex);
166 if (fsel_cnt[idx] < size)
167 size = fsel_cnt[idx];
168 printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
169 fsel_cnt[idx] -= size;
170 pthread_mutex_unlock(&fsel_mutex);
171
172 memset(buf, fsel_hex_map[idx], size);
173 return size;
174}
175
176 static int fsel_poll(const char *path, struct fuse_file_info *fi,
177 struct fuse_pollhandle *ph, unsigned *reventsp)
178{
179 static unsigned polled_zero;
180 int idx = fi->fh;
181
182 (void) path;
183
184 /*
185 * Poll notification requires pointer to struct fuse which
186 * can't be obtained when using fuse_main(). As notification
187 * happens only after poll is called, fill it here from
188 * fuse_context.
189 */
190 if (!fsel_fuse) {
191 struct fuse_context *cxt = fuse_get_context();
192 if (cxt)
193 fsel_fuse = cxt->fuse;
194 }
195
196 pthread_mutex_lock(&fsel_mutex);
197
198 if (ph != NULL) {
199 struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
200
201 if (oldph)
202 fuse_pollhandle_destroy(oldph);
203
204 fsel_poll_notify_mask |= (1 << idx);
205 fsel_poll_handle[idx] = ph;
206 }
207
208 if (fsel_cnt[idx]) {
209 *reventsp |= POLLIN;
210 printf("POLL %X cnt=%u polled_zero=%u\n",
211 idx, fsel_cnt[idx], polled_zero);
212 polled_zero = 0;
213 } else
214 polled_zero++;
215
216 pthread_mutex_unlock(&fsel_mutex);
217 return 0;
218}
219
220 static const struct fuse_operations fsel_oper = {
221 .destroy = fsel_destroy,
222 .getattr = fsel_getattr,
223 .readdir = fsel_readdir,
224 .open = fsel_open,
225 .release = fsel_release,
226 .read = fsel_read,
227 .poll = fsel_poll,
228};
229
230 static void *fsel_producer(void *data)
231{
232 const struct timespec interval = { 0, 250000000 };
233 unsigned idx = 0, nr = 1;
234
235 (void) data;
236
237 while (!fsel_stop) {
238 int i, t;
239
240 pthread_mutex_lock(&fsel_mutex);
241
242 /*
243 * This is the main producer loop which is executed
244 * ever 500ms. On each iteration, it fills one byte
245 * to 1, 2 or 4 files and sends poll notification if
246 * requested.
247 */
248 for (i = 0, t = idx; i < nr;
249 i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
250 if (fsel_cnt[t] == FSEL_CNT_MAX)
251 continue;
252
253 fsel_cnt[t]++;
254 if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
255 struct fuse_pollhandle *ph;
256
257 printf("NOTIFY %X\n", t);
258 ph = fsel_poll_handle[t];
259 fuse_notify_poll(ph);
260 fuse_pollhandle_destroy(ph);
261 fsel_poll_notify_mask &= ~(1 << t);
262 fsel_poll_handle[t] = NULL;
263 }
264 }
265
266 idx = (idx + 1) % FSEL_FILES;
267 if (idx == 0)
268 nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
269
270 pthread_mutex_unlock(&fsel_mutex);
271
272 nanosleep(&interval, NULL);
273 }
274
275 return NULL;
276}
277
278 int main(int argc, char *argv[])
279{
280 pthread_attr_t attr;
281 int ret;
282
283 errno = pthread_mutex_init(&fsel_mutex, NULL);
284 if (errno) {
285 perror("pthread_mutex_init");
286 return 1;
287 }
288
289 errno = pthread_attr_init(&attr);
290 if (errno) {
291 perror("pthread_attr_init");
292 return 1;
293 }
294
295 errno = pthread_create(&fsel_producer_thread, &attr, fsel_producer, NULL);
296 if (errno) {
297 perror("pthread_create");
298 return 1;
299 }
300
301 ret = fuse_main(argc, argv, &fsel_oper, NULL);
302
303 return ret;
304}
fuse_get_context
struct fuse_context * fuse_get_context(void)
Definition fuse.c:4644
fuse_fill_dir_t
int(* fuse_fill_dir_t)(void *buf, const char *name, const struct stat *stbuf, off_t off, enum fuse_fill_dir_flags flags)
Definition fuse.h:87
FUSE_FILL_DIR_DEFAULTS
@ FUSE_FILL_DIR_DEFAULTS
Definition fuse.h:68
fuse_readdir_flags
fuse_readdir_flags
Definition fuse.h:42
fuse_pollhandle_destroy
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
Definition fuse_lowlevel.c:1906
fuse_context
Definition fuse.h:860
fuse_context::fuse
struct fuse * fuse
Definition fuse.h:862
fuse_file_info
Definition fuse_common.h:58
fuse_file_info::fh
uint64_t fh
Definition fuse_common.h:115
fuse_file_info::nonseekable
uint32_t nonseekable
Definition fuse_common.h:86
fuse_file_info::direct_io
uint32_t direct_io
Definition fuse_common.h:71
fuse_file_info::flags
int32_t flags
Definition fuse_common.h:60
fuse_operations
Definition fuse.h:349
fuse_operations::destroy
void(* destroy)(void *private_data)
Definition fuse.h:649