1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * (tty_compat.c)
7  * The compatible layer which interacts with process management core (lwp)
8  *
9  * Change Logs:
10  * Date           Author       Notes
11  * 2023-11-13     Shell        init ver.
12  */
13 
14 #define DBG_TAG "lwp.tty"
15 #define DBG_LVL DBG_INFO
16 #include <rtdbg.h>
17 
18 #include "../tty_config.h"
19 #include "../tty_internal.h"
20 #include "../terminal.h"
21 
22 /*-
23  * SPDX-License-Identifier: BSD-2-Clause
24  *
25  * Copyright (c) 1994-1995 Søren Schmidt
26  * All rights reserved.
27  *
28  * Redistribution and use in source and binary forms, with or without
29  * modification, are permitted provided that the following conditions
30  * are met:
31  * 1. Redistributions of source code must retain the above copyright
32  *    notice, this list of conditions and the following disclaimer.
33  * 2. Redistributions in binary form must reproduce the above copyright
34  *    notice, this list of conditions and the following disclaimer in the
35  *    documentation and/or other materials provided with the distribution.
36  *
37  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
38  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
39  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
40  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
41  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
42  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
43  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
44  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
45  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
46  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  */
49 
50 /* is the tty and session leader already binding ? */
_is_already_binding(lwp_tty_t tp,rt_lwp_t p)51 static rt_bool_t _is_already_binding(lwp_tty_t tp, rt_lwp_t p)
52 {
53     rt_bool_t rc;
54     rt_processgroup_t pgrp = p->pgrp;
55 
56     /* lwp is already locked */
57     RT_ASSERT(pgrp);
58 
59     /* Note: pgrp->session is constant after process group is created */
60     if (tp->t_session && tp->t_session == pgrp->session)
61     {
62         rc = RT_TRUE;
63     }
64     else
65     {
66         rc = RT_FALSE;
67     }
68     return rc;
69 }
70 
_is_tty_or_sess_busy(lwp_tty_t tp,rt_lwp_t p)71 static rt_bool_t _is_tty_or_sess_busy(lwp_tty_t tp, rt_lwp_t p)
72 {
73     rt_bool_t rc;
74     rt_session_t sess = p->pgrp->session;
75 
76     SESS_LOCK(sess);
77     if (sess->ctty)
78     {
79         rc = RT_TRUE;
80     }
81     else if (tp->t_session)
82     {
83         /**
84          * TODO: allow TTY stolen if the sess leader is killed while resource
85          * had not been collected
86          */
87         if (tp->t_session->leader == RT_NULL)
88             rc = RT_FALSE;
89         else
90             rc = RT_TRUE;
91     }
92     else
93     {
94         rc = RT_FALSE;
95     }
96     SESS_UNLOCK(sess);
97     return rc;
98 }
99 
lwp_tty_bg_stop(struct lwp_tty * tp,struct rt_condvar * cv)100 int lwp_tty_bg_stop(struct lwp_tty *tp, struct rt_condvar *cv)
101 {
102     int error;
103     int revokecnt = tp->t_revokecnt;
104     rt_lwp_t self_lwp;
105     rt_thread_t header_thr;
106     rt_thread_t cur_thr = rt_thread_self();
107     int jobctl_stopped;
108 
109     self_lwp = cur_thr->lwp;
110     RT_ASSERT(self_lwp);
111 
112     jobctl_stopped = self_lwp->jobctl_stopped;
113 
114     tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
115     MPASS(!tty_gone(tp));
116 
117     LWP_LOCK(self_lwp);
118     header_thr = rt_list_entry(self_lwp->t_grp.prev, struct rt_thread, sibling);
119     if (!jobctl_stopped && header_thr == cur_thr &&
120         cur_thr->sibling.prev == &self_lwp->t_grp)
121     {
122         /* update lwp status */
123         jobctl_stopped = self_lwp->jobctl_stopped = RT_TRUE;
124     }
125     LWP_UNLOCK(self_lwp);
126 
127     error = cv_wait(cv, tp->t_mtx);
128 
129     if (jobctl_stopped)
130     {
131         self_lwp->jobctl_stopped = RT_FALSE;
132     }
133 
134     /* Bail out when the device slipped away. */
135     if (tty_gone(tp))
136         return -ENXIO;
137 
138     /* Restart the system call when we may have been revoked. */
139     if (tp->t_revokecnt != revokecnt)
140         return -ERESTART;
141 
142     return error;
143 }
144 
145 /* process management */
lwp_tty_set_ctrl_proc(lwp_tty_t tp,rt_thread_t td)146 int lwp_tty_set_ctrl_proc(lwp_tty_t tp, rt_thread_t td)
147 {
148     int rc = -1;
149     struct rt_lwp *p = td->lwp;
150 
151     tty_unlock(tp);
152     LWP_LOCK(p);
153     tty_lock(tp);
154 
155     if (is_sess_leader(p))
156     {
157         if (_is_already_binding(tp, p))
158         {
159             rc = 0;
160         }
161         else if (_is_tty_or_sess_busy(tp, p))
162         {
163             rc = -EPERM;
164         }
165         else
166         {
167             /**
168              * Binding controlling process
169              * note: p->pgrp is protected by lwp lock;
170              *       pgrp->session is always constant.
171              */
172             tp->t_session = p->pgrp->session;
173             tp->t_session->ctty = tp;
174             tp->t_sessioncnt++;
175 
176             /* Assign foreground process group */
177             tp->t_pgrp = p->pgrp;
178             p->term_ctrlterm = RT_TRUE;
179 
180             LOG_D("%s(sid=%d)", __func__, tp->t_session->sid);
181             rc = 0;
182         }
183     }
184     else
185     {
186         rc = -EPERM;
187     }
188 
189     LWP_UNLOCK(p);
190 
191     return rc;
192 }
193 
lwp_tty_assign_foreground(lwp_tty_t tp,rt_thread_t td,int pgid)194 int lwp_tty_assign_foreground(lwp_tty_t tp, rt_thread_t td, int pgid)
195 {
196     struct rt_processgroup *pg;
197     rt_lwp_t cur_lwp = td->lwp;
198 
199     tty_unlock(tp);
200     pg = lwp_pgrp_find_and_inc_ref(pgid);
201     if (pg == NULL || cur_lwp == NULL)
202     {
203         tty_lock(tp);
204         return -EPERM;
205     }
206     else
207     {
208         PGRP_LOCK(pg);
209 
210         if (pg->sid != cur_lwp->sid)
211         {
212             PGRP_UNLOCK(pg);
213             lwp_pgrp_dec_ref(pg);
214             LOG_D("%s: NoPerm current process (pid=%d, pgid=%d, sid=%d), "
215                   "tagget group (pgid=%d, sid=%d)", __func__,
216                   cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid);
217             tty_lock(tp);
218             return -EPERM;
219         }
220     }
221     tty_lock(tp);
222 
223     /**
224      * Determine if this TTY is the controlling TTY after
225      * relocking the TTY.
226      */
227     if (!tty_is_ctty(tp, td->lwp))
228     {
229         PGRP_UNLOCK(pg);
230         LOG_D("%s: NoCTTY current process (pid=%d, pgid=%d, sid=%d), "
231               "tagget group (pgid=%d, sid=%d)", __func__,
232               cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid);
233         return -ENOTTY;
234     }
235     tp->t_pgrp = pg;
236     PGRP_UNLOCK(pg);
237     lwp_pgrp_dec_ref(pg);
238 
239     /* Wake up the background process groups. */
240     cv_broadcast(&tp->t_bgwait);
241 
242     LOG_D("%s: Foreground group %p (pgid=%d)", __func__, tp->t_pgrp,
243           tp->t_pgrp ? tp->t_pgrp->pgid : -1);
244 
245     return 0;
246 }
247 
248 /**
249  * Signalling processes.
250  */
251 
lwp_tty_signal_sessleader(struct lwp_tty * tp,int sig)252 void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig)
253 {
254     struct rt_lwp *p;
255     struct rt_session *s;
256 
257     tty_assert_locked(tp);
258     MPASS(sig >= 1 && sig < _LWP_NSIG);
259 
260     /* Make signals start output again. */
261     tp->t_flags &= ~TF_STOPPED;
262     tp->t_termios.c_lflag &= ~FLUSHO;
263 
264     /**
265      * Load s.leader exactly once to avoid race where s.leader is
266      * set to NULL by a concurrent invocation of killjobc() by the
267      * session leader.  Note that we are not holding t_session's
268      * lock for the read.
269      */
270     if ((s = tp->t_session) != NULL &&
271         (p = (void *)rt_atomic_load((rt_atomic_t *)&s->leader)) != NULL)
272     {
273         lwp_signal_kill(p, sig, SI_KERNEL, 0);
274     }
275 }
276 
lwp_tty_signal_pgrp(struct lwp_tty * tp,int sig)277 void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig)
278 {
279     tty_assert_locked(tp);
280     MPASS(sig >= 1 && sig < _LWP_NSIG);
281 
282     /* Make signals start output again. */
283     tp->t_flags &= ~TF_STOPPED;
284     tp->t_termios.c_lflag &= ~FLUSHO;
285 
286 #ifdef USING_BSD_SIGINFO
287     if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO))
288         tty_info(tp);
289 #endif /* USING_BSD_SIGINFO */
290 
291     if (tp->t_pgrp != NULL)
292     {
293         PGRP_LOCK(tp->t_pgrp);
294         lwp_pgrp_signal_kill(tp->t_pgrp, sig, SI_KERNEL, 0);
295         PGRP_UNLOCK(tp->t_pgrp);
296     }
297 }
298 
299 /* bsd_ttydev_methods.d_ioctl */
300 
_copy_to_user(void * to,void * from,size_t n)301 rt_inline size_t _copy_to_user(void *to, void *from, size_t n)
302 {
303     return lwp_put_to_user(to, from, n) == n ? 0 : -EFAULT;
304 }
305 
_copy_from_user(void * to,void * from,size_t n)306 rt_inline size_t _copy_from_user(void *to, void *from, size_t n)
307 {
308     return lwp_get_from_user(to, from, n) == n ? 0 : -EFAULT;
309 }
310 
termios_to_termio(struct termios * tios,struct termio * tio)311 static void termios_to_termio(struct termios *tios, struct termio *tio)
312 {
313     memset(tio, 0, sizeof(*tio));
314     tio->c_iflag = tios->c_iflag;
315     tio->c_oflag = tios->c_oflag;
316     tio->c_cflag = tios->c_cflag;
317     tio->c_lflag = tios->c_lflag;
318     tio->c_line = tios->c_line;
319     memcpy(tio->c_cc, tios->c_cc, NCC);
320 }
321 
termio_to_termios(struct termio * tio,struct termios * tios)322 static void termio_to_termios(struct termio *tio, struct termios *tios)
323 {
324     int i;
325 
326     tios->c_iflag = tio->c_iflag;
327     tios->c_oflag = tio->c_oflag;
328     tios->c_cflag = tio->c_cflag;
329     tios->c_lflag = tio->c_lflag;
330     for (i = NCC; i < NCCS; i++)
331         tios->c_cc[i] = _POSIX_VDISABLE;
332     memcpy(tios->c_cc, tio->c_cc, NCC);
333 }
334 
335 #define IOCTL(cmd, data, fflags, td) \
336     bsd_ttydev_methods.d_ioctl(tp, cmd, data, fflags, td)
337 
lwp_tty_ioctl_adapter(lwp_tty_t tp,int cmd,int oflags,void * args,rt_thread_t td)338 int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thread_t td)
339 {
340     long fflags = FFLAGS(oflags);
341     struct termios tios;
342     struct termio tio;
343     int error;
344 
345     LOG_D("%s(cmd=0x%x, args=%p)", __func__, cmd, args);
346     switch (cmd & 0xffff)
347     {
348         case TCGETS:
349             error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td);
350             if (error)
351                 break;
352             cfsetospeed(&tios, tios.__c_ispeed);
353             error = _copy_to_user(args, &tios, sizeof(tios));
354             break;
355 
356         case TCSETS:
357             error = _copy_from_user(&tios, args, sizeof(tios));
358             if (error)
359                 break;
360             tios.__c_ispeed = tios.__c_ospeed = cfgetospeed(&tios);
361             error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td));
362             break;
363 
364         case TCSETSW:
365             error = _copy_from_user(&tios, args, sizeof(tios));
366             if (error)
367                 break;
368             error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td));
369             break;
370 
371         case TCSETSF:
372             error = _copy_from_user(&tios, args, sizeof(tios));
373             if (error)
374                 break;
375             error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td));
376             break;
377 
378         case TCGETA:
379             error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td);
380             if (error)
381                 break;
382             termios_to_termio(&tios, &tio);
383             error = _copy_to_user((void *)args, &tio, sizeof(tio));
384             break;
385 
386         case TCSETA:
387             error = _copy_from_user(&tio, (void *)args, sizeof(tio));
388             if (error)
389                 break;
390             termio_to_termios(&tio, &tios);
391             error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td));
392             break;
393 
394         case TCSETAW:
395             error = _copy_from_user(&tio, (void *)args, sizeof(tio));
396             if (error)
397                 break;
398             termio_to_termios(&tio, &tios);
399             error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td));
400             break;
401 
402         case TCSETAF:
403             error = _copy_from_user(&tio, (void *)args, sizeof(tio));
404             if (error)
405                 break;
406             termio_to_termios(&tio, &tios);
407             error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td));
408             break;
409 
410         case TCSBRK:
411             if (args != 0)
412             {
413                 /**
414                  * Linux manual: SVr4, UnixWare, Solaris, and Linux treat
415                  * tcsendbreak(fd,arg) with nonzero arg like tcdrain(fd).
416                  */
417                 error = IOCTL(TIOCDRAIN, (rt_caddr_t)&tios, fflags, td);
418             }
419             else
420             {
421                 /**
422                  * Linux manual: If the terminal is using asynchronous serial
423                  * data transmission, and arg is zero, then send a break (a
424                  * stream of zero bits) for between 0.25 and 0.5 seconds.
425                  */
426                 LOG_D("%s: ioctl TCSBRK arg 0 not implemented", __func__);
427                 error = -ENOSYS;
428             }
429             break;
430 
431 #ifdef USING_BSD_IOCTL_EXT
432         /* Software flow control */
433         case TCXONC: {
434             switch (args->arg)
435             {
436                 case TCOOFF:
437                     args->cmd = TIOCSTOP;
438                     break;
439                 case TCOON:
440                     args->cmd = TIOCSTART;
441                     break;
442                 case TCIOFF:
443                 case TCION: {
444                     int c;
445                     struct write_args wr;
446                     error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags,
447                                      td);
448                     if (error)
449                         break;
450                     fdrop(fp, td);
451                     c = (args->arg == TCIOFF) ? VSTOP : VSTART;
452                     c = tios.c_cc[c];
453                     if (c != _POSIX_VDISABLE)
454                     {
455                         wr.fd = args->fd;
456                         wr.buf = &c;
457                         wr.nbyte = sizeof(c);
458                         return (sys_write(td, &wr));
459                     }
460                     else
461                         return 0;
462                 }
463                 default:
464                     fdrop(fp, td);
465                     return -EINVAL;
466             }
467             args->arg = 0;
468             error = (sys_ioctl(td, (struct ioctl_args *)args));
469             break;
470         }
471 #endif /* USING_BSD_IOCTL_EXT */
472         case TCFLSH: {
473             int val;
474             error = 0;
475             switch ((rt_base_t)args)
476             {
477                 case TCIFLUSH:
478                     val = FREAD;
479                     break;
480                 case TCOFLUSH:
481                     val = FWRITE;
482                     break;
483                 case TCIOFLUSH:
484                     val = FREAD | FWRITE;
485                     break;
486                 default:
487                     error = -EINVAL;
488                     break;
489             }
490             if (!error)
491                 error = (IOCTL(TIOCFLUSH, (rt_caddr_t)&val, fflags, td));
492             break;
493         }
494 
495 #ifdef USING_BSD_IOCTL_EXT
496         case TIOCEXCL:
497             args->cmd = TIOCEXCL;
498             error = (sys_ioctl(td, (struct ioctl_args *)args));
499             break;
500 
501         case TIOCNXCL:
502             args->cmd = TIOCNXCL;
503             error = (sys_ioctl(td, (struct ioctl_args *)args));
504             break;
505 #endif /* USING_BSD_IOCTL_EXT */
506 
507         /* Controlling terminal */
508         case TIOCSCTTY:
509         case TIOCNOTTY:
510 
511         /* Process group and session ID */
512         case TIOCGPGRP:
513         case TIOCSPGRP:
514         case TIOCGSID:
515 
516             /* TIOCOUTQ */
517             /* TIOCSTI */
518         case TIOCGWINSZ:
519         case TIOCSWINSZ:
520             error = IOCTL(cmd, (rt_caddr_t)args, fflags, td);
521             break;
522 #ifdef USING_BSD_IOCTL_EXT
523         case TIOCMGET:
524             args->cmd = TIOCMGET;
525             error = (sys_ioctl(td, (struct ioctl_args *)args));
526             break;
527 
528         case TIOCMBIS:
529             args->cmd = TIOCMBIS;
530             error = (sys_ioctl(td, (struct ioctl_args *)args));
531             break;
532 
533         case TIOCMBIC:
534             args->cmd = TIOCMBIC;
535             error = (sys_ioctl(td, (struct ioctl_args *)args));
536             break;
537 
538         case TIOCMSET:
539             args->cmd = TIOCMSET;
540             error = (sys_ioctl(td, (struct ioctl_args *)args));
541             break;
542 #endif /* USING_BSD_IOCTL_EXT */
543             /* TIOCGSOFTCAR */
544             /* TIOCSSOFTCAR */
545 
546         case FIONREAD: /* TIOCINQ */
547             error = (IOCTL(FIONREAD, args, fflags, td));
548             break;
549 
550 #ifdef USING_BSD_IOCTL_EXT
551             /* TIOCLINUX */
552 
553         case TIOCCONS:
554             args->cmd = TIOCCONS;
555             error = (sys_ioctl(td, (struct ioctl_args *)args));
556             break;
557 
558         case TIOCGSERIAL: {
559             struct linux_serial_struct lss;
560 
561             bzero(&lss, sizeof(lss));
562             lss.type = PORT_16550A;
563             lss.flags = 0;
564             lss.close_delay = 0;
565             error = copyout(&lss, (void *)args->arg, sizeof(lss));
566             break;
567         }
568 
569         case TIOCSSERIAL: {
570             struct linux_serial_struct lss;
571             error = copyin((void *)args->arg, &lss, sizeof(lss));
572             if (error)
573                 break;
574             /* XXX - It really helps to have an implementation that
575              * does nothing. NOT!
576              */
577             error = 0;
578             break;
579         }
580 
581         case TIOCPKT:
582             args->cmd = TIOCPKT;
583             error = (sys_ioctl(td, (struct ioctl_args *)args));
584             break;
585 
586         case FIONBIO:
587             args->cmd = FIONBIO;
588             error = (sys_ioctl(td, (struct ioctl_args *)args));
589             break;
590 
591         case TIOCSETD: {
592             int line;
593             switch (args->arg)
594             {
595                 case N_TTY:
596                     line = TTYDISC;
597                     break;
598                 case N_SLIP:
599                     line = SLIPDISC;
600                     break;
601                 case N_PPP:
602                     line = PPPDISC;
603                     break;
604                 default:
605                     fdrop(fp, td);
606                     return -EINVAL;
607             }
608             error = (ioctl_emit(TIOCSETD, (rt_caddr_t)&line, fflags, td));
609             break;
610         }
611 
612         case TIOCGETD: {
613             int linux_line;
614             int bsd_line = TTYDISC;
615             error =
616                 ioctl_emit(TIOCGETD, (rt_caddr_t)&bsd_line, fflags, td);
617             if (error)
618                 break;
619             switch (bsd_line)
620             {
621                 case TTYDISC:
622                     linux_line = N_TTY;
623                     break;
624                 case SLIPDISC:
625                     linux_line = N_SLIP;
626                     break;
627                 case PPPDISC:
628                     linux_line = N_PPP;
629                     break;
630                 default:
631                     fdrop(fp, td);
632                     return -EINVAL;
633             }
634             error = (copyout(&linux_line, (void *)args->arg, sizeof(int)));
635             break;
636         }
637 
638             /* TCSBRKP */
639             /* TIOCTTYGSTRUCT */
640 
641         case FIONCLEX:
642             args->cmd = FIONCLEX;
643             error = (sys_ioctl(td, (struct ioctl_args *)args));
644             break;
645 
646         case FIOCLEX:
647             args->cmd = FIOCLEX;
648             error = (sys_ioctl(td, (struct ioctl_args *)args));
649             break;
650 
651         case FIOASYNC:
652             args->cmd = FIOASYNC;
653             error = (sys_ioctl(td, (struct ioctl_args *)args));
654             break;
655 
656             /* TIOCSERCONFIG */
657             /* TIOCSERGWILD */
658             /* TIOCSERSWILD */
659             /* TIOCGLCKTRMIOS */
660             /* TIOCSLCKTRMIOS */
661 
662         case TIOCSBRK:
663             args->cmd = TIOCSBRK;
664             error = (sys_ioctl(td, (struct ioctl_args *)args));
665             break;
666 
667         case TIOCCBRK:
668             args->cmd = TIOCCBRK;
669             error = (sys_ioctl(td, (struct ioctl_args *)args));
670             break;
671         case TIOCGPTN: {
672             int nb;
673 
674             error = ioctl_emit(TIOCGPTN, (rt_caddr_t)&nb, fflags, td);
675             if (!error)
676                 error = copyout(&nb, (void *)args->arg, sizeof(int));
677             break;
678         }
679         case TIOCGPTPEER:
680             linux_msg(td, "unsupported ioctl TIOCGPTPEER");
681             error = -ENOIOCTL;
682             break;
683         case TIOCSPTLCK:
684             /*
685              * Our unlockpt() does nothing. Check that fd refers
686              * to a pseudo-terminal master device.
687              */
688             args->cmd = TIOCPTMASTER;
689             error = (sys_ioctl(td, (struct ioctl_args *)args));
690             break;
691 #endif /* USING_BSD_IOCTL_EXT */
692 
693         /**
694          * those are for current implementation of devfs, and we dont want to
695          * log them
696          */
697         case F_DUPFD:
698         case F_DUPFD_CLOEXEC:
699         case F_GETFD:
700         case F_SETFD:
701         case F_GETFL:
702         case F_SETFL:
703             /* fall back to fs */
704             error = -ENOIOCTL;
705             break;
706         default:
707             LOG_I("%s: unhandle commands 0x%x", __func__, cmd);
708             error = -ENOSYS;
709             break;
710     }
711 
712     return (error);
713 }
714