Compare commits

...

10 Commits

Author SHA1 Message Date
MJ Pooladkhay
046e5d172a download cloud-hypervisor and firmware binaries
Signed-off-by: MJ Pooladkhay <mj@pooladkhay.com>
2026-11-01 14:58:27 +00:00
MJ Pooladkhay
c4a3fda2e1 add the ability to have read-only virtio-fs shares
Signed-off-by: MJ Pooladkhay <mj@pooladkhay.com>
2026-11-01 14:58:27 +00:00
MJ Pooladkhay
b9e6593188 add 'make ch' that boots Redox via Cloud Hypervisor
Signed-off-by: MJ Pooladkhay <mj@pooladkhay.com>
2026-11-01 14:58:27 +00:00
MJ Pooladkhay
7e72f1339d share host directories with Redox from 'make qemu'
'make qemu share=<dir>' (or share="tag=dir ...") starts one virtiofsd per share and
adds the matching vhost-user-fs devices, so the guest sees /scheme/virtio-fsd.<tag>.

podman_bootstrap.sh installs virtiofsd, and the selftest recipe builds from the base source.

Signed-off-by: MJ Pooladkhay <mj@pooladkhay.com>
2026-11-01 14:58:27 +00:00
Jeremy Soller
51ca58cc2b Merge branch 'install-places' into 'master'
Improve install for bzip2, cmake, termcap, tcl, libicu, mupdf

See merge request redox-os/redox!2414
2026-11-01 06:09:04 -07:00
Wildan M
84eae1e507
Improve install for bzip2, cmake, termcap, tcl, libicu, mupdf 2026-11-01 13:35:01 +07:00
Jeremy Soller
9007d0a29f Format demo and jeremy configs the same 2026-10-30 14:03:40 -06:00
Jeremy Soller
aa01491b88 Add cosmic-monitor and gtk3 to demo, compile glib schemas in demo on boot 2026-10-30 12:26:24 -06:00
Jeremy Soller
6c094d1376 Merge branch 'gitup' into 'master'
Update git with workaround

See merge request redox-os/redox!2413
2026-10-30 12:07:38 -06:00
Wildan M
617d3c35b5
Update git with workaround 2026-10-30 04:35:28 +07:00
23 changed files with 1099 additions and 334 deletions

View File

@ -96,8 +96,10 @@ include mk/repo.mk
include mk/disk.mk
# Emulation recipes
include mk/virtiofs.mk
include mk/qemu.mk
include mk/virtualbox.mk
include mk/ch.mk
# CI
include mk/ci.mk

View File

