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
This commit is contained in:
Nexoniarz 2026-10-24 19:44:10 +02:00
commit a4ed556543
9 changed files with 428 additions and 28 deletions

View file

@ -492,6 +492,10 @@ int sockbind(struct socket*, const char *path, uint dev, uint64 ino)
int socklisten(struct socket*, int backlog); int socklisten(struct socket*, int backlog);
struct socket* sockaccept(struct socket*); struct socket* sockaccept(struct socket*);
int sockconnect(struct socket*, uint dev, uint64 ino); 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 sockreadable(struct socket*);
int sock_hup(struct socket*); int sock_hup(struct socket*);
int sock_shutdown(struct socket*, int how); 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_connect(void *h, uint addr, ushort port);
int inet_read(void *h, char *buf, int n); int inet_read(void *h, char *buf, int n);
int inet_write(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, int inet_recvfrom(void *h, char *buf, int n,
uint *addr, ushort *port); uint *addr, ushort *port);
int inet_sendto(void *h, char *buf, int n, 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_setpgid(int pid, int pgid);
int proc_setsid(void); int proc_setsid(void);
int sigsend(int pid, int sig); 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 sig_default_kills(int sig);
int proc_exists(int); int proc_exists(int);
int proc_num(void); int proc_num(void);

View file

@ -231,7 +231,11 @@ struct proc {
uint64 signal_mask; uint64 signal_mask;
uint64 pending_signals; 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]; char ofile_flags[NOFILE];
// Real signal delivery (added for SIGCHLD — see kernel/sys/syscall.c's // Real signal delivery (added for SIGCHLD — see kernel/sys/syscall.c's

View file

@ -400,6 +400,7 @@ kmain(uint64 fb_addr)
random_init(); random_init();
netuser_init(); netuser_init();
dbg_thread_start(); dbg_thread_start();
alarm_thread_start();
if (boot_verbose) cprintf("verbose boot: dbg thread started\n"); if (boot_verbose) cprintf("verbose boot: dbg thread started\n");
// The greeting is not printed here. // The greeting is not printed here.

View file

@ -94,6 +94,7 @@ struct inet_sock {
int inuse; int inuse;
int type; // SOCK_STREAM or SOCK_DGRAM int type; // SOCK_STREAM or SOCK_DGRAM
int proto; // 0, or IPPROTO_ICMP for a ping socket 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 tcp_pcb *tpcb;
struct udp_pcb *upcb; struct udp_pcb *upcb;
@ -149,13 +150,36 @@ net_seq(void)
return s; return s;
} }
// Sleep until the count moves past `seen`, or this process is killed. kill() // A blocked socket call gives up for a kill and for any signal it can take.
// makes a sleeping process runnable, so the loop re-checks and leaves. // 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 static void
net_wait(uint seen) net_wait(uint seen)
{ {
acquire(&netwait_lock); acquire(&netwait_lock);
while (netwait_seq == seen && !myproc()->killed) while (netwait_seq == seen && !net_interrupted())
sleep(&netwait_seq, &netwait_lock); sleep(&netwait_seq, &netwait_lock);
release(&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, // 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 // and a socket that sees a reply to somebody else's request will read a
// sequence number it never sent and ignore it. // 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 static u8_t
inet_icmp_recv_cb(void *arg, struct raw_pcb *pcb, struct pbuf *p, inet_icmp_recv_cb(void *arg, struct raw_pcb *pcb, struct pbuf *p,
const ip_addr_t *addr) 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) if (pbuf_copy_partial(p, &type, 1, hlen) != 1 || type != 0)
return 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) { if (icmplen <= rx_space(is) && dq_used(is) < NDGRAM) {
struct dgram *d = &is->dq[is->dhead % 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; d->len = icmplen;
is->dhead++; is->dhead++;
// Copy the ICMP message out, skipping the IP header, across whatever pbuf // Copy the message out, skipping the IP header unless this is a raw
// chain it arrived in. // socket, across whatever pbuf chain it arrived in.
u16_t off = hlen; u16_t off = skip;
while (off < p->tot_len) { while (off < p->tot_len) {
char tmp[64]; char tmp[64];
u16_t want = (u16_t)(p->tot_len - off); u16_t want = (u16_t)(p->tot_len - off);
@ -515,8 +545,15 @@ inet_alloc(int type)
void * void *
inet_new(int type, int proto) 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; 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 // Ready, not *up*: see net_is_ready() in kernel/net/net.c. Requiring an
// address here made every socket() fail until DHCP answered. // address here made every socket() fail until DHCP answered.
if (!net_is_ready()) if (!net_is_ready())
@ -529,6 +566,7 @@ inet_new(int type, int proto)
return 0; return 0;
} }
is->proto = proto; is->proto = proto;
is->iphdr = iphdr;
if (proto == IPPROTO_ICMP) { if (proto == IPPROTO_ICMP) {
// A ping socket. raw_new registers for one IP protocol number; the // A ping socket. raw_new registers for one IP protocol number; the
@ -688,7 +726,7 @@ inet_accept(void *h)
uint seen = net_seq(); uint seen = net_seq();
lwip_sys_unlock(); lwip_sys_unlock();
if (myproc()->killed) if (net_interrupted())
return 0; return 0;
net_wait(seen); net_wait(seen);
} }
@ -729,7 +767,7 @@ inet_connect(void *h, uint32 addr, uint16 port)
return 0; return 0;
if (failed) if (failed)
return -ECONNREFUSED; return -ECONNREFUSED;
if (myproc()->killed) if (net_interrupted())
return -EINTR; return -EINTR;
net_wait(seen); 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 // 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 // socket it belongs to the individual datagram, which is why UDP needs the
// queue above and TCP does not. // 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 int
inet_recvfrom(void *h, char *buf, int n, uint *addr, ushort *port) 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; return got;
if (eof) if (eof)
return 0; // end of file, not an error return 0; // end of file, not an error
if (myproc()->killed) if (net_interrupted())
return -EINTR; return -EINTR;
net_wait(seen); net_wait(seen);
} }
@ -909,7 +988,7 @@ inet_sendto(void *h, char *buf, int n, uint addr, ushort port)
lwip_sys_unlock(); lwip_sys_unlock();
if (done < n) { if (done < n) {
if (myproc()->killed) if (net_interrupted())
return done > 0 ? done : -EINTR; return done > 0 ? done : -EINTR;
// Woken by inet_sent_cb when an acknowledgement frees send buffer, or by // 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 // a failure. Without tcp_sent registered this would be a sleep with

View file

@ -688,6 +688,61 @@ sockbind(struct socket *s, const char *path, uint dev, uint64 ino)
return 0; 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 int
socklisten(struct socket *s, int backlog) socklisten(struct socket *s, int backlog)
{ {

View file

@ -311,6 +311,7 @@ found:
memset(p->sig_handler, 0, sizeof(p->sig_handler)); memset(p->sig_handler, 0, sizeof(p->sig_handler));
memset(p->sig_restorer, 0, sizeof(p->sig_restorer)); memset(p->sig_restorer, 0, sizeof(p->sig_restorer));
p->pending_signals = 0; 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; p->utime = p->stime = p->cutime = p->cstime = 0;
// A recycled slot must not inherit a dead process's place in the queue -- // 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); 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 int
sigsend(int pid, int sig) sigsend(int pid, int sig)
{ {

View file

@ -66,6 +66,28 @@ fetch_sun_path(addr_t uaddr, int ulen, char *out)
return 0; 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 // Wrap an established socket in a file and a descriptor. Consumes the
// socket's reference on success; on failure the caller still owns it. // socket's reference on success; on failure the caller still owns it.
// //
@ -144,7 +166,8 @@ SYSCALL_DEFINE(socket)
type &= 0xFF; type &= 0xFF;
if (domain == AF_UNIX && type != SOCK_STREAM) if (domain == AF_UNIX && type != SOCK_STREAM)
return -EPROTOTYPE; 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; return -EPROTOTYPE;
// AF_NETLINK takes SOCK_RAW or SOCK_DGRAM and its own protocol numbering; // 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 // 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); (domain == AF_INET && type == SOCK_DGRAM && protocol == IPPROTO_UDP);
if (redundant) if (redundant)
protocol = 0; protocol = 0;
else if (!(domain == AF_INET && type == SOCK_DGRAM && else if (!(domain == AF_INET &&
(type == SOCK_DGRAM || type == SOCK_RAW) &&
protocol == IPPROTO_ICMP)) protocol == IPPROTO_ICMP))
return -EPROTONOSUPPORT; 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 // ENETDOWN rather than ENFILE when the stack itself is not running: the
// old blanket ENFILE said "file table overflow" for a machine with no // 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)); 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) if ((r = fetch_sun_path(uaddr, ulen, path)) < 0)
return r; return r;
@ -315,6 +349,10 @@ SYSCALL_DEFINE(connect)
return sockconnect_in(s, ip, port_from_net(port)); 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) if ((r = fetch_sun_path(uaddr, ulen, path)) < 0)
return r; return r;
@ -550,6 +588,74 @@ SYSCALL_DEFINE(recvfrom)
return r; 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 // 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 // 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. // "the peer sees end-of-file", which is the behaviour programs depend on.

View file

@ -10,14 +10,36 @@
#include "core/uaccess.h" #include "core/uaccess.h"
#include "uapi/errno.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 addr_t
sys_alarm(void) sys_alarm(void)
{ {
int seconds; int seconds;
if (argint(0, &seconds) < 0) if (argint(0, &seconds) < 0)
return -1; return -1;
myproc()->alarm_ticks = seconds * HZ; struct proc *p = myproc();
return 0; uint left = alarm_left(p);
alarm_arm(p, seconds > 0 ? (uint)seconds * HZ : 0, 0);
return (left + HZ - 1) / HZ;
} }
addr_t addr_t
@ -77,21 +99,52 @@ sys_clock_gettime(void)
return 0; 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 addr_t
sys_setitimer(void) sys_setitimer(void)
{ {
int which; int which;
addr_t newp; addr_t newp, oldp;
argint(0, &which); argaddr(1, &newp); argint(0, &which); argaddr(1, &newp); argaddr(2, &oldp);
// We don't implement interval timers; just track the alarm if it's ITIMER_REAL. if (which != 0)
if (which == 0 && newp) { return -EINVAL;
struct itimerval iv; struct proc *p = myproc();
if (copy_from_user(&iv, newp, sizeof(iv)) == 0) struct itimerval iv;
myproc()->alarm_ticks = (int)(iv.it_value.tv_sec * HZ + if (oldp) {
iv.it_value.tv_usec / USEC_PER_TICK); 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; return 0;
} }

View file

@ -184,8 +184,8 @@ SYSCALL(102, 157, prctl)
SYSCALL(103, 234, tgkill) SYSCALL(103, 234, tgkill)
SYSCALL(104, 200, tkill) SYSCALL(104, 200, tkill)
SYSCALL(105, 34, pause) SYSCALL(105, 34, pause)
SYSCALL(106, NO_LINUX, getitimer) SYSCALL(106, 36, getitimer)
SYSCALL(107, NO_LINUX, setitimer) SYSCALL(107, 38, setitimer)
SYSCALL(108, 98, getrusage) SYSCALL(108, 98, getrusage)
SYSCALL(109, 100, times) SYSCALL(109, 100, times)
SYSCALL(110, 99, sysinfo) SYSCALL(110, 99, sysinfo)
@ -296,6 +296,8 @@ SYSCALL(203, 281, epoll_pwait)
SYSCALL(204, 282, signalfd) SYSCALL(204, 282, signalfd)
SYSCALL(205, 284, eventfd) SYSCALL(205, 284, eventfd)
SYSCALL(206, 73, flock) SYSCALL(206, 73, flock)
SYSCALL(207, 51, getsockname)
SYSCALL(208, 52, getpeername)
// --- mounts --- // --- mounts ---
SYSCALL(185, 165, mount) SYSCALL(185, 165, mount)