//! Stage 18: AF_UNIX sockets and /proc/self. The detailed checks live in //! user/unixtest.rs (exit code = failed checks); this file runs each //! group twice and checks nothing is left behind -- threads, processes, //! memory frames (fds in flight included), and bound names. #![no_std] #![no_main] #![feature(custom_test_frameworks)] #![test_runner(rustbelt::test_runner)] #![reexport_test_harness_main = "test_main"] extern crate alloc; use alloc::string::String; use bootloader::{BootInfo, entry_point}; use core::panic::PanicInfo; use rustbelt::process::{self, ExitStatus, SpawnArgs}; use rustbelt::{fs, memory, net, syscall, thread}; entry_point!(main); fn main(boot_info: &'static BootInfo) -> ! { rustbelt::init(); rustbelt::init_memory(boot_info); rustbelt::init_smp(); // all 4 CPUs run threads fs::init(); rustbelt::init_storage(); test_main(); rustbelt::hlt_loop(); } fn run(args: &[&str]) -> ExitStatus { let mut spawn = SpawnArgs::kernel("unixtest"); spawn.args.extend(args.iter().map(|a| String::from(*a))); let p = process::spawn_path("/mnt/disk0/bin/unixtest", spawn).expect("spawn failed"); process::wait(&p) } fn counts() -> (usize, usize, usize, usize) { thread::sleep_ticks(3); // let exited threads' stacks be freed (thread::thread_count(), process::count(), memory::frames_in_use(), net::unix::bound_count()) } /// Run one group twice (the first warms caches), check it passes and /// nothing is left behind. fn run_without_leaks(group: &str) { assert_eq!(run(&[group]), ExitStatus::Exited(0), "unixtest {} failed (warm-up)", group); let before = counts(); assert_eq!(run(&[group]), ExitStatus::Exited(0), "unixtest {} failed", group); assert_eq!(counts(), before, "unixtest {} leaked something", group); assert_eq!(net::unix::bound_count(), 0, "unixtest {} left bound sockets behind", group); } /// A server on a path (ramfs and ext2) echoes for a forked client; /// abstract names, autobind, ECONNRESET/EPIPE/ECONNREFUSED, SO_PEERCRED. #[test_case] fn stream_echo_across_fork() { run_without_leaks("stream"); } /// socketpair: stream (both ways, fork, shutdown, MSG_PEEK), seqpacket, /// dgram, SOCK_CLOEXEC/SOCK_NONBLOCK. #[test_case] fn socketpairs() { run_without_leaks("pair"); } /// Datagrams between named sockets: sender names, MSG_TRUNC, connect, /// EPERM/EPROTOTYPE/EMSGSIZE/ECONNREFUSED. #[test_case] fn datagrams() { run_without_leaks("dgram"); } /// SCM_RIGHTS: a file and a pipe end read through the received fds; /// MSG_CTRUNC, MSG_CMSG_CLOEXEC, fds in flight closed with the socket /// (and no frames leaked: see run_without_leaks); SCM_CREDENTIALS. #[test_case] fn passing_fds() { run_without_leaks("rights"); } /// Nonblocking EAGAIN, real buffer limits (and a blocked writer), poll, /// epoll EPOLLRDHUP/EPOLLHUP, nonblocking accept, full backlog. #[test_case] fn nonblocking_and_poll() { run_without_leaks("nonblock"); } /// /proc/self/fd: listing, readlink (paths, pipe:[N], socket:[N]), /// opening entries; /proc/self/exe. #[test_case] fn proc_self() { run_without_leaks("proc"); } /// `--trace` knows the new calls by name. #[test_case] fn trace_names() { assert_eq!(syscall::trace_name(46), "sendmsg"); assert_eq!(syscall::trace_name(47), "recvmsg"); assert_eq!(syscall::trace_name(53), "socketpair"); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { rustbelt::test_panic_handler(info) }