From d3e4fa71a51dd85731798216b82665097e89382e Mon Sep 17 00:00:00 2001 From: jD91mZM2 <me@krake.one> Date: Sun, 29 Jul 2018 16:41:39 +0200 Subject: [PATCH] Implement sys/select.h I really really wish I could actually test this on redox. All I know is: it compiles --- Cargo.lock | 9 +++ Cargo.toml | 1 + include/bits/sys/select.h | 22 ++++++ src/dirent/src/lib.rs | 12 ++-- src/lib.rs | 1 + src/platform/src/linux/mod.rs | 10 +++ src/platform/src/redox/mod.rs | 123 +++++++++++++++++++++++++++++++--- src/platform/src/types.rs | 6 ++ src/sys_select/Cargo.toml | 10 +++ src/sys_select/build.rs | 11 +++ src/sys_select/cbindgen.toml | 7 ++ src/sys_select/src/lib.rs | 20 ++++++ tests/Makefile | 1 + tests/dirent.c | 8 +-- tests/expected/args.stdout | 2 +- tests/expected/select.stderr | 0 tests/expected/select.stdout | 3 + tests/select.c | 22 ++++++ tests/signal.c | 2 +- 19 files changed, 247 insertions(+), 23 deletions(-) create mode 100644 include/bits/sys/select.h create mode 100644 src/sys_select/Cargo.toml create mode 100644 src/sys_select/build.rs create mode 100644 src/sys_select/cbindgen.toml create mode 100644 src/sys_select/src/lib.rs create mode 100644 tests/expected/select.stderr create mode 100644 tests/expected/select.stdout create mode 100644 tests/select.c diff --git a/Cargo.lock b/Cargo.lock index 18d19d482..259c5974e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,6 +346,7 @@ dependencies = [ "sys_ioctl 0.1.0", "sys_mman 0.1.0", "sys_resource 0.1.0", + "sys_select 0.1.0", "sys_socket 0.1.0", "sys_stat 0.1.0", "sys_time 0.1.0", @@ -557,6 +558,14 @@ dependencies = [ "sys_time 0.1.0", ] +[[package]] +name = "sys_select" +version = "0.1.0" +dependencies = [ + "cbindgen 0.5.2", + "platform 0.1.0", +] + [[package]] name = "sys_socket" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 999b40b17..cc49b5c18 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ strings = { path = "src/strings" } sys_ioctl = { path = "src/sys_ioctl" } sys_mman = { path = "src/sys_mman" } sys_resource = { path = "src/sys_resource" } +sys_select = { path = "src/sys_select" } sys_socket = { path = "src/sys_socket" } sys_stat = { path = "src/sys_stat" } sys_time = { path = "src/sys_time" } diff --git a/include/bits/sys/select.h b/include/bits/sys/select.h new file mode 100644 index 000000000..02fa52a21 --- /dev/null +++ b/include/bits/sys/select.h @@ -0,0 +1,22 @@ +#ifndef _BITS_SYS_SELECT_H +#define _BITS_SYS_SELECT_H + +#define FD_SETSIZE 1024 + +typedef struct fd_set { + unsigned long fds_bits[FD_SETSIZE / (8 * sizeof(unsigned long))]; +} fd_set; + +#define _FD_INDEX(fd) ((fd) / (8 * sizeof(unsigned long))) +#define _FD_BITMASK(fd) (1UL << ((fd) & (8 * sizeof(unsigned long) - 1))) + +#define FD_ZERO(set) for (int i = 0; i < sizeof((set)->fds_bits) / sizeof(unsigned long); i += 1) { \ + (set)->fds_bits[i] = 0; \ + } + +#define FD_SET(fd, set) ((set)->fds_bits[_FD_INDEX(fd)] |= _FD_BITMASK(fd)) +#define FD_CLR(fd, set) ((set)->fds_bits[_FD_INDEX(fd)] &= ~(_FD_BITMASK(fd))) + +#define FD_ISSET(fd, set) (((set)->fds_bits[_FD_INDEX(fd)] & _FD_BITMASK(fd)) == _FD_BITMASK(fd)) + +#endif diff --git a/src/dirent/src/lib.rs b/src/dirent/src/lib.rs index 380b400e0..90549e013 100644 --- a/src/dirent/src/lib.rs +++ b/src/dirent/src/lib.rs @@ -14,17 +14,13 @@ use alloc::boxed::Box; use core::{mem, ptr}; use platform::types::*; -// This is here because cbindgen doesn't understand size_of calls. -// We set this constant inside C too, from bits/dirent.h -const _DIRENT_SIZE: usize = mem::size_of::<dirent>(); - -const _DIR_BUF_SIZE: usize = _DIRENT_SIZE * 3; +const DIR_BUF_SIZE: usize = mem::size_of::<dirent>() * 3; // No repr(C) needed, C won't see the content // TODO: ***THREAD SAFETY*** pub struct DIR { fd: c_int, - buf: [c_char; _DIR_BUF_SIZE], + buf: [c_char; DIR_BUF_SIZE], // index & len are specified in bytes index: usize, len: usize, @@ -57,7 +53,7 @@ pub extern "C" fn opendir(path: *const c_char) -> *mut DIR { Box::into_raw(Box::new(DIR { fd, - buf: [0; _DIR_BUF_SIZE], + buf: [0; DIR_BUF_SIZE], index: 0, len: 0, offset: 0, @@ -97,7 +93,7 @@ pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent { if (*dir).index != 0 || (*dir).offset != 0 { // This should happen every time but the first, making the offset // point to the current element and not the next - (*dir).offset += _DIRENT_SIZE; + (*dir).offset += mem::size_of::<dirent>(); } (*ptr).d_off = (*dir).offset as off_t; } diff --git a/src/lib.rs b/src/lib.rs index 8f1b88757..c085bd812 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ pub extern crate strings; pub extern crate sys_ioctl; pub extern crate sys_mman; pub extern crate sys_resource; +pub extern crate sys_select; pub extern crate sys_socket; pub extern crate sys_stat; pub extern crate sys_time; diff --git a/src/platform/src/linux/mod.rs b/src/platform/src/linux/mod.rs index 873720ed2..692e5f684 100644 --- a/src/platform/src/linux/mod.rs +++ b/src/platform/src/linux/mod.rs @@ -324,6 +324,16 @@ pub fn rmdir(path: *const c_char) -> c_int { e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path, AT_REMOVEDIR) }) as c_int } +pub fn select( + nfds: c_int, + readfds: *mut fd_set, + writefds: *mut fd_set, + exceptfds: *mut fd_set, + timeout: *mut timeval, +) -> c_int { + e(unsafe { syscall!(SELECT, nfds, readfds, writefds, exceptfds, timeout) }) as c_int +} + pub unsafe fn sendto( socket: c_int, buf: *const c_void, diff --git a/src/platform/src/redox/mod.rs b/src/platform/src/redox/mod.rs index 17dedeaae..1c13ff2df 100644 --- a/src/platform/src/redox/mod.rs +++ b/src/platform/src/redox/mod.rs @@ -364,7 +364,7 @@ pub fn getgid() -> gid_t { pub fn getrusage(who: c_int, r_usage: *mut rusage) -> c_int { let _ = write!( - ::FileWriter(2), + FileWriter(2), "unimplemented: getrusage({}, {:p})", who, r_usage @@ -424,7 +424,7 @@ unsafe fn inner_get_name( pub fn getitimer(which: c_int, out: *mut itimerval) -> c_int { let _ = write!( - ::FileWriter(2), + FileWriter(2), "unimplemented: getitimer({}, {:p})", which, out @@ -472,7 +472,7 @@ pub fn getsockopt( option_len: *mut socklen_t, ) -> c_int { let _ = write!( - ::FileWriter(2), + FileWriter(2), "unimplemented: getsockopt({}, {}, {}, {:p}, {:p})", socket, level, @@ -661,6 +661,111 @@ pub fn rmdir(path: *const c_char) -> c_int { e(syscall::rmdir(path)) as c_int } +pub fn select( + nfds: c_int, + readfds: *mut fd_set, + writefds: *mut fd_set, + exceptfds: *mut fd_set, + timeout: *mut timeval, +) -> c_int { + fn isset(set: *mut fd_set, fd: usize) -> bool { + if set.is_null() { + return false; + } + + let mask = 1 << (fd & (8 * mem::size_of::<c_ulong>() - 1)); + unsafe { (*set).fds_bits[fd / (8 * mem::size_of::<c_ulong>())] & mask == mask } + } + + let event_file = match RawFile::open("event:\0".as_ptr() as *const c_char, 0, 0) { + Ok(file) => file, + Err(_) => return -1, + }; + + let mut total = 0; + + for fd in 0..nfds as usize { + macro_rules! register { + ($fd:expr, $flags:expr) => { + if write( + *event_file, + &syscall::Event { + id: $fd, + flags: $flags, + data: 0, + }, + ) < 0 + { + return -1; + } + }; + } + if isset(readfds, fd) { + register!(fd, syscall::EVENT_READ); + total += 1; + } + if isset(writefds, fd) { + register!(fd, syscall::EVENT_WRITE); + total += 1; + } + if isset(exceptfds, fd) { + total += 1; + } + } + + const TIMEOUT_TOKEN: usize = 1; + + let timeout_file = if timeout.is_null() { + None + } else { + let timeout_file = match RawFile::open( + format!("time:{}\0", syscall::CLOCK_MONOTONIC).as_ptr() as *const c_char, + 0, + 0, + ) { + Ok(file) => file, + Err(_) => return -1, + }; + let timeout = unsafe { &*timeout }; + if write( + *timeout_file, + &syscall::TimeSpec { + tv_sec: timeout.tv_sec, + tv_nsec: timeout.tv_usec * 1000, + }, + ) < 0 + { + return -1; + } + if write( + *event_file, + &syscall::Event { + id: *timeout_file as usize, + flags: syscall::EVENT_READ, + data: TIMEOUT_TOKEN, + }, + ) < 0 + { + return -1; + } + + Some(timeout_file) + }; + + let mut event = syscall::Event::default(); + if read(*event_file, &mut event) < 0 { + return -1; + } + + if timeout_file.is_some() && event.data == TIMEOUT_TOKEN { + return 0; + } + + // I really don't get why, but select wants me to return the total number + // of file descriptors that was inputted. I'm confused. + total +} + pub unsafe fn sendto( socket: c_int, buf: *const c_void, @@ -682,7 +787,7 @@ pub unsafe fn sendto( pub fn setitimer(which: c_int, new: *const itimerval, old: *mut itimerval) -> c_int { let _ = write!( - ::FileWriter(2), + FileWriter(2), "unimplemented: setitimer({}, {:p}, {:p})", which, new, @@ -715,7 +820,7 @@ pub fn setsockopt( option_len: socklen_t, ) -> c_int { let _ = write!( - ::FileWriter(2), + FileWriter(2), "unimplemented: setsockopt({}, {}, {}, {:p}, {})", socket, level, @@ -728,7 +833,7 @@ pub fn setsockopt( pub fn shutdown(socket: c_int, how: c_int) -> c_int { let _ = write!( - ::FileWriter(2), + FileWriter(2), "unimplemented: shutdown({}, {})", socket, how @@ -770,7 +875,7 @@ pub unsafe fn sigaction(sig: c_int, act: *const sigaction, oact: *mut sigaction) pub fn sigprocmask(how: c_int, set: *const sigset_t, oset: *mut sigset_t) -> c_int { let _ = write!( - ::FileWriter(2), + FileWriter(2), "unimplemented: sigprocmask({}, {:p}, {:p})", how, set, @@ -825,7 +930,7 @@ pub unsafe fn socket(domain: c_int, mut kind: c_int, protocol: c_int) -> c_int { pub fn socketpair(domain: c_int, kind: c_int, protocol: c_int, socket_vector: *mut c_int) -> c_int { let _ = write!( - ::FileWriter(2), + FileWriter(2), "unimplemented: socketpair({}, {}, {}, {:p})", domain, kind, @@ -836,7 +941,7 @@ pub fn socketpair(domain: c_int, kind: c_int, protocol: c_int, socket_vector: *m } pub fn times(out: *mut tms) -> clock_t { - let _ = write!(::FileWriter(2), "unimplemented: times({:p})", out); + let _ = write!(FileWriter(2), "unimplemented: times({:p})", out); !0 } diff --git a/src/platform/src/types.rs b/src/platform/src/types.rs index 4e83ffb90..271302347 100644 --- a/src/platform/src/types.rs +++ b/src/platform/src/types.rs @@ -229,3 +229,9 @@ pub struct tms { tms_cutime: clock_t, tms_cstime: clock_t, } + +pub const FD_SETSIZE: usize = 1024; +#[repr(C)] +pub struct fd_set { + pub fds_bits: [c_ulong; FD_SETSIZE / (8 * mem::size_of::<c_ulong>())], +} diff --git a/src/sys_select/Cargo.toml b/src/sys_select/Cargo.toml new file mode 100644 index 000000000..09d7dcae7 --- /dev/null +++ b/src/sys_select/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "sys_select" +version = "0.1.0" +authors = ["jD91mZM2 <me@krake.one>"] + +[build-dependencies] +cbindgen = { path = "../../cbindgen" } + +[dependencies] +platform = { path = "../platform" } diff --git a/src/sys_select/build.rs b/src/sys_select/build.rs new file mode 100644 index 000000000..5314b67db --- /dev/null +++ b/src/sys_select/build.rs @@ -0,0 +1,11 @@ +extern crate cbindgen; + +use std::{env, fs}; + +fn main() { + let crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"); + fs::create_dir_all("../../target/include").expect("failed to create include directory"); + cbindgen::generate(crate_dir) + .expect("failed to generate bindings") + .write_to_file("../../target/include/sys/select.h"); +} diff --git a/src/sys_select/cbindgen.toml b/src/sys_select/cbindgen.toml new file mode 100644 index 000000000..4aacaa85d --- /dev/null +++ b/src/sys_select/cbindgen.toml @@ -0,0 +1,7 @@ +sys_includes = ["bits/sys/select.h", "sys/time.h"] +include_guard = "_SYS_SELECT_H" +language = "C" +style = "Tag" + +[enum] +prefix_with_name = true diff --git a/src/sys_select/src/lib.rs b/src/sys_select/src/lib.rs new file mode 100644 index 000000000..8b63fe29f --- /dev/null +++ b/src/sys_select/src/lib.rs @@ -0,0 +1,20 @@ +//! sys/select.h implementation +#![no_std] + +extern crate platform; + +use core::mem; +use platform::types::*; + +// fd_set is defined in C because cbindgen is incompatible with mem::size_of booo + +#[no_mangle] +pub extern "C" fn select( + nfds: c_int, + readfds: *mut fd_set, + writefds: *mut fd_set, + exceptfds: *mut fd_set, + timeout: *mut timeval, +) -> c_int { + platform::select(nfds, readfds, writefds, exceptfds, timeout) +} diff --git a/tests/Makefile b/tests/Makefile index c52f6501a..b2bee9f19 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -9,6 +9,7 @@ EXPECT_BINS=\ fcntl/fcntl \ locale \ math \ + select \ setjmp \ signal \ stdio/all \ diff --git a/tests/dirent.c b/tests/dirent.c index b44cfa2e3..b10a81b44 100644 --- a/tests/dirent.c +++ b/tests/dirent.c @@ -14,14 +14,14 @@ int main() { struct dirent* entry; - int tell = 0; + //int tell = 0; for (char counter = 0; (entry = readdir(dir)); counter += 1) { puts(entry->d_name); - if (counter == 4) { - tell = telldir(dir); - } + //if (counter == 4) { + // tell = telldir(dir); + //} } puts("--- Testing rewind ---"); diff --git a/tests/expected/args.stdout b/tests/expected/args.stdout index 3b9ef538c..4076ce936 100644 --- a/tests/expected/args.stdout +++ b/tests/expected/args.stdout @@ -1 +1 @@ -./args test args +bins/args test args diff --git a/tests/expected/select.stderr b/tests/expected/select.stderr new file mode 100644 index 000000000..e69de29bb diff --git a/tests/expected/select.stdout b/tests/expected/select.stdout new file mode 100644 index 000000000..965e612d0 --- /dev/null +++ b/tests/expected/select.stdout @@ -0,0 +1,3 @@ +Is set before? 1 +Amount of things ready: 1 +Is set after? 1 diff --git a/tests/select.c b/tests/select.c new file mode 100644 index 000000000..ba053a1e0 --- /dev/null +++ b/tests/select.c @@ -0,0 +1,22 @@ +#include <fcntl.h> +#include <stdio.h> +#include <sys/select.h> +#include <unistd.h> + +int main() { + int fd = open("select.c", 0, 0); + + fd_set read; + FD_ZERO(&read); + FD_SET(fd, &read); + + printf("Is set before? %d\n", FD_ISSET(fd, &read)); + + // This should actually test TCP streams and stuff, but for now I'm simply + // testing whether it ever returns or not. + printf("Amount of things ready: %d\n", select(fd + 1, &read, NULL, NULL, NULL)); + + printf("Is set after? %d\n", FD_ISSET(fd, &read)); + + close(fd); +} diff --git a/tests/signal.c b/tests/signal.c index 1b2f66fc2..7f4178561 100644 --- a/tests/signal.c +++ b/tests/signal.c @@ -8,7 +8,7 @@ void handler(int sig) { } int main() { - if (((size_t) signal(SIGUSR1, &handler)) == SIG_ERR) { + if (signal(SIGUSR1, &handler) == SIG_ERR) { puts("Signal error!"); printf("%d\n", errno); return 1; -- GitLab