1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
#!/bin/sh
# Build script for xlibre-xserver (Meson, Template B) — Xvfb-only headless build.
#
# THE crux port of the desktop initiative. Builds ONLY the Xvfb DDX (headless
# software framebuffer): no hardware/DRM/GL, no udev/logind, no xorg/xephyr/etc.
# x11vnc (Task 7a) later bridges the Xvfb display to VNC.
#
# XLibre has BSD CI but no illumos/Hammerhead target, so this port carries
# Hammerhead portability patches in patches/ (applied below).
#
# Meson option notes (verified against this tree's meson_options.txt):
# * -Dgbm / -Dxf86-input-null from the original manifest capture do NOT
# exist as options in 25.1.8 and are intentionally dropped (gbm is only
# ever probed as required:false, and the null input driver isn't an
# option here). Intent preserved: Xvfb on, all hardware/GL/DRM off.
# * -Dsha1=libcrypto uses base OpenSSL's libcrypto (find_library('crypto')
# resolves /usr/lib/libcrypto.so; <openssl/sha.h> in base /usr/include).
# * libdrm/pciaccess/epoxy/gbm are all probed required:false in meson.build,
# so the -D*=false switches don't trip a hard missing-dependency error on
# Hammerhead (none of those libs exist here). Verified by source read.
set -e
# Resolve the port dir (holds patches/) BEFORE cd'ing into the extracted source.
PORTDIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
cd "$SRCDIR"
# --- Hammerhead/illumos portability patches (rationale in each patch header) ---
for p in "$PORTDIR"/patches/*.patch; do
[ -f "$p" ] || continue
echo "==> Applying patch: $(basename "$p")"
patch -p1 < "$p"
done
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$PREFIX/share/pkgconfig:/usr/lib/pkgconfig:$PKG_CONFIG_PATH"
# Template B illumos flags: __EXTENSIONS__ + _POSIX_PTHREAD_SEMANTICS select the
# XPG/POSIX prototypes (msg_control, *_r, etc.). coral's default CFLAGS is only
# "-O2 -pipe" and its LDFLAGS is just "-R$PREFIX/lib", so add the rest here.
export CFLAGS="${CFLAGS} -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS"
export LDFLAGS="${LDFLAGS} -L$PREFIX/lib -Wl,-R,$PREFIX/lib"
meson setup build \
--prefix="$PREFIX" \
--libdir=lib \
--buildtype=release \
-Dxvfb=true \
-Dxorg=false -Dxephyr=false -Dxnest=false -Dxfbdev=false -Dxwin=false -Dxquartz=false \
-Dglamor=false -Dglx=false -Dglx_dri=false \
-Ddri1=false -Ddri2=false -Ddri3=false -Ddrm=false \
-Dudev=false -Dudev_kms=false -Dhal=false \
-Dsystemd_logind=false -Dsystemd_notify=false -Dseatd_libseat=false \
-Dsuid_wrapper=false -Dxcsecurity=false -Dxselinux=false \
-Dpciaccess=false -Dagp=false -Dint10=false -Dvgahw=false \
-Dxf86bigfont=false -Dtests=false \
-Ddocs=false -Ddevel-docs=false -Ddocs-pdf=false \
-Dxf86-input-inputtest=false \
-Ddtrace=false -Dlinux_acpi=false -Dlinux_apm=false \
-Dsha1=libcrypto \
-Dxkb_dir=/usr/local/share/X11/xkb \
-Dxkb_bin_dir=/usr/local/bin \
-Dxkb_output_dir=/var/lib/xkb
ninja -C build -j${JOBS:-1}
DESTDIR="$PKGDIR" ninja -C build install
# xlibre-xserver - XLibre X server, built Xvfb-only (headless software framebuffer)
# THE crux port of the desktop initiative. No hardware/DRM/GL DDX - just Xvfb,
# which x11vnc then bridges to VNC. XLibre has no illumos upstream target, so
# this port carries Hammerhead/illumos portability patches (see patches/).
# https://github.com/X11Libre/xserver
[package]
name = "xlibre-xserver"
version = "25.1.8"
release = 1
description = "XLibre X server (Xvfb-only headless build)"
url = "https://github.com/X11Libre/xserver"
license = "MIT"
maintainer = "Chris Tusa <chris.tusa@leafscale.com>"
arch = "x86_64"
[dependencies]
runtime = ["pixman", "libX11", "libXfont2", "libxkbfile", "libXau", "libXdmcp", "libXext", "font-util", "xkeyboard-config", "xkbcomp", "xauth"]
build = ["pixman", "xorgproto", "xtrans", "libXfont2", "libxkbfile", "font-util", "util-macros", "libX11", "libXau", "libXdmcp", "libXext", "libXfixes"]
[[source]]
file = "xlibre-xserver-25.1.8.tar.gz"
urls = ["https://github.com/X11Libre/xserver/archive/refs/tags/xlibre-xserver-25.1.8.tar.gz"]
checksum = "18091f53e8700801b61f0d20c30d850d1060cb31f6a0d2046249bb413809ae16"
[build]
parallel = true
Treat Hammerhead like Solaris/illumos in the os-layer meson build.
Meson derives host_machine.system() from uname sysname, which on Hammerhead
is "hammerhead", not "sunos". The os/ static library's Solaris branch (which
adds -lsocket/-lnsl and the _XOPEN_SOURCE/__EXTENSIONS__ defines needed for
struct msghdr.msg_control and the XPG socket API) therefore never fires,
producing undefined socket()/gethostbyname() symbols at link time. Hammerhead
IS illumos, so extend the guard to match its sysname.
--- a/os/meson.build
+++ b/os/meson.build
@@ -63,7 +63,7 @@
os_c_args = []
# eg. struct msghdr -> msg_control
-if host_machine.system() == 'sunos'
+if host_machine.system() == 'sunos' or host_machine.system() == 'hammerhead'
os_c_args += '-D_XOPEN_SOURCE=1'
os_c_args += '-D_XOPEN_SOURCE_EXTENDED=1'
os_c_args += '-D__EXTENSIONS__'
Fix inverted re-arm check in the illumos event-ports (PORT_SOURCE_FD) ospoll
backend - the listening socket only ever accepted exactly one client for the
lifetime of the server.
Root cause (found 2026-07-22, hh-alpha9, Task 8 M5 integration test):
illumos event ports are one-shot per delivered event - once port_getn()
returns an event for a PORT_SOURCE_FD object, that fd is automatically
dissociated from the port and MUST be re-associated (port_associate() again,
done here via epoll_mod()) to keep receiving further events on it.
ospoll_wait()'s PORT-backend dispatch loop re-arms level-triggered fds with:
if (osfd->trigger == ospoll_trigger_level &&
!xorg_list_is_empty(&osfd->deleted)) {
epoll_mod(ospoll, osfd);
}
osfd->deleted is the intrusive list node ospoll_remove() links onto
ospoll->deleted when an fd is torn down (see the xorg_list_add call right
after port_dissociate() in ospoll_remove()). So xorg_list_is_empty(&osfd->deleted)
is true exactly when this specific fd has NOT just been removed by its own
callback - i.e. it's still a live, valid fd safe to re-arm.
The listening socket is a level-triggered fd that lives for the entire
server lifetime and is never removed, so its deleted node is always empty.
With the `!` in front, the condition only fires when the fd HAS just been
deleted (backwards - re-arming an fd that's about to be freed), and never
fires for the common "still alive" case. Net effect: the listen socket's
single initial port_associate() (done at ospoll_add time) gets consumed by
the first accept() event and is never renewed, so every connection after
the first sits unnoticed in the kernel accept backlog forever.
Confirmed via truss -p <Xvfb pid>: after the first client's full connection
setup, port_getn() goes back to sleeping and no further accept(4, ...) call
is ever made, even though a second client's connect() has already completed
at the socket layer (visible in `netstat -f unix` as a live but un-accepted
pair). Fix: drop the negation so accepting fds get re-armed, and only skip
fds that were actually just torn down.
--- a/os/ospoll.c
+++ b/os/ospoll.c
@@ -638,7 +638,7 @@
osfd->callback(osfd->fd, xevents, osfd->data);
if (osfd->trigger == ospoll_trigger_level &&
- !xorg_list_is_empty(&osfd->deleted)) {
+ xorg_list_is_empty(&osfd->deleted)) {
epoll_mod(ospoll, osfd);
}
}
|