From 9d8094baeeec2c54d8f010ebe88ae93f8bd265ec Mon Sep 17 00:00:00 2001 From: Darley Barreto Date: Wed, 17 Jan 2024 03:05:50 +0000 Subject: [PATCH] Add forkpty, ptsname and posix_openpt --- src/header/pty/cbindgen.toml | 4 + src/header/pty/mod.rs | 79 +++++++++++++++- src/header/stdlib/mod.rs | 85 +++++++++++++++++- tests/Makefile | 2 + tests/expected/bins_static/pty/forkpty.stderr | 0 tests/expected/bins_static/pty/forkpty.stdout | 0 .../bins_static/stdlib/ptsname.stderr | 0 .../bins_static/stdlib/ptsname.stdout | 0 tests/pty/forkpty.c | 38 ++++++++ tests/stdlib/ptsname.c | 90 +++++++++++++++++++ 10 files changed, 291 insertions(+), 7 deletions(-) create mode 100644 tests/expected/bins_static/pty/forkpty.stderr create mode 100644 tests/expected/bins_static/pty/forkpty.stdout create mode 100644 tests/expected/bins_static/stdlib/ptsname.stderr create mode 100644 tests/expected/bins_static/stdlib/ptsname.stdout create mode 100644 tests/pty/forkpty.c create mode 100644 tests/stdlib/ptsname.c diff --git a/src/header/pty/cbindgen.toml b/src/header/pty/cbindgen.toml index 8686d7a6d4..3ecd31cfcf 100644 --- a/src/header/pty/cbindgen.toml +++ b/src/header/pty/cbindgen.toml @@ -7,3 +7,7 @@ cpp_compat = true [enum] prefix_with_name = true + +[export.rename] +"winsize" = "struct winsize" +"termios" = "struct termios" \ No newline at end of file diff --git a/src/header/pty/mod.rs b/src/header/pty/mod.rs index 02be0da6bc..592bf4afaf 100644 --- a/src/header/pty/mod.rs +++ b/src/header/pty/mod.rs @@ -1,10 +1,10 @@ //! pty.h implementation, not POSIX specified -use core::slice; +use core::{mem, ptr, slice}; use crate::{ - header::{limits, sys_ioctl, termios, unistd}, - platform::types::*, + header::{fcntl, limits, pthread, signal, sys_ioctl, sys_wait, termios, unistd, utmp}, + platform::{self, types::*}, }; #[cfg(target_os = "linux")] @@ -24,7 +24,7 @@ pub unsafe extern "C" fn openpty( winp: *const sys_ioctl::winsize, ) -> c_int { let mut tmp_name = [0; limits::PATH_MAX]; - let mut name = if !namep.is_null() { + let name = if !namep.is_null() { slice::from_raw_parts_mut(namep as *mut u8, limits::PATH_MAX) } else { &mut tmp_name @@ -48,3 +48,74 @@ pub unsafe extern "C" fn openpty( return 0; } + +#[no_mangle] +pub unsafe extern "C" fn forkpty( + pm: *mut c_int, + name: *mut c_char, + tio: *const termios::termios, + ws: *const sys_ioctl::winsize, +) -> c_int { + let mut m = 0; + let mut s = 0; + let mut ec = 0; + let mut p: [c_int; 2] = [0; 2]; + let mut cs = 0; + let mut pid = -1; + let mut set = signal::sigset_t::default(); + let mut oldset = signal::sigset_t::default(); + + if openpty(&mut m, &mut s, name, tio, ws) < 0 { + return -1; + } + + signal::sigfillset(&mut set); + signal::pthread_sigmask(signal::SIG_BLOCK, &mut set, &mut oldset); + pthread::pthread_setcancelstate(pthread::PTHREAD_CANCEL_DISABLE, &mut cs); + + if unistd::pipe2(p.as_mut_ptr(), fcntl::O_CLOEXEC) != 0 { + unistd::close(s); + } else { + pid = unistd::fork(); + if pid == 0 { + unistd::close(m); + unistd::close(p[0]); + if utmp::login_tty(s) != 0 { + unistd::write( + p[1], + &platform::errno as *const _ as *const c_void, + mem::size_of::(), + ); + unistd::_exit(127); + } + unistd::close(p[1]); + pthread::pthread_setcancelstate(cs, ptr::null_mut()); + signal::pthread_sigmask(signal::SIG_SETMASK, &mut oldset, ptr::null_mut()); + return 0; + } + + unistd::close(s); + unistd::close(p[1]); + + if unistd::read( + p[0], + &mut ec as *mut c_int as *mut c_void, + mem::size_of::(), + ) > 0 + { + let mut status = 0; + sys_wait::waitpid(pid, &mut status, 0); + pid = -1; + platform::errno = ec; + } + unistd::close(p[0]); + } + if pid > 0 { + *pm = m; + } else { + unistd::close(m); + } + pthread::pthread_setcancelstate(cs, ptr::null_mut()); + signal::pthread_sigmask(signal::SIG_SETMASK, &mut oldset, ptr::null_mut()); + pid +} diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 64ca259a87..3ef3431302 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -726,9 +726,88 @@ pub unsafe extern "C" fn posix_memalign( } } -// #[no_mangle] -pub extern "C" fn ptsname(fildes: c_int) -> *mut c_char { - unimplemented!(); +#[no_mangle] +pub unsafe extern "C" fn posix_openpt(flags: c_int) -> c_int { + #[cfg(target_os = "redox")] + let r = open((b"pty:\0" as *const u8).cast(), O_CREAT); + + #[cfg(target_os = "linux")] + let r = open((b"/dev/ptmx\0" as *const u8).cast(), flags); + + if r < 0 && platform::errno == ENOSPC { + platform::errno = EAGAIN; + } + + return r; +} + +#[no_mangle] +unsafe extern "C" fn ptsname(fd: c_int) -> *mut c_char { + static mut PTS_BUFFER: [c_char; 9 + mem::size_of::() * 3 + 1] = + [0; 9 + mem::size_of::() * 3 + 1]; + if ptsname_r(fd, PTS_BUFFER.as_mut_ptr(), PTS_BUFFER.len()) != 0 { + ptr::null_mut() + } else { + PTS_BUFFER.as_mut_ptr() + } +} + +#[no_mangle] +unsafe extern "C" fn ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + if buf.is_null() { + platform::errno = EINVAL; + EINVAL + } else { + __ptsname_r(fd, buf, buflen) + } +} + +#[cfg(target_os = "redox")] +#[inline(always)] +unsafe fn __ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + let tty_ptr = unistd::ttyname(fd); + + if !tty_ptr.is_null() { + if let Ok(name) = CStr::from_ptr(tty_ptr).to_str() { + let len = name.len(); + if len > buflen { + platform::errno = ERANGE; + return ERANGE; + } else { + // we have checked the string will fit in the buffer + // so can use strcpy safely + let s = name.as_ptr().cast(); + ptr::copy_nonoverlapping(s, buf, len); + return 0; + } + } + } + platform::errno +} + +#[cfg(target_os = "linux")] +#[inline(always)] +unsafe fn __ptsname_r(fd: c_int, buf: *mut c_char, buflen: size_t) -> c_int { + let mut pty = 0; + let err = platform::errno; + + if ioctl(fd, TIOCGPTN, &mut pty as *mut _ as *mut c_void) == 0 { + let name = format!("/dev/pts/{}", pty); + let len = name.len(); + if len > buflen { + platform::errno = ERANGE; + ERANGE + } else { + // we have checked the string will fit in the buffer + // so can use strcpy safely + let s = name.as_ptr().cast(); + ptr::copy_nonoverlapping(s, buf, len); + platform::errno = err; + 0 + } + } else { + platform::errno + } } unsafe fn put_new_env(insert: *mut c_char) { diff --git a/tests/Makefile b/tests/Makefile index 12062689c5..744733215c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -19,6 +19,7 @@ EXPECT_NAMES=\ math \ netdb/getaddrinfo \ ptrace \ + pty/forkpty \ regex \ select \ setjmp \ @@ -54,6 +55,7 @@ EXPECT_NAMES=\ stdlib/div \ stdlib/env \ stdlib/mkostemps \ + stdlib/ptsname \ stdlib/qsort \ stdlib/rand \ stdlib/rand48 \ diff --git a/tests/expected/bins_static/pty/forkpty.stderr b/tests/expected/bins_static/pty/forkpty.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/pty/forkpty.stdout b/tests/expected/bins_static/pty/forkpty.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/ptsname.stderr b/tests/expected/bins_static/stdlib/ptsname.stderr new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/expected/bins_static/stdlib/ptsname.stdout b/tests/expected/bins_static/stdlib/ptsname.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/pty/forkpty.c b/tests/pty/forkpty.c new file mode 100644 index 0000000000..740acd457a --- /dev/null +++ b/tests/pty/forkpty.c @@ -0,0 +1,38 @@ +/* Test of pty.h and forkpty function. + Copyright (C) 2009-2011 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Simon Josefsson , 2009. */ + +#include +#include +#include + +int main () { + int res = 0; + int amaster = 0; + + res = forkpty (&amaster, NULL, NULL, NULL); + if (res == 0) { + printf("This is child process\n"); + } else if (res > 0) { + printf("This is parent process\n"); + wait(NULL); + } else { + printf ("forkpty returned %d\n", res); + return 1; + } + return 0; +} \ No newline at end of file diff --git a/tests/stdlib/ptsname.c b/tests/stdlib/ptsname.c new file mode 100644 index 0000000000..d5a9591f34 --- /dev/null +++ b/tests/stdlib/ptsname.c @@ -0,0 +1,90 @@ +/* Test for ptsname/ptsname_r. + Copyright (C) 2014-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#define DEV_TTY "/dev/tty" +#define PTSNAME_EINVAL "./ptsname-einval" + +int do_single_test(int fd, char * buf, size_t buflen, int expected_err) { + + int ret = ptsname_r(fd, buf, buflen); + int err = errno; + + if (expected_err == 0) { + if (ret != 0) { + printf("ptsname_r: expected: return = 0\n"); + printf(" got: return = %d, errno = %d (%s)\n", + ret, err, strerror(err)); + return 1; + } + } else { + if (ret == 0 || errno != expected_err) { + printf("ptsname_r: expected: return = %d, errno = %d (%s)\n", + -1, expected_err, strerror(expected_err)); + printf(" got: return = %d, errno = %d (%s)\n", + ret, err, strerror(err)); + return 1; + } + } + + return 0; +} + +int main(void) { + char buf[512] = {0}; + int result = 0; + errno = 0; + /* Tests with a real PTS master. */ + int fd = posix_openpt(O_RDWR); + if (fd != -1) { + result |= do_single_test(fd, buf, sizeof(buf), 0); + result |= do_single_test(fd, buf, 1, ERANGE); + close(fd); + } else + printf("posix_openpt (O_RDWR) failed\nerrno %d (%s)\n", + errno, strerror(errno)); + + // TODO: open(DEV_TTY, O_RDONLY) gives error on CI + // /* Test with a terminal device which is not a PTS master. */ + // fd = open(DEV_TTY, O_RDONLY); + // if (fd != -1) { + // result |= do_single_test(fd, buf, sizeof(buf), ENOTTY); + // close(fd); + // } else + // printf("open (\"%s\", O_RDWR) failed\nerrno %d (%s)\n", + // DEV_TTY, errno, strerror(errno)); + + /* Test with a file. */ + fd = open(PTSNAME_EINVAL, O_RDWR | O_CREAT, 0600); + if (fd != -1) { + result |= do_single_test(fd, buf, sizeof(buf), ENOTTY); + close(fd); + unlink(PTSNAME_EINVAL); + } else + printf("open (\"%s\", O_RDWR | OCREAT) failed\nerrno %d (%s)\n", + PTSNAME_EINVAL, errno, strerror(errno)); + + return result; +} \ No newline at end of file