Merge branch 'socketpair' into 'master'

Various socket-related changes and other

See merge request redox-os/relibc!289
This commit is contained in:
Jeremy Soller
2020-07-01 13:56:58 +00:00
44 changed files with 280 additions and 119 deletions
+119 -10
View File
@@ -1,6 +1,10 @@
use crate::platform::{self, types::*};
use crate::{
io::{self, Write},
platform::{self, WriteByte, types::*},
};
use core::{
cmp,
fmt,
iter::IntoIterator,
mem,
ops::{Deref, DerefMut},
@@ -53,8 +57,13 @@ impl<T> CVec<T> {
}
unsafe fn resize(&mut self, cap: usize) -> Result<(), AllocError> {
let size = Self::check_mul(cap, mem::size_of::<T>())?;
let ptr = NonNull::new(platform::realloc(self.ptr.as_ptr() as *mut c_void, size) as *mut T)
.ok_or(AllocError)?;
let ptr = if cap == 0 {
NonNull::dangling()
} else if self.cap > 0 {
NonNull::new(platform::realloc(self.ptr.as_ptr() as *mut c_void, size) as *mut T).ok_or(AllocError)?
} else {
NonNull::new((platform::alloc(size)) as *mut T).ok_or(AllocError)?
};
self.ptr = ptr;
self.cap = cap;
Ok(())
@@ -67,14 +76,17 @@ impl<T> CVec<T> {
start = start.add(1);
}
}
// Push stuff
pub fn reserve(&mut self, required: usize) -> Result<(), AllocError> {
let reserved_len = self
let required_len = self
.len
.checked_add(required)
.ok_or(AllocError)
.and_then(Self::check_bounds)?;
let new_cap = cmp::min(reserved_len.next_power_of_two(), core::isize::MAX as usize);
if new_cap > self.cap {
if required_len > self.cap {
let new_cap = cmp::min(required_len.next_power_of_two(), core::isize::MAX as usize);
unsafe {
self.resize(new_cap)?;
}
@@ -82,8 +94,8 @@ impl<T> CVec<T> {
Ok(())
}
pub fn push(&mut self, elem: T) -> Result<(), AllocError> {
self.reserve(1)?;
unsafe {
self.reserve(1)?;
ptr::write(self.ptr.as_ptr().add(self.len), elem);
}
self.len += 1; // no need to bounds check, as new len <= cap
@@ -93,21 +105,26 @@ impl<T> CVec<T> {
where
T: Copy,
{
self.reserve(elems.len())?;
unsafe {
self.reserve(elems.len())?;
ptr::copy_nonoverlapping(elems.as_ptr(), self.ptr.as_ptr().add(self.len), elems.len());
}
self.len += elems.len(); // no need to bounds check, as new len <= cap
Ok(())
}
pub fn append(&mut self, other: &mut Self) -> Result<(), AllocError> {
let len = other.len;
other.len = 0; // move
self.reserve(len)?;
unsafe {
self.reserve(other.len())?;
ptr::copy_nonoverlapping(other.as_ptr(), self.ptr.as_ptr().add(self.len), other.len());
ptr::copy_nonoverlapping(other.as_ptr(), self.ptr.as_ptr().add(self.len), len);
}
self.len += other.len(); // no need to bounds check, as new len <= cap
Ok(())
}
// Pop stuff
pub fn truncate(&mut self, len: usize) {
if len < self.len {
unsafe {
@@ -126,6 +143,18 @@ impl<T> CVec<T> {
}
Ok(())
}
pub fn pop(&mut self) -> Option<T> {
if self.is_empty() {
None
} else {
let elem = unsafe { ptr::read(self.as_ptr().add(self.len - 1)) };
self.len -= 1;
Some(elem)
}
}
// Misc stuff
pub fn capacity(&self) -> usize {
self.cap
}
@@ -176,3 +205,83 @@ impl<'a, T> IntoIterator for &'a mut CVec<T> {
<&mut [T]>::into_iter(&mut *self)
}
}
impl Write for CVec<u8> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.extend_from_slice(buf).map_err(|err| io::Error::new(
io::ErrorKind::Other,
"AllocStringWriter::write failed to allocate",
))?;
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl fmt::Write for CVec<u8> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write(s.as_bytes()).map_err(|_| fmt::Error)?;
Ok(())
}
}
impl WriteByte for CVec<u8> {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
self.write(&[byte]).map_err(|_| fmt::Error)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::CVec;
#[test]
fn push_pop() {
let mut vec = CVec::new();
vec.push(1).unwrap();
vec.push(2).unwrap();
vec.push(3).unwrap();
assert_eq!(&vec[..], &[1, 2, 3]);
assert_eq!(vec.pop().unwrap(), 3);
assert_eq!(&vec[..], &[1, 2]);
}
#[test]
fn extend_from_slice() {
use core_io::Write;
let mut vec = CVec::new();
vec.extend_from_slice(&[1, 2, 3]).unwrap();
vec.extend_from_slice(&[4, 5, 6]).unwrap();
assert_eq!(&vec[..], &[1, 2, 3, 4, 5, 6]);
assert_eq!(vec.write(&[7, 8, 9]).unwrap(), 3);
assert_eq!(&vec[..], &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
#[test]
fn dropped() {
use alloc::rc::Rc;
let counter = Rc::new(());
let mut vec = CVec::with_capacity(3).unwrap();
vec.push(Rc::clone(&counter)).unwrap();
vec.push(Rc::clone(&counter)).unwrap();
vec.push(Rc::clone(&counter)).unwrap();
assert_eq!(Rc::strong_count(&counter), 4);
let popped = vec.pop().unwrap();
assert_eq!(Rc::strong_count(&counter), 4);
drop(popped);
assert_eq!(Rc::strong_count(&counter), 3);
vec.push(Rc::clone(&counter)).unwrap();
vec.push(Rc::clone(&counter)).unwrap();
vec.push(Rc::clone(&counter)).unwrap();
assert_eq!(vec.len(), 5);
assert_eq!(Rc::strong_count(&counter), 6);
vec.truncate(1);
assert_eq!(Rc::strong_count(&counter), 2);
drop(vec);
assert_eq!(Rc::strong_count(&counter), 1);
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_AIO_H"
include_guard = "_RELIBC_AIO_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stdint.h", "sys/types.h"]
include_guard = "_FENV_H"
include_guard = "_RELIBC_FENV_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_TEMPLATE_H"
include_guard = "_RELIBC_TEMPLATE_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_WCTYPE_H"
include_guard = "_RELIBC_WCTYPE_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["bits/assert.h"]
include_guard = "_ASSERT_H"
include_guard = "_RELIBC_ASSERT_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["bits/ctype.h"]
include_guard = "_CTYPE_H"
include_guard = "_RELIBC_CTYPE_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["sys/types.h"]
include_guard = "_DIRENT_H"
include_guard = "_RELIBC_DIRENT_H"
language = "C"
style = "Both"
trailer = "#include <bits/dirent.h>"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_DLFCN_H"
include_guard = "_RELIBC_DLFCN_H"
language = "C"
style = "Type"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["bits/errno.h"]
include_guard = "_ERRNO_H"
include_guard = "_RELIBC_ERRNO_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stdarg.h", "sys/types.h"]
include_guard = "_FCNTL_H"
include_guard = "_RELIBC_FCNTL_H"
trailer = "#include <bits/fcntl.h>"
language = "C"
style = "Tag"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["sys/types.h", "bits/float.h"]
include_guard = "_FLOAT_H"
include_guard = "_RELIBC_FLOAT_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,4 +1,4 @@
include_guard = "_FNMATCH_H"
include_guard = "_RELIBC_FNMATCH_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["unistd.h"]
include_guard = "_GETOPT_H"
include_guard = "_RELIBC_GETOPT_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_GRP_H"
include_guard = "_RELIBC_GRP_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stdint.h", "wchar.h"]
include_guard = "_INTTYPES_H"
include_guard = "_RELIBC_INTTYPES_H"
trailer = "#include <bits/inttypes.h>"
language = "C"
style = "Type"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_LIBGEN_H"
include_guard = "_RELIBC_LIBGEN_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_LIMITS_H"
include_guard = "_RELIBC_LIMITS_H"
trailer = "#include <bits/limits.h>"
language = "C"
style = "Tag"
+1 -1
View File
@@ -1,4 +1,4 @@
include_guard = "_LOCALE_H"
include_guard = "_RELIBC_LOCALE_H"
trailer = "#include <bits/locale.h>"
language = "C"
style = "Tag"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["sys/socket.h", "netinet/in.h"]
include_guard = "_NETDB_H"
include_guard = "_RELIBC_NETDB_H"
trailer = "#include <bits/netdb.h>"
language = "C"
style = "Tag"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_POLL_H"
include_guard = "_RELIBC_POLL_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stddef.h", "sys/types.h"]
include_guard = "_PWD_H"
include_guard = "_RELIBC_PWD_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["sys/types.h"]
include_guard = "_REGEX_H"
include_guard = "_RELIBC_REGEX_H"
language = "C"
style = "Type"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_SEMAPHORE_H"
include_guard = "_RELIBC_SEMAPHORE_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["sys/ioctl.h"]
include_guard = "_SGTTY_H"
include_guard = "_RELIBC_SGTTY_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stdint.h", "sys/types.h"]
include_guard = "_SIGNAL_H"
include_guard = "_RELIBC_SIGNAL_H"
trailer = "#include <bits/signal.h>"
language = "C"
style = "Tag"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stdarg.h", "stddef.h", "stdint.h", "sys/types.h"]
include_guard = "_STDIO_H"
include_guard = "_RELIBC_STDIO_H"
trailer = "#include <bits/stdio.h>"
language = "C"
style = "Type"
+5 -2
View File
@@ -16,6 +16,7 @@ use core::{
use crate::{
c_str::CStr,
c_vec::CVec,
fs::File,
header::{
errno::{self, STR_ERROR},
@@ -1021,9 +1022,11 @@ pub unsafe extern "C" fn vasprintf(
format: *const c_char,
ap: va_list,
) -> c_int {
let mut alloc_writer = platform::AllocStringWriter(ptr::null_mut(), 0);
let mut alloc_writer = CVec::new();
let ret = printf::printf(&mut alloc_writer, format, ap);
*strp = alloc_writer.0 as *mut c_char;
alloc_writer.push(0).unwrap();
alloc_writer.shrink_to_fit().unwrap();
*strp = alloc_writer.leak() as *mut c_char;
ret
}
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stddef.h", "alloca.h"]
include_guard = "_STDLIB_H"
include_guard = "_RELIBC_STDLIB_H"
trailer = "#include <bits/stdlib.h>"
language = "C"
style = "Type"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stddef.h", "stdint.h", "strings.h"]
include_guard = "_STRING_H"
include_guard = "_RELIBC_STRING_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stddef.h", "stdint.h"]
include_guard = "_STRINGS_H"
include_guard = "_RELIBC_STRINGS_H"
language = "C"
style = "Tag"
no_includes = true
+9
View File
@@ -5,3 +5,12 @@ pub struct sockaddr_un {
pub sun_family: sa_family_t,
pub sun_path: [c_char; 108],
}
impl sockaddr_un {
pub fn path_offset(&self) -> usize {
let base = self as *const _ as usize;
let path = &self.sun_path as *const _ as usize;
trace!("base: {:#X}, path: {:#X}", base, path);
path - base
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stdint.h"]
include_guard = "_TERMIOS_H"
include_guard = "_RELIBC_TERMIOS_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["sys/types.h", "stdint.h", "stddef.h"]
include_guard = "_TIME_H"
include_guard = "_RELIBC_TIME_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stddef.h", "stdint.h", "sys/types.h"]
include_guard = "_UNISTD_H"
include_guard = "_RELIBC_UNISTD_H"
trailer = "#include <bits/fcntl.h>\n#include <bits/unistd.h>"
language = "C"
style = "Tag"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = []
include_guard = "_UTIME_H"
include_guard = "_RELIBC_UTIME_H"
language = "C"
style = "Tag"
no_includes = true
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stddef.h", "stdint.h", "time.h", "stdio.h" ]
include_guard = "_WCHAR_H"
include_guard = "_RELIBC_WCHAR_H"
header = "#include <bits/wchar.h>"
language = "C"
style = "Type"
+1 -1
View File
@@ -1,5 +1,5 @@
sys_includes = ["stddef.h", "stdint.h", "time.h", "stdio.h" ]
include_guard = "_WCTYPE_H"
include_guard = "_RELIBC_WCTYPE_H"
header = "#include <bits/wctype.h>"
language = "C"
style = "Type"
+7 -43
View File
@@ -104,45 +104,6 @@ impl Read for FileReader {
}
}
pub struct AllocStringWriter(pub *mut u8, pub usize);
impl Write for AllocStringWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let ptr = unsafe { realloc(self.0 as *mut c_void, self.1 + buf.len() + 1) as *mut u8 };
if ptr.is_null() {
return Err(io::Error::new(
io::ErrorKind::Other,
"AllocStringWriter::write failed to allocate",
));
}
self.0 = ptr;
unsafe {
ptr::copy_nonoverlapping(buf.as_ptr(), self.0.add(self.1), buf.len());
self.1 += buf.len();
*self.0.add(self.1) = 0;
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl fmt::Write for AllocStringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
// can't fail
self.write(s.as_bytes()).unwrap();
Ok(())
}
}
impl WriteByte for AllocStringWriter {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
// can't fail
self.write(&[byte]).unwrap();
Ok(())
}
}
pub struct StringWriter(pub *mut u8, pub usize);
impl Write for StringWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@@ -155,11 +116,14 @@ impl Write for StringWriter {
self.0 = self.0.add(copy_size);
*self.0 = 0;
}
Ok(copy_size)
} else {
Ok(0)
}
// Pretend the entire slice was written. This is because many functions
// (like snprintf) expects a return value that reflects how many bytes
// *would have* been written. So keeping track of this information is
// good, and then if we want the *actual* written size we can just go
// `cmp::min(written, maxlen)`.
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
+5 -5
View File
@@ -29,9 +29,9 @@ impl PalEpoll for Sys {
id: fd as usize,
flags: syscall::EventFlags::from_bits(unsafe { (*event).events as usize })
.expect("epoll: invalid bit pattern"),
// NOTE: Danger when using non 64-bit systems. If this is
// needed, use a box or something
data: unsafe { mem::transmute((*event).data) },
// NOTE: Danger when using something smaller than 64-bit
// systems. If this is needed, use a box or something
data: unsafe { (*event).data.u64 as usize },
},
) as c_int
}
@@ -102,7 +102,7 @@ impl PalEpoll for Sys {
if bytes_read == -1 {
return -1;
}
let read = bytes_read as usize / mem::size_of::<epoll_event>();
let read = bytes_read as usize / mem::size_of::<syscall::Event>();
let mut count = 0;
for i in 0..read {
@@ -117,7 +117,7 @@ impl PalEpoll for Sys {
}
*event_ptr = epoll_event {
events: event.flags.bits() as _,
data: mem::transmute(event.data),
data: epoll_data { u64: event.data as u64, },
..Default::default()
};
count += 1;
+84 -23
View File
@@ -9,6 +9,7 @@ use super::{
use crate::header::{
arpa_inet::inet_aton,
netinet_in::{in_port_t, sockaddr_in, in_addr},
string::strnlen,
sys_socket::{constants::*, sa_family_t, sockaddr, socklen_t},
sys_time::timeval,
sys_un::sockaddr_un,
@@ -32,13 +33,17 @@ macro_rules! bind_or_connect {
0
}};
($mode:ident copy, $socket:expr, $address:expr, $address_len:expr) => {{
if ($address_len as usize) < mem::size_of::<sockaddr>() {
if ($address_len as usize) < mem::size_of::<sa_family_t>() {
errno = syscall::EINVAL;
return -1;
}
let path = match (*$address).sa_family as c_int {
AF_INET => {
if ($address_len as usize) != mem::size_of::<sockaddr_in>() {
errno = syscall::EINVAL;
return -1;
}
let data = &*($address as *const sockaddr_in);
let addr = slice::from_raw_parts(
&data.sin_addr.s_addr as *const _ as *const u8,
@@ -58,14 +63,32 @@ macro_rules! bind_or_connect {
},
AF_UNIX => {
let data = &*($address as *const sockaddr_un);
// NOTE: It's UB to access data in given address that exceeds
// the given address length.
let maxlen = cmp::min(
// Max path length of the full-sized struct
data.sun_path.len(),
// Length inferred from given addrlen
$address_len as usize - data.path_offset()
);
let len = cmp::min(
// The maximum length of the address
maxlen,
// The first NUL byte, if any
strnlen(&data.sun_path as *const _, maxlen as size_t),
);
let addr = slice::from_raw_parts(
&data.sun_path as *const _ as *const u8,
mem::size_of_val(&data.sun_path),
len,
);
let path = format!(
"{}",
str::from_utf8(addr).unwrap()
);
trace!("path: {:?}", path);
path
},
@@ -91,7 +114,7 @@ unsafe fn inner_af_unix(buf: &[u8], address: *mut sockaddr, address_len: *mut so
let path = slice::from_raw_parts_mut(
&mut data.sun_path as *mut _ as *mut u8,
mem::size_of_val(&data.sun_path),
data.sun_path.len(),
);
let len = cmp::min(path.len(), buf.len());
@@ -165,6 +188,19 @@ unsafe fn inner_get_name(
Ok(0)
}
fn socket_kind(mut kind: c_int) -> (c_int, usize) {
let mut flags = O_RDWR;
if kind & SOCK_NONBLOCK == SOCK_NONBLOCK {
kind &= !SOCK_NONBLOCK;
flags |= O_NONBLOCK;
}
if kind & SOCK_CLOEXEC == SOCK_CLOEXEC {
kind &= !SOCK_CLOEXEC;
flags |= O_CLOEXEC;
}
(kind, flags)
}
impl PalSocket for Sys {
unsafe fn accept(socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int {
let stream = e(syscall::dup(socket as usize, b"listen")) as c_int;
@@ -357,7 +393,7 @@ impl PalSocket for Sys {
e(Err(syscall::Error::new(syscall::ENOSYS))) as c_int
}
unsafe fn socket(domain: c_int, mut kind: c_int, protocol: c_int) -> c_int {
unsafe fn socket(domain: c_int, kind: c_int, protocol: c_int) -> c_int {
if domain != AF_INET && domain != AF_UNIX {
errno = syscall::EAFNOSUPPORT;
return -1;
@@ -367,15 +403,7 @@ impl PalSocket for Sys {
// return -1;
// }
let mut flags = O_RDWR;
if kind & SOCK_NONBLOCK == SOCK_NONBLOCK {
kind &= !SOCK_NONBLOCK;
flags |= O_NONBLOCK;
}
if kind & SOCK_CLOEXEC == SOCK_CLOEXEC {
kind &= !SOCK_CLOEXEC;
flags |= O_CLOEXEC;
}
let (kind, flags) = socket_kind(kind);
// The tcp: and udp: schemes allow using no path,
// and later specifying one using `dup`.
@@ -386,19 +414,52 @@ impl PalSocket for Sys {
_ => {
errno = syscall::EPROTONOSUPPORT;
-1
}
},
}
}
fn socketpair(domain: c_int, kind: c_int, protocol: c_int, sv: &mut [c_int; 2]) -> c_int {
eprintln!(
"socketpair({}, {}, {}, {:p})",
domain,
kind,
protocol,
sv.as_mut_ptr()
);
unsafe { errno = syscall::ENOSYS };
return -1;
let (kind, flags) = socket_kind(kind);
match (domain, kind) {
(AF_UNIX, SOCK_STREAM) => {
let listener = e(syscall::open("chan:", flags | O_CREAT));
if listener == !0 {
return -1;
}
// For now, chan: lets connects be instant, and instead blocks
// on any I/O performed. So we don't need to mark this as
// nonblocking.
let fd0 = e(syscall::dup(listener, b"connect"));
if fd0 == !0 {
let _ = syscall::close(listener);
return -1;
}
let fd1 = e(syscall::dup(listener, b"listen"));
if fd1 == !0 {
let _ = syscall::close(fd0);
let _ = syscall::close(listener);
return -1;
}
sv[0] = fd0 as c_int;
sv[1] = fd1 as c_int;
0
},
_ => unsafe {
eprintln!(
"socketpair({}, {}, {}, {:p})",
domain,
kind,
protocol,
sv.as_mut_ptr()
);
errno = syscall::EPROTONOSUPPORT;
-1
},
}
}
}
+4
View File
@@ -60,3 +60,7 @@ Non-finite float madness:
%F: INF -INF NAN -NAN
%g: inf -inf nan -nan
%G: INF -INF NAN -NAN
Testing asprintf...
printed: test string, value: 11
printed: test string 2, value: 13
printed: test string 2, value: 13
+12
View File
@@ -74,4 +74,16 @@ int main(void) {
}
printf("\n");
}
puts("Testing asprintf...");
char *s = NULL;
int res = asprintf(&s, "test string");
printf("printed: %s, value: %d\n", s, res);
free(s);
res = asprintf(&s, "test string %d", 2);
printf("printed: %s, value: %d\n", s, res);
free(s);
res = asprintf(&s, "test %s %d", "string", 2);
printf("printed: %s, value: %d\n", s, res);
free(s);
}
-1
View File
@@ -40,6 +40,5 @@ do
if [ "${status}" != "0" ]
then
echo "# ${name}: failed with status ${status} #"
exit 1
fi
done