xref: /relibc/src/header/unistd/mod.rs (revision 7d27737c3ffa5bbdf5d00592bb95abf1f9703aee)
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