Kernel/LibC: Replace terminal syscalls with ioctls

isatty, tc{get,set}attr, tc{get,set}pgrp are now implemented as ioctls
instead of separate syscalls
This commit is contained in:
2026-05-15 16:38:36 +03:00
parent fe2c9f7d2d
commit 05c9f0640c
9 changed files with 114 additions and 160 deletions

View File

@@ -56,6 +56,7 @@ namespace Kernel
pid_t pid() const { return m_pid; }
bool is_session_leader() const { return pid() == sid(); }
bool is_pgrpg_in_this_session(pid_t) const;
const char* name() const { return m_cmdline.empty() ? "<unknown>" : m_cmdline.front().data(); }
@@ -63,9 +64,6 @@ namespace Kernel
BAN::ErrorOr<long> sys_exit(int status);
BAN::ErrorOr<long> sys_tcgetattr(int fildes, termios*);
BAN::ErrorOr<long> sys_tcsetattr(int fildes, int optional_actions, const termios*);
BAN::ErrorOr<long> sys_fork(uintptr_t rsp, uintptr_t rip);
BAN::ErrorOr<long> sys_exec(const char* path, const char* const* argv, const char* const* envp);
@@ -186,7 +184,6 @@ namespace Kernel
BAN::ErrorOr<long> sys_smo_map(SharedMemoryObjectManager::Key);
BAN::ErrorOr<long> sys_ttyname(int fildes, char* name, size_t namesize);
BAN::ErrorOr<long> sys_isatty(int fildes);
BAN::ErrorOr<long> sys_posix_openpt(int flags);
BAN::ErrorOr<long> sys_ptsname(int fildes, char* buffer, size_t buffer_len);
@@ -218,9 +215,6 @@ namespace Kernel
BAN::ErrorOr<long> sys_pthread_self();
BAN::ErrorOr<long> sys_pthread_kill(pthread_t thread, int signal);
BAN::ErrorOr<long> sys_tcgetpgrp(int fd);
BAN::ErrorOr<long> sys_tcsetpgrp(int fd, pid_t pgid);
BAN::ErrorOr<long> sys_clock_gettime(clockid_t, timespec*);
BAN::ErrorOr<long> sys_load_keymap(const char* path);

View File

