include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"` said nothing about where irq.h was or what it belonged to, and errno.h, stat.h and fcntl.h at the top shadowed the system's for any host tool built with -I./include. They are in folders now -- arch/ core/ fs/ dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and every include names the folder, as Linux's do: "core/proc.h". Done mechanically and checked the hard way: the kernel built from this tree disassembles identically to the one built from the tree before it. The full image boots, and userland is the same set of programs. Paths in docs and comments follow; docs/guides/layout.md has a table of what is where, and tools/fix-includes.py rewrites old-style includes from the folders in the tree, for code written before this. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
parent
0e39e711f1
commit
a1c965da1e
267 changed files with 1560 additions and 1440 deletions
357
include/socket.h
357
include/socket.h
|
|
@ -1,357 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Unix domain sockets.
|
||||
//
|
||||
// These exist for Schcom. A compositor and its clients have to exchange
|
||||
// messages, and a pipe cannot do it: a pipe is one-directional, has no
|
||||
// notion of a connection, and gives no way for a server to be *found* by a
|
||||
// client that starts later. A socket with a name in the filesystem is
|
||||
// exactly the missing piece.
|
||||
//
|
||||
// AF_INET arrived later, and exactly as planned: underneath the same syscalls
|
||||
// rather than reshaping them. A socket carries its domain and every entry point
|
||||
// dispatches on it -- kernel/net/unix.c for AF_UNIX, kernel/net/inet.c (lwIP)
|
||||
// for AF_INET.
|
||||
|
||||
#define AF_UNIX 1
|
||||
#define AF_LOCAL 1
|
||||
#define AF_INET 2
|
||||
// Linux's number, because a musl program passes Linux's constant. getifaddrs()
|
||||
// is implemented over this, which is why it exists here: enumerating interfaces
|
||||
// is something every networked program does and nothing else offers.
|
||||
#define AF_NETLINK 16
|
||||
#define PF_NETLINK AF_NETLINK
|
||||
#define NETLINK_ROUTE 0
|
||||
|
||||
#define SOCK_STREAM 1
|
||||
#define SOCK_DGRAM 2
|
||||
#define SOCK_RAW 3
|
||||
|
||||
// Flags ORed into socket()'s and accept4()'s `type`/`flags` argument. Linux's
|
||||
// values, because a musl program passes Linux's constants.
|
||||
#define SOCK_NONBLOCK 0x800 // O_NONBLOCK
|
||||
#define SOCK_CLOEXEC 0x80000 // O_CLOEXEC
|
||||
|
||||
// setsockopt/getsockopt levels and names, Linux's numbering.
|
||||
//
|
||||
// Only what is listed here is accepted; anything else is answered with
|
||||
// ENOPROTOOPT rather than a success it did not earn. See the comment above
|
||||
// sys_setsockopt for why that is worth the risk of failing a caller that never
|
||||
// checked before.
|
||||
#define SOL_SOCKET 1
|
||||
#define IPPROTO_ICMP 1
|
||||
#define IPPROTO_TCP 6
|
||||
#define IPPROTO_UDP 17
|
||||
|
||||
#define SO_REUSEADDR 2
|
||||
#define SO_TYPE 3
|
||||
#define SO_ERROR 4
|
||||
#define SO_BROADCAST 6
|
||||
#define SO_SNDBUF 7
|
||||
#define SO_RCVBUF 8
|
||||
#define SO_KEEPALIVE 9
|
||||
#define SO_ACCEPTCONN 30
|
||||
|
||||
#define TCP_NODELAY 1
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Ancillary data -- sendmsg/recvmsg, and SCM_RIGHTS
|
||||
//
|
||||
// A descriptor is the only thing that can be passed between two processes that
|
||||
// is not a number: it names an object the kernel already knows, and handing it
|
||||
// over is the difference between "here is a buffer" and "here is a *copy* of a
|
||||
// buffer". Wayland's wl_shm_create_pool passes one and has no integer-shaped
|
||||
// alternative; docs/plan/hybrid.md's mechanism 1 is this and nothing else.
|
||||
//
|
||||
// Every structure below is Linux's, byte for byte, because a musl-linked
|
||||
// program builds them with Linux's headers and we do not get a vote. The
|
||||
// offsets that matter on x86-64 are, for msghdr:
|
||||
//
|
||||
// 0 msg_name 8 msg_namelen(+4 pad) 16 msg_iov 24 msg_iovlen
|
||||
// 32 msg_control 40 msg_controllen 48 msg_flags
|
||||
//
|
||||
// -- 56 bytes total, which is what musl's int-typed msg_iovlen plus a pad word
|
||||
// adds up to as well.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#define MSG_OOB 0x0001
|
||||
#define MSG_PEEK 0x0002
|
||||
#define MSG_DONTROUTE 0x0004
|
||||
#define MSG_CTRUNC 0x0008 // some ancillary data was dropped
|
||||
#define MSG_TRUNC 0x0020
|
||||
#define MSG_DONTWAIT 0x0040
|
||||
#define MSG_EOR 0x0080
|
||||
#define MSG_WAITALL 0x0100
|
||||
#define MSG_NOSIGNAL 0x4000
|
||||
#define MSG_CMSG_CLOEXEC 0x40000000
|
||||
|
||||
// The only control message that exists here. SCM_CREDENTIALS is deliberately
|
||||
// absent rather than stubbed: a caller told it succeeded would read a uid out
|
||||
// of uninitialised memory and act on it.
|
||||
#define SCM_RIGHTS 1
|
||||
|
||||
// The most descriptors one message may carry. A cap has to exist -- the kernel
|
||||
// holds a reference to every one of them until it is received -- and saying so
|
||||
// here means a sender can size its control buffer against the same number the
|
||||
// kernel will enforce.
|
||||
#define SCM_MAXFD 8
|
||||
|
||||
// struct iovec is declared in two headers on purpose, and guarded so that
|
||||
// including both is not an error.
|
||||
//
|
||||
// posix.h has it because readv/writev need it; this file has it because a
|
||||
// userspace program including <socket.h> for sendmsg must get a complete
|
||||
// struct msghdr without also having to know about a kernel header. Both
|
||||
// definitions are identical and both are Linux's layout.
|
||||
//
|
||||
// The guard is not cosmetic. C23 permits redeclaring a struct with identical
|
||||
// members and GCC 15 defaults to C23, so the duplicate was invisible on the
|
||||
// gcc path -- while clang defaults to gnu17 and rejected it, which broke the
|
||||
// one toolchain shell.nix exists to provide and which the UEFI loader needs.
|
||||
#ifndef SCH_IOVEC_DEFINED
|
||||
#define SCH_IOVEC_DEFINED
|
||||
struct iovec {
|
||||
void *iov_base;
|
||||
uint64 iov_len;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct msghdr {
|
||||
void *msg_name; // struct sockaddr *, or 0
|
||||
uint32 msg_namelen;
|
||||
uint32 __pad1;
|
||||
struct iovec *msg_iov;
|
||||
uint64 msg_iovlen;
|
||||
void *msg_control; // struct cmsghdr *, or 0
|
||||
uint64 msg_controllen;
|
||||
int msg_flags; // out on recvmsg: MSG_CTRUNC
|
||||
};
|
||||
|
||||
struct cmsghdr {
|
||||
uint64 cmsg_len; // CMSG_LEN(payload), NOT including padding
|
||||
int cmsg_level; // SOL_SOCKET
|
||||
int cmsg_type; // SCM_RIGHTS
|
||||
};
|
||||
|
||||
// cmsg_len counts the header plus the payload; CMSG_SPACE additionally counts
|
||||
// the padding to the next header. Conflating the two is the classic way to
|
||||
// build a control buffer the kernel then rejects, which is why both exist.
|
||||
#define CMSG_ALIGN(n) (((uint64)(n) + sizeof(uint64) - 1) & ~(uint64)(sizeof(uint64) - 1))
|
||||
#define CMSG_LEN(n) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (uint64)(n))
|
||||
#define CMSG_SPACE(n) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(n))
|
||||
#define CMSG_DATA(c) ((unsigned char *)((struct cmsghdr *)(c) + 1))
|
||||
|
||||
#define CMSG_FIRSTHDR(m) \
|
||||
((m)->msg_control && (m)->msg_controllen >= sizeof(struct cmsghdr) \
|
||||
? (struct cmsghdr *)(m)->msg_control \
|
||||
: (struct cmsghdr *)0)
|
||||
|
||||
#define CMSG_NXTHDR(m, c) \
|
||||
((c)->cmsg_len < sizeof(struct cmsghdr) || \
|
||||
(uint64)((char *)(c) + CMSG_ALIGN((c)->cmsg_len) + sizeof(struct cmsghdr) \
|
||||
- (char *)(m)->msg_control) > (m)->msg_controllen \
|
||||
? (struct cmsghdr *)0 \
|
||||
: (struct cmsghdr *)((char *)(c) + CMSG_ALIGN((c)->cmsg_len)))
|
||||
|
||||
#define UNIX_PATH_MAX 108
|
||||
|
||||
#define SHUT_RD 0
|
||||
#define SHUT_WR 1
|
||||
#define SHUT_RDWR 2
|
||||
|
||||
struct sockaddr_un {
|
||||
uint16 sun_family;
|
||||
char sun_path[UNIX_PATH_MAX];
|
||||
};
|
||||
|
||||
// The generic form syscalls take. Same shape as Linux's, so a musl-linked
|
||||
// program's struct lines up byte for byte.
|
||||
struct sockaddr {
|
||||
uint16 sa_family;
|
||||
char sa_data[UNIX_PATH_MAX];
|
||||
};
|
||||
|
||||
// The AF_INET address. Same layout as Linux's, so a musl-linked program's
|
||||
// struct lines up byte for byte. sin_port and sin_addr are network byte order
|
||||
// and stay that way: lwIP wants them so, and converting on the way in and out
|
||||
// again would be two swaps that cancel.
|
||||
struct sockaddr_in {
|
||||
uint16 sin_family;
|
||||
uint16 sin_port;
|
||||
uint32 sin_addr;
|
||||
char sin_zero[8];
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// rtnetlink, enough of it for getifaddrs(3)
|
||||
//
|
||||
// Every structure below is Linux's, byte for byte, because musl parses what it
|
||||
// is handed with Linux's own headers. They are not ours to design.
|
||||
//
|
||||
// What musl actually does (src/network/netlink.c) is the whole specification
|
||||
// this has to meet: open PF_NETLINK/SOCK_RAW/NETLINK_ROUTE, send an
|
||||
// nlmsghdr+rtgenmsg asking RTM_GETLINK with NLM_F_DUMP, read replies until
|
||||
// NLMSG_DONE, then do it again for RTM_GETADDR on the same socket.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
struct nlmsghdr {
|
||||
uint32 nlmsg_len;
|
||||
uint16 nlmsg_type;
|
||||
uint16 nlmsg_flags;
|
||||
uint32 nlmsg_seq;
|
||||
uint32 nlmsg_pid;
|
||||
};
|
||||
|
||||
struct rtgenmsg { uchar rtgen_family; };
|
||||
|
||||
struct ifinfomsg {
|
||||
uchar ifi_family;
|
||||
uchar __ifi_pad;
|
||||
uint16 ifi_type; // ARPHRD_*
|
||||
int32 ifi_index;
|
||||
uint32 ifi_flags; // IFF_*
|
||||
uint32 ifi_change;
|
||||
};
|
||||
|
||||
struct ifaddrmsg {
|
||||
uchar ifa_family;
|
||||
uchar ifa_prefixlen;
|
||||
uchar ifa_flags;
|
||||
uchar ifa_scope;
|
||||
uint32 ifa_index;
|
||||
};
|
||||
|
||||
struct rtattr {
|
||||
uint16 rta_len;
|
||||
uint16 rta_type;
|
||||
};
|
||||
|
||||
#define NLMSG_ERROR 2
|
||||
#define NLMSG_DONE 3
|
||||
#define NLM_F_REQUEST 0x001
|
||||
#define NLM_F_MULTI 0x002
|
||||
#define NLM_F_DUMP 0x300
|
||||
|
||||
#define RTM_NEWLINK 16
|
||||
#define RTM_GETLINK 18
|
||||
#define RTM_NEWADDR 20
|
||||
#define RTM_GETADDR 22
|
||||
|
||||
#define IFLA_ADDRESS 1
|
||||
#define IFLA_IFNAME 3
|
||||
#define IFA_ADDRESS 1
|
||||
#define IFA_LOCAL 2
|
||||
#define IFA_LABEL 3
|
||||
|
||||
#define ARPHRD_ETHER 1
|
||||
#define ARPHRD_LOOPBACK 772
|
||||
|
||||
#define IFF_UP 0x1
|
||||
#define IFF_LOOPBACK 0x8
|
||||
#define IFF_RUNNING 0x40
|
||||
#define IFF_BROADCAST 0x2
|
||||
#define IFF_MULTICAST 0x1000
|
||||
|
||||
// Both are four-byte aligned on every architecture Linux supports.
|
||||
#define NLMSG_ALIGN(n) (((n) + 3) & ~3)
|
||||
#define RTA_ALIGN(n) (((n) + 3) & ~3)
|
||||
|
||||
#ifdef _KERNEL_SOCKET_INTERNALS
|
||||
|
||||
#define NSOCK 128 // sockets in the system, 4.9 KB each
|
||||
#define SOCKBUF 4096 // receive buffer per connected socket
|
||||
#define NBACKLOG 8
|
||||
|
||||
// Descriptors in flight.
|
||||
//
|
||||
// A batch is pinned to the stream position of the first byte of the message
|
||||
// that carried it, and that is the whole delivery rule: a reader that arrives
|
||||
// at that position gets the descriptors, and a reader that has not reached it
|
||||
// yet is stopped *short* of it rather than allowed to read past. Without the
|
||||
// second half a program that read 4096 bytes in one call would swallow three
|
||||
// messages and be handed one message's descriptors, with no way to tell which.
|
||||
//
|
||||
// Eight batches in flight is not a guess: a sender that has eight unreceived
|
||||
// messages carrying descriptors and keeps going is not going to be rescued by
|
||||
// a ninth slot, and ENOBUFS says so where a larger array would only move the
|
||||
// same failure further away.
|
||||
#define NSCM 8 // messages carrying descriptors, in flight
|
||||
|
||||
struct file;
|
||||
|
||||
struct scm_fds {
|
||||
uint pos; // stream position of the message's first byte
|
||||
int n;
|
||||
struct file *f[SCM_MAXFD]; // references held by the kernel, not by anyone
|
||||
};
|
||||
|
||||
enum sockstate {
|
||||
SS_FREE = 0,
|
||||
SS_UNBOUND,
|
||||
SS_BOUND,
|
||||
SS_LISTENING,
|
||||
SS_CONNECTED,
|
||||
};
|
||||
|
||||
// Every field here is protected by the socket table's single lock in
|
||||
// kernel/net/unix.c -- there is deliberately no per-socket lock; see the
|
||||
// comment at the top of that file for why.
|
||||
struct socket {
|
||||
int ref;
|
||||
int type;
|
||||
int state;
|
||||
int domain; // AF_UNIX or AF_INET
|
||||
|
||||
// For AF_INET: the lwIP-backed socket in kernel/net/inet.c. Opaque here so
|
||||
// that socket.h does not have to drag lwIP's headers into every file that
|
||||
// wants a struct socket.
|
||||
void *inet;
|
||||
|
||||
char name[UNIX_PATH_MAX]; // the bound path, for a listener and getsockname
|
||||
// The filesystem node bind() created, by identity rather than by name.
|
||||
//
|
||||
// Matching on the *path string* was what made these sockets invisible to the
|
||||
// filesystem: two spellings of one path missed each other, a relative path
|
||||
// never matched an absolute one, and there was nothing to stat() or
|
||||
// unlink(). An inode is the thing a path resolves *to*, so connect() can
|
||||
// walk the path however it likes and still arrive at the same socket.
|
||||
//
|
||||
// bind_dev 0 means "no node": a socket bound before this existed, or one
|
||||
// whose file has been unlinked. The socket stays bound and stays usable by
|
||||
// anyone already connected; it is simply no longer findable, which is
|
||||
// exactly what Linux does.
|
||||
uint bind_dev;
|
||||
uint64 bind_ino;
|
||||
|
||||
// A connected socket's peer. Writing to a socket appends to the *peer's*
|
||||
// buffer, which is what makes one buffer per socket enough for a
|
||||
// bidirectional connection.
|
||||
struct socket *peer;
|
||||
int peer_closed;
|
||||
|
||||
char data[SOCKBUF];
|
||||
uint nread, nwrite;
|
||||
|
||||
// Connections waiting to be accepted, on a listener.
|
||||
struct socket *queue[NBACKLOG];
|
||||
int nqueue;
|
||||
|
||||
// Socket options that are genuinely honoured. Kept here rather than only in
|
||||
// lwIP's pcb because getsockopt has to answer for an AF_UNIX socket too, and
|
||||
// because a flag set before bind() has to survive until there is a pcb to
|
||||
// apply it to.
|
||||
int o_reuseaddr;
|
||||
int o_keepalive;
|
||||
int o_broadcast;
|
||||
int o_nodelay;
|
||||
|
||||
// Descriptors sent to this socket and not yet received. They live on the
|
||||
// *receiving* side, next to the bytes they were sent with, because that is
|
||||
// the only side that can say when they have been delivered.
|
||||
struct scm_fds scm[NSCM];
|
||||
int nscm;
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in a new issue