From bcad1a6f22662ff0d04a6ae417adb30550252d97 Mon Sep 17 00:00:00 2001 From: Nikolaus Rath Date: Sun, 9 Oct 2016 19:50:51 -0700 Subject: Renamed ioctl and poll examples The new names should make it more obvious at first glance what each example demonstrates. --- example/poll.c | 295 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 295 insertions(+) create mode 100644 example/poll.c (limited to 'example/poll.c') diff --git a/example/poll.c b/example/poll.c new file mode 100644 index 0000000..61c5365 --- /dev/null +++ b/example/poll.c @@ -0,0 +1,295 @@ +/* + FUSE fsel: FUSE select example + Copyright (C) 2008 SUSE Linux Products GmbH + Copyright (C) 2008 Tejun Heo + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +/** @file + * @tableofcontents + * + * This example illustrates how to write a FUSE file system that + * supports polling for changes that don't come through the kernel. It + * can be tested with the poll_client.c program. + * + * \section section_compile compiling this example + * + * gcc -Wall poll.c `pkg-config fuse3 --cflags --libs` -o poll + * + * \section section_source the complete source + * \include poll.c + */ + +#define FUSE_USE_VERSION 30 + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * fsel_open_mask is used to limit the number of opens to 1 per file. + * This is to use file index (0-F) as fh as poll support requires + * unique fh per open file. Lifting this would require proper open + * file management. + */ +static unsigned fsel_open_mask; +static const char fsel_hex_map[] = "0123456789ABCDEF"; +static struct fuse *fsel_fuse; /* needed for poll notification */ + +#define FSEL_CNT_MAX 10 /* each file can store upto 10 chars */ +#define FSEL_FILES 16 + +static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */ +static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */ +static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */ +static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */ + +static int fsel_path_index(const char *path) +{ + char ch = path[1]; + + if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch)) + return -1; + return ch <= '9' ? ch - '0' : ch - 'A' + 10; +} + +static int fsel_getattr(const char *path, struct stat *stbuf) +{ + int idx; + + memset(stbuf, 0, sizeof(struct stat)); + + if (strcmp(path, "/") == 0) { + stbuf->st_mode = S_IFDIR | 0555; + stbuf->st_nlink = 2; + return 0; + } + + idx = fsel_path_index(path); + if (idx < 0) + return -ENOENT; + + stbuf->st_mode = S_IFREG | 0444; + stbuf->st_nlink = 1; + stbuf->st_size = fsel_cnt[idx]; + return 0; +} + +static int 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) +{ + char name[2] = { }; + int i; + + (void) offset; + (void) fi; + (void) flags; + + if (strcmp(path, "/") != 0) + return -ENOENT; + + for (i = 0; i < FSEL_FILES; i++) { + name[0] = fsel_hex_map[i]; + filler(buf, name, NULL, 0, 0); + } + + return 0; +} + +static int fsel_open(const char *path, struct fuse_file_info *fi) +{ + int idx = fsel_path_index(path); + + if (idx < 0) + return -ENOENT; + if ((fi->flags & 3) != O_RDONLY) + return -EACCES; + if (fsel_open_mask & (1 << idx)) + return -EBUSY; + fsel_open_mask |= (1 << idx); + + /* + * fsel files are nonseekable somewhat pipe-like files which + * gets filled up periodically by producer thread and consumed + * on read. Tell FUSE as such. + */ + fi->fh = idx; + fi->direct_io = 1; + fi->nonseekable = 1; + + return 0; +} + +static int fsel_release(const char *path, struct fuse_file_info *fi) +{ + int idx = fi->fh; + + (void) path; + + fsel_open_mask &= ~(1 << idx); + return 0; +} + +static int fsel_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + int idx = fi->fh; + + (void) path; + (void) offset; + + pthread_mutex_lock(&fsel_mutex); + if (fsel_cnt[idx] < size) + size = fsel_cnt[idx]; + printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]); + fsel_cnt[idx] -= size; + pthread_mutex_unlock(&fsel_mutex); + + memset(buf, fsel_hex_map[idx], size); + return size; +} + +static int fsel_poll(const char *path, struct fuse_file_info *fi, + struct fuse_pollhandle *ph, unsigned *reventsp) +{ + static unsigned polled_zero; + int idx = fi->fh; + + (void) path; + + /* + * Poll notification requires pointer to struct fuse which + * can't be obtained when using fuse_main(). As notification + * happens only after poll is called, fill it here from + * fuse_context. + */ + if (!fsel_fuse) { + struct fuse_context *cxt = fuse_get_context(); + if (cxt) + fsel_fuse = cxt->fuse; + } + + pthread_mutex_lock(&fsel_mutex); + + if (ph != NULL) { + struct fuse_pollhandle *oldph = fsel_poll_handle[idx]; + + if (oldph) + fuse_pollhandle_destroy(oldph); + + fsel_poll_notify_mask |= (1 << idx); + fsel_poll_handle[idx] = ph; + } + + if (fsel_cnt[idx]) { + *reventsp |= POLLIN; + printf("POLL %X cnt=%u polled_zero=%u\n", + idx, fsel_cnt[idx], polled_zero); + polled_zero = 0; + } else + polled_zero++; + + pthread_mutex_unlock(&fsel_mutex); + return 0; +} + +static struct fuse_operations fsel_oper = { + .getattr = fsel_getattr, + .readdir = fsel_readdir, + .open = fsel_open, + .release = fsel_release, + .read = fsel_read, + .poll = fsel_poll, +}; + +static void *fsel_producer(void *data) +{ + const struct timespec interval = { 0, 250000000 }; + unsigned idx = 0, nr = 1; + + (void) data; + + while (1) { + int i, t; + + pthread_mutex_lock(&fsel_mutex); + + /* + * This is the main producer loop which is executed + * ever 500ms. On each iteration, it fills one byte + * to 1, 2 or 4 files and sends poll notification if + * requested. + */ + for (i = 0, t = idx; i < nr; + i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) { + if (fsel_cnt[t] == FSEL_CNT_MAX) + continue; + + fsel_cnt[t]++; + if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) { + struct fuse_pollhandle *ph; + + printf("NOTIFY %X\n", t); + ph = fsel_poll_handle[t]; + fuse_notify_poll(ph); + fuse_pollhandle_destroy(ph); + fsel_poll_notify_mask &= ~(1 << t); + fsel_poll_handle[t] = NULL; + } + } + + idx = (idx + 1) % FSEL_FILES; + if (idx == 0) + nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */ + + pthread_mutex_unlock(&fsel_mutex); + + nanosleep(&interval, NULL); + } + + return NULL; +} + +int main(int argc, char *argv[]) +{ + pthread_t producer; + pthread_attr_t attr; + int ret; + + errno = pthread_mutex_init(&fsel_mutex, NULL); + if (errno) { + perror("pthread_mutex_init"); + return 1; + } + + errno = pthread_attr_init(&attr); + if (errno) { + perror("pthread_attr_init"); + return 1; + } + + errno = pthread_create(&producer, &attr, fsel_producer, NULL); + if (errno) { + perror("pthread_create"); + return 1; + } + + ret = fuse_main(argc, argv, &fsel_oper, NULL); + + pthread_cancel(producer); + pthread_join(producer, NULL); + + return ret; +} -- cgit v1.2.3