@@ -33,9 +33,6 @@ namespace Kernel
public:
virtual BAN::ErrorOr<void> set_font(LibFont::Font&&) { return BAN::Error::from_errno(EINVAL); }
void set_foreground_pgrp(pid_t pgrp) { m_foreground_pgrp = pgrp; }
pid_t foreground_pgrp() const { return m_foreground_pgrp; }
BAN::ErrorOr<void> tty_ctrl(int command, int flags);
// for kprint
@@ -52,9 +49,6 @@ namespace Kernel
void on_key_event(LibInput::KeyEvent);
void handle_input_byte(uint8_t);
void get_termios(termios*);
BAN::ErrorOr<void> set_termios(const termios*);
virtual bool is_tty() const override { return true; }
virtual dev_t rdev() const final override { return m_rdev; }
@@ -87,10 +81,12 @@ namespace Kernel
bool putchar(uint8_t ch);
void do_backspace();
termios get_termios();
private:
const dev_t m_rdev;
pid_t m_foreground_pgrp { 0 };
BAN::Atomic<pid_t> m_foreground_pgrp { 0 };
struct tty_ctrl_t
{

View File

@@ -226,6 +226,23 @@ namespace Kernel
ASSERT(!m_page_table);
}
bool Process::is_pgrpg_in_this_session(pid_t pgrp) const
{
bool valid_pgrp = false;
for_each_process(
[&](Process& process)
{
if (process.sid() == sid() && process.pgrp() == pgrp)
{
valid_pgrp = true;
return BAN::Iteration::Break;
}
return BAN::Iteration::Continue;
}
);
return valid_pgrp;
}
void Process::add_thread(Thread* thread)
{
LockGuard _(m_process_lock);
@@ -624,40 +641,6 @@ namespace Kernel
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<long> Process::sys_tcgetattr(int fildes, termios* user_termios)
{
auto inode = TRY(m_open_file_descriptors.inode_of(fildes));
if (!inode->is_tty())
return BAN::Error::from_errno(ENOTTY);
struct termios termios;
static_cast<TTY*>(inode.ptr())->get_termios(&termios);
TRY(write_to_user(user_termios, &termios, sizeof(struct termios)));
return 0;
}
BAN::ErrorOr<long> Process::sys_tcsetattr(int fildes, int optional_actions, const termios* user_termios)
{
//if (optional_actions != TCSANOW)
// return BAN::Error::from_errno(EINVAL);
(void)optional_actions;
auto inode = TRY(m_open_file_descriptors.inode_of(fildes));
if (!inode->is_tty())
return BAN::Error::from_errno(ENOTTY);
termios termios;
TRY(read_from_user(user_termios, &termios, sizeof(struct termios)));
TRY(static_cast<TTY*>(inode.ptr())->set_termios(&termios));
// FIXME: SIGTTOU
return 0;
}
BAN::ErrorOr<long> Process::sys_fork(uintptr_t sp, uintptr_t ip)
{
auto page_table = BAN::UniqPtr<PageTable>::adopt(TRY(PageTable::create_userspace()));
@@ -2764,15 +2747,6 @@ namespace Kernel
return 0;
}
BAN::ErrorOr<long> Process::sys_isatty(int fildes)
{
LockGuard _(m_process_lock);
auto inode = TRY(m_open_file_descriptors.inode_of(fildes));
if (!inode->is_tty())
return BAN::Error::from_errno(ENOTTY);
return 0;
}
BAN::ErrorOr<long> Process::sys_posix_openpt(int flags)
{
if (flags & ~(O_RDWR | O_NOCTTY))
@@ -3449,64 +3423,6 @@ namespace Kernel
return BAN::Error::from_errno(ESRCH);
}
BAN::ErrorOr<long> Process::sys_tcgetpgrp(int fd)
{
LockGuard _(m_process_lock);
auto inode = TRY(m_open_file_descriptors.inode_of(fd));
// NOTE: This is a hack but I'm not sure how terminal is supposted to get
// slave's foreground pgroup while not having controlling terminal
if (TRY(m_open_file_descriptors.path_of(fd)) == "<ptmx>"_sv)
return TRY(static_cast<PseudoTerminalMaster*>(inode.ptr())->slave())->foreground_pgrp();
if (!m_controlling_terminal)
return BAN::Error::from_errno(ENOTTY);
if (!inode->is_tty())
return BAN::Error::from_errno(ENOTTY);
auto* tty = static_cast<TTY*>(inode.ptr());
if (tty != m_controlling_terminal.ptr())
return BAN::Error::from_errno(ENOTTY);
return tty->foreground_pgrp();
}
BAN::ErrorOr<long> Process::sys_tcsetpgrp(int fd, pid_t pgrp)
{
LockGuard _(m_process_lock);
if (!m_controlling_terminal)
return BAN::Error::from_errno(ENOTTY);
bool valid_pgrp = false;
for_each_process(
[&](Process& process)
{
if (process.sid() == sid() && process.pgrp() == pgrp)
{
valid_pgrp = true;
return BAN::Iteration::Break;
}
return BAN::Iteration::Continue;
}
);
if (!valid_pgrp)
return BAN::Error::from_errno(EPERM);
auto inode = TRY(m_open_file_descriptors.inode_of(fd));
if (!inode->is_tty())
return BAN::Error::from_errno(ENOTTY);
auto* tty = static_cast<TTY*>(inode.ptr());
if (tty != m_controlling_terminal.ptr())
return BAN::Error::from_errno(ENOTTY);
tty->set_foreground_pgrp(pgrp);
return 0;
}
BAN::ErrorOr<long> Process::sys_setuid(uid_t uid)
{
if (uid < 0 || uid >= 1'000'000'000)

View File

@@ -173,7 +173,7 @@ namespace Kernel
return BAN::Error::from_errno(ENODEV);
}
return BAN::Error::from_errno(ENOTSUP);
return CharacterDevice::ioctl(request, argument);
}
PseudoTerminalSlave::PseudoTerminalSlave(BAN::String&& name, uint32_t number, mode_t mode, uid_t uid, gid_t gid)

View File

@@ -210,8 +210,47 @@ namespace Kernel
(void)Process::kill(-m_foreground_pgrp, SIGWINCH);
return 0;
}
case TCGETS:
{
SpinLockGuard _(m_termios_lock);
auto* termios = static_cast<struct termios*>(argument);
*termios = m_termios;
return 0;
}
case TCSETSW:
case TCSETSF:
dwarnln("TODO: proper TCSETSW/TCSETSWF");
[[fallthrough]];
case TCSETS:
{
// FIXME: do some validation
SpinLockGuard _(m_termios_lock);
const auto* termios = static_cast<const struct termios*>(argument);
m_termios = *termios;
return 0;
}
case TIOCGPGRP:
{
pid_t* pgrp = static_cast<pid_t*>(argument);
*pgrp = m_foreground_pgrp.load();
return 0;
}
case TIOCSPGRP:
{
const pid_t pgrp = *static_cast<const pid_t*>(argument);
if (!Process::current().is_pgrpg_in_this_session(pgrp))
return BAN::Error::from_errno(EPERM);
if (this != Process::current().controlling_terminal().ptr())
return BAN::Error::from_errno(ENOTTY);
m_foreground_pgrp = pgrp;
return 0;
}
}
return BAN::Error::from_errno(ENOTSUP);
return CharacterDevice::ioctl(request, argument);
}
void TTY::on_key_event(LibInput::RawKeyEvent event)
@@ -234,19 +273,10 @@ namespace Kernel
after_write();
}
void TTY::get_termios(termios* termios)
termios TTY::get_termios()
{
SpinLockGuard _(m_termios_lock);
*termios = m_termios;
}
BAN::ErrorOr<void> TTY::set_termios(const termios* termios)
{
// FIXME: do some validation
SpinLockGuard _(m_termios_lock);
m_termios = *termios;
return {};
return m_termios;
}
void TTY::handle_input_byte(uint8_t ch)
@@ -256,9 +286,7 @@ namespace Kernel
LockGuard _0(m_mutex);
termios termios;
get_termios(&termios);
const auto termios = get_termios();
if ((termios.c_iflag & ISTRIP))
ch &= 0x7F;
@@ -394,8 +422,7 @@ namespace Kernel
if (!m_tty_ctrl.draw_graphics)
return true;
termios termios;
get_termios(&termios);
const auto termios = get_termios();
SpinLockGuard _1(m_write_lock);
if (termios.c_oflag & OPOST)

