1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use core::convert::TryInto;

use errno::{set_errno, Errno};
use libc::c_int;

use crate::convert_res;

fn rustix_timespec_to_libc_timespec(
    rustix_time: rustix::time::Timespec,
) -> Result<libc::timespec, core::num::TryFromIntError> {
    // SAFETY: libc structs can be zero-initalized freely
    let mut time: libc::timespec = unsafe { core::mem::zeroed() };
    time.tv_sec = rustix_time.tv_sec.try_into()?;
    time.tv_nsec = rustix_time.tv_nsec.try_into()?;
    Ok(time)
}

#[no_mangle]
unsafe extern "C" fn clock_gettime(id: c_int, tp: *mut libc::timespec) -> c_int {
    libc!(libc::clock_gettime(id, tp));

    let id = match id {
        libc::CLOCK_MONOTONIC => rustix::time::ClockId::Monotonic,
        libc::CLOCK_REALTIME => rustix::time::ClockId::Realtime,
        _ => panic!("unimplemented clock({})", id),
    };

    match rustix_timespec_to_libc_timespec(rustix::time::clock_gettime(id)) {
        Ok(t) => {
            *tp = t;
            0
        }
        Err(_) => {
            set_errno(Errno(libc::EOVERFLOW));
            -1
        }
    }
}

#[no_mangle]
unsafe extern "C" fn time(t: *mut libc::time_t) -> libc::time_t {
    libc!(libc::time(t));

    let mut ts: libc::timespec = { core::mem::zeroed() };
    if clock_gettime(libc::CLOCK_REALTIME, &mut ts) == -1 {
        return -1;
    }

    if !t.is_null() {
        *t = ts.tv_sec;
    }

    ts.tv_sec
}

#[no_mangle]
unsafe extern "C" fn gettimeofday(t: *mut libc::timeval, _tz: *mut libc::timezone) -> c_int {
    libc!(libc::gettimeofday(t, _tz));

    if t.is_null() {
        return 0;
    }

    let mut ts: libc::timespec = { core::mem::zeroed() };
    if clock_gettime(libc::CLOCK_REALTIME, &mut ts) == -1 {
        return -1;
    }

    if !t.is_null() {
        (*t).tv_sec = ts.tv_sec;
        (*t).tv_usec = ts.tv_nsec / 1000;
    }

    0
}

#[no_mangle]
unsafe extern "C" fn nanosleep(req: *const libc::timespec, rem: *mut libc::timespec) -> c_int {
    libc!(libc::nanosleep(req, rem));

    let req = rustix::time::Timespec {
        tv_sec: (*req).tv_sec.into(),
        tv_nsec: (*req).tv_nsec as _,
    };
    match rustix::thread::nanosleep(&req) {
        rustix::thread::NanosleepRelativeResult::Ok => 0,
        rustix::thread::NanosleepRelativeResult::Interrupted(remaining) => {
            *rem = libc::timespec {
                tv_sec: remaining.tv_sec.try_into().unwrap(),
                tv_nsec: remaining.tv_nsec as _,
            };
            set_errno(Errno(libc::EINTR));
            -1
        }
        rustix::thread::NanosleepRelativeResult::Err(err) => {
            set_errno(Errno(err.raw_os_error()));
            -1
        }
    }
}

#[no_mangle]
unsafe extern "C" fn clock_settime(id: c_int, tp: *mut libc::timespec) -> c_int {
    libc!(libc::clock_settime(id, tp));

    let id = match id {
        libc::CLOCK_MONOTONIC => rustix::time::ClockId::Monotonic,
        libc::CLOCK_REALTIME => rustix::time::ClockId::Realtime,
        _ => panic!("unimplemented clock({})", id),
    };

    let timespec = rustix::time::Timespec {
        tv_sec: (*tp).tv_sec.into(),
        tv_nsec: (*tp).tv_nsec as _,
    };

    match convert_res(rustix::time::clock_settime(id, timespec)) {
        Some(()) => 0,
        None => -1,
    }
}

#[no_mangle]
unsafe extern "C" fn difftime(time1: libc::time_t, time0: libc::time_t) -> f64 {
    libc!(libc::difftime(time1, time0));

    (time1 as i128 - time0 as i128) as f64
}