diff --git a/ncc/build_and_run.sh b/ncc/build_and_run.sh index 3ee2c3a..38dfb33 100755 --- a/ncc/build_and_run.sh +++ b/ncc/build_and_run.sh @@ -1 +1,2 @@ -RUST_BACKTRACE=1 cargo run -- $* && cd ../vm && cargo run ../ncc/out.asm && cd ../vm +export RUST_BACKTRACE=1 +cargo run -- $* && cd ../vm && cargo run ../ncc/out.asm && cd ../vm diff --git a/ncc/examples/telnet_server.c b/ncc/examples/telnet_server.c index e413cec..0e9c3f2 100644 --- a/ncc/examples/telnet_server.c +++ b/ncc/examples/telnet_server.c @@ -17,11 +17,18 @@ void on_new_conn(u64 socket_id) void on_incoming_data(u64 socket_id, u64 num_bytes) { - puts("got incoming data"); + printf("received %d bytes of incoming data\n", num_bytes); char read_buf[1024]; memset(read_buf, 0, 1024); net_read(socket_id, read_buf, 1024 - 1); + + // If this is a telnet command (non-printable), ignore it + if (read_buf[0] & 0x80) + { + return; + } + puts(read_buf); if (strncmp(read_buf, "exit", 4) == 0) @@ -30,8 +37,6 @@ void on_incoming_data(u64 socket_id, u64 num_bytes) char* response = "Goodbye!\n"; net_write(socket_id, response, strlen(response)); net_close(socket_id); - - net_close(listen_sock); return; } diff --git a/vm/src/sys/net.rs b/vm/src/sys/net.rs index bd1b310..df1a2c8 100644 --- a/vm/src/sys/net.rs +++ b/vm/src/sys/net.rs @@ -58,20 +58,32 @@ fn listen_thread( { // Block until a connection can be accepted for result in listener.incoming() { + let stream = match result { + Ok(s) => s, + + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + // Sleep for a bit, then try again + thread::sleep(std::time::Duration::from_millis(10)); + continue; + } + + Err(e) => panic!("encountered IO error: {e}"), + }; + let arc = vm_mutex.upgrade().unwrap(); let mut vm = arc.lock().unwrap(); - // TODO: note, accepting the connection may error, - // for example if the socket was closed - let stream = result.unwrap(); - // Add the new connection to the queue let mut net_state = &mut vm.sys_state.net_state; match net_state.sockets.get_mut(&socket_id) { Some(Socket::Listen{ incoming, .. }) => { incoming.push_back(stream); } - _ => panic!() + + // Socket closed + _ => { + break; + } } // Call on_new_conn to signal an incoming connection @@ -100,6 +112,12 @@ pub fn net_listen( let listener = TcpListener::bind(listen_addr).unwrap(); let socket_fd = listener.as_raw_fd(); + // Set the listener to non-blocking + // We do this because Rust offers us no way to close the TcpListener + // from another thread, and so the listening thread has to periodically + // check if it should exit. + listener.set_nonblocking(true).expect("Cannot set non-blocking"); + // Assign a socket id to the socket let mut net_state = &mut vm.sys_state.net_state; let socket_id = net_state.next_id; @@ -141,8 +159,12 @@ fn read_thread( let mut buf: [u8; 16384] = [0; 16384]; match stream.read(&mut buf) { - Ok(num_bytes) => { + // End of file, connection closed + Ok(0) => { + break; + } + Ok(num_bytes) => { let arc = vm_mutex.upgrade().unwrap(); let mut vm = arc.lock().unwrap(); @@ -167,7 +189,10 @@ fn read_thread( } } - Err(_) => break + Err(e) => { + println!("error in read thread: {e}"); + break + } } } } @@ -199,6 +224,7 @@ pub fn net_accept( } let stream = incoming.pop_front().unwrap(); + stream.set_nonblocking(false).expect("could not set stream to blocking"); // TODO: handle the error case here // The connection could have dropped @@ -314,7 +340,8 @@ pub fn net_close( } Some(Socket::Listen { listener, .. }) => { - //listener.shutdown(std::net::Shutdown::Both).unwrap(); + // The listen thread will detect that the socket state + // has been removed and exit } _ => panic!()