Add Russian translation provided by Валерий Крувялис <valkru@mail.ru>
[xiph-mirror.git] / fusd / libfusd / libfusd.c
blobb199b604c9fe799f7e9de0604677df12364f548f
1 /*
3 * Copyright (c) 2003 The Regents of the University of California. All
4 * rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Neither the name of the University nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
19 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
21 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * fusd userspace library: functions that know how to properly talk
34 * to the fusd kernel module
36 * authors: jelson and girod
38 * $Id$
41 char libfusd_c_id[] = "$Id$";
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/time.h>
49 #include <sys/uio.h>
50 #include <sys/ioctl.h>
51 #include <fcntl.h>
52 #include <errno.h>
53 #include <string.h>
54 #include <time.h>
56 #include "fusd.h"
57 #include "fusd_msg.h"
59 #define MIN(a, b) ((a) < (b) ? (a) : (b))
61 /* maximum number of messages processed by a single call to fusd_dispatch */
62 #define MAX_MESSAGES_PER_DISPATCH 40
64 /* used for fusd_run */
65 static fd_set fusd_fds;
67 /* default prefix of devices (often "/dev/") */
68 char *dev_root = NULL;
70 /*
71 * fusd_fops_set is an array that keeps track of the file operations
72 * struct for each fusd fd.
74 static fusd_file_operations_t fusd_fops_set[FD_SETSIZE];
75 fusd_file_operations_t null_fops = { NULL };
78 * accessor macros
80 #define FUSD_GET_FOPS(fd) \
81 (fusd_fops_set + (fd))
83 #define FUSD_SET_FOPS(fd,fi) \
84 (fusd_fops_set[(fd)]=(*fi))
86 #define FUSD_FD_VALID(fd) \
87 (((fd)>=0) && \
88 ((fd)<FD_SETSIZE) && \
89 (memcmp(FUSD_GET_FOPS(fd), &null_fops, sizeof(fusd_file_operations_t))))
93 * fusd_init
95 * this is called automatically before the first
96 * register call
98 void fusd_init()
100 static int fusd_init_needed = 1;
102 if (fusd_init_needed) {
103 int i;
105 fusd_init_needed = 0;
107 for (i = 0; i < FD_SETSIZE; i++)
108 FUSD_SET_FOPS(i, &null_fops);
109 FD_ZERO(&fusd_fds);
111 dev_root = DEFAULT_DEV_ROOT;
116 int fusd_register(const char *name, const char* clazz, const char* devname, mode_t mode, void *device_info,
117 struct fusd_file_operations *fops)
119 int fd = -1, retval = 0;
120 fusd_msg_t message;
122 /* need initialization? */
123 fusd_init();
125 /* make sure the name is valid and we have a valid set of fops... */
126 if (name == NULL || fops == NULL) {
127 fprintf(stderr, "fusd_register: invalid name or fops argument\n");
128 retval = -EINVAL;
129 goto done;
133 * convenience: if the first characters of the name you're trying
134 * to register are SKIP_PREFIX (usually "/dev/"), skip over them.
136 if (dev_root != NULL && strlen(name) > strlen(dev_root) &&
137 !strncmp(name, dev_root, strlen(dev_root))) {
138 name += strlen(dev_root);
141 if (strlen(name) > FUSD_MAX_NAME_LENGTH) {
142 fprintf(stderr, "name '%s' too long, sorry :(", name);
143 retval = -EINVAL;
144 goto done;
147 /* open the fusd control channel */
148 if ((fd = open(FUSD_CONTROL_DEVNAME, O_RDWR | O_NONBLOCK)) < 0) {
150 /* if the problem is that /dev/fusd does not exist, return the
151 * message "Package not installed", which is hopefully more
152 * illuminating than "no such file or directory" */
153 if (errno == ENOENT) {
154 fprintf(stderr, "libfusd: %s does not exist; ensure FUSD's kernel module is installed\n",
155 FUSD_CONTROL_DEVNAME);
156 retval = -ENOPKG;
157 } else {
158 perror("libfusd: trying to open FUSD control channel");
159 retval = -errno;
161 goto done;
164 /* fd in use? */
165 if (FUSD_FD_VALID(fd)) {
166 retval = -EBADF;
167 goto done;
170 /* set up the message */
171 memset(&message, 0, sizeof(message));
172 message.magic = FUSD_MSG_MAGIC;
173 message.cmd = FUSD_REGISTER_DEVICE;
174 message.datalen = 0;
175 strcpy(message.parm.register_msg.name, name);
176 strcpy(message.parm.register_msg.clazz, clazz);
177 strcpy(message.parm.register_msg.devname, devname);
178 message.parm.register_msg.mode = mode;
179 message.parm.register_msg.device_info = device_info;
181 /* make the request */
182 if (write(fd, &message, sizeof(fusd_msg_t)) < 0) {
183 retval = -errno;
184 goto done;
187 /* OK, store the new file state */
188 FUSD_SET_FOPS(fd, fops);
189 FD_SET(fd, &fusd_fds);
191 /* success! */
192 done:
193 if (retval < 0) {
194 if (fd >= 0)
195 close(fd);
196 errno = -retval;
197 retval = -1;
198 } else {
199 errno = 0;
200 retval = fd;
203 return retval;
207 int fusd_unregister(int fd)
209 if (FUSD_FD_VALID(fd)) {
210 /* clear fd location */
211 FUSD_SET_FOPS(fd, &null_fops);
212 FD_CLR(fd, &fusd_fds);
213 /* close */
214 return close(fd);
217 else {
218 errno = EBADF;
219 return -1;
225 * fusd_run: a convenience function for automatically running a FUSD
226 * driver, for drivers that don't want to manually select on file
227 * descriptors and call fusd_dispatch. This function will
228 * automatically select on all devices the user has registered and
229 * call fusd_dispatch on any one that becomes readable.
231 void fusd_run(void)
233 fd_set tfds;
234 int status;
235 int maxfd;
236 int i;
238 /* locate maxmimum fd in use */
239 for (maxfd=0, i=0; i < FD_SETSIZE; i++) {
240 if (FD_ISSET(i, &fusd_fds)) {
241 maxfd = i;
244 maxfd++;
247 while (1) {
248 /* select */
249 memmove(&tfds, &fusd_fds, sizeof(fd_set));
250 status = select(maxfd, &tfds, NULL, NULL, NULL);
252 /* error? */
253 if (status < 0) {
254 perror("libfusd: fusd_run: error on select");
255 continue;
258 /* readable? */
259 for (i = 0; i < maxfd; i++)
260 if (FD_ISSET(i, &tfds))
261 fusd_dispatch(i);
266 /************************************************************************/
269 /* reads a fusd kernel-to-userspace message from fd, and puts a
270 * fusd_msg into the memory pointed to by msg (we assume we are passed
271 * a buffer managed by the caller). if there is a data portion to the
272 * message (msg->datalen > 0), we allocate memory for it, set data to
273 * point to that memory. the returned data pointer must also be
274 * managed by the caller. */
275 static int fusd_get_message(int fd, fusd_msg_t *msg)
277 /* read the header part into the kernel */
278 if (read(fd, msg, sizeof(fusd_msg_t)) < 0) {
279 if (errno != EAGAIN)
280 perror("error talking to FUSD control channel on header read");
281 return -errno;
283 msg->data = NULL; /* pointers in kernelspace have no meaning */
285 if (msg->magic != FUSD_MSG_MAGIC) {
286 fprintf(stderr, "libfusd magic number failure\n");
287 return -EINVAL;
290 /* if there's a data part to the message, read it from the kernel. */
291 if (msg->datalen) {
292 if ((msg->data = malloc(msg->datalen + 1)) == NULL) {
293 fprintf(stderr, "libfusd: can't allocate memory\n");
294 return -ENOMEM; /* this is bad, we are now unsynced */
297 if (read(fd, msg->data, msg->datalen) < 0) {
298 perror("error talking to FUSD control channel on data read");
299 free(msg->data);
300 msg->data = NULL;
301 return -EIO;
304 /* For convenience, we now ensure that the byte *after* the buffer
305 * is set to 0. (Note we malloc'd one extra byte above.) */
306 msg->data[msg->datalen] = '\0';
309 return 0;
314 * fusd_fdset_add: given an FDSET and "max", add the currently valid
315 * FUSD fds to the set and update max accordingly.
317 void fusd_fdset_add(fd_set *set, int *max)
319 int i;
321 for (i = 0; i < FD_SETSIZE; i++) {
322 if (FD_ISSET(i, &fusd_fds)) {
323 FD_SET(i, set);
324 if (i > *max) {
325 *max = i;
334 * fusd_dispatch_fdset: given an fd_set full of descriptors, call
335 * fusd_dispatch on every descriptor in the set which is a valid FUSD
336 * fd.
338 void fusd_dispatch_fdset(fd_set *set)
340 int i;
342 for (i = 0; i < FD_SETSIZE; i++)
343 if (FD_ISSET(i, set) && FD_ISSET(i, &fusd_fds))
344 fusd_dispatch(i);
349 * fusd_dispatch_one() -- read a single kernel-to-userspace message
350 * from fd, then call the appropriate userspace callback function,
351 * based on the message that was read. finally, return the result
352 * back to the kernel, IF the return value from the callback is not
353 * FUSD_NOREPLY.
355 * On success, returns 0.
356 * On failure, returns a negative number indicating the errno.
358 static int fusd_dispatch_one(int fd, fusd_file_operations_t *fops)
360 fusd_file_info_t *file = NULL;
361 fusd_msg_t *msg = NULL;
362 int driver_retval = 0; /* returned to the FUSD driver */
363 int user_retval = 0; /* returned to the user who made the syscall */
365 /* check for valid, look up ops */
366 if (fops == NULL) {
367 fprintf(stderr, "fusd_dispatch: no fops provided!\n");
368 driver_retval = -EBADF;
369 goto out_noreply;
372 /* allocate memory for fusd_msg_t */
373 if ((msg = malloc(sizeof(fusd_msg_t))) == NULL) {
374 driver_retval = -ENOMEM;
375 fprintf(stderr, "libfusd: can't allocate memory\n");
376 goto out_noreply;
378 memset(msg, '\0', sizeof(fusd_msg_t));
380 /* read header and data, if it's there */
381 if ((driver_retval = fusd_get_message(fd, msg)) < 0)
382 goto out_noreply;
384 /* allocate file info struct */
385 file = malloc(sizeof(fusd_file_info_t));
386 if (NULL == file) {
387 fprintf(stderr, "libfusd: can't allocate memory\n");
388 driver_retval = -ENOMEM;
389 goto out_noreply;
392 /* fill the file info struct */
393 memset(file, '\0', sizeof(fusd_file_info_t));
394 file->fd = fd;
395 file->device_info = msg->parm.fops_msg.device_info;
396 file->private_data = msg->parm.fops_msg.private_info;
397 file->flags = msg->parm.fops_msg.flags;
398 file->pid = msg->parm.fops_msg.pid;
399 file->uid = msg->parm.fops_msg.uid;
400 file->gid = msg->parm.fops_msg.gid;
401 file->fusd_msg = msg;
403 /* right now we only handle fops requests */
404 if (msg->cmd != FUSD_FOPS_CALL && msg->cmd != FUSD_FOPS_NONBLOCK &&
405 msg->cmd != FUSD_FOPS_CALL_DROPREPLY) {
406 fprintf(stderr, "libfusd: got unknown msg->cmd from kernel\n");
407 user_retval = -EINVAL;
408 goto send_reply;
411 /* dispatch on operation type */
412 user_retval = -ENOSYS;
413 switch (msg->subcmd) {
414 case FUSD_OPEN:
415 if (fops && fops->open)
416 user_retval = fops->open(file);
417 break;
418 case FUSD_CLOSE:
419 if (fops && fops->close)
420 user_retval = fops->close(file);
421 break;
422 case FUSD_READ:
423 /* allocate a buffer and make the call */
424 if (fops && fops->read) {
425 if ((msg->data = malloc(msg->parm.fops_msg.length)) == NULL) {
426 user_retval = -ENOMEM;
427 fprintf(stderr, "libfusd: can't allocate memory\n");
428 } else {
429 msg->datalen = msg->parm.fops_msg.length;
430 user_retval = fops->read(file, msg->data, msg->datalen,
431 &msg->parm.fops_msg.offset);
434 break;
435 case FUSD_WRITE:
436 if (fops && fops->write)
437 user_retval = fops->write(file, msg->data, msg->datalen,
438 &msg->parm.fops_msg.offset);
439 break;
440 case FUSD_MMAP:
441 if (fops && fops->mmap)
443 user_retval = fops->mmap(file, msg->parm.fops_msg.offset, msg->parm.fops_msg.length, msg->parm.fops_msg.flags,
444 &msg->parm.fops_msg.arg.ptr_arg, &msg->parm.fops_msg.length);
446 break;
447 case FUSD_IOCTL:
448 if (fops && fops->ioctl) {
449 /* in the case of an ioctl read, allocate a buffer for the
450 * driver to write to, IF there isn't already a buffer. (there
451 * might already be a buffer if this is a read+write) */
452 if ((_IOC_DIR(msg->parm.fops_msg.cmd) & _IOC_READ) &&
453 msg->data == NULL) {
454 msg->datalen = _IOC_SIZE(msg->parm.fops_msg.cmd);
455 if ((msg->data = malloc(msg->datalen)) == NULL) {
456 user_retval = -ENOMEM;
457 break;
460 if (msg->data != NULL)
461 user_retval = fops->ioctl(file, msg->parm.fops_msg.cmd, msg->data);
462 else
463 user_retval = fops->ioctl(file, msg->parm.fops_msg.cmd,
464 (void *) msg->parm.fops_msg.arg.ptr_arg);
466 break;
468 case FUSD_POLL_DIFF:
469 /* This callback requests notification when an event occurs on a file,
470 * e.g. becoming readable or writable */
471 if (fops && fops->poll_diff)
472 user_retval = fops->poll_diff(file, msg->parm.fops_msg.cmd);
473 break;
475 case FUSD_UNBLOCK:
476 /* This callback is called when a system call is interrupted */
477 if (fops && fops->unblock)
478 user_retval = fops->unblock(file);
479 break;
481 default:
482 fprintf(stderr, "libfusd: Got unsupported operation\n");
483 user_retval = -ENOSYS;
484 break;
487 goto send_reply;
490 /* out_noreply is only used for handling errors */
491 out_noreply:
492 if (msg->data != NULL)
493 free(msg->data);
494 if (msg != NULL)
495 free(msg);
496 goto done;
498 /* send_reply is only used for success */
499 send_reply:
500 if (-user_retval <= 0xff) {
501 /* 0xff is the maximum legal return value (?) - return val to user */
502 driver_retval = fusd_return(file, user_retval);
503 } else {
504 /* if we got a FUSD_NOREPLY, don't free the msg structure */
505 driver_retval = 0;
508 /* this is common to both errors and success */
509 done:
510 if (driver_retval < 0) {
511 errno = -driver_retval;
512 driver_retval = -1;
514 return driver_retval;
518 /* fusd_dispatch is now a wrapper around fusd_dispatch_one that calls
519 * it repeatedly, until it fails. this helps a lot with bulk data
520 * transfer since there is no intermediate select in between the
521 * reads. (the kernel module helps by running the user process in
522 * between).
524 * This function now prints an error to stderr in case of error,
525 * instead of returning a -1.
527 void fusd_dispatch(int fd)
529 int retval, num_dispatches = 0;
530 fusd_file_operations_t *fops = NULL;
532 /* make sure we have a valid FD, and get its fops structure */
533 if (!FUSD_FD_VALID(fd)) {
534 errno = EBADF;
535 retval = -1;
536 goto out;
538 fops = FUSD_GET_FOPS(fd);
540 /* now keep dispatching until a dispatch returns an error */
541 do {
542 retval = fusd_dispatch_one(fd, fops);
544 if (retval >= 0)
545 num_dispatches++;
546 } while (retval >= 0 && num_dispatches <= MAX_MESSAGES_PER_DISPATCH);
548 /* if we've dispatched at least one message successfully, and then
549 * stopped because of EAGAIN - do not report an error. this is the
550 * common case. */
551 if (num_dispatches > 0 && errno == EAGAIN) {
552 retval = 0;
553 errno = 0;
556 out:
557 if (retval < 0 && errno != EPIPE)
558 fprintf(stderr, "libfusd: fusd_dispatch error on fd %d: %m\n", fd);
563 * fusd_destroy destroys all state associated with a fusd_file_info
564 * pointer. (It is implicitly called by fusd_return.) If a driver
565 * saves a fusd_file_info pointer by calling -FUSD_NOREPLY in order to
566 * block a read, but gets a "close" request on the file before the
567 * pointer is returned with fusd_return, it should be thrown away
568 * using fusd_destroy.
570 void fusd_destroy(struct fusd_file_info *file)
572 if (file == NULL)
573 return;
575 if (file->fusd_msg->data != NULL)
576 free(file->fusd_msg->data);
577 free(file->fusd_msg);
578 free(file);
583 * construct a user-to-kernel message in reply to a file function
584 * call.
586 * On success, returns 0.
587 * On failure, returns a negative number indicating the errno.
589 int fusd_return(fusd_file_info_t *file, ssize_t retval)
591 fusd_msg_t *msg = NULL;
592 int fd;
593 int driver_retval = 0;
594 struct iovec iov[2];
596 if (file == NULL) {
597 fprintf(stderr, "fusd_return: NULL file\n");
598 return -EINVAL;
601 fd = file->fd;
602 if (!FUSD_FD_VALID(fd)) {
603 fprintf(stderr, "fusd_return: badfd (fd %d)\n", fd);
604 return -EBADF;
607 if ((msg = file->fusd_msg) == NULL) {
608 fprintf(stderr, "fusd_return: fusd_msg is gone\n");
609 return -EINVAL;
612 /* if this was a "DONTREPLY" message, just free the struct */
613 if (msg->cmd == FUSD_FOPS_CALL_DROPREPLY)
614 goto free_memory;
616 /* do we copy data back to kernel? how much? */
617 switch(msg->subcmd) {
618 case FUSD_READ:
619 /* these operations can return data to userspace */
620 if (retval > 0) {
621 msg->datalen = MIN(retval, msg->parm.fops_msg.length);
622 retval = msg->datalen;
623 } else {
624 msg->datalen = 0;
626 break;
627 case FUSD_IOCTL:
628 /* ioctl CAN (in read mode) return data to userspace */
629 if ((retval == 0) &&
630 (_IOC_DIR(msg->parm.fops_msg.cmd) & _IOC_READ))
631 msg->datalen = _IOC_SIZE(msg->parm.fops_msg.cmd);
632 else
633 msg->datalen = 0;
634 break;
635 default:
636 /* open, close, write, etc. do not return data */
637 msg->datalen = 0;
638 break;
641 /* fill the file info struct */
642 msg->cmd++; /* change FOPS_CALL to FOPS_REPLY; NONBLOCK to NONBLOCK_REPLY */
643 msg->parm.fops_msg.retval = retval;
644 msg->parm.fops_msg.device_info = file->device_info;
645 msg->parm.fops_msg.private_info = file->private_data;
646 msg->parm.fops_msg.flags = file->flags;
647 /* pid is NOT copied back. */
649 /* send message to kernel */
650 if (msg->datalen && msg->data != NULL) {
651 iov[0].iov_base = msg;
652 iov[0].iov_len = sizeof(fusd_msg_t);
653 iov[1].iov_base = msg->data;
654 iov[1].iov_len = msg->datalen;
655 driver_retval = writev(fd, iov, 2);
657 else {
658 driver_retval = write(fd, msg, sizeof(fusd_msg_t));
661 free_memory:
662 fusd_destroy(file);
664 if (driver_retval < 0)
665 return -errno;
666 else
667 return 0;
671 /* returns static string representing the flagset (e.g. RWE) */
672 #define RING 5
673 char *fusd_unparse_flags(int flags)
675 static int i = 0;
676 static char ringbuf[RING][5];
677 char *s = ringbuf[i];
678 i = (i + 1) % RING;
680 sprintf(s, "%c%c%c",
681 (flags & FUSD_NOTIFY_INPUT)?'R':'-',
682 (flags & FUSD_NOTIFY_OUTPUT)?'W':'-',
683 (flags & FUSD_NOTIFY_EXCEPT)?'E':'-');
685 return s;
687 #undef RING