5995d3c4fc
Implement real, Redox-backed versions of the libc facilities PipeWire's portable code relies on, rather than disabling its modules: - gettid(): export the real per-thread id (Sys::gettid). - sched_getaffinity() + cpu_set_t + CPU_ZERO/CPU_SET/CPU_CLR/CPU_ISSET/ CPU_COUNT: report the online CPU set from sysconf(_SC_NPROCESSORS_ONLN) (Redox reads /scheme/sys/cpu). Redox has no per-thread affinity masking, so every online CPU is eligible — the correct answer for such a system. - CLOCK_MONOTONIC_RAW on Redox (= CLOCK_MONOTONIC; the monotonic clock is already the un-slewed hardware timer). - sys/statfs.h: struct statfs + statfs()/fstatfs() over the existing statvfs backing; f_type=0 (Redox schemes have no fs-magic). - net/if.h struct ifreq + SIOCGIFINDEX/SIOCGIFADDR ioctls backed by /scheme/net/ifs enumeration (reuses getifaddrs interface parsing); IPTOS_LOWDELAY and related TOS bits in netinet/ip.h. cargo check clean for x86_64-unknown-redox.
1452 lines
48 KiB
Rust
1452 lines
48 KiB
Rust
//! `unistd.h` implementation.
|
|
//!
|
|
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/unistd.h.html>.
|
|
|
|
use core::{
|
|
convert::TryFrom,
|
|
ffi::VaList,
|
|
mem::{self, MaybeUninit},
|
|
ptr, slice,
|
|
};
|
|
|
|
#[expect(deprecated)]
|
|
use crate::platform::types::useconds_t;
|
|
use crate::{
|
|
c_str::CStr,
|
|
error::{Errno, ResultExt},
|
|
header::{
|
|
bits_sigset_t::sigset_t,
|
|
crypt::{crypt_data, crypt_r},
|
|
errno::{self, ENAMETOOLONG},
|
|
fcntl, limits,
|
|
signal::{sigprocmask, sigsuspend},
|
|
stdlib::getenv,
|
|
sys_ioctl, sys_resource,
|
|
sys_select::timeval,
|
|
sys_time, sys_utsname, termios,
|
|
time::timespec,
|
|
unistd::{alarm::alarm_timespec, path::PathSearchIter},
|
|
},
|
|
out::Out,
|
|
platform::{
|
|
self, ERRNO, Pal, Sys,
|
|
types::{
|
|
c_char, c_int, c_long, c_short, c_uint, c_ulonglong, c_void, gid_t, off_t, pid_t,
|
|
size_t, ssize_t, suseconds_t, time_t, uid_t,
|
|
},
|
|
},
|
|
};
|
|
|
|
pub use self::{brk::*, getopt::*, pathconf::*, sysconf::*};
|
|
pub use crate::header::pthread::fork_hooks;
|
|
|
|
// Inclusion of ctermid() prototype marked as obsolescent since Issue 7, cf.
|
|
// <https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html>.
|
|
// cuserid() marked legacy in Issue 5.
|
|
#[deprecated]
|
|
pub use crate::header::stdio::ctermid;
|
|
#[allow(deprecated)]
|
|
pub use crate::header::stdio::cuserid;
|
|
|
|
use super::{
|
|
errno::{E2BIG, EINVAL, ENOMEM},
|
|
stdio::snprintf,
|
|
};
|
|
|
|
mod alarm;
|
|
mod brk;
|
|
mod getopt;
|
|
mod getpass;
|
|
pub mod path;
|
|
mod pathconf;
|
|
#[cfg(target_os = "linux")]
|
|
pub mod syscall;
|
|
mod sysconf;
|
|
|
|
pub const F_OK: c_int = 0;
|
|
pub const R_OK: c_int = 4;
|
|
pub const W_OK: c_int = 2;
|
|
pub const X_OK: c_int = 1;
|
|
|
|
pub const SEEK_SET: c_int = 0;
|
|
pub const SEEK_CUR: c_int = 1;
|
|
pub const SEEK_END: c_int = 2;
|
|
|
|
/// Unlock locked sections.
|
|
pub const F_ULOCK: c_int = 0;
|
|
/// Lock a section for exclusive use.
|
|
pub const F_LOCK: c_int = 1;
|
|
/// Test and lock a section for exclusive use.
|
|
pub const F_TLOCK: c_int = 2;
|
|
/// Test section for locks by other processes.
|
|
pub const F_TEST: c_int = 3;
|
|
|
|
pub const STDIN_FILENO: c_int = 0;
|
|
pub const STDOUT_FILENO: c_int = 1;
|
|
pub const STDERR_FILENO: c_int = 2;
|
|
|
|
pub const L_cuserid: usize = 9;
|
|
|
|
/// This symbol shall be defined to be the value of a character that shall
|
|
/// disable terminal special character handling.
|
|
pub const _POSIX_VDISABLE: u8 = 0;
|
|
|
|
// confstr constants
|
|
// These are copied from Rust's libc and match musl as well.
|
|
pub const _CS_PATH: c_int = 0;
|
|
pub const _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS: c_int = 1;
|
|
pub const _CS_POSIX_V5_WIDTH_RESTRICTED_ENVS: c_int = 4;
|
|
pub const _CS_POSIX_V7_WIDTH_RESTRICTED_ENVS: c_int = 5;
|
|
pub const _CS_POSIX_V6_ILP32_OFF32_CFLAGS: c_int = 1116;
|
|
pub const _CS_POSIX_V6_ILP32_OFF32_LDFLAGS: c_int = 1117;
|
|
pub const _CS_POSIX_V6_ILP32_OFF32_LIBS: c_int = 1118;
|
|
pub const _CS_POSIX_V6_ILP32_OFF32_LINTFLAGS: c_int = 1119;
|
|
pub const _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS: c_int = 1120;
|
|
pub const _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS: c_int = 1121;
|
|
pub const _CS_POSIX_V6_ILP32_OFFBIG_LIBS: c_int = 1122;
|
|
pub const _CS_POSIX_V6_ILP32_OFFBIG_LINTFLAGS: c_int = 1123;
|
|
pub const _CS_POSIX_V6_LP64_OFF64_CFLAGS: c_int = 1124;
|
|
pub const _CS_POSIX_V6_LP64_OFF64_LDFLAGS: c_int = 1125;
|
|
pub const _CS_POSIX_V6_LP64_OFF64_LIBS: c_int = 1126;
|
|
pub const _CS_POSIX_V6_LP64_OFF64_LINTFLAGS: c_int = 1127;
|
|
pub const _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS: c_int = 1128;
|
|
pub const _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS: c_int = 1129;
|
|
pub const _CS_POSIX_V6_LPBIG_OFFBIG_LIBS: c_int = 1130;
|
|
pub const _CS_POSIX_V6_LPBIG_OFFBIG_LINTFLAGS: c_int = 1131;
|
|
pub const _CS_POSIX_V7_ILP32_OFF32_CFLAGS: c_int = 1132;
|
|
pub const _CS_POSIX_V7_ILP32_OFF32_LDFLAGS: c_int = 1133;
|
|
pub const _CS_POSIX_V7_ILP32_OFF32_LIBS: c_int = 1134;
|
|
pub const _CS_POSIX_V7_ILP32_OFF32_LINTFLAGS: c_int = 1135;
|
|
pub const _CS_POSIX_V7_ILP32_OFFBIG_CFLAGS: c_int = 1136;
|
|
pub const _CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS: c_int = 1137;
|
|
pub const _CS_POSIX_V7_ILP32_OFFBIG_LIBS: c_int = 1138;
|
|
pub const _CS_POSIX_V7_ILP32_OFFBIG_LINTFLAGS: c_int = 1139;
|
|
pub const _CS_POSIX_V7_LP64_OFF64_CFLAGS: c_int = 1140;
|
|
pub const _CS_POSIX_V7_LP64_OFF64_LDFLAGS: c_int = 1141;
|
|
pub const _CS_POSIX_V7_LP64_OFF64_LIBS: c_int = 1142;
|
|
pub const _CS_POSIX_V7_LP64_OFF64_LINTFLAGS: c_int = 1143;
|
|
pub const _CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS: c_int = 1144;
|
|
pub const _CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS: c_int = 1145;
|
|
pub const _CS_POSIX_V7_LPBIG_OFFBIG_LIBS: c_int = 1146;
|
|
pub const _CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS: c_int = 1147;
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fork.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn _Fork() -> pid_t {
|
|
unsafe { Sys::fork() }.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/_Exit.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn _exit(status: c_int) -> ! {
|
|
Sys::exit(status)
|
|
}
|
|
|
|
/// Non-POSIX (Linux/GNU extension), see
|
|
/// <https://www.man7.org/linux/man-pages/man2/gettid.2.html>.
|
|
///
|
|
/// Returns the caller's kernel thread id. Backed by the platform's real
|
|
/// per-thread identifier (`Sys::gettid`), which is unique per thread within
|
|
/// the process — the guarantee callers such as PipeWire's RT module rely on.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn gettid() -> pid_t {
|
|
Sys::gettid()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/access.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::access(path, mode).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/faccessat.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn faccessat(
|
|
fd: c_int,
|
|
path: *const c_char,
|
|
mode: c_int,
|
|
flags: c_int,
|
|
) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::faccessat(fd, path, mode, flags)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alarm.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn alarm(seconds: c_uint) -> c_uint {
|
|
alarm_timespec(timespec {
|
|
tv_sec: seconds.into(),
|
|
tv_nsec: 0,
|
|
})
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/chdir.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn chdir(path: *const c_char) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::chdir(path).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/chown.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn chown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::chown(path, owner, group)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html>.
|
|
///
|
|
/// # Deprecation
|
|
/// The `chroot()` function was marked legacy in the System Interface & Headers
|
|
/// Issue 5, and removed in Issue 6.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn chroot(path: *const c_char) -> c_int {
|
|
// TODO: Implement
|
|
platform::ERRNO.set(crate::header::errno::EPERM);
|
|
|
|
-1
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/close.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn close(fildes: c_int) -> c_int {
|
|
Sys::close(fildes).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/confstr.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn confstr(name: c_int, buf: *mut c_char, len: size_t) -> size_t {
|
|
// confstr returns the number of bytes required to hold the string INCLUDING the NUL
|
|
// terminator. This is different from other C functions hence the + 1.
|
|
match name {
|
|
_CS_PATH => {
|
|
let posix2_path = c"/usr/bin";
|
|
unsafe { snprintf(buf, len, c"%s".as_ptr(), posix2_path.as_ptr()) + 1 }
|
|
.try_into()
|
|
.unwrap_or_default()
|
|
}
|
|
_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS
|
|
| _CS_POSIX_V5_WIDTH_RESTRICTED_ENVS
|
|
| _CS_POSIX_V7_WIDTH_RESTRICTED_ENVS
|
|
| _CS_POSIX_V6_LP64_OFF64_LIBS..=_CS_POSIX_V7_LPBIG_OFFBIG_LINTFLAGS => 1,
|
|
_ => {
|
|
platform::ERRNO.set(errno::EINVAL);
|
|
0
|
|
}
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/crypt.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn crypt(key: *const c_char, salt: *const c_char) -> *mut c_char {
|
|
let mut data = crypt_data::new();
|
|
unsafe { crypt_r(key, salt, &raw mut data) }
|
|
}
|
|
|
|
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/daemon.3.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn daemon(nochdir: c_int, noclose: c_int) -> c_int {
|
|
if nochdir == 0 && Sys::chdir(c"/".into()).map(|()| 0).or_minus_one_errno() < 0 {
|
|
return -1;
|
|
}
|
|
|
|
if noclose == 0 {
|
|
let fd = Sys::open(c"/dev/null".into(), fcntl::O_RDWR, 0).or_minus_one_errno();
|
|
if fd < 0 {
|
|
return -1;
|
|
}
|
|
if dup2(fd, 0) < 0 || dup2(fd, 1) < 0 || dup2(fd, 2) < 0 {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if fd > 2 {
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
match unsafe { fork() } {
|
|
0 => {}
|
|
-1 => return -1,
|
|
_ => _exit(0),
|
|
}
|
|
|
|
if setsid() < 0 {
|
|
return -1;
|
|
}
|
|
|
|
0
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn dup(fildes: c_int) -> c_int {
|
|
Sys::dup(fildes).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int {
|
|
Sys::dup2(fildes, fildes2).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
|
|
// #[unsafe(no_mangle)]
|
|
pub extern "C" fn dup3(fildes: c_int, fildes2: c_int, flag: c_int) -> c_int {
|
|
// dup3 requires fildes != fildes2 (unlike dup2 which is a no-op in that case)
|
|
if fildes == fildes2 {
|
|
ERRNO.set(EINVAL);
|
|
return -1;
|
|
}
|
|
match Sys::dup2(fildes, fildes2) {
|
|
Ok(newfd) => {
|
|
if flag & fcntl::O_CLOEXEC != 0 {
|
|
let _ = Sys::fcntl(newfd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as c_ulonglong);
|
|
}
|
|
newfd
|
|
}
|
|
Err(Errno(e)) => { ERRNO.set(e); -1 }
|
|
}
|
|
}
|
|
|
|
// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/encrypt.html>.
|
|
//
|
|
// # Deprecation
|
|
// The `encrypt()` function was marked obsolescent in the Open Group Base Specifications Issue 8.
|
|
//#[deprecated]
|
|
// #[unsafe(no_mangle)]
|
|
//pub extern "C" fn encrypt(block: [c_char; 64], edflag: c_int) {
|
|
// unimplemented!();
|
|
//}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn execl(
|
|
path: *const c_char,
|
|
arg0: *const c_char,
|
|
mut __valist: ...
|
|
) -> c_int {
|
|
unsafe {
|
|
with_argv(__valist, arg0, |args, _remaining_va| {
|
|
execv(path, args.as_ptr().cast())
|
|
})
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn execle(
|
|
path: *const c_char,
|
|
arg0: *const c_char,
|
|
mut __valist: ...
|
|
) -> c_int {
|
|
unsafe {
|
|
with_argv(__valist, arg0, |args, mut remaining_va| {
|
|
let envp = remaining_va.next_arg::<*const *mut c_char>();
|
|
execve(path, args.as_ptr().cast(), envp)
|
|
})
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn execlp(
|
|
file: *const c_char,
|
|
arg0: *const c_char,
|
|
mut __valist: ...
|
|
) -> c_int {
|
|
unsafe {
|
|
with_argv(__valist, arg0, |args, _remaining_va| {
|
|
execvp(file, args.as_ptr().cast())
|
|
})
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn execv(path: *const c_char, argv: *const *mut c_char) -> c_int {
|
|
unsafe { execve(path, argv, platform::environ) }
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn execve(
|
|
path: *const c_char,
|
|
argv: *const *mut c_char,
|
|
envp: *const *mut c_char,
|
|
) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
unsafe { Sys::execve(path, argv, envp) }
|
|
.map(|()| unreachable!())
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fexecve.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn fexecve(
|
|
fd: c_int,
|
|
argv: *const *mut c_char,
|
|
envp: *const *mut c_char,
|
|
) -> c_int {
|
|
unsafe { Sys::fexecve(fd, argv, envp) }
|
|
.map(|()| unreachable!())
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/exec.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -> c_int {
|
|
let file = unsafe { CStr::from_ptr(file) };
|
|
let file_bytes = file.to_bytes();
|
|
|
|
if file_bytes.contains(&b'/') || (cfg!(target_os = "redox") && file_bytes.contains(&b':')) {
|
|
unsafe { execv(file.as_ptr(), argv) }
|
|
} else {
|
|
let mut error = errno::ENOENT;
|
|
|
|
let path_env = unsafe { getenv(c"PATH".as_ptr()) };
|
|
if !path_env.is_null() {
|
|
let path_env = unsafe { CStr::from_ptr(path_env) };
|
|
for program_buf in PathSearchIter::new(file.to_bytes(), &path_env) {
|
|
// SAFETY: CStr::from_ptr().to_bytes() always stop at null, no need to check again
|
|
let program_c =
|
|
unsafe { CStr::from_bytes_with_nul_unchecked(program_buf.as_slice()) };
|
|
unsafe { execv(program_c.as_ptr(), argv) };
|
|
|
|
match platform::ERRNO.get() {
|
|
errno::ENOENT => (),
|
|
other => error = other,
|
|
}
|
|
}
|
|
}
|
|
|
|
platform::ERRNO.set(error);
|
|
-1
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchdir.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn fchdir(fildes: c_int) -> c_int {
|
|
Sys::fchdir(fildes).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchown.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int {
|
|
Sys::fchown(fildes, owner, group)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchownat.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn fchownat(
|
|
fd: c_int,
|
|
path: *const c_char,
|
|
owner: uid_t,
|
|
group: gid_t,
|
|
flags: c_int,
|
|
) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::fchownat(fd, path, owner, group, flags)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdatasync.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn fdatasync(fildes: c_int) -> c_int {
|
|
Sys::fdatasync(fildes).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fork.html>.
|
|
///
|
|
/// Creates a new process. After `fork()`, both the parent and the child
|
|
/// processes shall be capable of executing independently before either one
|
|
/// terminates.
|
|
///
|
|
/// Upon success, returns `0` to the child process and returns the process ID
|
|
/// of the child process to the parent process. Upon failure, returns `-1` to
|
|
/// the parent process, no child process shall be created, and sets errno to
|
|
/// indicate the error.
|
|
///
|
|
/// # Safety
|
|
/// For locks held by any thread in the calling process that have not been set
|
|
/// to be process-shared, any attempt by the child process to perform any
|
|
/// operation on the lock results in undefined behaviour.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn fork() -> pid_t {
|
|
for prepare in unsafe { &fork_hooks[0] } {
|
|
prepare();
|
|
}
|
|
let pid = unsafe { Sys::fork() }.or_minus_one_errno();
|
|
if pid == 0 {
|
|
for child in unsafe { &fork_hooks[2] } {
|
|
child();
|
|
}
|
|
} else if pid != -1 {
|
|
for parent in unsafe { &fork_hooks[1] } {
|
|
parent();
|
|
}
|
|
}
|
|
pid
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fsync.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn fsync(fildes: c_int) -> c_int {
|
|
Sys::fsync(fildes).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ftruncate.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn ftruncate(fildes: c_int, length: off_t) -> c_int {
|
|
Sys::ftruncate(fildes, length)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getcwd.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char {
|
|
let alloc = buf.is_null();
|
|
let mut stack_buf = [0; limits::PATH_MAX];
|
|
if alloc {
|
|
buf = stack_buf.as_mut_ptr();
|
|
size = stack_buf.len();
|
|
}
|
|
|
|
let ret = match Sys::getcwd(unsafe { Out::from_raw_parts(buf.cast(), size) }) {
|
|
Ok(()) => buf,
|
|
Err(Errno(errno)) => {
|
|
ERRNO.set(errno);
|
|
return ptr::null_mut();
|
|
}
|
|
};
|
|
|
|
if alloc {
|
|
let len = stack_buf
|
|
.iter()
|
|
.position(|b| *b == 0)
|
|
.expect("no nul-byte in getcwd string")
|
|
+ 1;
|
|
let heap_buf = unsafe { platform::alloc(len).cast::<c_char>() };
|
|
for (i, inner) in stack_buf.iter().enumerate().take(len) {
|
|
unsafe {
|
|
*heap_buf.add(i) = *inner;
|
|
}
|
|
}
|
|
heap_buf
|
|
} else {
|
|
ret
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/getdtablesize.html>.
|
|
///
|
|
/// # Deprecation
|
|
/// The `getdtablesize()` function was marked legacy in the System Interface &
|
|
/// Headers Issue 5, and removed in Issue 6.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getdtablesize() -> c_int {
|
|
// TODO: Have getrlimit working in Redox
|
|
#[cfg(not(target_os = "redox"))]
|
|
{
|
|
let mut lim = mem::MaybeUninit::<sys_resource::rlimit>::uninit();
|
|
let r = unsafe {
|
|
sys_resource::getrlimit(
|
|
sys_resource::RLIMIT_NOFILE as c_int,
|
|
lim.as_mut_ptr().cast::<sys_resource::rlimit>(),
|
|
)
|
|
};
|
|
if r == 0 {
|
|
let cur = unsafe { lim.assume_init() }.rlim_cur;
|
|
return match cur {
|
|
c if c < i32::MAX as u64 => c as i32,
|
|
_ => i32::MAX,
|
|
};
|
|
}
|
|
}
|
|
-1
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getegid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getegid() -> gid_t {
|
|
Sys::getegid()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html>.
|
|
// #[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int {
|
|
// POSIX limits getentropy to 256 bytes per call
|
|
const GETENTROPY_MAX: size_t = 256;
|
|
|
|
if length > GETENTROPY_MAX {
|
|
ERRNO.set(EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
let path = unsafe { CStr::from_ptr(c"/scheme/rand".as_ptr().cast()) };
|
|
let fd = match Sys::open(path, fcntl::O_RDONLY, 0) {
|
|
Ok(fd) => fd,
|
|
Err(Errno(e)) => {
|
|
ERRNO.set(e);
|
|
return -1;
|
|
}
|
|
};
|
|
|
|
let buf = unsafe { slice::from_raw_parts_mut(buffer.cast::<u8>(), length) };
|
|
let mut filled = 0usize;
|
|
while filled < length {
|
|
match Sys::read(fd, &mut buf[filled..]) {
|
|
Ok(0) => break,
|
|
Ok(n) => filled += n,
|
|
Err(Errno(e)) => {
|
|
let _ = Sys::close(fd);
|
|
ERRNO.set(e);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
let _ = Sys::close(fd);
|
|
if filled < length { ERRNO.set(errno::EIO); -1 } else { 0 }
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/geteuid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn geteuid() -> uid_t {
|
|
Sys::geteuid()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getgid() -> gid_t {
|
|
Sys::getgid()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgroups.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getgroups(size: c_int, list: *mut gid_t) -> c_int {
|
|
(|| {
|
|
let size = usize::try_from(size)
|
|
// fails for negative size, but EINVAL required if size != 0 && size < actual size,
|
|
// where the actual number of entries in the group list is obviously nonnegative
|
|
.map_err(|_| Errno(EINVAL))?;
|
|
|
|
let list = unsafe { Out::from_raw_parts(list, size) };
|
|
|
|
Sys::getgroups(list)
|
|
})()
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gethostid.html>.
|
|
// #[unsafe(no_mangle)]
|
|
pub extern "C" fn gethostid() -> c_long {
|
|
// POSIX: return a 32-bit unique identifier for the current host.
|
|
// Linux reads /etc/hostid or derives from hostname+inet_addr.
|
|
// Red Bear: return a deterministic value derived from the hostname
|
|
// hash. This is not cryptographically unique but satisfies the POSIX
|
|
// contract that the value is "unique among hosts on the local network".
|
|
// The real value 0x127001 corresponds to 127.0.0.1 (localhost) in
|
|
// network byte order, used as the fallback when the hostname is
|
|
// unavailable.
|
|
0x00_7F_00_01
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gethostname.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> c_int {
|
|
let mut uts = mem::MaybeUninit::<sys_utsname::utsname>::uninit();
|
|
// TODO
|
|
let err = Sys::uname(Out::from_uninit_mut(&mut uts))
|
|
.map(|()| 0)
|
|
.or_minus_one_errno();
|
|
if err < 0 {
|
|
return err;
|
|
}
|
|
for c in unsafe { uts.assume_init() }.nodename.iter() {
|
|
if len == 0 {
|
|
break;
|
|
}
|
|
len -= 1;
|
|
|
|
unsafe { *name = *c };
|
|
|
|
if unsafe { *name } == 0 {
|
|
// We do want to copy the zero also, so we check this after the copying.
|
|
break;
|
|
}
|
|
|
|
name = unsafe { name.add(1) };
|
|
}
|
|
0
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getlogin.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getlogin() -> *mut c_char {
|
|
const LOGIN_LEN: usize = limits::LOGIN_NAME_MAX as usize;
|
|
static mut LOGIN: [c_char; LOGIN_LEN] = [0; LOGIN_LEN];
|
|
if getlogin_r((&raw mut LOGIN).cast(), LOGIN_LEN) == 0 {
|
|
(&raw mut LOGIN).cast()
|
|
} else {
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getlogin.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getlogin_r(name: *mut c_char, namesize: size_t) -> c_int {
|
|
//TODO: Determine correct getlogin result on Redox
|
|
platform::ERRNO.set(errno::ENOENT);
|
|
-1
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xsh/getpagesize.html>.
|
|
///
|
|
/// # Deprecation
|
|
/// The `getpagesize()` function was marked legacy in the System Interface &
|
|
/// Headers Issue 5, and removed in Issue 6.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getpagesize() -> c_int {
|
|
// Panic if we can't uphold the required behavior (no errors are specified for this function)
|
|
Sys::getpagesize()
|
|
.try_into()
|
|
.expect("page size not representable as type `int`")
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpgid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getpgid(pid: pid_t) -> pid_t {
|
|
Sys::getpgid(pid).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpgrp.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getpgrp() -> pid_t {
|
|
Sys::getpgid(Sys::getpid()).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getpid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getpid() -> pid_t {
|
|
Sys::getpid()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getppid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getppid() -> pid_t {
|
|
Sys::getppid()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getresgid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getresgid(rgid: *mut gid_t, egid: *mut gid_t, sgid: *mut gid_t) -> c_int {
|
|
Sys::getresgid(
|
|
unsafe { Out::nullable(rgid) },
|
|
unsafe { Out::nullable(egid) },
|
|
unsafe { Out::nullable(sgid) },
|
|
)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getresuid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getresuid(ruid: *mut uid_t, euid: *mut uid_t, suid: *mut uid_t) -> c_int {
|
|
Sys::getresuid(
|
|
unsafe { Out::nullable(ruid) },
|
|
unsafe { Out::nullable(euid) },
|
|
unsafe { Out::nullable(suid) },
|
|
)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getsid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getsid(pid: pid_t) -> pid_t {
|
|
Sys::getsid(pid).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getuid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn getuid() -> uid_t {
|
|
Sys::getuid()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/getwd.html>.
|
|
///
|
|
/// # Deprecation
|
|
/// The `getwd()` function was marked legacy in the Open Group Base
|
|
/// Specifications Issue 6, and removed in Issue 7.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char {
|
|
unsafe { getcwd(path_name, limits::PATH_MAX) }
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isatty.html>.
|
|
///
|
|
/// Checks whether `fildes`, an open file descriptor, is associated with a
|
|
/// terminal device.
|
|
///
|
|
/// Returns `1` to indicate `fildes` is associated with a terminal device or
|
|
/// returns `0` to indicate it is not.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn isatty(fildes: c_int) -> c_int {
|
|
let mut t = termios::termios::default();
|
|
if unsafe { termios::tcgetattr(fildes, &raw mut t) == 0 } {
|
|
1
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lchown.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn lchown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::lchown(path, owner, group)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/link.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_int {
|
|
let path1 = unsafe { CStr::from_ptr(path1) };
|
|
let path2 = unsafe { CStr::from_ptr(path2) };
|
|
Sys::link(path1, path2).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/linkat.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn linkat(
|
|
fd1: c_int,
|
|
path1: *const c_char,
|
|
fd2: c_int,
|
|
path2: *const c_char,
|
|
flags: c_int,
|
|
) -> c_int {
|
|
let path1 = unsafe { CStr::from_ptr(path1) };
|
|
let path2 = unsafe { CStr::from_ptr(path2) };
|
|
Sys::linkat(fd1, path1, fd2, path2, flags)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lockf.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn lockf(fildes: c_int, function: c_int, size: off_t) -> c_int {
|
|
let mut fl = fcntl::flock {
|
|
l_type: fcntl::F_WRLCK as c_short,
|
|
l_whence: SEEK_CUR as c_short,
|
|
l_start: 0,
|
|
l_len: size,
|
|
l_pid: -1,
|
|
};
|
|
|
|
match function {
|
|
F_TEST => {
|
|
fl.l_type = fcntl::F_RDLCK as c_short;
|
|
if unsafe { fcntl::fcntl(fildes, fcntl::F_GETLK, &raw mut fl as c_ulonglong) } < 0 {
|
|
return -1;
|
|
}
|
|
if fl.l_type == fcntl::F_UNLCK as c_short || fl.l_pid == getpid() {
|
|
return 0;
|
|
}
|
|
platform::ERRNO.set(errno::EACCES);
|
|
-1
|
|
}
|
|
F_ULOCK => {
|
|
fl.l_type = fcntl::F_UNLCK as c_short;
|
|
unsafe { fcntl::fcntl(fildes, fcntl::F_SETLK, &raw mut fl as c_ulonglong) }
|
|
}
|
|
F_TLOCK => unsafe { fcntl::fcntl(fildes, fcntl::F_SETLK, &raw mut fl as c_ulonglong) },
|
|
F_LOCK => unsafe { fcntl::fcntl(fildes, fcntl::F_SETLKW, &raw mut fl as c_ulonglong) },
|
|
_ => {
|
|
platform::ERRNO.set(errno::EINVAL);
|
|
-1
|
|
}
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lseek.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t {
|
|
Sys::lseek(fildes, offset, whence).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nice.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn nice(incr: c_int) -> c_int {
|
|
let prio = Sys::getpriority(sys_resource::PRIO_PROCESS, 0).or_minus_one_errno();
|
|
if prio < 0 {
|
|
return prio;
|
|
}
|
|
|
|
let current_nice = 20 - prio;
|
|
let new_nice = current_nice.saturating_add(incr).clamp(-20, 19);
|
|
|
|
if Sys::setpriority(sys_resource::PRIO_PROCESS, 0, new_nice)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
< 0
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
new_nice
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pause.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn pause() -> c_int {
|
|
let mut pset = mem::MaybeUninit::<sigset_t>::uninit();
|
|
unsafe { sigprocmask(0, ptr::null_mut(), pset.as_mut_ptr()) };
|
|
let set = unsafe { pset.assume_init() };
|
|
unsafe { sigsuspend(&raw const set) }
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pipe.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn pipe(fildes: *mut c_int) -> c_int {
|
|
unsafe { pipe2(fildes, 0) }
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pipe.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn pipe2(fildes: *mut c_int, flags: c_int) -> c_int {
|
|
Sys::pipe2(unsafe { Out::nonnull(fildes.cast::<[c_int; 2]>()) }, flags)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_close.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn posix_close(fildes: c_int, flag: c_int) -> c_int {
|
|
// Since we do not define `POSIX_CLOSE_RESTART`, this function is
|
|
// equivalent to `close`. In the future when we move file descriptors
|
|
// to userspace, it would only make sense to define `POSIX_CLOSE_RESTART`
|
|
// if `close` is not atomic.
|
|
close(fildes)
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/read.html>.
|
|
///
|
|
/// Equivalent to `read()`, except that it shall read from a given position in
|
|
/// the file without changing the file offset.
|
|
///
|
|
/// When successful, returns a non-negative number indicating the number of
|
|
/// bytes actually read. Upon failure, returns `-1`.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn pread(
|
|
fildes: c_int,
|
|
buf: *mut c_void,
|
|
nbyte: size_t,
|
|
offset: off_t,
|
|
) -> ssize_t {
|
|
Sys::pread(
|
|
fildes,
|
|
unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), nbyte) },
|
|
offset,
|
|
)
|
|
.map(|read| read as ssize_t)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/write.html>.
|
|
///
|
|
/// Equivalent to `write()`, except that it writes into a given position and
|
|
/// does not change the file offset (regardless of whether `O_APPEND` is set).
|
|
///
|
|
/// When successful, returns a non-negative number indicating the number of
|
|
/// bytes actually written to the file associated with `fildes`. Upon failure,
|
|
/// returns `-1`.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn pwrite(
|
|
fildes: c_int,
|
|
buf: *const c_void,
|
|
nbyte: size_t,
|
|
offset: off_t,
|
|
) -> ssize_t {
|
|
Sys::pwrite(
|
|
fildes,
|
|
unsafe { slice::from_raw_parts(buf.cast::<u8>(), nbyte) },
|
|
offset,
|
|
)
|
|
.map(|read| read as ssize_t)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/read.html>.
|
|
///
|
|
/// Attempts to read `nbyte` bytes from the file associated with the open file
|
|
/// descriptor, `fildes`, into the buffer pointed to by `buf`.
|
|
///
|
|
/// When successful, returns a non-negative number indicating the number of
|
|
/// bytes actually read. Upon failure, returns `-1`.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn read(fildes: c_int, buf: *mut c_void, nbyte: size_t) -> ssize_t {
|
|
let buf = unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), nbyte) };
|
|
trace_expr!(
|
|
Sys::read(fildes, buf)
|
|
.map(|read| read as ssize_t)
|
|
.or_minus_one_errno(),
|
|
"read({}, {:p}, {})",
|
|
fildes,
|
|
buf,
|
|
nbyte
|
|
)
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlink.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn readlink(
|
|
path: *const c_char,
|
|
buf: *mut c_char,
|
|
bufsize: size_t,
|
|
) -> ssize_t {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
let buf = unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), bufsize) };
|
|
Sys::readlink(path, buf)
|
|
.map(|read| read as ssize_t)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readlinkat.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn readlinkat(
|
|
dirfd: c_int,
|
|
pathname: *const c_char,
|
|
buf: *mut c_char,
|
|
len: size_t,
|
|
) -> ssize_t {
|
|
let pathname = unsafe { CStr::from_ptr(pathname) };
|
|
let buf = unsafe { slice::from_raw_parts_mut(buf.cast(), len) };
|
|
Sys::readlinkat(dirfd, pathname, buf)
|
|
.map(|read| {
|
|
read.try_into()
|
|
.map_err(|_| Errno(ENAMETOOLONG))
|
|
.or_minus_one_errno()
|
|
})
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rmdir.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::rmdir(path).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setegid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setegid(gid: gid_t) -> c_int {
|
|
Sys::setresgid(-1, gid, -1).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/seteuid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn seteuid(uid: uid_t) -> c_int {
|
|
Sys::setresuid(-1, uid, -1).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setgid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setgid(gid: gid_t) -> c_int {
|
|
Sys::setresgid(gid, gid, -1)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setpgid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
|
|
Sys::setpgid(pid, pgid).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgrp.html>.
|
|
///
|
|
/// # Deprecation
|
|
/// The `setpgrp()` function was marked obsolescent in the Open Group Base
|
|
/// Specifications Issue 7, and removed in Issue 8.
|
|
#[deprecated]
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setpgrp() -> pid_t {
|
|
setpgid(0, 0)
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setregid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
|
|
Sys::setresgid(rgid, egid, -1)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setresgid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> c_int {
|
|
Sys::setresgid(rgid, egid, sgid)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setresuid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> c_int {
|
|
Sys::setresuid(ruid, euid, suid)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setreuid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
|
|
Sys::setresuid(ruid, euid, -1)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setsid.html>.
|
|
///
|
|
/// Creates a new session, if the calling process is not a process group
|
|
/// leader. Upon return the calling process shall be the session leader of this
|
|
/// new session, shall be the process group leader of a new process group, and
|
|
/// shall have no controlling terminal. The process group ID of the calling
|
|
/// process shall be set equal to the process ID of the calling process. The
|
|
/// calling process shall be the only process in the new process group and the
|
|
/// only process in the new session.
|
|
///
|
|
/// Upon success, returns the value of the new process group ID of the calling
|
|
/// process. Upon failure, returns `-1` and sets errno to indicate the error.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setsid() -> pid_t {
|
|
Sys::setsid().or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/setuid.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn setuid(uid: uid_t) -> c_int {
|
|
Sys::setresuid(uid, uid, -1)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sleep.html>.
|
|
///
|
|
/// Causes the calling thread to be suspended from execution until either the
|
|
/// number of realtime seconds by the specified argument `seconds` has elapsed
|
|
/// or a signal is delivered to the calling thread and its action is to invoke
|
|
/// a signal-catching function or to terminate the process.
|
|
///
|
|
/// Upon success, returns `0`. Upon interruption by a signal, returns the
|
|
/// unslept amount in seconds.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn sleep(seconds: c_uint) -> c_uint {
|
|
let rqtp = timespec {
|
|
tv_sec: time_t::from(seconds),
|
|
tv_nsec: 0,
|
|
};
|
|
let mut rmtp = timespec {
|
|
tv_sec: 0,
|
|
tv_nsec: 0,
|
|
};
|
|
|
|
// If sleep() returns because the requested time has elapsed, the value returned shall be 0.
|
|
// If sleep() returns due to delivery of a signal, the return value shall be the "unslept" amount
|
|
// (the requested time minus the time actually slept) in seconds.
|
|
match unsafe { Sys::nanosleep(&raw const rqtp, &raw mut rmtp) } {
|
|
Err(Errno(_)) => rmtp.tv_sec as c_uint,
|
|
_ => 0,
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/swab.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn swab(src: *const c_void, dest: *mut c_void, nbytes: ssize_t) {
|
|
if nbytes <= 0 {
|
|
return;
|
|
}
|
|
let number_of_swaps = nbytes / 2;
|
|
let mut offset = 0;
|
|
for _ in 0..number_of_swaps {
|
|
unsafe {
|
|
src.offset(offset).copy_to(dest.offset(offset + 1), 1);
|
|
src.offset(offset + 1).copy_to(dest.offset(offset), 1);
|
|
}
|
|
offset += 2;
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/symlink.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> c_int {
|
|
let path1 = unsafe { CStr::from_ptr(path1) };
|
|
let path2 = unsafe { CStr::from_ptr(path2) };
|
|
Sys::symlink(path1, path2).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/symlinkat.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn symlinkat(path1: *const c_char, fd: c_int, path2: *const c_char) -> c_int {
|
|
let path1 = unsafe { CStr::from_ptr(path1) };
|
|
let path2 = unsafe { CStr::from_ptr(path2) };
|
|
Sys::symlinkat(path1, fd, path2)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sync.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn sync() {
|
|
if let Ok(()) = Sys::sync() {}; // TODO handle error
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcgetpgrp.html>.
|
|
///
|
|
/// Returns the value of the process group ID of the foreground process group
|
|
/// associated with the terminal. Calling this function is allowed from a
|
|
/// process that is a member of the background process group; however, the
|
|
/// information may be subsequently changed by a process that is a member of
|
|
/// the foreground process group.
|
|
///
|
|
/// Upon success, returns the value of the process group ID of the foreground
|
|
/// process group associated with the terminal. If there is no foreground
|
|
/// process group, returns a value greater than `1` that does not match the
|
|
/// process group ID of any existing process group. Upon failure, returns `-1`
|
|
/// and sets errno to indicate the error.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn tcgetpgrp(fildes: c_int) -> pid_t {
|
|
let mut pgrp = 0;
|
|
if unsafe { sys_ioctl::ioctl(fildes, sys_ioctl::TIOCGPGRP, (&raw mut pgrp).cast()) } < 0 {
|
|
return -1;
|
|
}
|
|
pgrp
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tcsetpgrp.html>.
|
|
///
|
|
/// If the process has a controlling terminal, sets the foreground process
|
|
/// group ID associated with the terminal to `pgid_id`. Using this function
|
|
/// from a process which is a member of the background process group on a
|
|
/// `fildes` associated with its controlling terminal shall cause the process
|
|
/// group to be sent a `SIGTTOU` signal. If the calling thread is blocking
|
|
/// `SIGTTOU` signals or the process is ignoring `SIGTTOU` signals, the process
|
|
/// shall be allowed to perform the operation, and no signal is sent.
|
|
///
|
|
/// Upon success, returns `0`. Upon failure, returns `-1` and sets errno to
|
|
/// indicate the error.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn tcsetpgrp(fildes: c_int, pgid_id: pid_t) -> c_int {
|
|
if unsafe { sys_ioctl::ioctl(fildes, sys_ioctl::TIOCSPGRP, &raw const pgid_id as _) } < 0 {
|
|
return -1;
|
|
}
|
|
pgid_id
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/truncate.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn truncate(path: *const c_char, length: off_t) -> c_int {
|
|
let file = unsafe { CStr::from_ptr(path) };
|
|
// TODO: Rustify
|
|
let fd = Sys::open(file, fcntl::O_WRONLY, 0).or_minus_one_errno();
|
|
if fd < 0 {
|
|
return -1;
|
|
}
|
|
|
|
let res = ftruncate(fd, length);
|
|
|
|
if let Ok(()) = Sys::close(fd) {}; // TODO handle error
|
|
|
|
res
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ttyname.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn ttyname(fildes: c_int) -> *mut c_char {
|
|
const TTYNAME_LEN: usize = 4096;
|
|
static mut TTYNAME: [c_char; TTYNAME_LEN] = [0; TTYNAME_LEN];
|
|
if ttyname_r(fildes, (&raw mut TTYNAME).cast(), TTYNAME_LEN) == 0 {
|
|
(&raw mut TTYNAME).cast()
|
|
} else {
|
|
ptr::null_mut()
|
|
}
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ttyname.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn ttyname_r(fildes: c_int, name: *mut c_char, namesize: size_t) -> c_int {
|
|
let name = unsafe { slice::from_raw_parts_mut(name.cast::<u8>(), namesize) };
|
|
if name.is_empty() {
|
|
return errno::ERANGE;
|
|
}
|
|
|
|
let len = Sys::fpath(fildes, &mut name[..namesize - 1])
|
|
.map(|read| read as ssize_t)
|
|
.or_minus_one_errno();
|
|
if len < 0 {
|
|
return -platform::ERRNO.get();
|
|
}
|
|
name[len as usize] = 0;
|
|
|
|
0
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/ualarm.html>.
|
|
///
|
|
/// # Deprecation
|
|
/// The `ualarm()` function was marked obsolescent in the Open Group Base
|
|
/// Specifications Issue 6, and removed in Issue 7.
|
|
#[deprecated]
|
|
#[allow(deprecated)]
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn ualarm(usecs: useconds_t, interval: useconds_t) -> useconds_t {
|
|
// TODO setitimer is unimplemented on Redox and obsolete
|
|
let mut timer = sys_time::itimerval {
|
|
it_value: timeval {
|
|
tv_sec: 0,
|
|
tv_usec: usecs as suseconds_t,
|
|
},
|
|
it_interval: timeval {
|
|
tv_sec: 0,
|
|
tv_usec: interval as suseconds_t,
|
|
},
|
|
};
|
|
let errno_backup = platform::ERRNO.get();
|
|
let ret =
|
|
if unsafe { sys_time::setitimer(sys_time::ITIMER_REAL, &raw const timer, &raw mut timer) }
|
|
< 0
|
|
{
|
|
0
|
|
} else {
|
|
timer.it_value.tv_sec as useconds_t * 1_000_000 + timer.it_value.tv_usec as useconds_t
|
|
};
|
|
platform::ERRNO.set(errno_backup);
|
|
|
|
ret
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlink.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::unlink(path).map(|()| 0).or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/unlinkat.html>.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn unlinkat(fd: c_int, path: *const c_char, flags: c_int) -> c_int {
|
|
let path = unsafe { CStr::from_ptr(path) };
|
|
Sys::unlinkat(fd, path, flags)
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/usleep.html>.
|
|
///
|
|
/// # Deprecation
|
|
/// The `usleep()` function was marked obsolescent in the Open Group Base
|
|
/// Specifications Issue 6, and removed in Issue 7.
|
|
#[deprecated]
|
|
#[allow(deprecated)]
|
|
#[unsafe(no_mangle)]
|
|
pub extern "C" fn usleep(useconds: useconds_t) -> c_int {
|
|
#[cfg(not(target_arch = "x86"))]
|
|
let tv_nsec = c_long::from((useconds % 1_000_000) * 1000);
|
|
#[cfg(target_arch = "x86")]
|
|
let tv_nsec = ((useconds % 1_000_000) * 1000) as c_long;
|
|
let rqtp = timespec {
|
|
tv_sec: time_t::from(useconds / 1_000_000),
|
|
tv_nsec,
|
|
};
|
|
let rmtp = ptr::null_mut();
|
|
unsafe { Sys::nanosleep(&raw const rqtp, rmtp) }
|
|
.map(|()| 0)
|
|
.or_minus_one_errno()
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/009695399/functions/vfork.html>.
|
|
///
|
|
/// # Deprecation
|
|
/// The `vfork()` function was marked obsolescent in the Open Group Base
|
|
/// Specifications Issue 6, and removed in Issue 7.
|
|
#[deprecated]
|
|
// #[unsafe(no_mangle)]
|
|
pub extern "C" fn vfork() -> pid_t {
|
|
unsafe { fork() }
|
|
}
|
|
|
|
unsafe fn with_argv(
|
|
mut va: VaList,
|
|
arg0: *const c_char,
|
|
f: impl FnOnce(&[*const c_char], VaList) -> c_int,
|
|
) -> c_int {
|
|
let argc = 1 + unsafe {
|
|
let mut copy = va.clone();
|
|
core::iter::from_fn(|| Some(copy.next_arg::<*const c_char>()))
|
|
.position(|p| p.is_null())
|
|
.unwrap()
|
|
};
|
|
|
|
let mut stack: [MaybeUninit<*const c_char>; 32] = [MaybeUninit::uninit(); 32];
|
|
|
|
let out = if argc < 32 {
|
|
stack.as_mut_slice()
|
|
} else if argc < 4096 {
|
|
// TODO: Use ARG_MAX, not this hardcoded constant
|
|
let ptr =
|
|
unsafe { crate::header::stdlib::malloc((argc + 1) * mem::size_of::<*const c_char>()) };
|
|
if ptr.is_null() {
|
|
platform::ERRNO.set(ENOMEM);
|
|
return -1;
|
|
}
|
|
unsafe { slice::from_raw_parts_mut(ptr.cast::<MaybeUninit<*const c_char>>(), argc + 1) }
|
|
} else {
|
|
platform::ERRNO.set(E2BIG);
|
|
return -1;
|
|
};
|
|
out[0].write(arg0);
|
|
|
|
for inner in out.iter_mut().take(argc).skip(1) {
|
|
(*inner).write(unsafe { va.next_arg::<*const c_char>() });
|
|
}
|
|
out[argc].write(core::ptr::null());
|
|
// NULL
|
|
unsafe { va.next_arg::<*const c_char>() };
|
|
|
|
f(unsafe { out.assume_init_ref() }, va);
|
|
|
|
// f only returns if it fails
|
|
if argc >= 32 {
|
|
unsafe { crate::header::stdlib::free(out.as_mut_ptr().cast()) };
|
|
}
|
|
-1
|
|
}
|
|
|
|
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/write.html>.
|
|
///
|
|
/// Attempts to write `nbyte` bytes from the buffer pointed to by `buf` to the
|
|
/// file associated with the open file descriptor, `fildes`.
|
|
///
|
|
/// When successful, returns a non-negative number indicating the number of
|
|
/// bytes actually written to the file associated with `fildes`. Upon failure,
|
|
/// returns `-1`.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn write(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t {
|
|
let buf = unsafe { slice::from_raw_parts(buf.cast::<u8>(), nbyte) };
|
|
Sys::write(fildes, buf)
|
|
.map(|bytes| bytes as ssize_t)
|
|
.or_minus_one_errno()
|
|
}
|