Merge remote-tracking branch 'origin/fuse_2_9_bugfix'
[fuse.git] / example / fsel.c
blob69202ee6af3e204b4384c9ec2983e0b76483a6fb
1 /*
2 FUSE fsel: FUSE select example
3 Copyright (C) 2008 SUSE Linux Products GmbH
4 Copyright (C) 2008 Tejun Heo <teheo@suse.de>
6 This program can be distributed under the terms of the GNU GPL.
7 See the file COPYING.
9 */
11 /** @file
12 * @tableofcontents
14 * fsel.c - FUSE fsel: FUSE select example
16 * \section section_compile compiling this example
18 * gcc -Wall fsel.c `pkg-config fuse3 --cflags --libs` -o fsel
20 * \section section_source the complete source
21 * \include fsel.c
25 #define FUSE_USE_VERSION 30
27 #include <config.h>
29 #include <fuse.h>
30 #include <unistd.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <errno.h>
36 #include <time.h>
37 #include <pthread.h>
38 #include <poll.h>
41 * fsel_open_mask is used to limit the number of opens to 1 per file.
42 * This is to use file index (0-F) as fh as poll support requires
43 * unique fh per open file. Lifting this would require proper open
44 * file management.
46 static unsigned fsel_open_mask;
47 static const char fsel_hex_map[] = "0123456789ABCDEF";
48 static struct fuse *fsel_fuse; /* needed for poll notification */
50 #define FSEL_CNT_MAX 10 /* each file can store upto 10 chars */
51 #define FSEL_FILES 16
53 static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
54 static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
55 static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
56 static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
58 static int fsel_path_index(const char *path)
60 char ch = path[1];
62 if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
63 return -1;
64 return ch <= '9' ? ch - '0' : ch - 'A' + 10;
67 static int fsel_getattr(const char *path, struct stat *stbuf)
69 int idx;
71 memset(stbuf, 0, sizeof(struct stat));
73 if (strcmp(path, "/") == 0) {
74 stbuf->st_mode = S_IFDIR | 0555;
75 stbuf->st_nlink = 2;
76 return 0;
79 idx = fsel_path_index(path);
80 if (idx < 0)
81 return -ENOENT;
83 stbuf->st_mode = S_IFREG | 0444;
84 stbuf->st_nlink = 1;
85 stbuf->st_size = fsel_cnt[idx];
86 return 0;
89 static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
90 off_t offset, struct fuse_file_info *fi)
92 char name[2] = { };
93 int i;
95 (void) offset;
96 (void) fi;
98 if (strcmp(path, "/") != 0)
99 return -ENOENT;
101 for (i = 0; i < FSEL_FILES; i++) {
102 name[0] = fsel_hex_map[i];
103 filler(buf, name, NULL, 0);
106 return 0;
109 static int fsel_open(const char *path, struct fuse_file_info *fi)
111 int idx = fsel_path_index(path);
113 if (idx < 0)
114 return -ENOENT;
115 if ((fi->flags & 3) != O_RDONLY)
116 return -EACCES;
117 if (fsel_open_mask & (1 << idx))
118 return -EBUSY;
119 fsel_open_mask |= (1 << idx);
122 * fsel files are nonseekable somewhat pipe-like files which
123 * gets filled up periodically by producer thread and consumed
124 * on read. Tell FUSE as such.
126 fi->fh = idx;
127 fi->direct_io = 1;
128 fi->nonseekable = 1;
130 return 0;
133 static int fsel_release(const char *path, struct fuse_file_info *fi)
135 int idx = fi->fh;
137 (void) path;
139 fsel_open_mask &= ~(1 << idx);
140 return 0;
143 static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
144 struct fuse_file_info *fi)
146 int idx = fi->fh;
148 (void) path;
149 (void) offset;
151 pthread_mutex_lock(&fsel_mutex);
152 if (fsel_cnt[idx] < size)
153 size = fsel_cnt[idx];
154 printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
155 fsel_cnt[idx] -= size;
156 pthread_mutex_unlock(&fsel_mutex);
158 memset(buf, fsel_hex_map[idx], size);
159 return size;
162 static int fsel_poll(const char *path, struct fuse_file_info *fi,
163 struct fuse_pollhandle *ph, unsigned *reventsp)
165 static unsigned polled_zero;
166 int idx = fi->fh;
168 (void) path;
171 * Poll notification requires pointer to struct fuse which
172 * can't be obtained when using fuse_main(). As notification
173 * happens only after poll is called, fill it here from
174 * fuse_context.
176 if (!fsel_fuse) {
177 struct fuse_context *cxt = fuse_get_context();
178 if (cxt)
179 fsel_fuse = cxt->fuse;
182 pthread_mutex_lock(&fsel_mutex);
184 if (ph != NULL) {
185 struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
187 if (oldph)
188 fuse_pollhandle_destroy(oldph);
190 fsel_poll_notify_mask |= (1 << idx);
191 fsel_poll_handle[idx] = ph;
194 if (fsel_cnt[idx]) {
195 *reventsp |= POLLIN;
196 printf("POLL %X cnt=%u polled_zero=%u\n",
197 idx, fsel_cnt[idx], polled_zero);
198 polled_zero = 0;
199 } else
200 polled_zero++;
202 pthread_mutex_unlock(&fsel_mutex);
203 return 0;
206 static struct fuse_operations fsel_oper = {
207 .getattr = fsel_getattr,
208 .readdir = fsel_readdir,
209 .open = fsel_open,
210 .release = fsel_release,
211 .read = fsel_read,
212 .poll = fsel_poll,
215 static void *fsel_producer(void *data)
217 const struct timespec interval = { 0, 250000000 };
218 unsigned idx = 0, nr = 1;
220 (void) data;
222 while (1) {
223 int i, t;
225 pthread_mutex_lock(&fsel_mutex);
228 * This is the main producer loop which is executed
229 * ever 500ms. On each iteration, it fills one byte
230 * to 1, 2 or 4 files and sends poll notification if
231 * requested.
233 for (i = 0, t = idx; i < nr;
234 i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
235 if (fsel_cnt[t] == FSEL_CNT_MAX)
236 continue;
238 fsel_cnt[t]++;
239 if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
240 struct fuse_pollhandle *ph;
242 printf("NOTIFY %X\n", t);
243 ph = fsel_poll_handle[t];
244 fuse_notify_poll(ph);
245 fuse_pollhandle_destroy(ph);
246 fsel_poll_notify_mask &= ~(1 << t);
247 fsel_poll_handle[t] = NULL;
251 idx = (idx + 1) % FSEL_FILES;
252 if (idx == 0)
253 nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
255 pthread_mutex_unlock(&fsel_mutex);
257 nanosleep(&interval, NULL);
260 return NULL;
263 int main(int argc, char *argv[])
265 pthread_t producer;
266 pthread_attr_t attr;
267 int ret;
269 errno = pthread_mutex_init(&fsel_mutex, NULL);
270 if (errno) {
271 perror("pthread_mutex_init");
272 return 1;
275 errno = pthread_attr_init(&attr);
276 if (errno) {
277 perror("pthread_attr_init");
278 return 1;
281 errno = pthread_create(&producer, &attr, fsel_producer, NULL);
282 if (errno) {
283 perror("pthread_create");
284 return 1;
287 ret = fuse_main(argc, argv, &fsel_oper, NULL);
289 pthread_cancel(producer);
290 pthread_join(producer, NULL);
292 return ret;