@ -11,6 +11,7 @@ filesystem_size = 3000
[packages]
# GUI Apps
cosmic-monitor = {}
cosmic-reader = {}
periodictable = {}
servo = {}
@ -29,13 +30,11 @@ vim = {}
wget = {}
# Emulators
dosbox = {}
mednafen = {}
flycast = {}
# Games
freedoom = {}
neverball = {}
prboom = {}
@ -44,6 +43,7 @@ sopwith = {}
syobonaction = {}
# Demos
gtk3 = {}
nushell = {}
orbclient = {}
pixelcannon = {}
@ -57,6 +57,12 @@ softbuffer = {}
# MIDI
freepats = {}
[[files]]
path = "/usr/lib/init.d/20_glib"
data = """
glib-compile-schemas /usr/share/glib-2.0/schemas/
"""
[[files]]
path = "/home/user/Welcome.txt"
data = """

View File

@ -5,23 +5,25 @@ include = ["demo.toml"]
# General settings
[general]
# Filesystem size in MiB
filesystem_size = 8192
filesystem_size = 16384
# Package settings
[packages]
# apps
# GUI Apps
#boxedwine = {}
cosmic-player = {}
cosmic-reader = {}
cosmic-settings = {}
cosmic-store = {}
cosmic-term = {}
cosmic-text = {}
#cosmic-player = {}
#cosmic-settings = {}
#cosmic-store = {}
#qemu = {} # not building
schismtracker = {}
servo = {}
# cli
# GUI Data
jeremy = {}
noto-color-emoji = {}
ttf-hack = {}
# Terminal Apps
cleye = {}
ffmpeg6 = {}
lua54 = {}
@ -34,26 +36,54 @@ sed = {}
shellharden = {}
xz = {}
# demos
# Emulators
mgba = {}
#retroarch = {} # need to package cores
rs-nes = {} # need game for testing
rust64 = {} # need roms
rustual-boy = {} # need game for testing
scummvm = {} # need game for testing
#vice = {} # broken on new toolchain
# Games
devilutionx = {}
eduke32 = {}
freeciv = {}
gigalomania = {}
love = {}
mednaffe = {}
openjazz = {}
openjk = {}
openttd = {}
openttd-opengfx = {}
openttd-openmsx = {}
openttd-opensfx = {}
quakespasm = {}
sm64ex = {}
#spacecadetpinball = {} # not building
#vvvvvv = {} # cannot find -lgcc_s
# Demos
#cairodemo = {}
cmatrix = {}
cpal = {}
gears = {}
glutin = {}
#glutin = {}
#hematite = {} # not building
iced = {}
libcosmic = {}
osdemo = {} # does not show anything
#libcosmic = {}
#osdemo = {} # does not show anything
#pathfinder = {} # not building
procedural-wallpapers-rs = {}
#rust-cairo = {}
#rust-cairo-demo = {}
sdl2-gears = {}
vttest = {}
#webrender = {}
winit = {}
# dev
# MIDI
generaluser-gs = {}
timidity = {}
# Development
autoconf = {}
automake = {}
#cookbook = {}
@ -63,38 +93,4 @@ gnu-make = {}
nasm = {}
patch = {}
pkg-config = {}
rust = {}
# games
devilutionx = {}
eduke32 = {}
flycast = {}
freeciv = {}
gigalomania = {}
love = {}
mednafen = {}
mgba = {}
openjazz = {}
openjk = {}
openttd = {}
openttd-opengfx = {}
openttd-openmsx = {}
openttd-opensfx = {}
quakespasm = {}
#retroarch = {} # need to package cores
rs-nes = {} # need game for testing
rust64 = {} # need roms
rustual-boy = {} # need game for testing
scummvm = {} # need game for testing
sm64ex = {}
#spacecadetpinball = {} # not building
syobonaction = {}
#vice = {} # broken on new toolchain
#vvvvvv = {} # cannot find -lgcc_s
# stuff
generaluser-gs = {}
jeremy = {}
noto-color-emoji = {}
timidity = {}
ttf-hack = {}
#rust = {}

162
mk/ch.mk Normal file
View File

@ -0,0 +1,162 @@
# Configuration file for Cloud Hypervisor
#
# make ch # boot $(DISK), serial console on this terminal
# make ch share=... # also share host directories, see mk/virtiofs.mk
# make ch net=tap # use the tap device $(CH_TAP) instead of passt
# make ch net=no # no network device
# make ch-shutdown # stop the VM, from another terminal
# make ch-fetch # only download Cloud Hypervisor and its firmware
#
# x86_64 only. The guest boots through the CLOUDHV.fd UEFI firmware
# (https://github.com/cloud-hypervisor/edk2/releases). The disks are virtio-blk.
#
# Cloud Hypervisor (cloud-hypervisor-static, ch-remote-static) and the firmware
# are pinned below. They are downloaded on first use into
# build/cloud-hypervisor/$(ARCH) and checked against their sha256. Set CH,
# CH_REMOTE or CH_FIRMWARE to use other files, e.g. CH=cloud-hypervisor for
# the one in PATH.
#
# The serial console puts this terminal in raw mode, so Ctrl-C goes to Redox.
# Redox cannot power off Cloud Hypervisor by itself yet: use make ch-shutdown.
#
# The network is passt by default: a user-mode network stack, started by
# scripts/passt-run.sh, that answers the guest's DHCP and DNS and carries its
# traffic through ordinary host sockets. No tap, root or firewall changes are
# needed. The guest gets the host's address, and nothing reaches it from
# outside unless forwarded. CH_PASST_FLAGS passes options to passt, e.g.
# "-t 8022:22" (host port 8022 to guest port 22) or
# "--pcap $XDG_RUNTIME_DIR/redox.pcap" (capture the traffic).
#
# net=tap uses the tap device $(CH_TAP) instead: a real link that the host
# can reach on any port and watch with tcpdump. Creating it needs root:
# scripts/ch-tap-setup.sh (firewalld only) creates it and makes the host
# the guest's DHCP server and NAT gateway.
# Pinned releases. To update one, change its version and the sha256 of its
# assets, which GitHub shows next to each file on the release page. Every
# version is downloaded into its own directory.
CH_VERSION=v54.0
CH_FIRMWARE_VERSION=ch-97eeb7b09
CH_URL=https://github.com/cloud-hypervisor/cloud-hypervisor/releases/download/$(CH_VERSION)
CH_FIRMWARE_URL=https://github.com/cloud-hypervisor/edk2/releases/download/$(CH_FIRMWARE_VERSION)
CH_DIR=build/cloud-hypervisor/$(ARCH)
# Release assets and their sha256, per ARCH
CH_ASSET_x86_64=cloud-hypervisor-static
CH_SHA256_x86_64=4f4f98a3d661a40f3e5272627cc895a1b2e525f2f6bedf5f895f9cd21ba5c65b
CH_REMOTE_ASSET_x86_64=ch-remote-static
CH_REMOTE_SHA256_x86_64=acd5be240d5c73373effbe818908fc03e313ec8486237904bff11d93b71b1c83
CH_FIRMWARE_ASSET_x86_64=CLOUDHV.fd
CH_FIRMWARE_SHA256_x86_64=dc2fc8f0e43b96712d9fccc52a3a590769606412b3e1bc911d217addd3bef624
ifneq ($(CH_ASSET_$(ARCH)),)
CH_PINNED=$(CH_DIR)/$(CH_VERSION)/$(CH_ASSET_$(ARCH))
CH_REMOTE_PINNED=$(CH_DIR)/$(CH_VERSION)/$(CH_REMOTE_ASSET_$(ARCH))
CH_FIRMWARE_PINNED=$(CH_DIR)/edk2-$(CH_FIRMWARE_VERSION)/$(CH_FIRMWARE_ASSET_$(ARCH))
endif
CH?=$(CH_PINNED)
CH_REMOTE?=$(CH_REMOTE_PINNED)
CH_FIRMWARE?=$(CH_FIRMWARE_PINNED)
# Only the pinned files in use are downloaded.
CH_DOWNLOADS=$(filter $(CH_PINNED) $(CH_REMOTE_PINNED) $(CH_FIRMWARE_PINNED),$(CH) $(CH_REMOTE) $(CH_FIRMWARE))
CH_SMP?=4
CH_MEM?=2048
CH_API_SOCK?=$(BUILD)/ch.sock
CH_LOG?=$(BUILD)/ch.log
CH_TAP?=redox0
CH_MAC?=52:54:00:12:34:56
# Not in $(BUILD): SELinux limits passt to $XDG_RUNTIME_DIR and /tmp.
CH_PASST_SOCK?=$(or $(XDG_RUNTIME_DIR),/tmp)/redox-passt-$(ARCH)-$(CONFIG_NAME).sock
CH_PASST_FLAGS?=
CHFLAGS?=-v --log-file $(CH_LOG)
CHFLAGS+=--cpus boot=$(CH_SMP)
CHFLAGS+=--firmware $(CH_FIRMWARE)
CHFLAGS+=--serial tty --console off
CHFLAGS+=--api-socket path=$(CH_API_SOCK)
CHFLAGS+= \
--disk path=$(DISK),image_type=raw,serial=NVME_SERIAL \
--disk path=$(EXTRA_DISK),image_type=raw,serial=NVME_EXTRA
PASST_RUN=
ifeq ($(net),tap)
# The Redox virtio-net driver only acknowledges VIRTIO_NET_F_MAC at the moment.
CHFLAGS+=--net tap=$(CH_TAP),mac=$(CH_MAC),offload_tso=off,offload_ufo=off,offload_csum=off
else ifneq ($(net),no)
CHFLAGS+=--net vhost_user=true,socket=$(CH_PASST_SOCK),mac=$(CH_MAC)
PASST_RUN=scripts/passt-run.sh "$(CH_PASST_SOCK)" $(CH_PASST_FLAGS) --
endif
ch_tap_found=$(shell ip link show "$(CH_TAP)" >/dev/null 2>&1 && echo yes)
# vhost-user backends (virtiofsd, passt) need shared guest RAM.
ifneq ($(strip $(share))$(PASST_RUN),)
CHFLAGS+=--memory size=$(CH_MEM)M,shared=on
else
CHFLAGS+=--memory size=$(CH_MEM)M
endif
ifneq ($(strip $(share)),)
CHFLAGS+=$(foreach i,$(share_idx),--fs tag=$(call vfs_tag,$(i)),socket=$(call vfs_sock,$(i)))
endif
# $(1): CH, CH_REMOTE or CH_FIRMWARE. When it is set to something other than
# the pinned download, it must be an existing file or a command in PATH.
ch_check_set=$(if $(filter-out $($(1)_PINNED),$($(1))),$(if $(or $(wildcard $($(1))),$(shell command -v $($(1)))),,$(error $(1)=$($(1)) not found. Leave $(1) unset to use the pinned download)))
.PHONY: ch ch-check ch-deps ch-fetch ch-shutdown
ch-check:
$(if $(filter x86_64,$(ARCH)),,$(error Cloud Hypervisor: only ARCH=x86_64 is supported))
$(if $(filter $(HOST_ARCH),$(ARCH)),,$(error Cloud Hypervisor: ARCH=$(ARCH) cannot run on this $(HOST_ARCH) host))
$(call ch_check_set,CH)
$(call ch_check_set,CH_REMOTE)
$(call ch_check_set,CH_FIRMWARE)
$(if $(filter tap,$(net)),$(if $(ch_tap_found),,$(error no tap device $(CH_TAP). Create it with scripts/ch-tap-setup.sh or leave out net=tap to use passt)))
ch-deps: $(DISK) $(EXTRA_DISK) $(CH_DOWNLOADS)
ch-fetch: ch-check $(CH_DOWNLOADS)
ch: ch-check ch-deps
ifneq ($(wildcard $(MOUNT_DIR)),)
@echo "Disk is mounted. Unmounting the disk..."
$(MAKE) unmount
endif
@if $(CH_REMOTE) --api-socket "$(CH_API_SOCK)" ping >/dev/null 2>&1; then \
echo "ch: a VM is already running on $(CH_API_SOCK); stop it with make ch-shutdown" >&2; \
exit 1; \
fi
# A leftover socket file makes Cloud Hypervisor fail with "Address in use".
rm -f "$(CH_API_SOCK)"
$(VIRTIOFS_RUN) $(PASST_RUN) $(CH) $(CHFLAGS)
ch-shutdown: $(filter $(CH_REMOTE_PINNED),$(CH_REMOTE))
$(call ch_check_set,CH_REMOTE)
# shutdown alone would leave Cloud Hypervisor running.
$(CH_REMOTE) --api-socket "$(CH_API_SOCK)" shutdown-vmm
# Downloads $(1) to $@ if its sha256 is $(2), and sets mode $(3) if given.
# It goes through $@.partial, so $@ only ever holds a checked download.
define ch_download
mkdir -p "$(@D)"
curl -fL --retry 3 --retry-delay 1 -o "$@.partial" "$(1)"
echo "$(2) $@.partial" | sha256sum --check --quiet - \
|| { rm -f "$@.partial"; echo "ch: $(1) does not match the pinned sha256 $(2)" >&2; exit 1; }
$(if $(3),chmod $(3) "$@.partial")
mv "$@.partial" "$@"
endef
ifneq ($(CH_PINNED),)
$(CH_PINNED):
$(call ch_download,$(CH_URL)/$(@F),$(CH_SHA256_$(ARCH)),+x)
$(CH_REMOTE_PINNED):
$(call ch_download,$(CH_URL)/$(@F),$(CH_REMOTE_SHA256_$(ARCH)),+x)
$(CH_FIRMWARE_PINNED):
$(call ch_download,$(CH_FIRMWARE_URL)/$(@F),$(CH_FIRMWARE_SHA256_$(ARCH)))
endif

View File

@ -189,6 +189,20 @@ else
QEMUFLAGS+=-machine $(QEMU_MACHINE)
endif
# virtio-fs shares (share=...): the list is parsed in mk/virtiofs.mk.
ifneq ($(strip $(share)),)
# vhost-user needs guest RAM to be shared with the virtiofsd processes, and
# the NUMA memdev must be exactly as large as -m. One backend serves all
# shares.
ifeq ($(UNAME),Linux)
QEMUFLAGS+=-object memory-backend-memfd,id=mem,size=$(QEMU_MEM)M,share=on
else
QEMUFLAGS+=-object memory-backend-file,id=mem,size=$(QEMU_MEM)M,mem-path=/dev/shm,share=on
endif
QEMUFLAGS+=-numa node,memdev=mem
QEMUFLAGS+=$(foreach i,$(share_idx),-chardev socket,id=vfs$(i),path=$(call vfs_sock,$(i)) -device vhost-user-fs-pci,queue-size=1024,chardev=vfs$(i),tag=$(call vfs_tag,$(i)))
endif
ifeq ($(audio),no)
# No audio
else ifeq ($(audio),ac97)
@ -383,9 +397,9 @@ $(BUILD)/qemu_uboot.rom:
qemu: qemu-deps
ifeq ($(wildcard $(MOUNT_DIR)),)
$(QEMU) $(QEMUFLAGS)
$(VIRTIOFS_RUN) $(QEMU) $(QEMUFLAGS)
else
@echo "\033[1;38;5;196mDisk is mounted. Unmounting the disk...\033[0m";
$(MAKE) unmount
$(QEMU) $(QEMUFLAGS)
$(VIRTIOFS_RUN) $(QEMU) $(QEMUFLAGS)
endif

44
mk/virtiofs.mk Normal file
View File

@ -0,0 +1,44 @@
# Share host directories with the guest through virtio-fs (QEMU and Cloud Hypervisor).
# make qemu share=/path/to/dir # one share, tag is "host" by default
# make qemu share="tag=/path/to/dir" # one share, tag is specified as "tag"
# make qemu share=/path/a share_tag=tag # one share, tag is specified as "tag"
# make qemu share="tag_1=/path/a tag_2=/path/b" # several shares: tag=dir ...
# make qemu share=redox # this build tree, tag "redox"
# make qemu share="redox=/other/path" # ordinary share, tag "redox"
# make qemu share=/path/to/dir:ro # read-only, also tag=dir:ro and redox:ro
# make ch share=... # the same, on Cloud Hypervisor
#
# Each share gets its own host-side virtiofsd daemon (started and stopped by
# scripts/virtiofs-run.sh), a vhost-user-fs device, and a virtio-fsd scheme
# process on Redox. In the guest, a share appears at /scheme/virtio-fsd.<tag>.
# The emulator flags for each share are added in mk/qemu.mk and mk/ch.mk.
# Shared directories must exist. The guest cannot change anything in a :ro share.
#
# WARNING: share=redox exposes the whole build tree to the guest, including
# the harddrive.img Redox is booted from, .git and .config. Writing to them
# from Redox can corrupt the build system. share=redox:ro avoids that.
share?=
share_tag?=host
share_sockdir?=$(BUILD)
VIRTIOFS_RUN=
ifneq ($(strip $(share)),)
share_norm:=$(foreach s,$(share),$(if $(filter redox redox:ro,$(s)),redox=$(ROOT)$(if $(filter %:ro,$(s)),:ro),$(s)))
ifeq ($(words $(share_norm)),1)
share_list:=$(if $(findstring =,$(share_norm)),$(share_norm),$(share_tag)=$(share_norm))
else
share_list:=$(share_norm)
share_bare:=$(foreach s,$(share_list),$(if $(findstring =,$(s)),,$(s)))
ifneq ($(strip $(share_bare)),)
# Keep the next line free of leading tabs: make would read it as a recipe.
$(error share: every entry must be tag=dir when sharing several directories (bad: $(share_bare)); directories containing spaces are not supported)
endif
endif
share_idx:=$(shell seq 1 $(words $(share_list)))
vfs_entry=$(word $(1),$(share_list))
vfs_tag=$(firstword $(subst =, ,$(call vfs_entry,$(1))))
vfs_sock=$(share_sockdir)/virtiofsd-$(1).sock
VIRTIOFS_RUN=scripts/virtiofs-run.sh $(foreach i,$(share_idx),--share "$(call vfs_sock,$(i))" "$(call vfs_entry,$(i))") --
endif

View File

@ -472,6 +472,116 @@ solus()
fi
}
###############################################################################
# virtiofsd is needed on the host by `make qemu share=<dir>` to share
# directories with Redox (scripts/virtiofs-run.sh only looks for it).
###############################################################################
virtiofsd_find()
{
local c
for c in "${VIRTIOFSD:-}" virtiofsd /usr/libexec/virtiofsd /usr/lib/qemu/virtiofsd \
"${XDG_CACHE_HOME:-$HOME/.cache}/virtiofsd-redox/virtiofsd"; do
[ -n "$c" ] || continue
if [ -f "$c" ] && [ -x "$c" ]; then
echo "$c"
return 0
fi
if command -v "$c" >/dev/null 2>&1; then
command -v "$c"
return 0
fi
done
return 1
}
###############################################################################
# Download a static x86_64 virtiofsd build from the upstream CI into
# ~/.cache/virtiofsd-redox, after asking for confirmation.
# Override the source with VIRTIOFSD_URL.
#
# TODO: this is the CI build of upstream's main branch, so it is neither
# pinned nor checked: virtiofsd releases do not ship binaries or checksums
# yet. Once they do, move this download into mk/virtiofs.mk, pinned to a
# release and checked against its sha256 like Cloud Hypervisor in mk/ch.mk,
# and pass it to scripts/virtiofs-run.sh through VIRTIOFSD.
###############################################################################
virtiofsd_download()
{
local url cache dest tmp answer
url="${VIRTIOFSD_URL:-https://gitlab.com/virtio-fs/virtiofsd/-/jobs/artifacts/main/raw/target/x86_64-unknown-linux-musl/release/virtiofsd?job=publish}"
cache="${XDG_CACHE_HOME:-$HOME/.cache}/virtiofsd-redox"
dest="$cache/virtiofsd"
if [ "$(uname -m)" != "x86_64" ]; then
echo "No prebuilt virtiofsd is available for $(uname -m)."
return 1
fi
echo "A static virtiofsd build can be downloaded from"
echo " gitlab.com/virtio-fs/virtiofsd (CI artifact of the main branch)"
echo "to $dest"
printf "Download it now? (y/N): "
read answer || answer=""
if ! echo "$answer" | grep -iq "^y"; then
return 1
fi
mkdir -p "$cache"
tmp="$(mktemp "$cache/virtiofsd.XXXXXX")"
if command -v curl >/dev/null 2>&1; then
curl -fL --retry 3 --retry-delay 1 -o "$tmp" "$url" || { rm -f "$tmp"; return 1; }
elif command -v wget >/dev/null 2>&1; then
wget -O "$tmp" "$url" || { rm -f "$tmp"; return 1; }
else
echo "Need curl or wget to download virtiofsd."
rm -f "$tmp"
return 1
fi
if [ "$(head -c 4 "$tmp")" != "$(printf '\177ELF')" ]; then
echo "The download is not an ELF binary (see $url)."
rm -f "$tmp"
return 1
fi
chmod +x "$tmp"
mv -f "$tmp" "$dest"
echo "virtiofsd installed at $dest"
}
###############################################################################
# This function makes sure virtiofsd is available: it looks for an existing
# one, then tries the distribution package, then offers a download.
# It never fails the bootstrap, virtiofsd is only needed for `share=`.
# @params: $1 the package manager to use on Debian-based Linux
###############################################################################
virtiofsd_setup()
{
local found
echo "Checking for virtiofsd (used by 'make qemu share=<dir>')..."
if found="$(virtiofsd_find)"; then
echo "virtiofsd found at $found"
return 0
fi
echo "virtiofsd not found."
if command -v pacman >/dev/null 2>&1; then
sudo pacman -S --needed virtiofsd || true
elif command -v dnf >/dev/null 2>&1; then
sudo dnf install virtiofsd || true
elif command -v apt-get >/dev/null 2>&1 && apt-cache show virtiofsd >/dev/null 2>&1; then
sudo "$1" install virtiofsd || true
fi
if found="$(virtiofsd_find)"; then
echo "virtiofsd found at $found"
return 0
fi
if virtiofsd_download; then
return 0
fi
echo "Continuing without virtiofsd. 'make qemu share=<dir>' will not work until"
echo "it is installed (or set VIRTIOFSD=/path/to/virtiofsd), re-run this script"
echo "with -d to retry."
}
######################################################################
# This function outlines the different options available for bootstrap
######################################################################
@ -659,6 +769,11 @@ else
fi
fi
# Directory sharing with `make qemu share=<dir>` needs virtiofsd and a Linux host
if [ "$emulator" == "qemu" ] && [ "$(uname -s)" == "Linux" ]; then
virtiofsd_setup "$defpackman"
fi
if [ "$dependenciesonly" = false ]; then
boot
fi

View File

@ -35,6 +35,15 @@ ln -s "libbz2.so.1.0" "${COOKBOOK_STAGE}/usr/lib/libbz2.so.1"
ln -s "libbz2.so.1.0" "${COOKBOOK_STAGE}/usr/lib/libbz2.so"
fi
mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig"
# Change symlinks in usr/bin as relative
find "${COOKBOOK_STAGE}/usr/bin" -type l | while read -r link; do
target=$(readlink "$link")
if [[ "$target" = /* ]]; then
ln -snfr "$target" "$link"
fi
done
mkdir -p "${COOKBOOK_STAGE}/usr/lib/pkgconfig" "${COOKBOOK_STAGE}/usr/share/"
cp "${COOKBOOK_RECIPE}/pkgconfig" "${COOKBOOK_STAGE}/usr/lib/pkgconfig/bzip2.pc"
mv "${COOKBOOK_STAGE}/usr/man" "${COOKBOOK_STAGE}/usr/share/"
"""

