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