123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * (tty_compat.c)
- * The compatible layer which interacts with process management core (lwp)
- *
- * Change Logs:
- * Date Author Notes
- * 2023-11-13 Shell init ver.
- */
- #define DBG_TAG "lwp.tty"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include "../tty_config.h"
- #include "../tty_internal.h"
- #include "../terminal.h"
- /*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 1994-1995 Søren Schmidt
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
- /* is the tty and session leader already binding ? */
- static rt_bool_t _is_already_binding(lwp_tty_t tp, rt_lwp_t p)
- {
- rt_bool_t rc;
- rt_processgroup_t pgrp = p->pgrp;
- /* lwp is already locked */
- RT_ASSERT(pgrp);
- /* Note: pgrp->session is constant after process group is created */
- if (tp->t_session && tp->t_session == pgrp->session)
- {
- rc = RT_TRUE;
- }
- else
- {
- rc = RT_FALSE;
- }
- return rc;
- }
- static rt_bool_t _is_tty_or_sess_busy(lwp_tty_t tp, rt_lwp_t p)
- {
- rt_bool_t rc;
- rt_session_t sess = p->pgrp->session;
- SESS_LOCK(sess);
- if (sess->ctty)
- {
- rc = RT_TRUE;
- }
- else if (tp->t_session)
- {
- /**
- * TODO: allow TTY stolen if the sess leader is killed while resource
- * had not been collected
- */
- if (tp->t_session->leader == RT_NULL)
- rc = RT_FALSE;
- else
- rc = RT_TRUE;
- }
- else
- {
- rc = RT_FALSE;
- }
- SESS_UNLOCK(sess);
- return rc;
- }
- int lwp_tty_bg_stop(struct lwp_tty *tp, struct rt_condvar *cv)
- {
- int error;
- int revokecnt = tp->t_revokecnt;
- rt_lwp_t self_lwp;
- rt_thread_t header_thr;
- rt_thread_t cur_thr = rt_thread_self();
- int jobctl_stopped;
- self_lwp = cur_thr->lwp;
- RT_ASSERT(self_lwp);
- jobctl_stopped = self_lwp->jobctl_stopped;
- tty_lock_assert(tp, MA_OWNED | MA_NOTRECURSED);
- MPASS(!tty_gone(tp));
- LWP_LOCK(self_lwp);
- header_thr = rt_list_entry(self_lwp->t_grp.prev, struct rt_thread, sibling);
- if (!jobctl_stopped && header_thr == cur_thr &&
- cur_thr->sibling.prev == &self_lwp->t_grp)
- {
- /* update lwp status */
- jobctl_stopped = self_lwp->jobctl_stopped = RT_TRUE;
- }
- LWP_UNLOCK(self_lwp);
- error = cv_wait(cv, tp->t_mtx);
- if (jobctl_stopped)
- {
- self_lwp->jobctl_stopped = RT_FALSE;
- }
- /* Bail out when the device slipped away. */
- if (tty_gone(tp))
- return -ENXIO;
- /* Restart the system call when we may have been revoked. */
- if (tp->t_revokecnt != revokecnt)
- return -ERESTART;
- return error;
- }
- /* process management */
- int lwp_tty_set_ctrl_proc(lwp_tty_t tp, rt_thread_t td)
- {
- int rc = -1;
- struct rt_lwp *p = td->lwp;
- tty_unlock(tp);
- LWP_LOCK(p);
- tty_lock(tp);
- if (is_sess_leader(p))
- {
- if (_is_already_binding(tp, p))
- {
- rc = 0;
- }
- else if (_is_tty_or_sess_busy(tp, p))
- {
- rc = -EPERM;
- }
- else
- {
- /**
- * Binding controlling process
- * note: p->pgrp is protected by lwp lock;
- * pgrp->session is always constant.
- */
- tp->t_session = p->pgrp->session;
- tp->t_session->ctty = tp;
- tp->t_sessioncnt++;
- /* Assign foreground process group */
- tp->t_pgrp = p->pgrp;
- p->term_ctrlterm = RT_TRUE;
- LOG_D("%s(sid=%d)", __func__, tp->t_session->sid);
- rc = 0;
- }
- }
- else
- {
- rc = -EPERM;
- }
- LWP_UNLOCK(p);
- return rc;
- }
- int lwp_tty_assign_foreground(lwp_tty_t tp, rt_thread_t td, int pgid)
- {
- struct rt_processgroup *pg;
- rt_lwp_t cur_lwp = td->lwp;
- tty_unlock(tp);
- pg = lwp_pgrp_find_and_inc_ref(pgid);
- if (pg == NULL || cur_lwp == NULL)
- {
- tty_lock(tp);
- return -EPERM;
- }
- else
- {
- PGRP_LOCK(pg);
- if (pg->sid != cur_lwp->sid)
- {
- PGRP_UNLOCK(pg);
- lwp_pgrp_dec_ref(pg);
- LOG_D("%s: NoPerm current process (pid=%d, pgid=%d, sid=%d), "
- "tagget group (pgid=%d, sid=%d)", __func__,
- cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid);
- tty_lock(tp);
- return -EPERM;
- }
- }
- tty_lock(tp);
- /**
- * Determine if this TTY is the controlling TTY after
- * relocking the TTY.
- */
- if (!tty_is_ctty(tp, td->lwp))
- {
- PGRP_UNLOCK(pg);
- LOG_D("%s: NoCTTY current process (pid=%d, pgid=%d, sid=%d), "
- "tagget group (pgid=%d, sid=%d)", __func__,
- cur_lwp->pid, cur_lwp->pgid, cur_lwp->sid, pgid, pg->sid);
- return -ENOTTY;
- }
- tp->t_pgrp = pg;
- PGRP_UNLOCK(pg);
- lwp_pgrp_dec_ref(pg);
- /* Wake up the background process groups. */
- cv_broadcast(&tp->t_bgwait);
- LOG_D("%s: Foreground group %p (pgid=%d)", __func__, tp->t_pgrp,
- tp->t_pgrp ? tp->t_pgrp->pgid : -1);
- return 0;
- }
- /**
- * Signalling processes.
- */
- void lwp_tty_signal_sessleader(struct lwp_tty *tp, int sig)
- {
- struct rt_lwp *p;
- struct rt_session *s;
- tty_assert_locked(tp);
- MPASS(sig >= 1 && sig < _LWP_NSIG);
- /* Make signals start output again. */
- tp->t_flags &= ~TF_STOPPED;
- tp->t_termios.c_lflag &= ~FLUSHO;
- /**
- * Load s.leader exactly once to avoid race where s.leader is
- * set to NULL by a concurrent invocation of killjobc() by the
- * session leader. Note that we are not holding t_session's
- * lock for the read.
- */
- if ((s = tp->t_session) != NULL &&
- (p = (void *)rt_atomic_load((rt_atomic_t *)&s->leader)) != NULL)
- {
- lwp_signal_kill(p, sig, SI_KERNEL, 0);
- }
- }
- void lwp_tty_signal_pgrp(struct lwp_tty *tp, int sig)
- {
- tty_assert_locked(tp);
- MPASS(sig >= 1 && sig < _LWP_NSIG);
- /* Make signals start output again. */
- tp->t_flags &= ~TF_STOPPED;
- tp->t_termios.c_lflag &= ~FLUSHO;
- #ifdef USING_BSD_SIGINFO
- if (sig == SIGINFO && !(tp->t_termios.c_lflag & NOKERNINFO))
- tty_info(tp);
- #endif /* USING_BSD_SIGINFO */
- if (tp->t_pgrp != NULL)
- {
- PGRP_LOCK(tp->t_pgrp);
- lwp_pgrp_signal_kill(tp->t_pgrp, sig, SI_KERNEL, 0);
- PGRP_UNLOCK(tp->t_pgrp);
- }
- }
- /* bsd_ttydev_methods.d_ioctl */
- rt_inline size_t _copy_to_user(void *to, void *from, size_t n)
- {
- return lwp_put_to_user(to, from, n) == n ? 0 : -EFAULT;
- }
- rt_inline size_t _copy_from_user(void *to, void *from, size_t n)
- {
- return lwp_get_from_user(to, from, n) == n ? 0 : -EFAULT;
- }
- static void termios_to_termio(struct termios *tios, struct termio *tio)
- {
- memset(tio, 0, sizeof(*tio));
- tio->c_iflag = tios->c_iflag;
- tio->c_oflag = tios->c_oflag;
- tio->c_cflag = tios->c_cflag;
- tio->c_lflag = tios->c_lflag;
- tio->c_line = tios->c_line;
- memcpy(tio->c_cc, tios->c_cc, NCC);
- }
- static void termio_to_termios(struct termio *tio, struct termios *tios)
- {
- int i;
- tios->c_iflag = tio->c_iflag;
- tios->c_oflag = tio->c_oflag;
- tios->c_cflag = tio->c_cflag;
- tios->c_lflag = tio->c_lflag;
- for (i = NCC; i < NCCS; i++)
- tios->c_cc[i] = _POSIX_VDISABLE;
- memcpy(tios->c_cc, tio->c_cc, NCC);
- }
- #define IOCTL(cmd, data, fflags, td) \
- bsd_ttydev_methods.d_ioctl(tp, cmd, data, fflags, td)
- int lwp_tty_ioctl_adapter(lwp_tty_t tp, int cmd, int oflags, void *args, rt_thread_t td)
- {
- long fflags = FFLAGS(oflags);
- struct termios tios;
- struct termio tio;
- int error;
- LOG_D("%s(cmd=0x%x, args=%p)", __func__, cmd, args);
- switch (cmd & 0xffff)
- {
- case TCGETS:
- error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td);
- if (error)
- break;
- error = _copy_to_user(args, &tios, sizeof(tios));
- break;
- case TCSETS:
- error = _copy_from_user(&tios, args, sizeof(tios));
- if (error)
- break;
- error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td));
- break;
- case TCSETSW:
- error = _copy_from_user(&tios, args, sizeof(tios));
- if (error)
- break;
- error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td));
- break;
- case TCSETSF:
- error = _copy_from_user(&tios, args, sizeof(tios));
- if (error)
- break;
- error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td));
- break;
- case TCGETA:
- error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags, td);
- if (error)
- break;
- termios_to_termio(&tios, &tio);
- error = _copy_to_user((void *)args, &tio, sizeof(tio));
- break;
- case TCSETA:
- error = _copy_from_user(&tio, (void *)args, sizeof(tio));
- if (error)
- break;
- termio_to_termios(&tio, &tios);
- error = (IOCTL(TIOCSETA, (rt_caddr_t)&tios, fflags, td));
- break;
- case TCSETAW:
- error = _copy_from_user(&tio, (void *)args, sizeof(tio));
- if (error)
- break;
- termio_to_termios(&tio, &tios);
- error = (IOCTL(TIOCSETAW, (rt_caddr_t)&tios, fflags, td));
- break;
- case TCSETAF:
- error = _copy_from_user(&tio, (void *)args, sizeof(tio));
- if (error)
- break;
- termio_to_termios(&tio, &tios);
- error = (IOCTL(TIOCSETAF, (rt_caddr_t)&tios, fflags, td));
- break;
- case TCSBRK:
- if (args != 0)
- {
- /**
- * Linux manual: SVr4, UnixWare, Solaris, and Linux treat
- * tcsendbreak(fd,arg) with nonzero arg like tcdrain(fd).
- */
- error = IOCTL(TIOCDRAIN, (rt_caddr_t)&tios, fflags, td);
- }
- else
- {
- /**
- * Linux manual: If the terminal is using asynchronous serial
- * data transmission, and arg is zero, then send a break (a
- * stream of zero bits) for between 0.25 and 0.5 seconds.
- */
- LOG_D("%s: ioctl TCSBRK arg 0 not implemented", __func__);
- error = -ENOSYS;
- }
- break;
- #ifdef USING_BSD_IOCTL_EXT
- /* Software flow control */
- case TCXONC: {
- switch (args->arg)
- {
- case TCOOFF:
- args->cmd = TIOCSTOP;
- break;
- case TCOON:
- args->cmd = TIOCSTART;
- break;
- case TCIOFF:
- case TCION: {
- int c;
- struct write_args wr;
- error = IOCTL(TIOCGETA, (rt_caddr_t)&tios, fflags,
- td);
- if (error)
- break;
- fdrop(fp, td);
- c = (args->arg == TCIOFF) ? VSTOP : VSTART;
- c = tios.c_cc[c];
- if (c != _POSIX_VDISABLE)
- {
- wr.fd = args->fd;
- wr.buf = &c;
- wr.nbyte = sizeof(c);
- return (sys_write(td, &wr));
- }
- else
- return 0;
- }
- default:
- fdrop(fp, td);
- return -EINVAL;
- }
- args->arg = 0;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- }
- #endif /* USING_BSD_IOCTL_EXT */
- case TCFLSH: {
- int val;
- error = 0;
- switch ((rt_base_t)args)
- {
- case TCIFLUSH:
- val = FREAD;
- break;
- case TCOFLUSH:
- val = FWRITE;
- break;
- case TCIOFLUSH:
- val = FREAD | FWRITE;
- break;
- default:
- error = -EINVAL;
- break;
- }
- if (!error)
- error = (IOCTL(TIOCFLUSH, (rt_caddr_t)&val, fflags, td));
- break;
- }
- #ifdef USING_BSD_IOCTL_EXT
- case TIOCEXCL:
- args->cmd = TIOCEXCL;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case TIOCNXCL:
- args->cmd = TIOCNXCL;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- #endif /* USING_BSD_IOCTL_EXT */
- /* Controlling terminal */
- case TIOCSCTTY:
- case TIOCNOTTY:
- /* Process group and session ID */
- case TIOCGPGRP:
- case TIOCSPGRP:
- case TIOCGSID:
- /* TIOCOUTQ */
- /* TIOCSTI */
- case TIOCGWINSZ:
- case TIOCSWINSZ:
- error = IOCTL(cmd, (rt_caddr_t)args, fflags, td);
- break;
- #ifdef USING_BSD_IOCTL_EXT
- case TIOCMGET:
- args->cmd = TIOCMGET;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case TIOCMBIS:
- args->cmd = TIOCMBIS;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case TIOCMBIC:
- args->cmd = TIOCMBIC;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case TIOCMSET:
- args->cmd = TIOCMSET;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- /* TIOCGSOFTCAR */
- /* TIOCSSOFTCAR */
- case FIONREAD: /* TIOCINQ */
- args->cmd = FIONREAD;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- /* TIOCLINUX */
- case TIOCCONS:
- args->cmd = TIOCCONS;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case TIOCGSERIAL: {
- struct linux_serial_struct lss;
- bzero(&lss, sizeof(lss));
- lss.type = PORT_16550A;
- lss.flags = 0;
- lss.close_delay = 0;
- error = copyout(&lss, (void *)args->arg, sizeof(lss));
- break;
- }
- case TIOCSSERIAL: {
- struct linux_serial_struct lss;
- error = copyin((void *)args->arg, &lss, sizeof(lss));
- if (error)
- break;
- /* XXX - It really helps to have an implementation that
- * does nothing. NOT!
- */
- error = 0;
- break;
- }
- case TIOCPKT:
- args->cmd = TIOCPKT;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case FIONBIO:
- args->cmd = FIONBIO;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case TIOCSETD: {
- int line;
- switch (args->arg)
- {
- case N_TTY:
- line = TTYDISC;
- break;
- case N_SLIP:
- line = SLIPDISC;
- break;
- case N_PPP:
- line = PPPDISC;
- break;
- default:
- fdrop(fp, td);
- return -EINVAL;
- }
- error = (ioctl_emit(TIOCSETD, (rt_caddr_t)&line, fflags, td));
- break;
- }
- case TIOCGETD: {
- int linux_line;
- int bsd_line = TTYDISC;
- error =
- ioctl_emit(TIOCGETD, (rt_caddr_t)&bsd_line, fflags, td);
- if (error)
- break;
- switch (bsd_line)
- {
- case TTYDISC:
- linux_line = N_TTY;
- break;
- case SLIPDISC:
- linux_line = N_SLIP;
- break;
- case PPPDISC:
- linux_line = N_PPP;
- break;
- default:
- fdrop(fp, td);
- return -EINVAL;
- }
- error = (copyout(&linux_line, (void *)args->arg, sizeof(int)));
- break;
- }
- /* TCSBRKP */
- /* TIOCTTYGSTRUCT */
- case FIONCLEX:
- args->cmd = FIONCLEX;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case FIOCLEX:
- args->cmd = FIOCLEX;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case FIOASYNC:
- args->cmd = FIOASYNC;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- /* TIOCSERCONFIG */
- /* TIOCSERGWILD */
- /* TIOCSERSWILD */
- /* TIOCGLCKTRMIOS */
- /* TIOCSLCKTRMIOS */
- case TIOCSBRK:
- args->cmd = TIOCSBRK;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case TIOCCBRK:
- args->cmd = TIOCCBRK;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- case TIOCGPTN: {
- int nb;
- error = ioctl_emit(TIOCGPTN, (rt_caddr_t)&nb, fflags, td);
- if (!error)
- error = copyout(&nb, (void *)args->arg, sizeof(int));
- break;
- }
- case TIOCGPTPEER:
- linux_msg(td, "unsupported ioctl TIOCGPTPEER");
- error = -ENOIOCTL;
- break;
- case TIOCSPTLCK:
- /*
- * Our unlockpt() does nothing. Check that fd refers
- * to a pseudo-terminal master device.
- */
- args->cmd = TIOCPTMASTER;
- error = (sys_ioctl(td, (struct ioctl_args *)args));
- break;
- #endif /* USING_BSD_IOCTL_EXT */
- /**
- * those are for current implementation of devfs, and we dont want to
- * log them
- */
- case F_DUPFD:
- case F_DUPFD_CLOEXEC:
- case F_GETFD:
- case F_SETFD:
- case F_GETFL:
- case F_SETFL:
- /* fall back to fs */
- error = -ENOIOCTL;
- break;
- default:
- LOG_I("%s: unhandle commands 0x%x", __func__, cmd);
- error = -ENOSYS;
- break;
- }
- return (error);
- }
|