Browse Source

Implement sys/select.h

I really really wish I could actually test this on redox. All I know is: it compiles
jD91mZM2 6 years ago
parent
commit
d3e4fa71a5

+ 9 - 0
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"

+ 1 - 0
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" }

+ 22 - 0
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

+ 4 - 8
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;
     }

+ 1 - 0
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;

+ 10 - 0
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,

+ 114 - 9
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
 }
 

+ 6 - 0
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>())],
+}

+ 10 - 0
src/sys_select/Cargo.toml

@@ -0,0 +1,10 @@
+[package]
+name = "sys_select"
+version = "0.1.0"
+authors = ["jD91mZM2 <[email protected]>"]
+
+[build-dependencies]
+cbindgen = { path = "../../cbindgen" }
+
+[dependencies]
+platform = { path = "../platform" }

+ 11 - 0
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");
+}

+ 7 - 0
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

+ 20 - 0
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)
+}

+ 1 - 0
tests/Makefile

@@ -9,6 +9,7 @@ EXPECT_BINS=\
 	fcntl/fcntl \
 	locale \
 	math \
+	select \
 	setjmp \
 	signal \
 	stdio/all \

+ 4 - 4
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 ---");

+ 1 - 1
tests/expected/args.stdout

@@ -1 +1 @@
-./args test args 
+bins/args test args 

+ 0 - 0
tests/expected/select.stderr


+ 3 - 0
tests/expected/select.stdout

@@ -0,0 +1,3 @@
+Is set before? 1
+Amount of things ready: 1
+Is set after? 1

+ 22 - 0
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);
+}

+ 1 - 1
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;