View File

@ -3,7 +3,7 @@ tar = "https://github.com/Kitware/CMake/releases/download/v4.3.2/cmake-4.3.2.tar
blake3 = "cbd0121189df1502f61e8f12f044b4a901b1ea16c74c016b85714e4661b8537f"
[build]
template = "cmake"
template = "custom"
dependencies = [
# "cppdap",
"curl",
@ -17,14 +17,19 @@ dependencies = [
"zlib",
"zstd",
]
cmakeflags = [
"-DBUILD_TESTING=Off",
"-DCMAKE_USE_SYSTEM_BZIP2=On",
"-DCMAKE_USE_SYSTEM_CURL=On",
"-DCMAKE_USE_SYSTEM_EXPAT=On",
"-DCMAKE_USE_SYSTEM_LIBARCHIVE=On",
"-DCMAKE_USE_SYSTEM_LIBUV=On",
"-DCMAKE_USE_SYSTEM_NGHTTP2=On",
"-DCMAKE_USE_SYSTEM_ZLIB=On",
"-DCMAKE_USE_SYSTEM_ZSTD=On",
]
script = """
DYNAMIC_INIT
COOKBOOK_CMAKE_FLAGS=(
-DBUILD_TESTING=Off
-DCMAKE_USE_SYSTEM_BZIP2=On
-DCMAKE_USE_SYSTEM_CURL=On
-DCMAKE_USE_SYSTEM_EXPAT=On
-DCMAKE_USE_SYSTEM_LIBARCHIVE=On
-DCMAKE_USE_SYSTEM_LIBUV=On
-DCMAKE_USE_SYSTEM_NGHTTP2=On
-DCMAKE_USE_SYSTEM_ZLIB=On
-DCMAKE_USE_SYSTEM_ZSTD=On
)
cookbook_cmake
mv "${COOKBOOK_STAGE}/usr/doc" "${COOKBOOK_STAGE}/usr/share/"
"""

View File

@ -1,240 +0,0 @@
diff -ruwN git-2.13.1/compat/bswap.h source/compat/bswap.h
--- git-2.13.1/compat/bswap.h 2017-06-04 19:08:11.000000000 -0600
+++ source/compat/bswap.h 2025-04-24 11:20:06.475749424 -0600
@@ -1,3 +1,7 @@
+#if defined(__redox__)
+#include <machine/endian.h>
+#endif
+
/*
* Let's make sure we always have a sane definition for ntohl()/htonl().
* Some libraries define those as a function call, just to perform byte
diff -ruwN git-2.13.1/compat/terminal.c source/compat/terminal.c
--- git-2.13.1/compat/terminal.c 2017-06-04 19:08:11.000000000 -0600
+++ source/compat/terminal.c 2025-04-18 10:00:11.318697446 -0600
@@ -137,6 +137,18 @@
return buf.buf;
}
+#elif defined(__redox__)
+
+ssize_t __getline(char **lptr, size_t *n, FILE *fp);
+
+char *git_terminal_prompt(const char *prompt, int echo)
+{
+ char *line = NULL;
+ size_t n = 0;
+ __getline(&line, &n, stdin);
+ return line; // XXX leak
+}
+
#else
char *git_terminal_prompt(const char *prompt, int echo)
diff -ruwN git-2.13.1/configure source/configure
--- git-2.13.1/configure 2017-06-04 19:08:11.000000000 -0600
+++ source/configure 2025-04-18 10:00:11.318697446 -0600
@@ -6156,7 +6156,7 @@
ac_res=$ac_cv_search_getaddrinfo
if test "$ac_res" != no; then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
- NO_IPV6=
+ NO_IPV6=YesPlease
else
NO_IPV6=YesPlease
fi
diff -ruwN git-2.13.1/daemon.c source/daemon.c
--- git-2.13.1/daemon.c 2017-06-04 19:08:11.000000000 -0600
+++ source/daemon.c 2025-04-18 10:00:11.319697447 -0600
@@ -71,13 +71,21 @@
return hi->ip_address.buf;
}
+#if defined(__redox__)
+#define LOG_ERR 0
+#define LOG_INFO 1
+#endif
+
static void logreport(int priority, const char *err, va_list params)
{
+#if !defined(__redox__)
if (log_syslog) {
char buf[1024];
vsnprintf(buf, sizeof(buf), err, params);
syslog(priority, "%s", buf);
- } else {
+ } else
+#endif
+ {
/*
* Since stderr is set to buffered mode, the
* logging of different processes will not overlap
@@ -888,8 +896,12 @@
if (!reuseaddr)
return 0;
+#if defined(__redox__)
+ return 0;
+#else
return setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
&on, sizeof(on));
+#endif
}
struct socketlist {
@@ -1174,11 +1186,7 @@
if (!group_name)
c.gid = c.pass->pw_gid;
else {
- struct group *group = getgrnam(group_name);
- if (!group)
die("group not found - %s", group_name);
-
- c.gid = group->gr_gid;
}
return &c;
@@ -1348,10 +1356,12 @@
usage(daemon_usage);
}
+#if !defined(__redox__)
if (log_syslog) {
openlog("git-daemon", LOG_PID, LOG_DAEMON);
set_die_routine(daemon_die);
} else
+#endif
/* avoid splitting a message in the middle */
setvbuf(stderr, NULL, _IOFBF, 4096);
diff -ruwN git-2.13.1/git-compat-util.h source/git-compat-util.h
--- git-2.13.1/git-compat-util.h 2017-06-04 19:08:11.000000000 -0600
+++ source/git-compat-util.h 2025-04-18 10:00:11.319697447 -0600
@@ -1,6 +1,18 @@
#ifndef GIT_COMPAT_UTIL_H
#define GIT_COMPAT_UTIL_H
+#ifndef SIG_DFL
+#define SIG_DFL ((void (*)(int))0)
+#endif
+
+#ifndef SIG_IGN
+#define SIG_IGN ((void (*)(int))1)
+#endif
+
+#ifndef SIG_ERR
+#define SIG_ERR ((void (*)(int))-1)
+#endif
+
#define _FILE_OFFSET_BITS 64
@@ -323,6 +335,14 @@
#define PATH_SEP ':'
#endif
+#ifndef DEV_NULL
+#if defined(__redox__)
+#define DEV_NULL "/scheme/null"
+#else
+#define DEV_NULL "/dev/null"
+#endif
+#endif
+
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
diff -ruwN git-2.13.1/Makefile source/Makefile
--- git-2.13.1/Makefile 2017-06-05 08:08:11.000000000 +0700
+++ source/Makefile 2025-09-01 04:41:10.339224568 +0700
@@ -979,7 +979,7 @@
BUILTIN_OBJS += builtin/write-tree.o
GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB)
-EXTLIBS =
+EXTLIBS = -lnghttp2
GIT_USER_AGENT = git/$(GIT_VERSION)
@@ -1802,7 +1802,6 @@
$(BUILT_INS): git$X
$(QUIET_BUILT_IN)$(RM) $@ && \
- ln $< $@ 2>/dev/null || \
ln -s $< $@ 2>/dev/null || \
cp $< $@
@@ -2096,7 +2095,6 @@
$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
$(QUIET_LNCP)$(RM) $@ && \
- ln $< $@ 2>/dev/null || \
ln -s $< $@ 2>/dev/null || \
cp $< $@
@@ -2449,14 +2447,12 @@
for p in $(filter $(install_bindir_programs),$(BUILT_INS)); do \
$(RM) "$$bindir/$$p" && \
test -z "$(NO_INSTALL_HARDLINKS)" && \
- ln "$$bindir/git$X" "$$bindir/$$p" 2>/dev/null || \
ln -s "git$X" "$$bindir/$$p" 2>/dev/null || \
cp "$$bindir/git$X" "$$bindir/$$p" || exit; \
done && \
for p in $(BUILT_INS); do \
$(RM) "$$execdir/$$p" && \
test -z "$(NO_INSTALL_HARDLINKS)" && \
- ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
done && \
@@ -2464,7 +2460,6 @@
for p in $$remote_curl_aliases; do \
$(RM) "$$execdir/$$p" && \
test -z "$(NO_INSTALL_HARDLINKS)" && \
- ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
done && \
diff -ruwN git-2.13.1/run-command.c source/run-command.c
--- git-2.13.1/run-command.c 2017-06-04 19:08:11.000000000 -0600
+++ source/run-command.c 2025-04-18 10:00:11.320697447 -0600
@@ -120,9 +120,9 @@
#ifndef GIT_WINDOWS_NATIVE
static inline void dup_devnull(int to)
{
- int fd = open("/dev/null", O_RDWR);
+ int fd = open(DEV_NULL, O_RDWR);
if (fd < 0)
- die_errno(_("open /dev/null failed"));
+ die_errno(_("open %s failed"), DEV_NULL);
if (dup2(fd, to) < 0)
die_errno(_("dup2(%d,%d) failed"), fd, to);
close(fd);
diff -ruwN git-2.13.1/setup.c source/setup.c
--- git-2.13.1/setup.c 2017-06-05 08:08:11.000000000 +0700
+++ source/setup.c 2025-09-01 04:41:10.339224568 +0700
@@ -1146,11 +1146,11 @@
/* if any standard file descriptor is missing open it to /dev/null */
void sanitize_stdfds(void)
{
- int fd = open("/dev/null", O_RDWR, 0);
+ int fd = open(DEV_NULL, O_RDWR, 0);
while (fd != -1 && fd < 2)
fd = dup(fd);
if (fd == -1)
- die_errno("open /dev/null or dup failed");
+ die_errno("open %s or dup failed", DEV_NULL);
if (fd > 2)
close(fd);
}
@@ -1169,8 +1169,10 @@
default:
exit(0);
}
+#if !defined(__redox__)
if (setsid() == -1)
die_errno("setsid failed");
+#endif
close(0);
close(1);
close(2);

