From a4ed556543f9a35b85216fdbede618322ea96e88 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Sat, 24 Oct 2026 19:44:10 +0200 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4 --- include/core/defs.h | 8 +++ include/core/proc.h | 6 ++- kernel/core/main.c | 1 + kernel/net/inet.c | 103 ++++++++++++++++++++++++++++++++++----- kernel/net/unix.c | 55 +++++++++++++++++++++ kernel/proc/proc.c | 92 +++++++++++++++++++++++++++++++++++ kernel/sys/sys_net.c | 110 +++++++++++++++++++++++++++++++++++++++++- kernel/sys/sys_time.c | 75 +++++++++++++++++++++++----- kernel/sys/table.inc | 6 ++- 9 files changed, 428 insertions(+), 28 deletions(-) diff --git a/include/core/defs.h b/include/core/defs.h index feaa099..0e20201 100644 --- a/include/core/defs.h +++ b/include/core/defs.h @@ -492,6 +492,10 @@ int sockbind(struct socket*, const char *path, uint dev, uint64 ino) int socklisten(struct socket*, int backlog); struct socket* sockaccept(struct socket*); int sockconnect(struct socket*, uint dev, uint64 ino); +int sockbind_abstract(struct socket*, uint64 key); +int sockname_unix(struct socket*, int peer, char *path); +int sockname_inet(struct socket*, int peer, uint *addr, ushort *port); +#define SOCK_ABSTRACT_DEV 0xFFFFFFFFu // no filesystem has it; see unix.c int sockreadable(struct socket*); int sock_hup(struct socket*); int sock_shutdown(struct socket*, int how); @@ -523,6 +527,7 @@ void* inet_accept(void *h); int inet_connect(void *h, uint addr, ushort port); int inet_read(void *h, char *buf, int n); int inet_write(void *h, char *buf, int n); +int inet_name(void *h, int peer, uint *addr, ushort *port); int inet_recvfrom(void *h, char *buf, int n, uint *addr, ushort *port); int inet_sendto(void *h, char *buf, int n, @@ -544,6 +549,9 @@ int proc_pgid(int pid); int proc_setpgid(int pid, int pgid); int proc_setsid(void); int sigsend(int pid, int sig); +void alarm_arm(struct proc *p, uint after, uint every); +uint alarm_left(struct proc *p); +void alarm_thread_start(void); int sig_default_kills(int sig); int proc_exists(int); int proc_num(void); diff --git a/include/core/proc.h b/include/core/proc.h index 369ddef..fbb692e 100644 --- a/include/core/proc.h +++ b/include/core/proc.h @@ -231,7 +231,11 @@ struct proc { uint64 signal_mask; uint64 pending_signals; - int alarm_ticks; + // ITIMER_REAL, which alarm() is the whole-second form of: SIGALRM is due at + // tick alarm_at (0 = not armed) and then every alarm_every ticks after. + // alarmd, in kernel/proc/proc.c, is what posts it. + uint alarm_at; + uint alarm_every; char ofile_flags[NOFILE]; // Real signal delivery (added for SIGCHLD — see kernel/sys/syscall.c's diff --git a/kernel/core/main.c b/kernel/core/main.c index 475e171..1a779c1 100644 --- a/kernel/core/main.c +++ b/kernel/core/main.c @@ -400,6 +400,7 @@ kmain(uint64 fb_addr) random_init(); netuser_init(); dbg_thread_start(); + alarm_thread_start(); if (boot_verbose) cprintf("verbose boot: dbg thread started\n"); // The greeting is not printed here. diff --git a/kernel/net/inet.c b/kernel/net/inet.c index 47426ab..499cf81 100644 --- a/kernel/net/inet.c +++ b/kernel/net/inet.c @@ -94,6 +94,7 @@ struct inet_sock { int inuse; int type; // SOCK_STREAM or SOCK_DGRAM int proto; // 0, or IPPROTO_ICMP for a ping socket + int iphdr; // SOCK_RAW: reads start at the IP header struct tcp_pcb *tpcb; struct udp_pcb *upcb; @@ -149,13 +150,36 @@ net_seq(void) return s; } -// Sleep until the count moves past `seen`, or this process is killed. kill() -// makes a sleeping process runnable, so the loop re-checks and leaves. +// A blocked socket call gives up for a kill and for any signal it can take. +// The second used to be missing, so a signal woke the sleeper and the loop +// below put it straight back: busybox's ping sends each request from a +// SIGALRM handler while it sits in recvfrom, and got one reply and then +// silence. There is no SA_RESTART, so the call returns EINTR and the caller +// asks again, which is what every one of them is written to do. +// +// Only signals with a handler count, as on Linux. A SIGCHLD left at its +// default is posted to a parent all the same, and ending a server's accept() +// for it would be an EINTR that program had never been handed before. +static int +net_interrupted(void) +{ + struct proc *p = myproc(); + if (p->killed) + return 1; + uint64 live = p->pending_signals & ~p->signal_mask; + for (int sig = 1; sig < 32; sig++) + if ((live & (1ULL << sig)) && p->sig_handler[sig] > 1) // not DFL/IGN + return 1; + return 0; +} + +// Sleep until the count moves past `seen`, or a signal arrives. kill() makes +// a sleeping process runnable, so the loop re-checks and leaves. static void net_wait(uint seen) { acquire(&netwait_lock); - while (netwait_seq == seen && !myproc()->killed) + while (netwait_seq == seen && !net_interrupted()) sleep(&netwait_seq, &netwait_lock); release(&netwait_lock); } @@ -438,6 +462,11 @@ inet_udp_recv_cb(void *arg, struct udp_pcb *pcb, struct pbuf *p, // processes ping at once; with one ping at a time the difference is invisible, // and a socket that sees a reply to somebody else's request will read a // sequence number it never sent and ignore it. +// +// A SOCK_RAW ICMP socket is the same socket, except that a read starts at the +// IP header: that is the raw-socket contract, and busybox's ping reads the TTL +// out of it and finds the ICMP message by IHL. Writing is the same for both -- +// without IP_HDRINCL a raw socket's caller sends the ICMP message alone too. static u8_t inet_icmp_recv_cb(void *arg, struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *addr) @@ -462,7 +491,8 @@ inet_icmp_recv_cb(void *arg, struct raw_pcb *pcb, struct pbuf *p, if (pbuf_copy_partial(p, &type, 1, hlen) != 1 || type != 0) return 0; - u16_t icmplen = (u16_t)(p->tot_len - hlen); + u16_t skip = is->iphdr ? 0 : hlen; + u16_t icmplen = (u16_t)(p->tot_len - skip); if (icmplen <= rx_space(is) && dq_used(is) < NDGRAM) { struct dgram *d = &is->dq[is->dhead % NDGRAM]; @@ -471,9 +501,9 @@ inet_icmp_recv_cb(void *arg, struct raw_pcb *pcb, struct pbuf *p, d->len = icmplen; is->dhead++; - // Copy the ICMP message out, skipping the IP header, across whatever pbuf - // chain it arrived in. - u16_t off = hlen; + // Copy the message out, skipping the IP header unless this is a raw + // socket, across whatever pbuf chain it arrived in. + u16_t off = skip; while (off < p->tot_len) { char tmp[64]; u16_t want = (u16_t)(p->tot_len - off); @@ -515,8 +545,15 @@ inet_alloc(int type) void * inet_new(int type, int proto) { - if (type != SOCK_STREAM && type != SOCK_DGRAM) + if (type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_RAW) return 0; + if (type == SOCK_RAW && proto != IPPROTO_ICMP) + return 0; + // Past this point a raw ICMP socket is a ping socket with one flag: every + // datagram path below already does the right thing for it. + int iphdr = (type == SOCK_RAW); + if (iphdr) + type = SOCK_DGRAM; // Ready, not *up*: see net_is_ready() in kernel/net/net.c. Requiring an // address here made every socket() fail until DHCP answered. if (!net_is_ready()) @@ -529,6 +566,7 @@ inet_new(int type, int proto) return 0; } is->proto = proto; + is->iphdr = iphdr; if (proto == IPPROTO_ICMP) { // A ping socket. raw_new registers for one IP protocol number; the @@ -688,7 +726,7 @@ inet_accept(void *h) uint seen = net_seq(); lwip_sys_unlock(); - if (myproc()->killed) + if (net_interrupted()) return 0; net_wait(seen); } @@ -729,7 +767,7 @@ inet_connect(void *h, uint32 addr, uint16 port) return 0; if (failed) return -ECONNREFUSED; - if (myproc()->killed) + if (net_interrupted()) return -EINTR; net_wait(seen); } @@ -742,6 +780,47 @@ inet_connect(void *h, uint32 addr, uint16 port) // peer it is connected to and is the same for every byte; for a datagram // socket it belongs to the individual datagram, which is why UDP needs the // queue above and TCP does not. +// getsockname / getpeername: this end's address or the other's, the address +// in network order and the port in host order, as inet_recvfrom gives them. A +// socket not yet bound reads as 0.0.0.0:0 locally, which is what Linux says; +// one with no peer is ENOTCONN. A listening pcb has no remote fields at all +// -- lwIP's listen pcb is the smaller struct -- so it is never asked for them. +int +inet_name(void *h, int peer, uint *addr, ushort *port) +{ + struct inet_sock *is = h; + int r = 0; + + *addr = 0; + *port = 0; + lwip_sys_lock(); + if (is->tpcb) { + if (peer && (is->listening || is->tpcb->state < SYN_SENT)) { + r = -ENOTCONN; + } else if (peer) { + *addr = ip4_addr_get_u32(ip_2_ip4(&is->tpcb->remote_ip)); + *port = is->tpcb->remote_port; + } else { + *addr = ip4_addr_get_u32(ip_2_ip4(&is->tpcb->local_ip)); + *port = is->tpcb->local_port; + } + } else if (is->upcb) { + if (peer && !(is->upcb->flags & UDP_FLAGS_CONNECTED)) { + r = -ENOTCONN; + } else if (peer) { + *addr = ip4_addr_get_u32(ip_2_ip4(&is->upcb->remote_ip)); + *port = is->upcb->remote_port; + } else { + *addr = ip4_addr_get_u32(ip_2_ip4(&is->upcb->local_ip)); + *port = is->upcb->local_port; + } + } else if (peer) { + r = -ENOTCONN; // a ping socket has no peer + } + lwip_sys_unlock(); + return r; +} + int inet_recvfrom(void *h, char *buf, int n, uint *addr, ushort *port) { @@ -787,7 +866,7 @@ inet_recvfrom(void *h, char *buf, int n, uint *addr, ushort *port) return got; if (eof) return 0; // end of file, not an error - if (myproc()->killed) + if (net_interrupted()) return -EINTR; net_wait(seen); } @@ -909,7 +988,7 @@ inet_sendto(void *h, char *buf, int n, uint addr, ushort port) lwip_sys_unlock(); if (done < n) { - if (myproc()->killed) + if (net_interrupted()) return done > 0 ? done : -EINTR; // Woken by inet_sent_cb when an acknowledgement frees send buffer, or by // a failure. Without tcp_sent registered this would be a sleep with diff --git a/kernel/net/unix.c b/kernel/net/unix.c index 335aa66..6a5036d 100644 --- a/kernel/net/unix.c +++ b/kernel/net/unix.c @@ -688,6 +688,61 @@ sockbind(struct socket *s, const char *path, uint dev, uint64 ino) return 0; } +// The abstract namespace: a sun_path that starts with a NUL names no file. +// Linux keeps these names in the socket layer, and Xorg binds one before its +// /tmp/.X11-unix path -- which this used to read as the empty path, resolve +// to the directory it was called from and refuse with EADDRINUSE, so Xorg +// reported "server already running" and exited. +// +// The name becomes a key on a device number no filesystem has, and the rest +// is the inode machinery unchanged: connect() finds the listener bound to +// (SOCK_ABSTRACT_DEV, key). The name goes when the socket does, because +// nothing else holds it. +int +sockbind_abstract(struct socket *s, uint64 key) +{ + acquire(&stab.lock); + if(s->state != SS_UNBOUND){ + release(&stab.lock); + return -EINVAL; + } + for(struct socket *o = stab.sock; o < stab.sock + NSOCK; o++) + if(o != s && o->state != SS_FREE && o->state != SS_UNBOUND && + o->bind_dev == SOCK_ABSTRACT_DEV && o->bind_ino == key){ + release(&stab.lock); + return -EADDRINUSE; + } + s->name[0] = '\0'; // getsockname reports it unnamed, not as a path + s->bind_dev = SOCK_ABSTRACT_DEV; + s->bind_ino = key; + s->state = SS_BOUND; + release(&stab.lock); + return 0; +} + +// The path for getsockname (peer 0) or getpeername (peer 1) of an AF_UNIX +// socket. Empty for an unnamed one -- a client that never bound, or an +// abstract name, which the caller reports as the family alone. +int +sockname_inet(struct socket *s, int peer, uint *addr, ushort *port) +{ + return inet_name(s->inet, peer, addr, port); +} + +int +sockname_unix(struct socket *s, int peer, char *path) +{ + acquire(&stab.lock); + struct socket *o = peer ? s->peer : s; + if(o == 0){ + release(&stab.lock); + return -ENOTCONN; + } + safestrcpy(path, o->name, UNIX_PATH_MAX); + release(&stab.lock); + return 0; +} + int socklisten(struct socket *s, int backlog) { diff --git a/kernel/proc/proc.c b/kernel/proc/proc.c index f132828..c9a1ac4 100644 --- a/kernel/proc/proc.c +++ b/kernel/proc/proc.c @@ -311,6 +311,7 @@ found: memset(p->sig_handler, 0, sizeof(p->sig_handler)); memset(p->sig_restorer, 0, sizeof(p->sig_restorer)); p->pending_signals = 0; + p->alarm_at = p->alarm_every = 0; // a child's timers start disarmed p->utime = p->stime = p->cutime = p->cstime = 0; // A recycled slot must not inherit a dead process's place in the queue -- @@ -1543,6 +1544,97 @@ sig_kills_now(struct proc *p, int sig) return p->sig_handler[sig] == 0 && sig_default_kills(sig); } +// SIGALRM, on time. +// +// alarm() and setitimer() stored a countdown that nothing ever counted down, +// so no timer anyone set has ever fired. Most programs never noticed, because +// what they used it for was a timeout; busybox's ping sends every request but +// the first from its SIGALRM handler, and on the Alpine root it is /bin/ping, +// so ping printed one reply and then nothing. +// +// A kernel thread rather than the timer interrupt, which is in trap.c, and +// which would have to walk the process table with interrupts off: this walks +// it once a tick, and only while some process has a timer armed. +static int alarms_armed; // processes with alarm_at set; a hint, not a lock + +void +alarm_arm(struct proc *p, uint after, uint every) +{ + acquire(&ptable.lock); + if (p->alarm_at == 0 && after) + alarms_armed++; + else if (p->alarm_at && after == 0) + alarms_armed--; + p->alarm_at = after ? ticks + after : 0; + if (p->alarm_at == 0 && after) + p->alarm_at = 1; // 0 means disarmed; a tick late is fine + p->alarm_every = after ? every : 0; + release(&ptable.lock); +} + +// Ticks until p's timer is due, 0 if it is not armed. +uint +alarm_left(struct proc *p) +{ + uint at = p->alarm_at; + if (at == 0) + return 0; + int left = (int)(at - ticks); + return left > 0 ? (uint)left : 1; +} + +static void +alarm_thread(void) +{ + for (;;) { + acquire(&tickslock); + sleep(&ticks, &tickslock); + uint now = ticks; + release(&tickslock); + if (alarms_armed == 0) + continue; + + acquire(&ptable.lock); + for (struct proc *p = ptable.proc; p < &ptable.proc[NPROC]; p++) { + if (p->alarm_at == 0 || (int)(now - p->alarm_at) < 0) + continue; + if (p->state == UNUSED || p->state == ZOMBIE) { + p->alarm_at = 0; + alarms_armed--; + continue; + } + if (p->alarm_every) { + p->alarm_at = now + p->alarm_every; + } else { + p->alarm_at = 0; + alarms_armed--; + } + // sigsend takes ptable.lock itself, and with this many lines it is + // clearer to repeat its three effects than to split it. + if (!init_ignores(p, SIGALRM)) { + p->pending_signals |= 1ULL << SIGALRM; + if (sig_kills_now(p, SIGALRM)) { + p->killed = 1; + if (p->killsig == 0) + p->killsig = SIGALRM; + } + if (p->state == SLEEPING) { + p->state = RUNNABLE; + sched_enqueue(p); + } + } + } + release(&ptable.lock); + } +} + +void +alarm_thread_start(void) +{ + if (kthread_create(alarm_thread, "alarmd") < 0) + cprintf("alarm: no kernel thread; alarm() and setitimer() will not fire\n"); +} + int sigsend(int pid, int sig) { diff --git a/kernel/sys/sys_net.c b/kernel/sys/sys_net.c index bf52d9e..fbef77f 100644 --- a/kernel/sys/sys_net.c +++ b/kernel/sys/sys_net.c @@ -66,6 +66,28 @@ fetch_sun_path(addr_t uaddr, int ulen, char *out) return 0; } +// For an abstract address -- sun_path[0] == 0 -- the name as a key, and 1. +// The name is every byte after the NUL up to the length given, NULs included, +// as Linux has it; FNV-1a, which is plenty for a table of NSOCK. +static int +sun_abstract(addr_t uaddr, int ulen, uint64 *key) +{ + struct sockaddr_un sa; + int plen = ulen - (int)sizeof(sa.sun_family); + + if (plen < 2 || ulen > (int)sizeof(sa)) + return 0; + if (copy_from_user(&sa, uaddr, (uint64)ulen) < 0 || sa.sun_path[0] != 0) + return 0; + uint64 h = 1469598103934665603ULL; + for (int i = 1; i < plen; i++) { + h ^= (uchar)sa.sun_path[i]; + h *= 1099511628211ULL; + } + *key = h ? h : 1; + return 1; +} + // Wrap an established socket in a file and a descriptor. Consumes the // socket's reference on success; on failure the caller still owns it. // @@ -144,7 +166,8 @@ SYSCALL_DEFINE(socket) type &= 0xFF; if (domain == AF_UNIX && type != SOCK_STREAM) return -EPROTOTYPE; - if (domain == AF_INET && type != SOCK_STREAM && type != SOCK_DGRAM) + if (domain == AF_INET && type != SOCK_STREAM && type != SOCK_DGRAM && + type != SOCK_RAW) return -EPROTOTYPE; // AF_NETLINK takes SOCK_RAW or SOCK_DGRAM and its own protocol numbering; // socknew checks both, because which protocols exist is the socket layer's @@ -168,10 +191,17 @@ SYSCALL_DEFINE(socket) (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP); if (redundant) protocol = 0; - else if (!(domain == AF_INET && type == SOCK_DGRAM && + else if (!(domain == AF_INET && + (type == SOCK_DGRAM || type == SOCK_RAW) && protocol == IPPROTO_ICMP)) return -EPROTONOSUPPORT; } + // A raw socket is ICMP or nothing: it exists because busybox's ping, which + // is /bin/ping on the Alpine root, opens SOCK_RAW and stops at + // EPROTOTYPE. Raw TCP or UDP would be the stack handing over packets it is + // also processing, and nothing has asked for that. + if (domain == AF_INET && type == SOCK_RAW && protocol != IPPROTO_ICMP) + return -EPROTONOSUPPORT; // ENETDOWN rather than ENFILE when the stack itself is not running: the // old blanket ENFILE said "file table overflow" for a machine with no @@ -240,6 +270,10 @@ SYSCALL_DEFINE(bind) return sockbind_in(s, ip, port_from_net(port)); } + uint64 key; + if (sun_abstract(uaddr, ulen, &key)) + return sockbind_abstract(s, key); + if ((r = fetch_sun_path(uaddr, ulen, path)) < 0) return r; @@ -315,6 +349,10 @@ SYSCALL_DEFINE(connect) return sockconnect_in(s, ip, port_from_net(port)); } + uint64 key; + if (sun_abstract(uaddr, ulen, &key)) + return sockconnect(s, SOCK_ABSTRACT_DEV, key); + if ((r = fetch_sun_path(uaddr, ulen, path)) < 0) return r; @@ -550,6 +588,74 @@ SYSCALL_DEFINE(recvfrom) return r; } +// getsockname(2) and getpeername(2). +// +// Neither existed, and the first program to mind was every X client: libxcb +// picks the cookie to send by the family of the connection's address, asked +// with getpeername and then getsockname, and with both failing it sent none. +// Xorg answered "Authorization required, but no authorization protocol +// specified" and startx gave up. +static int +sock_name(int peer) +{ + struct socket *s; + addr_t uaddr, ulenp; + int r, cap; + + if ((r = argsock(0, &s)) < 0) + return r; + argaddr(1, &uaddr); + argaddr(2, &ulenp); + if (get_user(cap, ulenp) < 0) + return -EFAULT; + if (cap < 0) + return -EINVAL; + + union { + struct sockaddr_in in; + struct sockaddr_un un; + } sa; + int alen; + memset(&sa, 0, sizeof(sa)); + + if (sock_domain(s) == AF_INET) { + uint ip; + ushort port; + if ((r = sockname_inet(s, peer, &ip, &port)) < 0) + return r; + sa.in.sin_family = AF_INET; + sa.in.sin_addr = ip; + sa.in.sin_port = port_to_net(port); + alen = (int)sizeof(sa.in); + } else if (sock_domain(s) == AF_NETLINK) { + // A sockaddr_nl of zeros: family, padding, port id 0 (the kernel), + // no groups. + sa.un.sun_family = AF_NETLINK; + alen = 12; + } else { + if ((r = sockname_unix(s, peer, sa.un.sun_path)) < 0) + return r; + sa.un.sun_family = (uint16)sock_domain(s); + // The family, the path and its NUL; an unnamed socket is the family + // alone, as on Linux. + alen = (int)sizeof(sa.un.sun_family); + if (sa.un.sun_path[0]) + alen += (int)strlen(sa.un.sun_path) + 1; + } + + // Truncated to the caller's buffer, with the full length written back. + if (cap > alen) + cap = alen; + if (cap > 0 && copy_to_user(uaddr, &sa, (uint64)cap) < 0) + return -EFAULT; + if (copy_to_user(ulenp, &alen, sizeof(alen)) < 0) + return -EFAULT; + return 0; +} + +SYSCALL_DEFINE(getsockname) { return sock_name(0); } +SYSCALL_DEFINE(getpeername) { return sock_name(1); } + // shutdown(2). Half-close is not modelled -- there is one buffer per // direction and no per-direction flag -- so SHUT_WR and SHUT_RDWR both mean // "the peer sees end-of-file", which is the behaviour programs depend on. diff --git a/kernel/sys/sys_time.c b/kernel/sys/sys_time.c index 9175c19..412a3fa 100644 --- a/kernel/sys/sys_time.c +++ b/kernel/sys/sys_time.c @@ -10,14 +10,36 @@ #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; - myproc()->alarm_ticks = seconds * HZ; - return 0; + 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 @@ -77,21 +99,52 @@ sys_clock_gettime(void) return 0; } -addr_t sys_getitimer(void) { return 0; } // stub +// 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; - argint(0, &which); argaddr(1, &newp); - // We don't implement interval timers; just track the alarm if it's ITIMER_REAL. - if (which == 0 && newp) { - struct itimerval iv; - if (copy_from_user(&iv, newp, sizeof(iv)) == 0) - myproc()->alarm_ticks = (int)(iv.it_value.tv_sec * HZ + - iv.it_value.tv_usec / USEC_PER_TICK); + 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; } diff --git a/kernel/sys/table.inc b/kernel/sys/table.inc index 0002680..c2ac019 100644 --- a/kernel/sys/table.inc +++ b/kernel/sys/table.inc @@ -184,8 +184,8 @@ SYSCALL(102, 157, prctl) SYSCALL(103, 234, tgkill) SYSCALL(104, 200, tkill) SYSCALL(105, 34, pause) -SYSCALL(106, NO_LINUX, getitimer) -SYSCALL(107, NO_LINUX, setitimer) +SYSCALL(106, 36, getitimer) +SYSCALL(107, 38, setitimer) SYSCALL(108, 98, getrusage) SYSCALL(109, 100, times) SYSCALL(110, 99, sysinfo) @@ -296,6 +296,8 @@ SYSCALL(203, 281, epoll_pwait) SYSCALL(204, 282, signalfd) SYSCALL(205, 284, eventfd) SYSCALL(206, 73, flock) +SYSCALL(207, 51, getsockname) +SYSCALL(208, 52, getpeername) // --- mounts --- SYSCALL(185, 165, mount)