View File

@@ -49,8 +49,14 @@ struct winsize
unsigned short ws_xpixel; /* unused by kernel */
unsigned short ws_ypixel; /* unused by kernel */
};
#define TIOCGWINSZ 50
#define TIOCSWINSZ 51
#define TIOCGWINSZ 50
#define TIOCSWINSZ 51
#define TCGETS 52
#define TCSETS 53
#define TCSETSW 54
#define TCSETSF 55
#define TIOCGPGRP 56
#define TIOCSPGRP 57
struct snd_volume_info
{

View File

@@ -14,8 +14,6 @@ __BEGIN_DECLS
O(SYS_SEEK, seek) \
O(SYS_TELL, tell) \
O(SYS_RENAMEAT, renameat) \
O(SYS_TCGETATTR, tcgetattr) \
O(SYS_TCSETATTR, tcsetattr) \
O(SYS_FORK, fork) \
O(SYS_EXEC, exec) \
O(SYS_SLEEP, sleep) \
@@ -39,8 +37,6 @@ __BEGIN_DECLS
O(SYS_PIPE, pipe) \
O(SYS_DUP2, dup2) \
O(SYS_KILL, kill) \
O(SYS_TCGETPGRP, tcgetpgrp) \
O(SYS_TCSETPGRP, tcsetpgrp) \
O(SYS_GET_PPID, getppid) \
O(SYS_GET_PID, getpid) \
O(SYS_GET_PGID, getpgid) \
@@ -79,7 +75,6 @@ __BEGIN_DECLS
O(SYS_SMO_CREATE, smo_create) \
O(SYS_SMO_DELETE, smo_delete) \
O(SYS_SMO_MAP, smo_map) \
O(SYS_ISATTY, isatty) \
O(SYS_GETSOCKNAME, getsockname) \
O(SYS_GETPEERNAME, getpeername) \
O(SYS_GETSOCKOPT, getsockopt) \

View File

@@ -3,20 +3,11 @@
#include <errno.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <termios.h>
#include <unistd.h>
speed_t cfgetispeed(const struct termios* termios)
{
return termios->c_ispeed;
}
speed_t cfgetospeed(const struct termios* termios)
{
return termios->c_ospeed;
}
static bool is_valid_speed(speed_t speed)
{
switch (speed)
@@ -43,6 +34,16 @@ static bool is_valid_speed(speed_t speed)
}
}
speed_t cfgetispeed(const struct termios* termios)
{
return termios->c_ispeed;
}
speed_t cfgetospeed(const struct termios* termios)
{
return termios->c_ospeed;
}
int cfsetispeed(struct termios* termios, speed_t speed)
{
if (!is_valid_speed(speed))
@@ -87,18 +88,32 @@ int tcflush(int fd, int queue_selector)
int tcgetattr(int fildes, struct termios* termios)
{
return syscall(SYS_TCGETATTR, fildes, termios);
return ioctl(fildes, TCGETS, termios);
}
pid_t tcgetsid(int);
int tcsetattr(int fildes, int optional_actions, const struct termios* termios)
{
int ioctl_num;
switch (optional_actions)
{
case TCSANOW:
ioctl_num = TCSETS;
break;
case TCSADRAIN:
ioctl_num = TCSETSW;
break;
case TCSAFLUSH:
ioctl_num = TCSETSF;
break;
default:
errno = EINVAL;
return -1;
}
return ioctl(fildes, ioctl_num, termios);
}
int tcsendbreak(int fd, int duration)
{
dwarnln("FIXME: tcsendbreak({}, {})", fd, duration);
return -1;
}
int tcsetattr(int fildes, int optional_actions, const struct termios* termios)
{
return syscall(SYS_TCSETATTR, fildes, optional_actions, termios);
}

View File

@@ -20,6 +20,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/banan-os.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
@@ -349,7 +350,8 @@ int dup2(int fildes, int fildes2)
int isatty(int fildes)
{
return syscall(SYS_ISATTY, fildes) >= 0;
struct termios termios;
return tcgetattr(fildes, &termios) + 1;
}
int gethostname(char* name, size_t namelen)
@@ -838,7 +840,10 @@ pid_t getsid(pid_t pid)
int tcgetpgrp(int fildes)
{
return syscall(SYS_TCGETPGRP, fildes);
pid_t pgrp;
if (ioctl(fildes, TIOCGPGRP, &pgrp) == -1)
return -1;
return pgrp;
}
int seteuid(uid_t uid)
@@ -889,7 +894,7 @@ int setpgid(pid_t pid, pid_t pgid)
int tcsetpgrp(int fildes, pid_t pgid_id)
{
return syscall(SYS_TCSETPGRP, fildes, pgid_id);
return ioctl(fildes, TIOCSPGRP, &pgid_id);
}
char* getlogin(void)