]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libcuse/cuse_lib.c
ifconfig(8): wordsmith -G and -g descriptions
[FreeBSD/FreeBSD.git] / lib / libcuse / cuse_lib.c
1 /*-
2  * Copyright (c) 2010-2022 Hans Petter Selasky. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <pthread.h>
29 #include <signal.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <errno.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35
36 #include <sys/types.h>
37 #include <sys/queue.h>
38 #include <sys/fcntl.h>
39 #include <sys/mman.h>
40 #include <sys/param.h>
41
42 #include <fs/cuse/cuse_ioctl.h>
43
44 #include "cuse.h"
45
46 int     cuse_debug_level;
47
48 #ifdef HAVE_DEBUG
49 static const char *cuse_cmd_str(int cmd);
50
51 #define DPRINTF(...) do {                       \
52         if (cuse_debug_level != 0)              \
53                 printf(__VA_ARGS__);            \
54 } while (0)
55 #else
56 #define DPRINTF(...) do { } while (0)
57 #endif
58
59 struct cuse_vm_allocation {
60         uint8_t *ptr;
61         uint32_t size;
62 };
63
64 struct cuse_dev_entered {
65         TAILQ_ENTRY(cuse_dev_entered) entry;
66         pthread_t thread;
67         void   *per_file_handle;
68         struct cuse_dev *cdev;
69         int     cmd;
70         int     is_local;
71         int     got_signal;
72 };
73
74 struct cuse_dev {
75         TAILQ_ENTRY(cuse_dev) entry;
76         const struct cuse_methods *mtod;
77         void   *priv0;
78         void   *priv1;
79 };
80
81 static int f_cuse = -1;
82
83 static pthread_mutex_t m_cuse;
84 static TAILQ_HEAD(, cuse_dev) h_cuse __guarded_by(m_cuse);
85 static TAILQ_HEAD(, cuse_dev_entered) h_cuse_entered __guarded_by(m_cuse);
86 static struct cuse_vm_allocation a_cuse[CUSE_ALLOC_UNIT_MAX]
87     __guarded_by(m_cuse);
88
89 #define CUSE_LOCK() \
90         pthread_mutex_lock(&m_cuse)
91
92 #define CUSE_UNLOCK() \
93         pthread_mutex_unlock(&m_cuse)
94
95 int
96 cuse_init(void)
97 {
98         pthread_mutexattr_t attr;
99
100         f_cuse = open("/dev/cuse", O_RDWR);
101         if (f_cuse < 0) {
102                 if (feature_present("cuse") == 0)
103                         return (CUSE_ERR_NOT_LOADED);
104                 else
105                         return (CUSE_ERR_INVALID);
106         }
107         pthread_mutexattr_init(&attr);
108         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
109         pthread_mutex_init(&m_cuse, &attr);
110
111         TAILQ_INIT(&h_cuse);
112         TAILQ_INIT(&h_cuse_entered);
113
114         return (0);
115 }
116
117 int
118 cuse_uninit(void)
119 {
120         int f;
121
122         if (f_cuse < 0)
123                 return (CUSE_ERR_INVALID);
124
125         f = f_cuse;
126         f_cuse = -1;
127
128         close(f);
129
130         pthread_mutex_destroy(&m_cuse);
131
132         memset(a_cuse, 0, sizeof(a_cuse));
133
134         return (0);
135 }
136
137 unsigned long
138 cuse_vmoffset(void *_ptr)
139 {
140         uint8_t *ptr_min;
141         uint8_t *ptr_max;
142         uint8_t *ptr = _ptr;
143         unsigned long remainder;
144         unsigned long n;
145
146         CUSE_LOCK();
147         for (n = remainder = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
148                 if (a_cuse[n].ptr == NULL)
149                         continue;
150
151                 ptr_min = a_cuse[n].ptr;
152                 ptr_max = a_cuse[n].ptr + a_cuse[n].size - 1;
153
154                 if ((ptr >= ptr_min) && (ptr <= ptr_max)) {
155                         remainder = (ptr - ptr_min);
156                         break;
157                 }
158         }
159         CUSE_UNLOCK();
160
161         return ((n << CUSE_ALLOC_UNIT_SHIFT) + remainder);
162 }
163
164 void   *
165 cuse_vmalloc(unsigned size)
166 {
167         struct cuse_alloc_info info;
168         unsigned long pgsize;
169         unsigned long x;
170         unsigned long m;
171         unsigned long n;
172         void *ptr;
173         int error;
174
175         /* some sanity checks */
176         if (f_cuse < 0 || size < 1 || size > CUSE_ALLOC_BYTES_MAX)
177                 return (NULL);
178
179         memset(&info, 0, sizeof(info));
180
181         pgsize = getpagesize();
182         info.page_count = howmany(size, pgsize);
183
184         /* compute how many units the allocation needs */
185         m = howmany(size, 1 << CUSE_ALLOC_UNIT_SHIFT);
186         if (m == 0 || m > CUSE_ALLOC_UNIT_MAX)
187                 return (NULL);
188
189         CUSE_LOCK();
190         for (n = 0; n <= CUSE_ALLOC_UNIT_MAX - m; ) {
191                 if (a_cuse[n].size != 0) {
192                         /* skip to next available unit, depending on allocation size */
193                         n += howmany(a_cuse[n].size, 1 << CUSE_ALLOC_UNIT_SHIFT);
194                         continue;
195                 }
196                 /* check if there are "m" free units ahead */
197                 for (x = 1; x != m; x++) {
198                         if (a_cuse[n + x].size != 0)
199                                 break;
200                 }
201                 if (x != m) {
202                         /* skip to next available unit, if any */
203                         n += x + 1;
204                         continue;
205                 }
206                 /* reserve this unit by setting the size to a non-zero value */
207                 a_cuse[n].size = size;
208                 CUSE_UNLOCK();
209
210                 info.alloc_nr = n;
211
212                 error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_MEMORY, &info);
213
214                 if (error == 0) {
215                         ptr = mmap(NULL, info.page_count * pgsize,
216                             PROT_READ | PROT_WRITE,
217                             MAP_SHARED, f_cuse, n << CUSE_ALLOC_UNIT_SHIFT);
218
219                         if (ptr != MAP_FAILED) {
220                                 CUSE_LOCK();
221                                 a_cuse[n].ptr = ptr;
222                                 CUSE_UNLOCK();
223
224                                 return (ptr);           /* success */
225                         }
226
227                         (void) ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
228                 }
229
230                 CUSE_LOCK();
231                 a_cuse[n].size = 0;
232                 n++;
233         }
234         CUSE_UNLOCK();
235         return (NULL);                  /* failure */
236 }
237
238 int
239 cuse_is_vmalloc_addr(void *ptr)
240 {
241         int n;
242
243         if (f_cuse < 0 || ptr == NULL)
244                 return (0);             /* false */
245
246         CUSE_LOCK();
247         for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
248                 if (a_cuse[n].ptr == ptr)
249                         break;
250         }
251         CUSE_UNLOCK();
252
253         return (n != CUSE_ALLOC_UNIT_MAX);
254 }
255
256 void
257 cuse_vmfree(void *ptr)
258 {
259         struct cuse_vm_allocation temp;
260         struct cuse_alloc_info info;
261         int error;
262         int n;
263
264         if (f_cuse < 0 || ptr == NULL)
265                 return;
266
267         CUSE_LOCK();
268         for (n = 0; n != CUSE_ALLOC_UNIT_MAX; n++) {
269                 if (a_cuse[n].ptr != ptr)
270                         continue;
271
272                 temp = a_cuse[n];
273
274                 CUSE_UNLOCK();
275
276                 munmap(temp.ptr, temp.size);
277
278                 memset(&info, 0, sizeof(info));
279
280                 info.alloc_nr = n;
281
282                 error = ioctl(f_cuse, CUSE_IOCTL_FREE_MEMORY, &info);
283
284                 if (error != 0) {
285                         /* ignore any errors */
286                         DPRINTF("Freeing memory failed: %d\n", errno);
287                 }
288                 CUSE_LOCK();
289
290                 a_cuse[n].ptr = NULL;
291                 a_cuse[n].size = 0;
292
293                 break;
294         }
295         CUSE_UNLOCK();
296 }
297
298 int
299 cuse_alloc_unit_number_by_id(int *pnum, int id)
300 {
301         int error;
302
303         if (f_cuse < 0)
304                 return (CUSE_ERR_INVALID);
305
306         *pnum = (id & CUSE_ID_MASK);
307
308         error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT_BY_ID, pnum);
309         if (error)
310                 return (CUSE_ERR_NO_MEMORY);
311
312         return (0);
313
314 }
315
316 int
317 cuse_free_unit_number_by_id(int num, int id)
318 {
319         int error;
320
321         if (f_cuse < 0)
322                 return (CUSE_ERR_INVALID);
323
324         if (num != -1 || id != -1)
325                 num = (id & CUSE_ID_MASK) | (num & 0xFF);
326
327         error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT_BY_ID, &num);
328         if (error)
329                 return (CUSE_ERR_NO_MEMORY);
330
331         return (0);
332 }
333
334 int
335 cuse_alloc_unit_number(int *pnum)
336 {
337         int error;
338
339         if (f_cuse < 0)
340                 return (CUSE_ERR_INVALID);
341
342         error = ioctl(f_cuse, CUSE_IOCTL_ALLOC_UNIT, pnum);
343         if (error)
344                 return (CUSE_ERR_NO_MEMORY);
345
346         return (0);
347 }
348
349 int
350 cuse_free_unit_number(int num)
351 {
352         int error;
353
354         if (f_cuse < 0)
355                 return (CUSE_ERR_INVALID);
356
357         error = ioctl(f_cuse, CUSE_IOCTL_FREE_UNIT, &num);
358         if (error)
359                 return (CUSE_ERR_NO_MEMORY);
360
361         return (0);
362 }
363
364 struct cuse_dev *
365 cuse_dev_create(const struct cuse_methods *mtod, void *priv0, void *priv1,
366     uid_t _uid, gid_t _gid, int _perms, const char *_fmt,...)
367 {
368         struct cuse_create_dev info;
369         struct cuse_dev *cdev;
370         va_list args;
371         int error;
372
373         if (f_cuse < 0)
374                 return (NULL);
375
376         cdev = malloc(sizeof(*cdev));
377         if (cdev == NULL)
378                 return (NULL);
379
380         memset(cdev, 0, sizeof(*cdev));
381
382         cdev->mtod = mtod;
383         cdev->priv0 = priv0;
384         cdev->priv1 = priv1;
385
386         memset(&info, 0, sizeof(info));
387
388         info.dev = cdev;
389         info.user_id = _uid;
390         info.group_id = _gid;
391         info.permissions = _perms;
392
393         va_start(args, _fmt);
394         vsnprintf(info.devname, sizeof(info.devname), _fmt, args);
395         va_end(args);
396
397         error = ioctl(f_cuse, CUSE_IOCTL_CREATE_DEV, &info);
398         if (error) {
399                 free(cdev);
400                 return (NULL);
401         }
402         CUSE_LOCK();
403         TAILQ_INSERT_TAIL(&h_cuse, cdev, entry);
404         CUSE_UNLOCK();
405
406         return (cdev);
407 }
408
409
410 void
411 cuse_dev_destroy(struct cuse_dev *cdev)
412 {
413         int error;
414
415         if (f_cuse < 0)
416                 return;
417
418         CUSE_LOCK();
419         TAILQ_REMOVE(&h_cuse, cdev, entry);
420         CUSE_UNLOCK();
421
422         error = ioctl(f_cuse, CUSE_IOCTL_DESTROY_DEV, &cdev);
423         if (error)
424                 return;
425
426         free(cdev);
427 }
428
429 void   *
430 cuse_dev_get_priv0(struct cuse_dev *cdev)
431 {
432         return (cdev->priv0);
433 }
434
435 void   *
436 cuse_dev_get_priv1(struct cuse_dev *cdev)
437 {
438         return (cdev->priv1);
439 }
440
441 void
442 cuse_dev_set_priv0(struct cuse_dev *cdev, void *priv)
443 {
444         cdev->priv0 = priv;
445 }
446
447 void
448 cuse_dev_set_priv1(struct cuse_dev *cdev, void *priv)
449 {
450         cdev->priv1 = priv;
451 }
452
453 int
454 cuse_wait_and_process(void)
455 {
456         pthread_t curr = pthread_self();
457         struct cuse_dev_entered *pe;
458         struct cuse_dev_entered enter;
459         struct cuse_command info;
460         struct cuse_dev *cdev;
461         int error;
462
463         if (f_cuse < 0)
464                 return (CUSE_ERR_INVALID);
465
466         error = ioctl(f_cuse, CUSE_IOCTL_GET_COMMAND, &info);
467         if (error)
468                 return (CUSE_ERR_OTHER);
469
470         cdev = info.dev;
471
472         CUSE_LOCK();
473         enter.thread = curr;
474         enter.per_file_handle = (void *)info.per_file_handle;
475         enter.cmd = info.command;
476         enter.is_local = 0;
477         enter.got_signal = 0;
478         enter.cdev = cdev;
479         TAILQ_INSERT_TAIL(&h_cuse_entered, &enter, entry);
480         CUSE_UNLOCK();
481
482         DPRINTF("cuse: Command = %d = %s, flags = %d, arg = 0x%08x, ptr = 0x%08x\n",
483             (int)info.command, cuse_cmd_str(info.command), (int)info.fflags,
484             (int)info.argument, (int)info.data_pointer);
485
486         switch (info.command) {
487         case CUSE_CMD_OPEN:
488                 if (cdev->mtod->cm_open != NULL)
489                         error = (cdev->mtod->cm_open) (cdev, (int)info.fflags);
490                 else
491                         error = 0;
492                 break;
493
494         case CUSE_CMD_CLOSE:
495
496                 /* wait for other threads to stop */
497
498                 while (1) {
499
500                         error = 0;
501
502                         CUSE_LOCK();
503                         TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
504                                 if (pe->cdev != cdev)
505                                         continue;
506                                 if (pe->thread == curr)
507                                         continue;
508                                 if (pe->per_file_handle !=
509                                     enter.per_file_handle)
510                                         continue;
511                                 pe->got_signal = 1;
512                                 pthread_kill(pe->thread, SIGHUP);
513                                 error = CUSE_ERR_BUSY;
514                         }
515                         CUSE_UNLOCK();
516
517                         if (error == 0)
518                                 break;
519                         else
520                                 usleep(10000);
521                 }
522
523                 if (cdev->mtod->cm_close != NULL)
524                         error = (cdev->mtod->cm_close) (cdev, (int)info.fflags);
525                 else
526                         error = 0;
527                 break;
528
529         case CUSE_CMD_READ:
530                 if (cdev->mtod->cm_read != NULL) {
531                         error = (cdev->mtod->cm_read) (cdev, (int)info.fflags,
532                             (void *)info.data_pointer, (int)info.argument);
533                 } else {
534                         error = CUSE_ERR_INVALID;
535                 }
536                 break;
537
538         case CUSE_CMD_WRITE:
539                 if (cdev->mtod->cm_write != NULL) {
540                         error = (cdev->mtod->cm_write) (cdev, (int)info.fflags,
541                             (void *)info.data_pointer, (int)info.argument);
542                 } else {
543                         error = CUSE_ERR_INVALID;
544                 }
545                 break;
546
547         case CUSE_CMD_IOCTL:
548                 if (cdev->mtod->cm_ioctl != NULL) {
549                         error = (cdev->mtod->cm_ioctl) (cdev, (int)info.fflags,
550                             (unsigned int)info.argument, (void *)info.data_pointer);
551                 } else {
552                         error = CUSE_ERR_INVALID;
553                 }
554                 break;
555
556         case CUSE_CMD_POLL:
557                 if (cdev->mtod->cm_poll != NULL) {
558                         error = (cdev->mtod->cm_poll) (cdev, (int)info.fflags,
559                             (int)info.argument);
560                 } else {
561                         error = CUSE_POLL_ERROR;
562                 }
563                 break;
564
565         case CUSE_CMD_SIGNAL:
566                 CUSE_LOCK();
567                 TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
568                         if (pe->cdev != cdev)
569                                 continue;
570                         if (pe->thread == curr)
571                                 continue;
572                         if (pe->per_file_handle !=
573                             enter.per_file_handle)
574                                 continue;
575                         pe->got_signal = 1;
576                         pthread_kill(pe->thread, SIGHUP);
577                 }
578                 CUSE_UNLOCK();
579                 break;
580
581         default:
582                 error = CUSE_ERR_INVALID;
583                 break;
584         }
585
586         DPRINTF("cuse: Command error = %d for %s\n",
587             error, cuse_cmd_str(info.command));
588
589         CUSE_LOCK();
590         TAILQ_REMOVE(&h_cuse_entered, &enter, entry);
591         CUSE_UNLOCK();
592
593         /* we ignore any sync command failures */
594         ioctl(f_cuse, CUSE_IOCTL_SYNC_COMMAND, &error);
595
596         return (0);
597 }
598
599 static struct cuse_dev_entered *
600 cuse_dev_get_entered(void)
601 {
602         struct cuse_dev_entered *pe;
603         pthread_t curr = pthread_self();
604
605         CUSE_LOCK();
606         TAILQ_FOREACH(pe, &h_cuse_entered, entry) {
607                 if (pe->thread == curr)
608                         break;
609         }
610         CUSE_UNLOCK();
611         return (pe);
612 }
613
614 void
615 cuse_dev_set_per_file_handle(struct cuse_dev *cdev, void *handle)
616 {
617         struct cuse_dev_entered *pe;
618
619         pe = cuse_dev_get_entered();
620         if (pe == NULL || pe->cdev != cdev)
621                 return;
622
623         pe->per_file_handle = handle;
624         ioctl(f_cuse, CUSE_IOCTL_SET_PFH, &handle);
625 }
626
627 void   *
628 cuse_dev_get_per_file_handle(struct cuse_dev *cdev)
629 {
630         struct cuse_dev_entered *pe;
631
632         pe = cuse_dev_get_entered();
633         if (pe == NULL || pe->cdev != cdev)
634                 return (NULL);
635
636         return (pe->per_file_handle);
637 }
638
639 void
640 cuse_set_local(int val)
641 {
642         struct cuse_dev_entered *pe;
643
644         pe = cuse_dev_get_entered();
645         if (pe == NULL)
646                 return;
647
648         pe->is_local = val;
649 }
650
651 #ifdef HAVE_DEBUG
652 static const char *
653 cuse_cmd_str(int cmd)
654 {
655         static const char *str[CUSE_CMD_MAX] = {
656                 [CUSE_CMD_NONE] = "none",
657                 [CUSE_CMD_OPEN] = "open",
658                 [CUSE_CMD_CLOSE] = "close",
659                 [CUSE_CMD_READ] = "read",
660                 [CUSE_CMD_WRITE] = "write",
661                 [CUSE_CMD_IOCTL] = "ioctl",
662                 [CUSE_CMD_POLL] = "poll",
663                 [CUSE_CMD_SIGNAL] = "signal",
664                 [CUSE_CMD_SYNC] = "sync",
665         };
666
667         if ((cmd >= 0) && (cmd < CUSE_CMD_MAX) &&
668             (str[cmd] != NULL))
669                 return (str[cmd]);
670         else
671                 return ("unknown");
672 }
673
674 #endif
675
676 int
677 cuse_get_local(void)
678 {
679         struct cuse_dev_entered *pe;
680
681         pe = cuse_dev_get_entered();
682         if (pe == NULL)
683                 return (0);
684
685         return (pe->is_local);
686 }
687
688 int
689 cuse_copy_out(const void *src, void *user_dst, int len)
690 {
691         struct cuse_data_chunk info;
692         struct cuse_dev_entered *pe;
693         int error;
694
695         if ((f_cuse < 0) || (len < 0))
696                 return (CUSE_ERR_INVALID);
697
698         pe = cuse_dev_get_entered();
699         if (pe == NULL)
700                 return (CUSE_ERR_INVALID);
701
702         DPRINTF("cuse: copy_out(%p,%p,%d), cmd = %d = %s\n",
703             src, user_dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
704
705         if (pe->is_local) {
706                 memcpy(user_dst, src, len);
707         } else {
708                 info.local_ptr = (uintptr_t)src;
709                 info.peer_ptr = (uintptr_t)user_dst;
710                 info.length = len;
711
712                 error = ioctl(f_cuse, CUSE_IOCTL_WRITE_DATA, &info);
713                 if (error) {
714                         DPRINTF("cuse: copy_out() error = %d\n", errno);
715                         return (CUSE_ERR_FAULT);
716                 }
717         }
718         return (0);
719 }
720
721 int
722 cuse_copy_in(const void *user_src, void *dst, int len)
723 {
724         struct cuse_data_chunk info;
725         struct cuse_dev_entered *pe;
726         int error;
727
728         if ((f_cuse < 0) || (len < 0))
729                 return (CUSE_ERR_INVALID);
730
731         pe = cuse_dev_get_entered();
732         if (pe == NULL)
733                 return (CUSE_ERR_INVALID);
734
735         DPRINTF("cuse: copy_in(%p,%p,%d), cmd = %d = %s\n",
736             user_src, dst, len, pe->cmd, cuse_cmd_str(pe->cmd));
737
738         if (pe->is_local) {
739                 memcpy(dst, user_src, len);
740         } else {
741                 info.local_ptr = (uintptr_t)dst;
742                 info.peer_ptr = (uintptr_t)user_src;
743                 info.length = len;
744
745                 error = ioctl(f_cuse, CUSE_IOCTL_READ_DATA, &info);
746                 if (error) {
747                         DPRINTF("cuse: copy_in() error = %d\n", errno);
748                         return (CUSE_ERR_FAULT);
749                 }
750         }
751         return (0);
752 }
753
754 struct cuse_dev *
755 cuse_dev_get_current(int *pcmd)
756 {
757         struct cuse_dev_entered *pe;
758
759         pe = cuse_dev_get_entered();
760         if (pe == NULL) {
761                 if (pcmd != NULL)
762                         *pcmd = 0;
763                 return (NULL);
764         }
765         if (pcmd != NULL)
766                 *pcmd = pe->cmd;
767         return (pe->cdev);
768 }
769
770 int
771 cuse_got_peer_signal(void)
772 {
773         struct cuse_dev_entered *pe;
774
775         pe = cuse_dev_get_entered();
776         if (pe == NULL)
777                 return (CUSE_ERR_INVALID);
778
779         if (pe->got_signal)
780                 return (0);
781
782         return (CUSE_ERR_OTHER);
783 }
784
785 void
786 cuse_poll_wakeup(void)
787 {
788         int error = 0;
789
790         if (f_cuse < 0)
791                 return;
792
793         ioctl(f_cuse, CUSE_IOCTL_SELWAKEUP, &error);
794 }