SchOS/kernel/sys/sys_time.c
Nexoniarz a4ed556543 kernel: timers that fire, raw ping, and the sockets X needs
alarm() and setitimer() stored a countdown nothing counted down, so no
timer has ever fired; setitimer and getitimer were also registered with
no Linux number. alarmd now posts SIGALRM on the tick, while any process
has a timer armed. busybox's ping, /bin/ping on the Alpine root, sends
every request after the first from its SIGALRM handler.

It also opens SOCK_RAW, which was EPROTOTYPE. A raw ICMP socket is now a
ping socket whose reads start at the IP header. And a blocked socket
call gives up for a signal with a handler, not only for a kill: the
alarm woke recvfrom, and the wait put it straight back to sleep.

For Xorg: AF_UNIX abstract names, which read as the empty path and so as
EADDRINUSE ("server already running"); and getsockname and getpeername,
which did not exist, so libxcb sent no cookie and the server refused
every client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-24 19:44:10 +02:00

192 lines
4.7 KiB
C

#include "core/types.h"
#include "core/defs.h"
#include "core/param.h"
#include "core/date.h"
#include "core/spinlock.h"
#include "core/proc.h"
#include "arch/x86.h"
#include "core/syscall.h"
#include "uapi/posix.h"
#include "core/uaccess.h"
#include "uapi/errno.h"
// Timer values in ticks, rounding a nonzero time up: POSIX says a timer set
// to 1 us goes off, not that it is disarmed.
static uint
tv_ticks(struct timeval *tv)
{
if (tv->tv_sec < 0 || tv->tv_usec < 0)
return 0;
uint64 t = (uint64)tv->tv_sec * HZ +
((uint64)tv->tv_usec + USEC_PER_TICK - 1) / USEC_PER_TICK;
return t > 0x7fffffff ? 0x7fffffff : (uint)t;
}
static void
ticks_tv(uint t, struct timeval *tv)
{
tv->tv_sec = t / HZ;
tv->tv_usec = (t % HZ) * USEC_PER_TICK;
}
// The seconds left on the previous alarm, rounded up, as POSIX asks.
addr_t
sys_alarm(void)
{
int seconds;
if (argint(0, &seconds) < 0)
return -1;
struct proc *p = myproc();
uint left = alarm_left(p);
alarm_arm(p, seconds > 0 ? (uint)seconds * HZ : 0, 0);
return (left + HZ - 1) / HZ;
}
addr_t
sys_gettimeofday(void)
{
struct timeval *tv;
if (argptr(0, (char**)&tv, sizeof(*tv)) < 0)
return -1;
if (tv) {
// Real time, from the hardware clock read at boot plus uptime -- not
// uptime alone, which is what this used to return and why every machine
// thought it was 1970. See kernel/core/time.c.
uint64 sec, usec;
wallclock(&sec, &usec);
tv->tv_sec = (long)sec;
tv->tv_usec = (long)usec;
}
return 0;
}
addr_t
sys_nanosleep(void)
{
addr_t tsaddr, raw;
if (argaddr(0, &tsaddr) < 0 || fetchaddr(tsaddr, &raw) < 0)
return -1;
int64 sec = (int64)raw;
if (sec > 0) {
acquire(&tickslock);
uint t0 = ticks;
while (ticks - t0 < (uint)(sec * HZ)) {
if (myproc()->killed) { release(&tickslock); return -EINTR; }
sleep(&ticks, &tickslock);
}
release(&tickslock);
}
return 0;
}
addr_t
sys_clock_gettime(void)
{
int clkid;
struct timespec *tp;
argint(0, &clkid);
if (argptr(1, (char**)&tp, sizeof(*tp)) < 0)
return -1;
// CLOCK_MONOTONIC (1) counts from boot; everything else is treated as a
// realtime clock and counts from the epoch. Returning boot-relative values
// for CLOCK_REALTIME is what made timestamps land in 1970.
uint64 sec, usec;
wallclock(&sec, &usec);
if (clkid == 1)
sec -= boot_epoch;
tp->tv_sec = (long)sec;
tp->tv_nsec = (long)(usec * 1000);
return 0;
}
// ITIMER_REAL only. ITIMER_VIRTUAL and ITIMER_PROF count CPU time, which
// nothing here measures per tick, and are refused rather than accepted and
// never fired -- which is what all three used to be.
static int
itimer_get(struct proc *p, struct itimerval *iv)
{
ticks_tv(p->alarm_every, &iv->it_interval);
ticks_tv(alarm_left(p), &iv->it_value);
return 0;
}
addr_t
sys_getitimer(void)
{
int which;
addr_t curp;
argint(0, &which); argaddr(1, &curp);
if (which != 0)
return -EINVAL;
struct itimerval iv;
itimer_get(myproc(), &iv);
if (copy_to_user(curp, &iv, sizeof(iv)) != 0)
return -EFAULT;
return 0;
}
addr_t
sys_setitimer(void)
{
int which;
addr_t newp, oldp;
argint(0, &which); argaddr(1, &newp); argaddr(2, &oldp);
if (which != 0)
return -EINVAL;
struct proc *p = myproc();
struct itimerval iv;
if (oldp) {
itimer_get(p, &iv);
if (copy_to_user(oldp, &iv, sizeof(iv)) != 0)
return -EFAULT;
}
if (newp == 0)
return 0;
if (copy_from_user(&iv, newp, sizeof(iv)) != 0)
return -EFAULT;
alarm_arm(p, tv_ticks(&iv.it_value), tv_ticks(&iv.it_interval));
return 0;
}
addr_t
sys_time(void)
{
addr_t tloc;
argaddr(0, &tloc);
uint64 sec;
wallclock(&sec, 0);
long t = (long)sec;
if (tloc)
put_user(t, tloc);
return (addr_t)t;
}
addr_t
sys_clock_getres(void)
{
struct timespec *tp;
if (argptr(1, (char**)&tp, sizeof(*tp)) < 0) return 0;
if (tp) { tp->tv_sec = 0; tp->tv_nsec = 10000000L; } // 10ms resolution
return 0;
}
addr_t
sys_clock_nanosleep(void)
{
// Treat the same as nanosleep: arg2 is the timespec, arg1 is flags.
// Linux: clock_nanosleep(clockid, flags, rqtp, rmtp)
addr_t tsaddr, raw;
if (argaddr(2, &tsaddr) < 0 || fetchaddr(tsaddr, &raw) < 0)
return -1;
int64 sec = (int64)raw;
if (sec > 0) {
acquire(&tickslock);
uint t0 = ticks;
while (ticks - t0 < (uint)(sec * HZ)) {
if (myproc()->killed) { release(&tickslock); return -EINTR; }
sleep(&ticks, &tickslock);
}
release(&tickslock);
}
return 0;
}