You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 lines
5.9 KiB

  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. This program can be distributed under the terms of the GNU GPL.
  6. See the file COPYING.
  7. gcc -Wall fsel.c `pkg-config fuse --cflags --libs` -o fsel
  8. */
  9. #define FUSE_USE_VERSION 29
  10. #include <fuse.h>
  11. #include <unistd.h>
  12. #include <ctype.h>
  13. #include <string.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <errno.h>
  17. #include <time.h>
  18. #include <pthread.h>
  19. #include <poll.h>
  20. /*
  21. * fsel_open_mask is used to limit the number of opens to 1 per file.
  22. * This is to use file index (0-F) as fh as poll support requires
  23. * unique fh per open file. Lifting this would require proper open
  24. * file management.
  25. */
  26. static unsigned fsel_open_mask;
  27. static const char fsel_hex_map[] = "0123456789ABCDEF";
  28. static struct fuse *fsel_fuse; /* needed for poll notification */
  29. #define FSEL_CNT_MAX 10 /* each file can store upto 10 chars */
  30. #define FSEL_FILES 16
  31. static pthread_mutex_t fsel_mutex; /* protects notify_mask and cnt array */
  32. static unsigned fsel_poll_notify_mask; /* poll notification scheduled? */
  33. static struct fuse_pollhandle *fsel_poll_handle[FSEL_FILES]; /* poll notify handles */
  34. static unsigned fsel_cnt[FSEL_FILES]; /* nbytes stored in each file */
  35. static int fsel_path_index(const char *path)
  36. {
  37. char ch = path[1];
  38. if (strlen(path) != 2 || path[0] != '/' || !isxdigit(ch) || islower(ch))
  39. return -1;
  40. return ch <= '9' ? ch - '0' : ch - 'A' + 10;
  41. }
  42. static int fsel_getattr(const char *path, struct stat *stbuf)
  43. {
  44. int idx;
  45. memset(stbuf, 0, sizeof(struct stat));
  46. if (strcmp(path, "/") == 0) {
  47. stbuf->st_mode = S_IFDIR | 0555;
  48. stbuf->st_nlink = 2;
  49. return 0;
  50. }
  51. idx = fsel_path_index(path);
  52. if (idx < 0)
  53. return -ENOENT;
  54. stbuf->st_mode = S_IFREG | 0444;
  55. stbuf->st_nlink = 1;
  56. stbuf->st_size = fsel_cnt[idx];
  57. return 0;
  58. }
  59. static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
  60. off_t offset, struct fuse_file_info *fi)
  61. {
  62. char name[2] = { };
  63. int i;
  64. (void) offset;
  65. (void) fi;
  66. if (strcmp(path, "/") != 0)
  67. return -ENOENT;
  68. for (i = 0; i < FSEL_FILES; i++) {
  69. name[0] = fsel_hex_map[i];
  70. filler(buf, name, NULL, 0);
  71. }
  72. return 0;
  73. }
  74. static int fsel_open(const char *path, struct fuse_file_info *fi)
  75. {
  76. int idx = fsel_path_index(path);
  77. if (idx < 0)
  78. return -ENOENT;
  79. if ((fi->flags & 3) != O_RDONLY)
  80. return -EACCES;
  81. if (fsel_open_mask & (1 << idx))
  82. return -EBUSY;
  83. fsel_open_mask |= (1 << idx);
  84. /*
  85. * fsel files are nonseekable somewhat pipe-like files which
  86. * gets filled up periodically by producer thread and consumed
  87. * on read. Tell FUSE as such.
  88. */
  89. fi->fh = idx;
  90. fi->direct_io = 1;
  91. fi->nonseekable = 1;
  92. return 0;
  93. }
  94. static int fsel_release(const char *path, struct fuse_file_info *fi)
  95. {
  96. int idx = fi->fh;
  97. (void) path;
  98. fsel_open_mask &= ~(1 << idx);
  99. return 0;
  100. }
  101. static int fsel_read(const char *path, char *buf, size_t size, off_t offset,
  102. struct fuse_file_info *fi)
  103. {
  104. int idx = fi->fh;
  105. (void) path;
  106. (void) offset;
  107. pthread_mutex_lock(&fsel_mutex);
  108. if (fsel_cnt[idx] < size)
  109. size = fsel_cnt[idx];
  110. printf("READ %X transferred=%zu cnt=%u\n", idx, size, fsel_cnt[idx]);
  111. fsel_cnt[idx] -= size;
  112. pthread_mutex_unlock(&fsel_mutex);
  113. memset(buf, fsel_hex_map[idx], size);
  114. return size;
  115. }
  116. static int fsel_poll(const char *path, struct fuse_file_info *fi,
  117. struct fuse_pollhandle *ph, unsigned *reventsp)
  118. {
  119. static unsigned polled_zero;
  120. int idx = fi->fh;
  121. (void) path;
  122. /*
  123. * Poll notification requires pointer to struct fuse which
  124. * can't be obtained when using fuse_main(). As notification
  125. * happens only after poll is called, fill it here from
  126. * fuse_context.
  127. */
  128. if (!fsel_fuse) {
  129. struct fuse_context *cxt = fuse_get_context();
  130. if (cxt)
  131. fsel_fuse = cxt->fuse;
  132. }
  133. pthread_mutex_lock(&fsel_mutex);
  134. if (ph != NULL) {
  135. struct fuse_pollhandle *oldph = fsel_poll_handle[idx];
  136. if (oldph)
  137. fuse_pollhandle_destroy(oldph);
  138. fsel_poll_notify_mask |= (1 << idx);
  139. fsel_poll_handle[idx] = ph;
  140. }
  141. if (fsel_cnt[idx]) {
  142. *reventsp |= POLLIN;
  143. printf("POLL %X cnt=%u polled_zero=%u\n",
  144. idx, fsel_cnt[idx], polled_zero);
  145. polled_zero = 0;
  146. } else
  147. polled_zero++;
  148. pthread_mutex_unlock(&fsel_mutex);
  149. return 0;
  150. }
  151. static struct fuse_operations fsel_oper = {
  152. .getattr = fsel_getattr,
  153. .readdir = fsel_readdir,
  154. .open = fsel_open,
  155. .release = fsel_release,
  156. .read = fsel_read,
  157. .poll = fsel_poll,
  158. };
  159. static void *fsel_producer(void *data)
  160. {
  161. const struct timespec interval = { 0, 250000000 };
  162. unsigned idx = 0, nr = 1;
  163. (void) data;
  164. while (1) {
  165. int i, t;
  166. pthread_mutex_lock(&fsel_mutex);
  167. /*
  168. * This is the main producer loop which is executed
  169. * ever 500ms. On each iteration, it fills one byte
  170. * to 1, 2 or 4 files and sends poll notification if
  171. * requested.
  172. */
  173. for (i = 0, t = idx; i < nr;
  174. i++, t = (t + FSEL_FILES / nr) % FSEL_FILES) {
  175. if (fsel_cnt[t] == FSEL_CNT_MAX)
  176. continue;
  177. fsel_cnt[t]++;
  178. if (fsel_fuse && (fsel_poll_notify_mask & (1 << t))) {
  179. struct fuse_pollhandle *ph;
  180. printf("NOTIFY %X\n", t);
  181. ph = fsel_poll_handle[t];
  182. fuse_notify_poll(ph);
  183. fuse_pollhandle_destroy(ph);
  184. fsel_poll_notify_mask &= ~(1 << t);
  185. fsel_poll_handle[t] = NULL;
  186. }
  187. }
  188. idx = (idx + 1) % FSEL_FILES;
  189. if (idx == 0)
  190. nr = (nr * 2) % 7; /* cycle through 1, 2 and 4 */
  191. pthread_mutex_unlock(&fsel_mutex);
  192. nanosleep(&interval, NULL);
  193. }
  194. return NULL;
  195. }
  196. int main(int argc, char *argv[])
  197. {
  198. pthread_t producer;
  199. pthread_attr_t attr;
  200. int ret;
  201. errno = pthread_mutex_init(&fsel_mutex, NULL);
  202. if (errno) {
  203. perror("pthread_mutex_init");
  204. return 1;
  205. }
  206. errno = pthread_attr_init(&attr);
  207. if (errno) {
  208. perror("pthread_attr_init");
  209. return 1;
  210. }
  211. errno = pthread_create(&producer, &attr, fsel_producer, NULL);
  212. if (errno) {
  213. perror("pthread_create");
  214. return 1;
  215. }
  216. ret = fuse_main(argc, argv, &fsel_oper, NULL);
  217. pthread_cancel(producer);
  218. pthread_join(producer, NULL);
  219. return ret;
  220. }