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
192 lines
4.7 KiB
C
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;
|
|
}
|