View File

@ -0,0 +1,2 @@
[protocol]
version = 0

View File

@ -1,15 +1,13 @@
[source]
tar = "https://www.kernel.org/pub/software/scm/git/git-2.13.1.tar.xz"
blake3 = "bc78271bffd60c5b8b938d8c08fd74dc2de8d21fbaf8f8e0e3155436d9263f17"
patches = ["git.patch"]
tar = "https://www.kernel.org/pub/software/scm/git/git-2.56.0.tar.xz"
blake3 = "596b02fea3d67e2ca45208aabebc92d2c14e7f2827f82cba59a630d55eea0854"
[build]
dependencies=[
"curl",
"expat",
"nghttp2",
"openssl3",
"zlib"
"zlib",
"pcre2",
]
template = "custom"
script = """
@ -18,36 +16,34 @@ DYNAMIC_INIT
MAKEFLAGS=(
NEEDS_SSL_WITH_CURL=1
NEEDS_CRYPTO_WITH_SSL=1
NO_IPV6=1
NO_PREAD=1
NO_MMAP=1
NO_SETITIMER=1
NO_UNIX_SOCKETS=1
NO_IPV6=1 # not implemented
NO_MMAP=1 # mmap writes extra null padding
NO_SETITIMER=1 # deprecated
NO_UNIX_SOCKETS=1 # need inotify
NEEDS_LIBICONV=
NEEDS_LIBRT=
BLK_SHA1=1
V=1
RUST_TARGETS=$TARGET
INSTALL_SYMLINKS=1
)
if [ "${COOKBOOK_DYNAMIC}" != "1" ]; then
# git does not use pkgconfig to be aware with zstd
MAKEFLAGS+=( EXTLIBS="-lzstd -lnghttp2 -lz" )
fi
export CURL_CONFIG="${COOKBOOK_SYSROOT}/usr/bin/curl-config"
./configure \
--host="${GNU_TARGET}" \
--prefix=/usr \
--sysconfdir=/etc \
ac_cv_fread_reads_directories=yes \
ac_cv_snprintf_returns_bogus=yes \
ac_cv_lib_curl_curl_global_init=yes
"${COOKBOOK_MAKE}" "${MAKEFLAGS[@]}" -j"${COOKBOOK_MAKE_JOBS}"
"${COOKBOOK_MAKE}" DESTDIR="${COOKBOOK_STAGE}" "${MAKEFLAGS[@]}" install
rm -rf "${COOKBOOK_STAGE}/usr/share/man"
# TODO: pinned to protocol v0 otherwise silently quit
mkdir -p "${COOKBOOK_STAGE}/etc"
cp "${COOKBOOK_RECIPE}/gitconfig" "${COOKBOOK_STAGE}/etc/"
"""
[package]
dependencies = [
"ca-certificates",
"nghttp2"
]

View File

@ -6,13 +6,20 @@ patches = [
]
[build]
template = "custom"
compiler = "no-lto"
script = """
DYNAMIC_STATIC_INIT
export ac_cv_header_stdc=yes
COOKBOOK_CONFIGURE_FLAGS+=(
--prefix="${COOKBOOK_STAGE}/usr"
--enable-install-termcap
--with-termcap="${COOKBOOK_STAGE}/etc/termcap"
)
COOKBOOK_MAKE_FLAGS=(
infodir="$COOKBOOK_STAGE/usr/share/info"
)
# termcap only emit static object
export CC+=" -fPIC"
mkdir -p "${COOKBOOK_STAGE}/etc"
cookbook_configure
"""

View File

@ -0,0 +1,7 @@
[source]
# The selftest is a member of the base workspace (base/selftests/virtio-fsd)
same_as = "../../core/base"
[build]
template = "cargo"
cargopackages = ["virtio-fsd-selftest"]

View File

@ -1,4 +1,4 @@
#TODO: require git update
#TODO: unable to init TUI due to bugs in pty
[source]
git = "https://github.com/willnode/lazygit"
branch = "redox"

View File

@ -1,7 +1,10 @@
#TODO compiled but not tested
[source]
tar = "http://prdownloads.sourceforge.net/tcl/tcl8.6.13-src.tar.gz"
blake3 = "a44611c5d38d7dbc1e6ce49d6a78ae7d43817b861412b54b30357bc431510978"
tar = "http://prdownloads.sourceforge.net/tcl/tcl9.1.0-src.tar.gz"
blake3 = "a5c4860fdbd9e01f002ab2cf8508f912f43ab218a5035d93cad23d57ac8b8db3"
patches = [
"redox.patch"
]
[build]
template = "custom"
@ -12,6 +15,7 @@ COOKBOOK_CONFIGURE_FLAGS+=(
ac_cv_func_memcmp_working=yes
tcl_cv_strstr_unbroken=
tcl_cv_strtoul_unbroken=
--disable-zipfs
)
cookbook_configure
"""

View File

@ -0,0 +1,23 @@
diff -ruwN source/generic/tclBasic.c source-new/generic/tclBasic.c
--- source/generic/tclBasic.c 2026-09-23 18:52:24.000000000 +0000
+++ source-new/generic/tclBasic.c 2026-10-31 22:41:10.290905986 +0000
@@ -8034,7 +8034,7 @@
return TCL_ERROR;
}
errno = 0;
- return CheckDoubleResult(interp, fma(d1, d2, d3));
+ return CheckDoubleResult(interp, d1 * d2 + d3);
}
static int
diff -ruwN source/pkgs/sqlite3.53.4/compat/sqlite3/sqlite3.c source-new/pkgs/sqlite3.53.4/compat/sqlite3/sqlite3.c
--- source/pkgs/sqlite3.53.4/compat/sqlite3/sqlite3.c 2026-09-11 17:37:04.000000000 +0000
+++ source-new/pkgs/sqlite3.53.4/compat/sqlite3/sqlite3.c 2026-10-31 22:44:08.440361284 +0000
@@ -15688,6 +15688,7 @@
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
+#include <alloca.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

View File

@ -16,6 +16,7 @@ CPPFLAGS+=" -fno-lto"
COOKBOOK_CONFIGURE="${COOKBOOK_SOURCE}/source/configure"
COOKBOOK_CONFIGURE_FLAGS+=(
icu_cv_host_frag=mh-linux
--sbindir=/usr/bin
)
if [ "$TARGET" != "$COOKBOOK_HOST_TARGET" ]; then
COOKBOOK_CONFIGURE_FLAGS+=(

View File

@ -14,10 +14,11 @@ dependencies = [
"libgumbo",
# "freeglut", TODO: HAVE_GLUT=yes
]
compiler = "gcc"
script = """
DYNAMIC_INIT
cd $COOKBOOK_SOURCE
mkdir -p $COOKBOOK_BUILD/shared-release
"${COOKBOOK_MAKE}" install-libs install-apps shared=yes build=release USE_SYSTEM_LIBS=yes \
"${COOKBOOK_MAKE}" install-libs install-apps shared=yes build=release USE_SYSTEM_LIBS=yes prefix=/usr \
-j "${COOKBOOK_MAKE_JOBS}" DESTDIR="${COOKBOOK_STAGE}" "OUT=$COOKBOOK_BUILD/shared-release"
"""

365
scripts/ch-tap-setup.sh Executable file
View File

@ -0,0 +1,365 @@
#!/usr/bin/env bash
# Host setup for `make ch net=tap`: tap redox0, IPv4 forwarding, firewalld NAT
# and dnsmasq DHCP/DNS. Needs firewalld and dnsmasq. Re-run after a reboot.
# As root without sudo, set TAP_USER=<user>.
set -euo pipefail
# Debian does not put the sbin directories in a normal user's PATH.
PATH="${PATH}:/usr/local/sbin:/usr/sbin:/sbin"
TAP=redox0
GUEST_MAC=52:54:00:12:34:56
TAP_ADDR=192.168.249.1
TAP_CIDR="${TAP_ADDR}/24"
DHCP_FIRST=192.168.249.10
DHCP_LAST=192.168.249.50
DHCP_RANGE="${DHCP_FIRST},${DHCP_LAST},255.255.255.0,12h"
ZONE=chnet
POLICY=chnet-nat
SYSCTL_DROPIN=/etc/sysctl.d/90-redox-tap.conf
DNSMASQ_PID=/run/redox0-dnsmasq.pid
WAIT_TIMEOUT=3 # seconds
step() {
printf '\n==> %s\n' "$*"
}
info() {
printf ' %s\n' "$*"
}
die() {
echo "error: $*" >&2
exit 1
}
as_root() {
if [[ "${EUID}" -eq 0 ]]; then
"$@"
else
sudo "$@"
fi
}
wait_until() {
local deadline=$((SECONDS + $1))
shift
until "$@"; do
((SECONDS < deadline)) || return 1
sleep 0.1
done
}
# Only tun/tap devices have tun_flags. IFF_TAP is 0x0002, IFF_UP is 0x1,
# owner -1 means none.
tap_exists() {
[[ -e "/sys/class/net/${TAP}" ]]
}
is_tap() {
[[ -r "/sys/class/net/${TAP}/tun_flags" ]] \
&& (($(<"/sys/class/net/${TAP}/tun_flags") & 0x0002))
}
tap_owner() {
cat "/sys/class/net/${TAP}/owner"
}
link_is_up() {
(($(<"/sys/class/net/${TAP}/flags") & 0x1))
}
tap_addrs() {
ip -o -4 addr show dev "${TAP}" | awk '{ print $4 }'
}
has_addr() {
tap_addrs | grep -qx "${TAP_CIDR}"
}
forwarding_on() {
[[ "$(sysctl -n net.ipv4.ip_forward)" == "1" ]]
}
forwarding_persisted_by() {
grep -RlsE '^[[:space:]]*net\.ipv4\.ip_forward[[:space:]]*=[[:space:]]*1[[:space:]]*(#.*)?$' \
/etc/sysctl.conf /etc/sysctl.d /usr/lib/sysctl.d | head -n 1
}
FW_CHANGED=0
fw_ensure_exists() {
local kind="$1" name="$2" list
case "${kind}" in
zone) list=--get-zones ;;
policy) list=--get-policies ;;
esac
if firewall-cmd --permanent "${list}" | tr ' ' '\n' | grep -qx "${name}"; then
info "${kind} ${name}: exists"
else
info "${kind} ${name}: creating"
as_root firewall-cmd --permanent "--new-${kind}=${name}" >/dev/null
FW_CHANGED=1
fi
}
fw_ensure() {
local what="$1" scope="$2" query="$3" add="$4"
if firewall-cmd --permanent "${scope}" "${query}" >/dev/null; then
info "${what}: already set"
else
info "${what}: adding"
as_root firewall-cmd --permanent "${scope}" "${add}" >/dev/null
FW_CHANGED=1
fi
}
# --get-target is permanent-only. The runtime value is the target: line.
policy_accepts() {
[[ "$(firewall-cmd --policy="${POLICY}" --list-all | awk '$1 == "target:" { print $2 }')" == "ACCEPT" ]]
}
dnsmasq_pids() {
pgrep -f "(^|/)dnsmasq .*--interface=${TAP}( |$)" || true
}
# dnsmasq binds DHCP to 0.0.0.0%redox0:67 but DNS to 192.168.249.1:53.
dhcp_listening() {
ss -H -uln | awk -v t="${TAP}" '$4 ~ ("%" t ":67$") { f = 1 } END { exit !f }'
}
dns_listening() {
ss -H -uln | awk -v a="${TAP_ADDR}" -v t="${TAP}" \
'$4 == (a ":53") || $4 == (a "%" t ":53") { f = 1 } END { exit !f }'
}
dnsmasq_answering() {
dhcp_listening && dns_listening
}
dhcp_port_free() {
! dhcp_listening
}
preflight() {
if [[ -z "${TAP_USER:-}" ]]; then
if [[ "${EUID}" -eq 0 ]]; then
TAP_USER="${SUDO_USER:-}"
else
TAP_USER="$(id -un)"
fi
fi
if [[ -z "${TAP_USER}" || "${TAP_USER}" == "root" ]]; then
die "run this as the user that starts cloud-hypervisor (sudo from that user is fine), or set TAP_USER=<user>."
fi
TAP_UID="$(id -u "${TAP_USER}")" || die "no such user: ${TAP_USER}"
if ! command -v firewall-cmd >/dev/null 2>&1 || ! firewall-cmd --state >/dev/null 2>&1; then
die "firewalld is not running. This script only supports firewalld; without it, use plain 'make ch', whose passt network needs no tap."
fi
local cmd
for cmd in ip sysctl dnsmasq ss pgrep; do
command -v "${cmd}" >/dev/null 2>&1 || die "missing command: ${cmd}"
done
echo "Setting up ${TAP} for 'make ch net=tap': host ${TAP_CIDR}, guest DHCP pool ${DHCP_FIRST}-${DHCP_LAST}."
if [[ "${EUID}" -ne 0 ]]; then
echo "Privileged steps run through sudo, which may ask you to authenticate."
fi
}
setup_tap() {
step "Tap device ${TAP}"
TAP_IS_NEW=0
if tap_exists; then
is_tap || die "${TAP} exists but is not a tap device. Remove or rename it first."
local owner
owner="$(tap_owner)"
if [[ "${owner}" == "${TAP_UID}" ]]; then
info "exists and is owned by ${TAP_USER} (uid ${TAP_UID}), keeping it"
return
fi
if [[ "${owner}" == "-1" ]]; then
info "exists but has no owner, not ${TAP_USER} (uid ${TAP_UID}): deleting it"
else
info "exists but is owned by uid ${owner}, not ${TAP_USER} (uid ${TAP_UID}): deleting it"
fi
as_root ip link del "${TAP}"
else
info "does not exist"
fi
info "creating it, owned by ${TAP_USER} so cloud-hypervisor can open it without root"
as_root ip tuntap add "${TAP}" mode tap user "${TAP_USER}"
TAP_IS_NEW=1
}
setup_address() {
step "Host address ${TAP_CIDR} on ${TAP} (the guest's gateway and DNS server)"
local addr
for addr in $(tap_addrs); do
[[ "${addr}" == "${TAP_CIDR}" ]] || die "${TAP} already has ${addr}; this script expects ${TAP_CIDR}."
done
if has_addr; then
info "already set"
else
info "adding it"
as_root ip addr add "${TAP_CIDR}" dev "${TAP}"
fi
if link_is_up; then
info "link is already up"
else
info "setting the link up"
as_root ip link set "${TAP}" up
fi
}
setup_forwarding() {
step "IPv4 forwarding (routes guest packets out of the host)"
if forwarding_on; then
info "net.ipv4.ip_forward is already 1"
else
info "setting net.ipv4.ip_forward=1"
as_root sysctl -q -w net.ipv4.ip_forward=1
fi
local persist
persist="$(forwarding_persisted_by || true)"
if [[ -n "${persist}" ]]; then
info "kept across reboots by ${persist}"
else
info "writing ${SYSCTL_DROPIN} so it survives a reboot"
printf 'net.ipv4.ip_forward = 1\n' | as_root tee "${SYSCTL_DROPIN}" >/dev/null
fi
}
setup_firewalld() {
step "firewalld zone ${ZONE} (lets the guest reach dnsmasq on the host)"
fw_ensure_exists zone "${ZONE}"
fw_ensure "interface ${TAP}" --zone="${ZONE}" --query-interface="${TAP}" --add-interface="${TAP}"
fw_ensure "service dhcp (UDP 67)" --zone="${ZONE}" --query-service=dhcp --add-service=dhcp
fw_ensure "service dns (port 53)" --zone="${ZONE}" --query-service=dns --add-service=dns
step "firewalld policy ${POLICY} (forwards and masquerades guest traffic to the outside)"
fw_ensure_exists policy "${POLICY}"
fw_ensure "ingress zone ${ZONE}" --policy="${POLICY}" --query-ingress-zone="${ZONE}" --add-ingress-zone="${ZONE}"
fw_ensure "egress zone ANY" --policy="${POLICY}" --query-egress-zone=ANY --add-egress-zone=ANY
if [[ "$(firewall-cmd --permanent --policy="${POLICY}" --get-target)" == "ACCEPT" ]]; then
info "target ACCEPT: already set"
else
info "target ACCEPT: setting"
as_root firewall-cmd --permanent --policy="${POLICY}" --set-target=ACCEPT >/dev/null
FW_CHANGED=1
fi
fw_ensure "masquerade" --policy="${POLICY}" --query-masquerade --add-masquerade
step "Applying firewalld settings"
if [[ "${FW_CHANGED}" -eq 1 ]]; then
info "reloading firewalld to apply the permanent changes"
as_root firewall-cmd --reload >/dev/null
else
info "nothing changed, no reload needed"
fi
# firewalld does not always attach an interface that appeared after it
# started.
if firewall-cmd --zone="${ZONE}" --query-interface="${TAP}" >/dev/null; then
info "${TAP} is in zone ${ZONE} at runtime"
else
info "attaching ${TAP} to zone ${ZONE} at runtime (firewalld missed the new interface)"
as_root firewall-cmd --zone="${ZONE}" --add-interface="${TAP}" >/dev/null
fi
}
# dnsmasq --bind-interfaces keeps the old interface index, so a new tap needs
# a new dnsmasq.
setup_dnsmasq() {
step "dnsmasq on ${TAP} (DHCP ${DHCP_FIRST}-${DHCP_LAST}, gateway and DNS ${TAP_ADDR})"
local pids pid
mapfile -t pids < <(dnsmasq_pids)
if [[ "${TAP_IS_NEW}" -eq 0 ]] && dnsmasq_answering; then
info "already answering DHCP and DNS (pid ${pids[*]:-unknown}), keeping it"
return
fi
if [[ "${TAP_IS_NEW}" -eq 0 ]]; then
info "dnsmasq is not answering both DHCP and DNS on ${TAP}"
elif [[ ${#pids[@]} -gt 0 ]]; then
info "${TAP} was recreated, so dnsmasq (pid ${pids[*]}) is bound to the old interface"
fi
for pid in "${pids[@]}"; do
info "stopping dnsmasq pid ${pid}"
as_root kill "${pid}" || true
done
if ! wait_until "${WAIT_TIMEOUT}" dhcp_port_free; then
ss -uln | grep "${TAP}" >&2 || true
die "UDP 67 on ${TAP} is still in use."
fi
info "starting dnsmasq (pid file ${DNSMASQ_PID})"
# --conf-file=/dev/null: /etc/dnsmasq.conf must not add other interfaces.
as_root dnsmasq \
--conf-file=/dev/null \
--interface="${TAP}" \
--bind-interfaces \
--except-interface=lo \
--pid-file="${DNSMASQ_PID}" \
--dhcp-range="${DHCP_RANGE}" \
--dhcp-option=3,"${TAP_ADDR}" \
--dhcp-option=6,"${TAP_ADDR}"
wait_until "${WAIT_TIMEOUT}" dnsmasq_answering || true
}
CHECK_FAILED=0
check() {
local what="$1"
shift
if "$@" >/dev/null 2>&1; then
info "ok ${what}"
else
info "FAILED ${what}"
CHECK_FAILED=1
fi
}
run_checks() {
step "Checking"
check "${TAP} has ${TAP_CIDR}" has_addr
check "net.ipv4.ip_forward is 1" forwarding_on
check "${TAP} is in zone ${ZONE}" firewall-cmd --zone="${ZONE}" --query-interface="${TAP}"
check "zone ${ZONE} allows dhcp" firewall-cmd --zone="${ZONE}" --query-service=dhcp
check "zone ${ZONE} allows dns" firewall-cmd --zone="${ZONE}" --query-service=dns
check "policy ${POLICY} target is ACCEPT" policy_accepts
check "policy ${POLICY} masquerades" firewall-cmd --policy="${POLICY}" --query-masquerade
check "dnsmasq answers DHCP on ${TAP} (UDP 67)" dhcp_listening
check "dnsmasq answers DNS on ${TAP_ADDR} (UDP 53)" dns_listening
if [[ "${CHECK_FAILED}" -ne 0 ]]; then
echo >&2
echo "Sockets on ${TAP}:" >&2
ss -uln | grep -E "%${TAP}:|${TAP_ADDR}:" >&2 || true
die "setup is incomplete, see the FAILED lines above."
fi
}
print_summary() {
echo
echo "${TAP} is ready. It shows NO-CARRIER until cloud-hypervisor opens it; that is normal."
echo "Start Redox with 'make ch net=tap'. It adds:"
echo " --net tap=${TAP},mac=${GUEST_MAC},offload_tso=off,offload_ufo=off,offload_csum=off"
echo "Then optionally run 'dhcpd -v' in Redox and watch leases happen on the host with 'journalctl -t dnsmasq-dhcp -f'."
}
main() {
preflight
setup_tap
setup_address
setup_forwarding
setup_firewalld
setup_dnsmasq
run_checks
print_summary
}
main "$@"

96
scripts/passt-run.sh Executable file
View File

@ -0,0 +1,96 @@
#!/usr/bin/env bash
# Runs a command next to a passt vhost-user network backend, for `make ch`.
# passt-run.sh <socket>.sock [passt options...] -- <command...>
# Under SELinux, passt may only use sockets in $XDG_RUNTIME_DIR or /tmp.
set -euo pipefail
STARTUP_TIMEOUT=5 # seconds
die() {
echo "passt: $*" >&2
exit 1
}
usage() {
echo "usage: $0 <socket>.sock [passt options...] -- <command...>" >&2
exit 2
}
[[ $# -ge 1 ]] || usage
SOCK="$1"
shift
PASST_ARGS=()
while [[ $# -gt 0 && "$1" != "--" ]]; do
PASST_ARGS+=("$1")
shift
done
[[ $# -ge 2 ]] || usage
shift # the "--"
[[ "${SOCK}" == *.sock ]] || die "socket '${SOCK}' must end in .sock"
[[ ${#SOCK} -lt 108 ]] || die "socket path too long (${#SOCK} bytes): ${SOCK}"
LOG="${SOCK%.sock}.log"
REPAIR_SOCK="${SOCK}.repair"
PASST_BIN="${PASST:-passt}"
command -v "${PASST_BIN}" >/dev/null 2>&1 \
|| die "passt not found. Install the passt package (dnf, apt and pacman all call it passt) or set PASST=/path/to/passt"
"${PASST_BIN}" --help 2>&1 | grep -q -- '--vhost-user' \
|| die "${PASST_BIN} has no --vhost-user mode, it is too old. Install a passt from 2025 or later."
socket_in_use() {
ss -Hxl | awk -v sock="${SOCK}" '$5 == sock { found = 1 } END { exit !found }'
}
if socket_in_use; then
die "${SOCK} is in use by another passt. Stop that VM first, or choose another socket."
fi
PASST_PID=
cleanup() {
trap - EXIT INT TERM
if [[ -n "${PASST_PID}" ]]; then
kill "${PASST_PID}" 2>/dev/null || true
wait "${PASST_PID}" 2>/dev/null || true
fi
rm -f "${SOCK}" "${REPAIR_SOCK}"
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
start_passt() {
mkdir -p "$(dirname "${SOCK}")"
rm -f "${SOCK}" "${REPAIR_SOCK}"
"${PASST_BIN}" --vhost-user --foreground --one-off --socket "${SOCK}" "${PASST_ARGS[@]}" \
</dev/null >"${LOG}" 2>&1 &
PASST_PID=$!
}
start_failed() {
if [[ -s "${LOG}" ]]; then
# After a bad option passt prints its whole usage text.
sed '/^Usage:/,$d' "${LOG}" | tail -n 20 >&2
die "$1; see ${LOG}"
fi
die "$1 and logged nothing. If SELinux is enforcing, put the socket in \$XDG_RUNTIME_DIR or /tmp."
}
wait_for_socket() {
local deadline=$((SECONDS + STARTUP_TIMEOUT))
until [[ -S "${SOCK}" ]]; do
kill -0 "${PASST_PID}" 2>/dev/null || start_failed "passt exited"
((SECONDS < deadline)) || start_failed "passt did not create ${SOCK} within ${STARTUP_TIMEOUT}s"
sleep 0.1
done
}
start_passt
wait_for_socket
echo "passt: user-mode network at ${SOCK} (log: ${LOG})" >&2
# Not exec'd, so cleanup runs when the command ends.
"$@"

150
scripts/virtiofs-run.sh Executable file
View File

@ -0,0 +1,150 @@
#!/usr/bin/env bash
# Runs a command next to one virtiofsd per shared directory, for
# `make qemu share=...` and `make ch share=...`.
# virtiofs-run.sh --share <socket>.sock <tag>=<dir>[:ro] [--share ...] -- <command...>
set -euo pipefail
STARTUP_TIMEOUT=5 # seconds
die() {
echo "virtiofs: $*" >&2
exit 1
}
usage() {
echo "usage: $0 --share <socket>.sock <tag>=<dir>[:ro] [--share ...] -- <command...>" >&2
exit 2
}
SOCKS=()
TAGS=()
DIRS=()
OPTS=()
LABELS=()
while [[ "${1:-}" == --share ]]; do
[[ $# -ge 3 ]] || usage
[[ "$3" == *=* ]] || die "share '$3': expected <tag>=<dir>"
entry="$3" opt="" label=""
if [[ "${entry}" == *:ro ]]; then
entry="${entry%:ro}" opt=--readonly label=" (read-only)"
fi
OPTS+=("${opt}")
LABELS+=("${label}")
SOCKS+=("$2")
TAGS+=("${entry%%=*}")
DIRS+=("${entry#*=}")
shift 3
done
[[ ${#SOCKS[@]} -gt 0 && "${1:-}" == -- && $# -ge 2 ]] || usage
shift # the "--"
# virtiofsd holds a lock on <socket>.pid for as long as it runs.
share_in_use() {
[[ -e "$1.pid" ]] && ! flock -n "$1.pid" true
}
declare -A SEEN_TAGS=()
validate_share() {
local sock="${SOCKS[$1]}" tag="${TAGS[$1]}" dir="${DIRS[$1]}"
[[ -n "${tag}" ]] || die "share '=${dir}': empty tag"
[[ "${tag}" =~ ^[A-Za-z0-9._-]+$ ]] \
|| die "tag '${tag}': only letters, digits, '.', '_' and '-' are allowed (is '${tag}=${dir}' a directory with an '=' in it? use tag=dir)"
[[ ${#tag} -le 36 ]] || die "tag '${tag}': longer than 36 bytes"
[[ -z "${SEEN_TAGS[${tag}]:-}" ]] || die "tag '${tag}' is used twice"
SEEN_TAGS[${tag}]=1
[[ -n "${dir}" ]] || die "share '${tag}': empty directory"
[[ "${dir}" != *[[:space:]]* ]] \
|| die "share '${tag}': directories containing whitespace are not supported ('${dir}')"
[[ -d "${dir}" ]] || die "share '${tag}': ${dir} is not a directory"
[[ "${sock}" == *.sock ]] || die "socket '${sock}' must end in .sock"
[[ ${#sock} -lt 108 ]] \
|| die "socket path too long (${#sock} bytes): ${sock}; use a shorter share_sockdir"
if share_in_use "${sock}"; then
die "${sock} is in use by another virtiofsd. Stop that VM first, or use another share_sockdir."
fi
}
for i in "${!SOCKS[@]}"; do
validate_share "${i}"
done
find_virtiofsd() {
local c
for c in "${VIRTIOFSD:-}" virtiofsd /usr/libexec/virtiofsd /usr/lib/qemu/virtiofsd \
"${XDG_CACHE_HOME:-${HOME:-/tmp}/.cache}/virtiofsd-redox/virtiofsd"; do
[[ -n "${c}" ]] && command -v "${c}" && return 0
done
return 1
}
VIRTIOFSD_BIN="$(find_virtiofsd)" \
|| die "virtiofsd not found. Install it (e.g. 'dnf install virtiofsd' or 'pacman -S virtiofsd'), run ./podman_bootstrap.sh -d, or set VIRTIOFSD=/path/to/virtiofsd"
PIDS=()
cleanup() {
trap - EXIT INT TERM
if [[ ${#PIDS[@]} -gt 0 ]]; then
kill "${PIDS[@]}" 2>/dev/null || true
wait "${PIDS[@]}" 2>/dev/null || true
fi
local sock
for sock in "${SOCKS[@]}"; do
rm -f "${sock}" "${sock}.pid"
done
}
trap cleanup EXIT
trap 'exit 130' INT
trap 'exit 143' TERM
declare -A SHARED_DIRS=()
start_virtiofsd() {
local sock="${SOCKS[$1]}" dir
mkdir -p "$(dirname "${sock}")"
dir="$(cd "${DIRS[$1]}" && pwd)"
if [[ -n "${SHARED_DIRS[${dir}]:-}" ]]; then
echo "virtiofs: warning: ${dir} is shared more than once; the instances do not share caches" >&2
fi
SHARED_DIRS[${dir}]=1
"${VIRTIOFSD_BIN}" \
--socket-path="${sock}" \
--shared-dir "${dir}" \
--sandbox none \
--log-level warn \
--translate-uid="map:0:$(id -u):1" \
--translate-gid="map:0:$(id -g):1" \
${OPTS[$1]} \
</dev/null >"${sock%.sock}.log" 2>&1 &
PIDS+=("$!")
}
start_failed() {
local log="${SOCKS[$1]%.sock}.log"
tail -n 20 "${log}" >&2 || true
die "virtiofsd for '${TAGS[$1]}' $2; see ${log}"
}
wait_for_sockets() {
local deadline=$((SECONDS + STARTUP_TIMEOUT)) i
for i in "${!SOCKS[@]}"; do
until [[ -S "${SOCKS[$i]}" ]]; do
kill -0 "${PIDS[$i]}" 2>/dev/null || start_failed "${i}" "exited"
((SECONDS < deadline)) || start_failed "${i}" "did not create ${SOCKS[$i]} within ${STARTUP_TIMEOUT}s"
sleep 0.1
done
done
}
for i in "${!SOCKS[@]}"; do
start_virtiofsd "${i}"
done
wait_for_sockets
for i in "${!SOCKS[@]}"; do
echo "virtiofs: sharing '${TAGS[$i]}' = ${DIRS[$i]}${LABELS[$i]} (log: ${SOCKS[$i]%.sock}.log)" >&2
done
# Not exec'd, so cleanup runs when the command ends.
"$@"

View File

@ -220,7 +220,7 @@ COOKBOOK_MAKE="make"
function cookbook_configure {
"${COOKBOOK_CONFIGURE}" "${COOKBOOK_CONFIGURE_FLAGS[@]}" "$@"
"${COOKBOOK_MAKE}" -j "${COOKBOOK_MAKE_JOBS}" "${COOKBOOK_MAKE_FLAGS[@]}"
"${COOKBOOK_MAKE}" install DESTDIR="${COOKBOOK_STAGE}"
"${COOKBOOK_MAKE}" install DESTDIR="${COOKBOOK_STAGE}" "${COOKBOOK_MAKE_FLAGS[@]}"
}
COOKBOOK_CMAKE="cmake"