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