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