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:
parent
d0941780f5
commit
a4ed556543
9 changed files with 428 additions and 28 deletions
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue