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 GPL.
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 & 3) != 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)
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 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);
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 }
uint64_t fh
Definition: fuse_common.h:72
unsigned int direct_io
Definition: fuse_common.h:46
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:82
fuse_readdir_flags
Definition: fuse.h:42
#define fuse_main(argc, argv, op, private_data)
Definition: fuse.h:829
struct fuse * fuse
Definition: fuse.h:762
unsigned int nonseekable
Definition: fuse_common.h:60
void fuse_pollhandle_destroy(struct fuse_pollhandle *ph)
int(* getattr)(const char *, struct stat *, struct fuse_file_info *fi)
Definition: fuse.h:311
struct fuse_context * fuse_get_context(void)
Definition: fuse.c:4415