1 //! unistd implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/unistd.h.html 2 3 use core::{convert::TryFrom, mem, ptr, slice}; 4 5 use crate::{ 6 c_str::CStr, 7 header::{ 8 errno, fcntl, limits, stdlib::getenv, sys_ioctl, sys_time, sys_utsname, termios, 9 time::timespec, 10 }, 11 platform::{self, types::*, Pal, Sys}, 12 }; 13 use alloc::collections::LinkedList; 14 15 pub use self::{brk::*, getopt::*, pathconf::*, sysconf::*}; 16 17 mod brk; 18 mod getopt; 19 mod pathconf; 20 mod sysconf; 21 22 pub const F_OK: c_int = 0; 23 pub const R_OK: c_int = 4; 24 pub const W_OK: c_int = 2; 25 pub const X_OK: c_int = 1; 26 27 pub const SEEK_SET: c_int = 0; 28 pub const SEEK_CUR: c_int = 1; 29 pub const SEEK_END: c_int = 2; 30 31 pub const F_ULOCK: c_int = 0; 32 pub const F_LOCK: c_int = 1; 33 pub const F_TLOCK: c_int = 2; 34 pub const F_TEST: c_int = 3; 35 36 pub const STDIN_FILENO: c_int = 0; 37 pub const STDOUT_FILENO: c_int = 1; 38 pub const STDERR_FILENO: c_int = 2; 39 40 #[thread_local] 41 pub static mut fork_hooks_static: Option<[LinkedList<extern "C" fn()>; 3]> = None; 42 43 unsafe fn init_fork_hooks<'a>() -> &'a mut [LinkedList<extern "C" fn()>; 3] { 44 // Transmute the lifetime so we can return here. Should be safe as 45 // long as one does not access the original fork_hooks. 46 mem::transmute( 47 fork_hooks_static 48 .get_or_insert_with(|| [LinkedList::new(), LinkedList::new(), LinkedList::new()]), 49 ) 50 } 51 52 #[no_mangle] 53 pub extern "C" fn _exit(status: c_int) { 54 Sys::exit(status) 55 } 56 57 #[no_mangle] 58 pub unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> c_int { 59 let path = CStr::from_ptr(path); 60 Sys::access(path, mode) 61 } 62 63 #[no_mangle] 64 pub extern "C" fn alarm(seconds: c_uint) -> c_uint { 65 let mut timer = sys_time::itimerval { 66 it_value: sys_time::timeval { 67 tv_sec: seconds as time_t, 68 tv_usec: 0, 69 }, 70 ..Default::default() 71 }; 72 let errno_backup = unsafe { platform::errno }; 73 let secs = if sys_time::setitimer(sys_time::ITIMER_REAL, &timer, &mut timer) < 0 { 74 0 75 } else { 76 timer.it_value.tv_sec as c_uint + if timer.it_value.tv_usec > 0 { 1 } else { 0 } 77 }; 78 unsafe { 79 platform::errno = errno_backup; 80 } 81 82 secs 83 } 84 85 #[no_mangle] 86 pub unsafe extern "C" fn chdir(path: *const c_char) -> c_int { 87 let path = CStr::from_ptr(path); 88 Sys::chdir(path) 89 } 90 91 #[no_mangle] 92 pub extern "C" fn chroot(path: *const c_char) -> c_int { 93 // TODO: Implement 94 unsafe { 95 platform::errno = crate::header::errno::EPERM; 96 } 97 98 -1 99 } 100 101 #[no_mangle] 102 pub unsafe extern "C" fn chown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int { 103 let path = CStr::from_ptr(path); 104 Sys::chown(path, owner, group) 105 } 106 107 #[no_mangle] 108 pub extern "C" fn close(fildes: c_int) -> c_int { 109 Sys::close(fildes) 110 } 111 112 // #[no_mangle] 113 pub extern "C" fn confstr(name: c_int, buf: *mut c_char, len: size_t) -> size_t { 114 unimplemented!(); 115 } 116 117 // #[no_mangle] 118 pub extern "C" fn crypt(key: *const c_char, salt: *const c_char) -> *mut c_char { 119 unimplemented!(); 120 } 121 122 #[no_mangle] 123 pub extern "C" fn dup(fildes: c_int) -> c_int { 124 Sys::dup(fildes) 125 } 126 127 #[no_mangle] 128 pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int { 129 Sys::dup2(fildes, fildes2) 130 } 131 132 // #[no_mangle] 133 pub extern "C" fn encrypt(block: [c_char; 64], edflag: c_int) { 134 unimplemented!(); 135 } 136 137 // #[no_mangle] 138 // pub extern "C" fn execl(path: *const c_char, args: *const *mut c_char) -> c_int { 139 // unimplemented!(); 140 // } 141 142 // #[no_mangle] 143 // pub extern "C" fn execle( 144 // path: *const c_char, 145 // args: *const *mut c_char, 146 // envp: *const *mut c_char, 147 // ) -> c_int { 148 // unimplemented!(); 149 // } 150 151 // #[no_mangle] 152 // pub extern "C" fn execlp(file: *const c_char, args: *const *mut c_char) -> c_int { 153 // unimplemented!(); 154 // } 155 156 #[no_mangle] 157 pub unsafe extern "C" fn execv(path: *const c_char, argv: *const *mut c_char) -> c_int { 158 execve(path, argv, platform::environ) 159 } 160 161 #[no_mangle] 162 pub unsafe extern "C" fn execve( 163 path: *const c_char, 164 argv: *const *mut c_char, 165 envp: *const *mut c_char, 166 ) -> c_int { 167 let path = CStr::from_ptr(path); 168 Sys::execve(path, argv, envp) 169 } 170 171 #[cfg(target_os = "linux")] 172 const PATH_SEPARATOR: u8 = b':'; 173 174 #[cfg(target_os = "redox")] 175 const PATH_SEPARATOR: u8 = b';'; 176 177 #[no_mangle] 178 pub unsafe extern "C" fn execvp(file: *const c_char, argv: *const *mut c_char) -> c_int { 179 let file = CStr::from_ptr(file); 180 181 if file.to_bytes().contains(&b'/') 182 || (cfg!(target_os = "redox") && file.to_bytes().contains(&b':')) 183 { 184 execv(file.as_ptr(), argv) 185 } else { 186 let mut error = errno::ENOENT; 187 188 let path_env = getenv(c_str!("PATH\0").as_ptr()); 189 if !path_env.is_null() { 190 let path_env = CStr::from_ptr(path_env); 191 for path in path_env.to_bytes().split(|&b| b == PATH_SEPARATOR) { 192 let mut program = path.to_vec(); 193 program.push(b'/'); 194 program.extend_from_slice(file.to_bytes()); 195 program.push(b'\0'); 196 197 let program_c = CStr::from_bytes_with_nul(&program).unwrap(); 198 execv(program_c.as_ptr(), argv); 199 200 match platform::errno { 201 errno::ENOENT => (), 202 other => error = other, 203 } 204 } 205 } 206 207 platform::errno = error; 208 -1 209 } 210 } 211 212 #[no_mangle] 213 pub extern "C" fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int { 214 Sys::fchown(fildes, owner, group) 215 } 216 217 #[no_mangle] 218 pub extern "C" fn fchdir(fildes: c_int) -> c_int { 219 Sys::fchdir(fildes) 220 } 221 222 // #[no_mangle] 223 pub extern "C" fn fdatasync(fildes: c_int) -> c_int { 224 unimplemented!(); 225 } 226 227 #[no_mangle] 228 pub extern "C" fn fork() -> pid_t { 229 let fork_hooks = unsafe { init_fork_hooks() }; 230 for prepare in &fork_hooks[0] { 231 prepare(); 232 } 233 let pid = Sys::fork(); 234 if pid == 0 { 235 for child in &fork_hooks[2] { 236 child(); 237 } 238 } else if pid != -1 { 239 for parent in &fork_hooks[1] { 240 parent(); 241 } 242 } 243 pid 244 } 245 246 #[no_mangle] 247 pub extern "C" fn fsync(fildes: c_int) -> c_int { 248 Sys::fsync(fildes) 249 } 250 251 #[no_mangle] 252 pub extern "C" fn ftruncate(fildes: c_int, length: off_t) -> c_int { 253 Sys::ftruncate(fildes, length) 254 } 255 256 #[no_mangle] 257 pub extern "C" fn getcwd(mut buf: *mut c_char, mut size: size_t) -> *mut c_char { 258 let alloc = buf.is_null(); 259 let mut stack_buf = [0; limits::PATH_MAX]; 260 if alloc { 261 buf = stack_buf.as_mut_ptr(); 262 size = stack_buf.len(); 263 } 264 265 let ret = Sys::getcwd(buf, size); 266 if ret.is_null() { 267 return ptr::null_mut(); 268 } 269 270 if alloc { 271 let len = stack_buf 272 .iter() 273 .position(|b| *b == 0) 274 .expect("no nul-byte in getcwd string") 275 + 1; 276 let heap_buf = unsafe { platform::alloc(len) as *mut c_char }; 277 for i in 0..len { 278 unsafe { 279 *heap_buf.add(i) = stack_buf[i]; 280 } 281 } 282 heap_buf 283 } else { 284 ret 285 } 286 } 287 288 // #[no_mangle] 289 pub extern "C" fn getdtablesize() -> c_int { 290 unimplemented!(); 291 } 292 293 #[no_mangle] 294 pub extern "C" fn getegid() -> gid_t { 295 Sys::getegid() 296 } 297 298 #[no_mangle] 299 pub extern "C" fn geteuid() -> uid_t { 300 Sys::geteuid() 301 } 302 303 #[no_mangle] 304 pub extern "C" fn getgid() -> gid_t { 305 Sys::getgid() 306 } 307 308 // #[no_mangle] 309 pub extern "C" fn getgroups(gidsetsize: c_int, grouplist: *mut gid_t) -> c_int { 310 unimplemented!(); 311 } 312 313 // #[no_mangle] 314 pub extern "C" fn gethostid() -> c_long { 315 unimplemented!(); 316 } 317 318 #[no_mangle] 319 pub unsafe extern "C" fn gethostname(mut name: *mut c_char, mut len: size_t) -> c_int { 320 let mut uts = mem::MaybeUninit::<sys_utsname::utsname>::uninit(); 321 let err = Sys::uname(uts.as_mut_ptr()); 322 if err < 0 { 323 mem::forget(uts); 324 return err; 325 } 326 for c in uts.assume_init().nodename.iter() { 327 if len == 0 { 328 break; 329 } 330 len -= 1; 331 332 *name = *c; 333 334 if *name == 0 { 335 // We do want to copy the zero also, so we check this after the copying. 336 break; 337 } 338 339 name = name.offset(1); 340 } 341 0 342 } 343 344 #[no_mangle] 345 pub unsafe extern "C" fn getlogin() -> *mut c_char { 346 static mut LOGIN: [c_char; 256] = [0; 256]; 347 if getlogin_r(LOGIN.as_mut_ptr(), LOGIN.len()) == 0 { 348 LOGIN.as_mut_ptr() 349 } else { 350 ptr::null_mut() 351 } 352 } 353 354 #[no_mangle] 355 pub extern "C" fn getlogin_r(name: *mut c_char, namesize: size_t) -> c_int { 356 //TODO: Determine correct getlogin result on Redox 357 unsafe { platform::errno = errno::ENOENT }; 358 -1 359 } 360 361 #[no_mangle] 362 pub extern "C" fn getpagesize() -> c_int { 363 match c_int::try_from(sysconf(_SC_PAGESIZE)) { 364 Ok(page_size) => page_size, 365 Err(_) => { 366 /* Behavior not specified by POSIX for this case. The -1 367 * value mimics sysconf()'s behavior, though. 368 * 369 * As specified for the limits.h header, the minimum 370 * acceptable value for {PAGESIZE} is 1. The -1 value thus 371 * cannot be mistaken for an acceptable value. 372 * 373 * POSIX does not specify any possible errors for this 374 * function, hence no errno setting. */ 375 -1 376 } 377 } 378 } 379 380 // #[no_mangle] 381 pub extern "C" fn getpass(prompt: *const c_char) -> *mut c_char { 382 unimplemented!(); 383 } 384 385 #[no_mangle] 386 pub extern "C" fn getpgid(pid: pid_t) -> pid_t { 387 Sys::getpgid(pid) 388 } 389 390 #[no_mangle] 391 pub extern "C" fn getpgrp() -> pid_t { 392 Sys::getpgid(Sys::getpid()) 393 } 394 395 #[no_mangle] 396 pub extern "C" fn getpid() -> pid_t { 397 Sys::getpid() 398 } 399 400 #[no_mangle] 401 pub extern "C" fn getppid() -> pid_t { 402 Sys::getppid() 403 } 404 405 #[no_mangle] 406 pub extern "C" fn getsid(pid: pid_t) -> pid_t { 407 Sys::getsid(pid) 408 } 409 410 #[no_mangle] 411 pub extern "C" fn getuid() -> uid_t { 412 Sys::getuid() 413 } 414 415 #[no_mangle] 416 pub extern "C" fn getwd(path_name: *mut c_char) -> *mut c_char { 417 getcwd(path_name, limits::PATH_MAX) 418 } 419 420 #[no_mangle] 421 pub extern "C" fn isatty(fd: c_int) -> c_int { 422 let mut t = termios::termios::default(); 423 if unsafe { termios::tcgetattr(fd, &mut t as *mut termios::termios) == 0 } { 424 1 425 } else { 426 0 427 } 428 } 429 430 #[no_mangle] 431 pub unsafe extern "C" fn lchown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int { 432 let path = CStr::from_ptr(path); 433 Sys::lchown(path, owner, group) 434 } 435 436 #[no_mangle] 437 pub unsafe extern "C" fn link(path1: *const c_char, path2: *const c_char) -> c_int { 438 let path1 = CStr::from_ptr(path1); 439 let path2 = CStr::from_ptr(path2); 440 Sys::link(path1, path2) 441 } 442 443 // #[no_mangle] 444 pub extern "C" fn lockf(fildes: c_int, function: c_int, size: off_t) -> c_int { 445 unimplemented!(); 446 } 447 448 #[no_mangle] 449 pub extern "C" fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t { 450 Sys::lseek(fildes, offset, whence) 451 } 452 453 // #[no_mangle] 454 pub extern "C" fn nice(incr: c_int) -> c_int { 455 unimplemented!(); 456 } 457 458 // #[no_mangle] 459 pub extern "C" fn pause() -> c_int { 460 unimplemented!(); 461 } 462 463 #[no_mangle] 464 pub unsafe extern "C" fn pipe(fildes: *mut c_int) -> c_int { 465 pipe2(fildes, 0) 466 } 467 468 #[no_mangle] 469 pub unsafe extern "C" fn pipe2(fildes: *mut c_int, flags: c_int) -> c_int { 470 Sys::pipe2(slice::from_raw_parts_mut(fildes, 2), flags) 471 } 472 473 #[no_mangle] 474 pub extern "C" fn pread(fildes: c_int, buf: *mut c_void, nbyte: size_t, offset: off_t) -> ssize_t { 475 //TODO: better pread using system calls 476 477 let previous = lseek(fildes, offset, SEEK_SET); 478 if previous == -1 { 479 return -1; 480 } 481 482 let res = read(fildes, buf, nbyte); 483 if res < 0 { 484 return res; 485 } 486 487 if lseek(fildes, previous, SEEK_SET) == -1 { 488 return -1; 489 } 490 491 res 492 } 493 494 #[no_mangle] 495 pub extern "C" fn pthread_atfork( 496 prepare: Option<extern "C" fn()>, 497 parent: Option<extern "C" fn()>, 498 child: Option<extern "C" fn()>, 499 ) -> c_int { 500 let fork_hooks = unsafe { init_fork_hooks() }; 501 if let Some(prepare) = prepare { 502 fork_hooks[0].push_back(prepare); 503 } 504 if let Some(parent) = parent { 505 fork_hooks[1].push_back(parent); 506 } 507 if let Some(child) = child { 508 fork_hooks[2].push_back(child); 509 } 510 0 511 } 512 513 #[no_mangle] 514 pub extern "C" fn pwrite( 515 fildes: c_int, 516 buf: *const c_void, 517 nbyte: size_t, 518 offset: off_t, 519 ) -> ssize_t { 520 //TODO: better pwrite using system calls 521 522 let previous = lseek(fildes, offset, SEEK_SET); 523 if previous == -1 { 524 return -1; 525 } 526 527 let res = write(fildes, buf, nbyte); 528 if res < 0 { 529 return res; 530 } 531 532 if lseek(fildes, previous, SEEK_SET) == -1 { 533 return -1; 534 } 535 536 res 537 } 538 539 #[no_mangle] 540 pub extern "C" fn read(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t { 541 let buf = unsafe { slice::from_raw_parts_mut(buf as *mut u8, nbyte as usize) }; 542 trace_expr!( 543 Sys::read(fildes, buf), 544 "read({}, {:p}, {})", 545 fildes, 546 buf, 547 nbyte 548 ) 549 } 550 551 #[no_mangle] 552 pub unsafe extern "C" fn readlink( 553 path: *const c_char, 554 buf: *mut c_char, 555 bufsize: size_t, 556 ) -> ssize_t { 557 let path = CStr::from_ptr(path); 558 let buf = slice::from_raw_parts_mut(buf as *mut u8, bufsize as usize); 559 Sys::readlink(path, buf) 560 } 561 562 #[no_mangle] 563 pub unsafe extern "C" fn rmdir(path: *const c_char) -> c_int { 564 let path = CStr::from_ptr(path); 565 Sys::rmdir(path) 566 } 567 568 #[no_mangle] 569 pub extern "C" fn setgid(gid: gid_t) -> c_int { 570 Sys::setregid(gid, gid) 571 } 572 573 #[no_mangle] 574 pub extern "C" fn setpgid(pid: pid_t, pgid: pid_t) -> c_int { 575 Sys::setpgid(pid, pgid) 576 } 577 578 #[no_mangle] 579 pub extern "C" fn setpgrp() -> pid_t { 580 setpgid(0, 0) 581 } 582 583 #[no_mangle] 584 pub extern "C" fn setregid(rgid: gid_t, egid: gid_t) -> c_int { 585 Sys::setregid(rgid, egid) 586 } 587 588 #[no_mangle] 589 pub extern "C" fn setreuid(ruid: uid_t, euid: uid_t) -> c_int { 590 Sys::setreuid(ruid, euid) 591 } 592 593 // #[no_mangle] 594 pub extern "C" fn setsid() -> pid_t { 595 unimplemented!(); 596 } 597 598 #[no_mangle] 599 pub extern "C" fn setuid(uid: uid_t) -> c_int { 600 Sys::setreuid(uid, uid) 601 } 602 603 #[no_mangle] 604 pub extern "C" fn sleep(seconds: c_uint) -> c_uint { 605 let rqtp = timespec { 606 tv_sec: seconds as time_t, 607 tv_nsec: 0, 608 }; 609 let rmtp = ptr::null_mut(); 610 Sys::nanosleep(&rqtp, rmtp); 611 0 612 } 613 614 #[no_mangle] 615 pub extern "C" fn swab(src: *const c_void, dest: *mut c_void, nbytes: ssize_t) { 616 if nbytes <= 0 { 617 return; 618 } 619 let number_of_swaps = nbytes / 2; 620 let mut offset = 0; 621 for i in 0..number_of_swaps { 622 unsafe { 623 src.offset(offset).copy_to(dest.offset(offset + 1), 1); 624 src.offset(offset + 1).copy_to(dest.offset(offset), 1); 625 } 626 offset += 2; 627 } 628 } 629 630 #[no_mangle] 631 pub unsafe extern "C" fn symlink(path1: *const c_char, path2: *const c_char) -> c_int { 632 let path1 = CStr::from_ptr(path1); 633 let path2 = CStr::from_ptr(path2); 634 Sys::symlink(path1, path2) 635 } 636 637 // #[no_mangle] 638 pub extern "C" fn sync() { 639 unimplemented!(); 640 } 641 642 #[no_mangle] 643 pub extern "C" fn tcgetpgrp(fd: c_int) -> pid_t { 644 let mut pgrp = 0; 645 if unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCGPGRP, &mut pgrp as *mut pid_t as _) } < 0 { 646 return -1; 647 } 648 pgrp 649 } 650 651 #[no_mangle] 652 pub extern "C" fn tcsetpgrp(fd: c_int, pgrp: pid_t) -> c_int { 653 if unsafe { sys_ioctl::ioctl(fd, sys_ioctl::TIOCSPGRP, &pgrp as *const pid_t as _) } < 0 { 654 return -1; 655 } 656 pgrp 657 } 658 659 #[no_mangle] 660 pub extern "C" fn truncate(path: *const c_char, length: off_t) -> c_int { 661 let file = unsafe { CStr::from_ptr(path) }; 662 let fd = Sys::open(file, fcntl::O_WRONLY, 0); 663 if fd < 0 { 664 return -1; 665 } 666 667 let res = ftruncate(fd, length); 668 669 Sys::close(fd); 670 671 res 672 } 673 674 #[no_mangle] 675 pub unsafe extern "C" fn ttyname(fildes: c_int) -> *mut c_char { 676 static mut TTYNAME: [c_char; 4096] = [0; 4096]; 677 if ttyname_r(fildes, TTYNAME.as_mut_ptr(), TTYNAME.len()) == 0 { 678 TTYNAME.as_mut_ptr() 679 } else { 680 ptr::null_mut() 681 } 682 } 683 684 #[no_mangle] 685 pub extern "C" fn ttyname_r(fildes: c_int, name: *mut c_char, namesize: size_t) -> c_int { 686 let name = unsafe { slice::from_raw_parts_mut(name as *mut u8, namesize) }; 687 if name.is_empty() { 688 return errno::ERANGE; 689 } 690 691 let len = Sys::fpath(fildes, &mut name[..namesize - 1]); 692 if len < 0 { 693 return unsafe { -platform::errno }; 694 } 695 name[len as usize] = 0; 696 697 0 698 } 699 700 #[no_mangle] 701 pub extern "C" fn ualarm(usecs: useconds_t, interval: useconds_t) -> useconds_t { 702 let mut timer = sys_time::itimerval { 703 it_value: sys_time::timeval { 704 tv_sec: 0, 705 tv_usec: usecs as suseconds_t, 706 }, 707 it_interval: sys_time::timeval { 708 tv_sec: 0, 709 tv_usec: interval as suseconds_t, 710 }, 711 }; 712 let errno_backup = unsafe { platform::errno }; 713 let ret = if sys_time::setitimer(sys_time::ITIMER_REAL, &timer, &mut timer) < 0 { 714 0 715 } else { 716 timer.it_value.tv_sec as useconds_t * 1_000_000 + timer.it_value.tv_usec as useconds_t 717 }; 718 unsafe { 719 platform::errno = errno_backup; 720 } 721 722 ret 723 } 724 725 #[no_mangle] 726 pub unsafe extern "C" fn unlink(path: *const c_char) -> c_int { 727 let path = CStr::from_ptr(path); 728 Sys::unlink(path) 729 } 730 731 #[no_mangle] 732 pub extern "C" fn usleep(useconds: useconds_t) -> c_int { 733 let rqtp = timespec { 734 tv_sec: (useconds / 1_000_000) as time_t, 735 tv_nsec: ((useconds % 1_000_000) * 1000) as c_long, 736 }; 737 let rmtp = ptr::null_mut(); 738 Sys::nanosleep(&rqtp, rmtp) 739 } 740 741 // #[no_mangle] 742 pub extern "C" fn vfork() -> pid_t { 743 unimplemented!(); 744 } 745 746 #[no_mangle] 747 pub extern "C" fn write(fildes: c_int, buf: *const c_void, nbyte: size_t) -> ssize_t { 748 let buf = unsafe { slice::from_raw_parts(buf as *const u8, nbyte as usize) }; 749 Sys::write(fildes, buf) 750 } 751