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