From dd3de96b8e4f49c3f352853fc8338613edeb2ae9 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Wed, 24 Apr 2019 19:25:09 +0800 Subject: [PATCH] hack unix socket for php --- src/libos/src/fs/io_multiplexing.rs | 53 +++++++++++++++++++++++++++-- src/libos/src/fs/unix_socket.rs | 53 ++++++++++++++++++++++++----- src/libos/src/syscall/mod.rs | 33 ++++++++++++++++++ src/libos/src/util/ring_buf.rs | 25 ++++++++++++++ 4 files changed, 152 insertions(+), 12 deletions(-) diff --git a/src/libos/src/fs/io_multiplexing.rs b/src/libos/src/fs/io_multiplexing.rs index 419a1ab8..0856a18f 100644 --- a/src/libos/src/fs/io_multiplexing.rs +++ b/src/libos/src/fs/io_multiplexing.rs @@ -3,6 +3,7 @@ use std::any::Any; use std::collections::btree_map::BTreeMap; use std::fmt; use std::vec::Vec; +use std::sync::atomic::spin_loop_hint; /// Forward to host `poll` /// (sgx_libc doesn't have `select`) @@ -13,6 +14,7 @@ pub fn do_select( exceptfds: &mut libc::fd_set, timeout: Option, ) -> Result { + info!("select: nfds: {}", nfds); // convert libos fd to Linux fd let mut host_to_libos_fd = [0; libc::FD_SETSIZE]; let mut polls = Vec::::new(); @@ -30,6 +32,29 @@ pub fn do_select( if !(r || w || e) { continue; } + if let Ok(socket) = file_table_ref.get(fd as FileDesc)?.as_unix_socket() { + warn!("select unix socket is unimplemented, spin for read"); + readfds.clear(); + writefds.clear(); + exceptfds.clear(); + + // FIXME: spin poll until can read (hack for php) + while r && socket.poll()?.0 == false { + spin_loop_hint(); + } + + let (rr, ww, ee) = socket.poll()?; + if r && rr { + readfds.set(fd); + } + if w && ww { + writefds.set(fd); + } + if e && ee { + writefds.set(fd); + } + return Ok(1); + } let host_fd = file_table_ref.get(fd as FileDesc)?.as_socket()?.fd(); host_to_libos_fd[host_fd as usize] = fd; @@ -80,7 +105,7 @@ pub fn do_select( } pub fn do_poll(polls: &mut [libc::pollfd], timeout: c_int) -> Result { - info!("poll: [..], timeout: {}", timeout); + info!("poll: {:?}, timeout: {}", polls.iter().map(|p| p.fd).collect::>(), timeout); let current_ref = process::get_current(); let mut proc = current_ref.lock().unwrap(); @@ -88,8 +113,30 @@ pub fn do_poll(polls: &mut [libc::pollfd], timeout: c_int) -> Result @@ -127,6 +128,16 @@ impl UnixSocketFile { let mut inner = self.inner.lock().unwrap(); inner.connect(path) } + + pub fn poll(&self) -> Result<(bool, bool, bool), Error> { + let mut inner = self.inner.lock().unwrap(); + inner.poll() + } + + pub fn ioctl(&self, cmd: c_int, argp: *mut c_int) -> Result<(), Error> { + let mut inner = self.inner.lock().unwrap(); + inner.ioctl(cmd, argp) + } } impl Debug for UnixSocketFile { @@ -189,14 +200,19 @@ impl UnixSocket { Ok(()) } - /// Server 4: Accept a connection on listening. Non-blocking. + /// Server 4: Accept a connection on listening. pub fn accept(&mut self) -> Result { match self.status { Status::Listening => {} _ => return errno!(EINVAL, "unix socket is not listening"), }; - let socket = self.obj.as_mut().unwrap().pop() - .ok_or(Error::new(EAGAIN, "no connections are present to be accepted"))?; + // FIXME: Block. Now spin loop. + let socket = loop { + if let Some(socket) = self.obj.as_mut().unwrap().pop() { + break socket; + } + spin_loop_hint(); + }; Ok(socket) } @@ -217,16 +233,35 @@ impl UnixSocket { } pub fn read(&self, buf: &mut [u8]) -> Result { - if let Status::Connected(channel) = &self.status { - channel.reader.read(buf) - } else { - errno!(EBADF, "UnixSocket is not connected") - } + self.channel()?.reader.read(buf) } pub fn write(&self, buf: &[u8]) -> Result { + self.channel()?.writer.write(buf) + } + + pub fn poll(&self) -> Result<(bool, bool, bool), Error> { // (read, write, error) + let channel = self.channel()?; + let r = channel.reader.can_read(); + let w = channel.writer.can_write(); + Ok((r, w, false)) + } + + pub fn ioctl(&self, cmd: c_int, argp: *mut c_int) -> Result<(), Error> { + const FIONREAD: c_int = 0x541B; // Get the number of bytes to read + if cmd == FIONREAD { + let bytes_to_read = self.channel()?.reader.bytes_to_read(); + unsafe { argp.write(bytes_to_read as c_int); } + Ok(()) + } else { + warn!("ioctl for unix socket is unimplemented"); + errno!(ENOSYS, "ioctl for unix socket is unimplemented") + } + } + + fn channel(&self) -> Result<&Channel, Error> { if let Status::Connected(channel) = &self.status { - channel.writer.write(buf) + Ok(channel) } else { errno!(EBADF, "UnixSocket is not connected") } diff --git a/src/libos/src/syscall/mod.rs b/src/libos/src/syscall/mod.rs index fd460417..cb992656 100644 --- a/src/libos/src/syscall/mod.rs +++ b/src/libos/src/syscall/mod.rs @@ -258,6 +258,11 @@ pub extern "C" fn dispatch_syscall( arg1 as *mut libc::sockaddr, arg2 as *mut libc::socklen_t, ), + SYS_GETSOCKNAME => do_getsockname( + arg0 as c_int, + arg1 as *mut libc::sockaddr, + arg2 as *mut libc::socklen_t, + ), SYS_SENDTO => do_sendto( arg0 as c_int, arg1 as *const c_void, @@ -926,6 +931,10 @@ fn do_ioctl(fd: FileDesc, cmd: c_int, argp: *mut c_int) -> Result if let Ok(socket) = file_ref.as_socket() { let ret = try_libc!(libc::ocall::ioctl_arg1(socket.fd(), cmd, argp)); Ok(ret as isize) + } else if let Ok(unix_socket) = file_ref.as_unix_socket() { + // TODO: check argp + unix_socket.ioctl(cmd, argp)?; + Ok(0) } else { warn!("ioctl is unimplemented"); errno!(ENOSYS, "ioctl is unimplemented") @@ -1156,6 +1165,30 @@ fn do_getpeername( Ok(ret as isize) } else if let Ok(unix_socket) = file_ref.as_unix_socket() { warn!("getpeername for unix socket is unimplemented"); + errno!(ENOTCONN, "hack for php: Transport endpoint is not connected") + } else { + errno!(EBADF, "not a socket") + } +} + +fn do_getsockname( + fd: c_int, + addr: *mut libc::sockaddr, + addr_len: *mut libc::socklen_t, +) -> Result { + info!("getsockname: fd: {}, addr: {:?}, addr_len: {:?}", fd, addr, addr_len); + let current_ref = process::get_current(); + let mut proc = current_ref.lock().unwrap(); + let file_ref = proc.get_files().lock().unwrap().get(fd as FileDesc)?; + if let Ok(socket) = file_ref.as_socket() { + let ret = try_libc!(libc::ocall::getsockname( + socket.fd(), + addr, + addr_len + )); + Ok(ret as isize) + } else if let Ok(unix_socket) = file_ref.as_unix_socket() { + warn!("getsockname for unix socket is unimplemented"); Ok(0) } else { errno!(EBADF, "not a socket") diff --git a/src/libos/src/util/ring_buf.rs b/src/libos/src/util/ring_buf.rs index 56afe700..045e1e4e 100644 --- a/src/libos/src/util/ring_buf.rs +++ b/src/libos/src/util/ring_buf.rs @@ -155,6 +155,20 @@ impl RingBufReader { } Ok(buf_pos) } + + pub fn can_read(&self) -> bool { + self.bytes_to_read() != 0 + } + + pub fn bytes_to_read(&self) -> usize { + let tail = self.inner.get_tail(); + let head = self.inner.get_head(); + if tail <= head { + head - tail + } else { + self.inner.capacity - tail + head + } + } } impl Drop for RingBufReader { @@ -202,4 +216,15 @@ impl RingBufWriter { } Ok(buf_pos) } + + pub fn can_write(&self) -> bool { + let tail = self.inner.get_tail(); + let head = self.inner.get_head(); + let may_write_nbytes = if tail <= head { + self.inner.capacity - head + } else { + tail - head - 1 + }; + may_write_nbytes != 0 + } }