/* * Copyright (c) 2013, 2014, 2021 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * user-timer.cpp * Timers that send signals when triggered. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Sortix { // TODO: We also need to fetch the pthread attr if there is one. static bool FetchSigevent(struct sigevent* dst, const struct sigevent* src) { if ( src ) return CopyFromUser(dst, src, sizeof(struct sigevent)); dst->sigev_notify = SIGEV_SIGNAL; dst->sigev_signo = SIGALRM; // TODO: "and the sigev_value member having the value of the timer ID." // - Does POSIX want the caller to be psychic and should we write back the // final default sigevent? return true; } static UserTimer* LookupUserTimer(Process* process, timer_t timerid) { if ( PROCESS_TIMER_NUM_MAX <= timerid ) return errno = EINVAL, (UserTimer*) NULL; UserTimer* user_timer = &process->user_timers[timerid]; if ( !user_timer->timer.IsAttached() ) return errno = EINVAL, (UserTimer*) NULL; return user_timer; } static Timer* LookupTimer(Process* process, timer_t timerid) { UserTimer* user_timer = LookupUserTimer(process, timerid); return user_timer ? &user_timer->timer : (Timer*) NULL; } int sys_timer_create(clockid_t clockid, struct sigevent* sigevp, timer_t* timerid_ptr) { Process* process = CurrentProcess(); ScopedLock lock(&process->user_timers_lock); Clock* clock = Time::GetClock(clockid); if ( !clock ) return -1; struct sigevent sigev; if ( !FetchSigevent(&sigev, sigevp) ) return -1; // Allocate a timer for this request. timer_t timerid; for ( timerid = 0; timerid < PROCESS_TIMER_NUM_MAX; timerid++ ) { Timer* timer = &process->user_timers[timerid].timer; if ( timer->IsAttached() ) continue; timer->Attach(clock); break; } if ( PROCESS_TIMER_NUM_MAX <= timerid ) return -1; if ( !CopyToUser(timerid_ptr, &timerid, sizeof(timerid)) ) { process->user_timers[timerid].timer.Detach(); return -1; } process->user_timers[timerid].process = process; process->user_timers[timerid].event = sigev; process->user_timers[timerid].timerid = timerid; return 0; } int sys_timer_delete(timer_t timerid) { Process* process = CurrentProcess(); ScopedLock lock(&process->user_timers_lock); Timer* timer = LookupTimer(process, timerid); if ( !timer ) return -1; timer->Cancel(); timer->Detach(); return 0; } int sys_timer_getoverrun(timer_t timerid) { Process* process = CurrentProcess(); ScopedLock lock(&process->user_timers_lock); Timer* timer = LookupTimer(process, timerid); if ( !timer ) return -1; if ( (size_t) INT_MAX < timer->num_overrun_events) return INT_MAX; // TODO: How does the caller reset the overrun count back to 0? Should we // adopt the Linux semantics where it resets back to 0 after INT_MAX? // How about signed overflow in the kernel and in the user process? return 0; } int sys_timer_gettime(timer_t timerid, struct itimerspec* user_value) { Process* process = CurrentProcess(); ScopedLock lock(&process->user_timers_lock); Timer* timer = LookupTimer(process, timerid); if ( !timer ) return -1; struct itimerspec value; timer->Get(&value); return CopyToUser(user_value, &value, sizeof(value)) ? 0 : -1; } static void timer_callback(Clock* /*clock*/, Timer* timer, void* user) { UserTimer* user_timer = (UserTimer*) user; Process* process = user_timer->process; ScopedLock lock(&process->user_timers_lock); // TODO: This delivery facility is insufficient! sigevent is much more // powerful than sending a simple old-school signal. if ( !process->DeliverSignal(user_timer->event.sigev_signo) && errno == ESIGPENDING ) { if ( timer->num_overrun_events < SIZE_MAX ) timer->num_overrun_events++; } } int sys_timer_settime(timer_t timerid, int flags, const struct itimerspec* user_value, struct itimerspec* user_ovalue) { Process* process = CurrentProcess(); ScopedLock lock(&process->user_timers_lock); UserTimer* user_timer = LookupUserTimer(process, timerid); if ( !user_timer ) return -1; Timer* timer = &user_timer->timer; struct itimerspec value, ovalue; if ( !CopyFromUser(&value, user_value, sizeof(value)) ) return -1; if ( !timespec_is_canonical(value.it_value) || !timespec_is_canonical(value.it_interval) ) return errno = EINVAL, -1; if ( timespec_lt(value.it_value, timespec_nul()) || timespec_lt(value.it_interval, timespec_nul()) || (flags & ~(TIMER_ABSTIME)) != 0 ) return errno = EINVAL, -1; int timer_flags = 0; if ( flags & TIMER_ABSTIME ) timer_flags |= TIMER_ABSOLUTE; if ( !value.it_value.tv_sec && !value.it_value.tv_nsec ) timer_flags |= TIMER_DISARM; timer->Set(&value, &ovalue, timer_flags, timer_callback, user_timer); // Let the caller know how much time was left on the timer. if ( user_ovalue && !CopyToUser(user_ovalue, &ovalue, sizeof(ovalue)) ) return -1; return 0; } int sys_clock_gettimeres(clockid_t clockid, struct timespec* time, struct timespec* res) { Clock* clock = Time::GetClock(clockid); if ( !clock ) return -1; struct timespec ktime, kres; clock->Get(&ktime, &kres); return (!time || CopyToUser(time, &ktime, sizeof(ktime))) && (!res || CopyToUser(res, &kres, sizeof(kres))) ? 0 : -1; } int sys_clock_settimeres(clockid_t clockid, const struct timespec* time, const struct timespec* res) { Clock* clock = Time::GetClock(clockid); if ( !clock ) return -1; struct timespec ktime, kres; if ( (time && !CopyFromUser(&ktime, time, sizeof(ktime))) || (res && !CopyFromUser(&kres, res, sizeof(kres))) ) return -1; if ( time && !timespec_is_canonical(ktime) ) return errno = EINVAL, -1; if ( res && !timespec_is_canonical(kres) ) return errno = EINVAL, -1; clock->Set(time ? &ktime : NULL, res ? &kres : NULL); return 0; } int sys_clock_nanosleep(clockid_t clockid, int flags, const struct timespec* user_duration, struct timespec* user_remainder) { struct timespec time; Clock* clock = Time::GetClock(clockid); if ( !clock ) return -1; if ( !CopyFromUser(&time, user_duration, sizeof(time)) ) return -1; if ( !timespec_is_canonical(time) ) return errno = EINVAL, -1; time = flags & TIMER_ABSTIME ? clock->SleepUntil(time) : clock->SleepDelay(time); if ( user_remainder && !CopyToUser(user_remainder, &time, sizeof(time)) ) return -1; return timespec_eq(time, timespec_nul()) ? 0 : (errno = EINTR, -1); } int sys_timens(struct tmns* user_tmns) { Clock* execute_clock = Time::GetClock(CLOCK_PROCESS_CPUTIME_ID); Clock* system_clock = Time::GetClock(CLOCK_PROCESS_SYSTIME_ID); Clock* child_execute_clock = Time::GetClock(CLOCK_CHILD_CPUTIME_ID); Clock* child_system_clock = Time::GetClock(CLOCK_CHILD_SYSTIME_ID); // Note: It is safe to access the clocks in this manner as each of them are // locked by disabling interrupts. This is perhaps not SMP-ready, but // it will do for now. struct tmns tmns; Interrupt::Disable(); tmns.tmns_utime = execute_clock->current_time; tmns.tmns_stime = system_clock->current_time; tmns.tmns_cutime = child_execute_clock->current_time; tmns.tmns_cstime = child_system_clock->current_time; Interrupt::Enable(); return CopyToUser(user_tmns, &tmns, sizeof(tmns)) ? 0 : -1; } } // namespace Sortix