Compare commits

...

16 Commits
trunk ... synit

35 changed files with 1852 additions and 220 deletions

View File

@ -20,18 +20,16 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1685604690,
"narHash": "sha256-TEAMrNsfZZ2vuSBRU6giSy4xKkXyzdw41Xtio7UWa7M=",
"lastModified": 1690860117,
"narHash": "sha256-srkCfjMlg777HxDVMfhkIFgRhhtuZjIOIyR2ejLYK+Y=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "50bc0fd68fcadd68df77d554dff6a27d456827cd",
"rev": "96d403ee2479f2070050353b94808209f1352edb",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-23.05",
"repo": "nixpkgs",
"type": "github"
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
@ -48,11 +46,11 @@
]
},
"locked": {
"lastModified": 1685587239,
"narHash": "sha256-zpOir1AWpWyQscP5dMpqMrCgBzjzH7Wv0FNUsQ0dcS0=",
"lastModified": 1690856601,
"narHash": "sha256-OP1w4rwMJscne5ckSfzOjXXQTPzAWM3Mkm4wRmHwCh8=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "acb7e896a73b0cf2c6ffe40b2051eb7f88fc2a10",
"rev": "657d170ebe594da9fa32797e38704d45732d0c72",
"type": "github"
},
"original": {

248
flake.nix
View File

@ -2,119 +2,177 @@
description = "Syndicate utilities";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/release-23.05";
rust = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, rust }: {
lib = nixpkgs.lib.extend (import ./lib.nix);
outputs = { self, nixpkgs, rust }:
let inherit (self) lib;
in {
lib = nixpkgs.lib.extend (import ./lib.nix);
overlays.default = final: prev:
let rust' = (prev.extend rust.overlay).rust-bin.nightly.latest.default;
in {
inherit (self) lib;
overlays.default = final: prev:
let rust' = (prev.extend rust.overlay).rust-bin.nightly.latest.default;
in {
lib = prev.lib.extend (import ./lib.nix);
acpi_actor = final.nimPackages.callPackage ./packages/acpi_actor { };
acpi_actor = final.nimPackages.callPackage ./packages/acpi_actor { };
fontconfig_actor =
final.nimPackages.callPackage ./packages/fontconfig_actor { };
fontconfig_actor =
final.nimPackages.callPackage ./packages/fontconfig_actor { };
libnotify_actor =
final.nimPackages.callPackage ./packages/libnotify_actor { };
libnotify_actor =
final.nimPackages.callPackage ./packages/libnotify_actor { };
nimPackages = prev.nimPackages.overrideScope' (final': prev': {
noiseprotocol = final'.callPackage ./packages/nim/noiseprotocol { };
preserves = final'.callPackage ./packages/preserves-nim { };
syndicate = final'.callPackage ./packages/syndicate-nim { };
syndicate_utils =
final'.callPackage ./packages/syndicate_utils-nim { };
xdg_open_ng = final'.callPackage ./packages/xdg_open_ng { };
});
nimPackages = prev.nimPackages.overrideScope' (final': prev': {
noiseprotocol = final'.callPackage ./packages/nim/noiseprotocol { };
nix_actor = final.nimPackages.callPackage ./packages/nix_actor { };
preserves = prev'.preserves.overrideAttrs (f: p: {
version = "20230801";
src = prev.fetchFromGitea {
domain = "git.syndicate-lang.org";
owner = "ehmry";
repo = "preserves-nim";
rev = f.version;
sha256 = "sha256-60QsbXMYYfEWvXQAXu7XSpvg2J9YaGKDkDrfclcK6pc=";
};
});
noise-c = final.callPackage ./packages/noise-c { };
syndicate = prev'.syndicate.overrideAttrs (f: p: {
version = "20230801";
src = prev.fetchFromGitea {
domain = "git.syndicate-lang.org";
owner = "ehmry";
repo = "syndicate-nim";
rev = f.version;
hash = "sha256-/mZGWVdQ5FtZf2snPIjTG2tNFVzxQmxvkKuLCAGARYs=";
};
});
python3Packages = prev.python3Packages.overrideScope (final': prev': {
preserves = final'.callPackage ./packages/preserves-py { };
syndicate = final'.callPackage ./packages/syndicate-py { };
});
syndicate_utils =
final'.callPackage ./packages/syndicate_utils-nim { };
syndicated-open = final'.callPackage ./packages/syndicated-open { };
xdg_open_ng =
builtins.trace "xdg_open_ng has been renamed to syndicated-open"
final'.syndicated-open;
});
preserves-tools =
final.callPackage ./packages/preserves-tools { rust = rust'; };
nix_actor = final.nimPackages.callPackage ./packages/nix_actor { };
sqlite_actor =
final.nimPackages.callPackage ./packages/sqlite_actor { };
noise-c = final.callPackage ./packages/noise-c { };
squeak = final.callPackage ./packages/squeak { };
squeaker = final.python3Packages.callPackage ./packages/squeaker { };
python3Packages = prev.python3Packages.overrideScope (final': prev': {
preserves = final'.callPackage ./packages/preserves-py { };
syndicate = final'.callPackage ./packages/syndicate-py { };
synit-daemons = final'.callPackage ./packages/synit-daemons { };
});
preserves-tools =
final.callPackage ./packages/preserves-tools { rust = rust'; };
sqlite_actor =
final.nimPackages.callPackage ./packages/sqlite_actor { };
squeak = final.callPackage ./packages/squeak { };
squeaker = final.python3Packages.callPackage ./packages/squeaker { };
syndicate-server =
final.callPackage ./packages/syndicate-server { rust = rust'; };
synit-pid1 =
final.callPackage ./packages/synit-pid1 { rust = rust'; };
xapian_actor =
final.nimPackages.callPackage ./packages/xapian_actor { };
};
legacyPackages =
lib.pipe { inherit (nixpkgs.legacyPackages) x86_64-linux; } [
(self.lib.mapAttrs (system: pkgs: pkgs.extend self.overlays.default))
];
packages = lib.pipe self.legacyPackages [
(self.lib.mapAttrs (system: pkgs:
with pkgs; {
inherit fontconfig_actor libnotify_actor noise-c preserves-tools
squeak squeaker syndicate-server;
inherit (nimPackages) syndicate_utils xdg_open_ng;
preserves-nim = nimPackages.preserves;
preserves-py = python3Packages.preserves;
syndicate-nim = nimPackages.syndicate;
syndicate-py = python3Packages.syndicate;
}))
];
nixosModules = {
default = self.nixosModules.syndicate-server;
syndicate-server =
final.callPackage ./packages/syndicate-server { rust = rust'; };
synit-pid1 = final.callPackage ./packages/synit-pid1 { rust = rust'; };
xapian_actor =
final.nimPackages.callPackage ./packages/xapian_actor { };
};
legacyPackages =
self.lib.mapAttrs (system: pkgs: pkgs.extend self.overlays.default) {
inherit (nixpkgs.legacyPackages) x86_64-linux;
};
packages = self.lib.mapAttrs (system: pkgs:
with pkgs; {
inherit fontconfig_actor libnotify_actor noise-c preserves-tools squeak
squeaker syndicate-server;
inherit (nimPackages) syndicate_utils xdg_open_ng;
preserves-nim = nimPackages.preserves;
preserves-py = python3Packages.preserves;
syndicate-nim = nimPackages.syndicate;
syndicate-py = python3Packages.syndicate;
}) self.legacyPackages;
nixosModules.default = self.nixosModules.syndicate-server;
nixosModules.syndicate-server =
# A little hack to apply our overlay to this module only.
{ config, lib, pkgs, ... }:
(import ./nixos/syndicate-server.nix) {
inherit config lib;
pkgs = pkgs.extend self.overlays.default;
};
devShells = self.lib.mapAttrs (system: pkgs:
with pkgs; {
default =
mkShell { packages = builtins.attrValues self.packages.${system}; };
}) self.legacyPackages;
checks = self.lib.mapAttrs (system: pkgs':
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
inherit system;
pkgs = pkgs';
}; {
simple = simpleTest {
name = "http";
nodes.machine = { config, pkgs, ... }: {
imports = [ self.nixosModules.syndicate-server ];
nixpkgs.pkgs = pkgs';
services.syndicate.tty1 = {
enable = true;
user = "loser";
config = [ ];
};
users.users.loser.isNormalUser = true;
# A little hack to apply our overlay to this module only.
{ config, lib, pkgs, ... }:
(import ./nixos/syndicate-server.nix) {
inherit config lib;
pkgs = pkgs.extend self.overlays.default;
};
testScript = ''
machine.wait_for_job("syndicate-tty1")
'';
};
}) { inherit (self.legacyPackages) x86_64-linux; };
};
synit = import ./nixos/modules/synit;
};
devShells = self.lib.mapAttrs (system: pkgs:
with pkgs; {
default =
mkShell { packages = builtins.attrValues self.packages.${system}; };
}) self.legacyPackages;
checks = lib.pipe { inherit (self.legacyPackages) x86_64-linux; } [
(self.lib.mapAttrs (system: pkgs':
with import (nixpkgs + "/nixos/lib/testing-python.nix") {
inherit system;
pkgs = pkgs';
}; {
simple = simpleTest {
name = "http";
nodes.machine = { config, pkgs, ... }: {
imports = [ self.nixosModules.syndicate-server ];
nixpkgs.pkgs = pkgs';
services.syndicate.tty1 = {
enable = true;
user = "loser";
config = [ ];
};
users.users.loser.isNormalUser = true;
};
testScript = ''
machine.wait_for_job("syndicate-tty1")
'';
};
synit = simpleTest {
name = "synit";
nodes.machine = { config, lib, utils, ... }: {
imports = [ self.nixosModules.synit ];
boot.initrd.verbose = true;
boot.initrd.kernelModules =
[ "virtio_blk" "ext4" "9p" "9pnet_virtio" "overlay" ];
environment.etc."syndicate/services/test-gateway.pr".text = ''
<require-service <relay-listener <tcp "192.168.1.1" 9001> $gatekeeper>>
<bind <ref { oid: "syndicate" key: #x"" }> $config #f>
'';
nixpkgs.pkgs = pkgs';
synit.logProgram = "${pkgs.busybox}/bin/cat";
};
testScript = ''
start_all()
machine.wait_for_open_port(9001)
'';
};
}))
];
};
}

View File

@ -1,8 +1,8 @@
final: prev:
let lib = final;
let lib = prev;
in with lib; {
generators = with final.generators;
generators =
prev.generators // {
/* Generates text-encoded Preserves from an arbitrary value.
@ -18,7 +18,7 @@ in with lib; {
*/
toPreserves = { }@args:
let
toPreserves' = toPreserves args;
toPreserves' = final.generators.toPreserves args;
concatItems = toString;
recordLabel = list:
with builtins;

View File

@ -0,0 +1,15 @@
{ lib, pkgs, ... }:
{
config = {
boot.initrd = {
enable = true;
systemd.enable = false;
verbose = true;
};
systemd.package = pkgs.systemd // { meta.broken = true; };
};
}

View File

@ -0,0 +1,590 @@
#! @shell@
targetRoot=/mnt-root
console=tty1
verbose="@verbose@"
info() {
if [[ -n "$verbose" ]]; then
echo "$@"
fi
}
extraUtils="@extraUtils@"
export LD_LIBRARY_PATH=@extraUtils@/lib
export PATH=@extraUtils@/bin
ln -s @extraUtils@/bin /bin
ln -s @extraUtils@/bin /sbin
# Copy the secrets to their needed location
if [ -d "@extraUtils@/secrets" ]; then
for secret in $(cd "@extraUtils@/secrets"; find . -type f); do
mkdir -p $(dirname "/$secret")
ln -s "@extraUtils@/secrets/$secret" "$secret"
done
fi
fail() {
if [ -n "$panicOnFail" ]; then exit 1; fi
@preFailCommands@
# If starting stage 2 failed, allow the user to repair the problem
# in an interactive shell.
cat <<EOF
An error occurred in stage 1 of the boot process, which must mount the
root filesystem on \`$targetRoot' and then start stage 2. Press one
of the following keys:
EOF
if [ -n "$allowShell" ]; then cat <<EOF
i) to launch an interactive shell
f) to start an interactive shell having pid 1 (needed if you want to
start stage 2's init manually)
EOF
fi
cat <<EOF
r) to reboot immediately
*) to ignore the error and continue
EOF
read -n 1 reply
if [ -n "$allowShell" -a "$reply" = f ]; then
exec setsid @shell@ -c "exec @shell@ < /dev/$console >/dev/$console 2>/dev/$console"
elif [ -n "$allowShell" -a "$reply" = i ]; then
echo "Starting interactive shell..."
setsid @shell@ -c "exec @shell@ < /dev/$console >/dev/$console 2>/dev/$console" || fail
elif [ "$reply" = r ]; then
echo "Rebooting..."
reboot -f
else
info "Continuing..."
fi
}
trap 'fail' 0
# Print a greeting.
info
info "<<< @distroName@ Stage 1 >>>"
info
# Make several required directories.
touch /etc/fstab # to shut up mount
ln -s /proc/mounts /etc/mtab # to shut up mke2fs
touch /etc/initrd-release
# Function for waiting for device(s) to appear.
waitDevice() {
local device="$1"
# Split device string using ':' as a delimiter as bcachefs
# uses this for multi-device filesystems, i.e. /dev/sda1:/dev/sda2:/dev/sda3
local IFS=':'
# USB storage devices tend to appear with some delay. It would be
# great if we had a way to synchronously wait for them, but
# alas... So just wait for a few seconds for the device to
# appear.
for dev in $device; do
if test ! -e $dev; then
echo -n "waiting for device $dev to appear..."
try=20
while [ $try -gt 0 ]; do
sleep 1
mdevd-coldplug -O4 -v 3
if test -e $dev; then break; fi
echo -n "."
try=$((try - 1))
done
echo
[ $try -ne 0 ]
fi
done
kill $mdevd_pid
}
# Mount special file systems.
specialMount() {
local device="$1"
local mountPoint="$2"
local options="$3"
local fsType="$4"
mkdir -m 0755 -p "$mountPoint"
mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
}
source @earlyMountScript@
# Copy initrd secrets from /.initrd-secrets to their actual destinations
if [ -d "/.initrd-secrets" ]; then
#
# Secrets are named by their full destination pathname and stored
# under /.initrd-secrets/
#
for secret in $(cd "/.initrd-secrets"; find . -type f); do
mkdir -p $(dirname "/$secret")
cp "/.initrd-secrets/$secret" "$secret"
done
fi
# Log the script output to /dev/kmsg or /run/log/stage-1-init.log.
mkdir -p /tmp
mkfifo /tmp/stage-1-init.log.fifo
logOutFd=8 && logErrFd=9
eval "exec $logOutFd>&1 $logErrFd>&2"
if test -w /dev/kmsg; then
tee -i < /tmp/stage-1-init.log.fifo /proc/self/fd/"$logOutFd" | while read -r line; do
if test -n "$line"; then
echo "<7>stage-1-init: [$(date)] $line" > /dev/kmsg
fi
done &
else
mkdir -p /run/log
tee -i < /tmp/stage-1-init.log.fifo /run/log/stage-1-init.log &
fi
exec > /tmp/stage-1-init.log.fifo 2>&1
# Process the kernel command line.
export stage2Init=/init
for o in $(cat /proc/cmdline); do
case $o in
console=*)
set -- $(IFS==; echo $o)
params=$2
set -- $(IFS=,; echo $params)
console=$1
;;
init=*)
set -- $(IFS==; echo $o)
stage2Init=$2
;;
boot.persistence=*)
set -- $(IFS==; echo $o)
persistence=$2
;;
boot.persistence.opt=*)
set -- $(IFS==; echo $o)
persistence_opt=$2
;;
boot.trace|debugtrace)
# Show each command.
set -x
;;
boot.shell_on_fail)
allowShell=1
;;
boot.debug1|debug1) # stop right away
allowShell=1
fail
;;
boot.debug1devices) # stop after loading modules and creating device nodes
allowShell=1
debug1devices=1
;;
boot.debug1mounts) # stop after mounting file systems
allowShell=1
debug1mounts=1
;;
boot.panic_on_fail|stage1panic=1)
panicOnFail=1
;;
root=*)
# If a root device is specified on the kernel command
# line, make it available through the symlink /dev/root.
# Recognise LABEL= and UUID= to support UNetbootin.
set -- $(IFS==; echo $o)
if [ $2 = "LABEL" ]; then
root="/dev/disk/by-label/$3"
elif [ $2 = "UUID" ]; then
root="/dev/disk/by-uuid/$3"
else
root=$2
fi
ln -s "$root" /dev/root
;;
copytoram)
copytoram=1
;;
findiso=*)
# if an iso name is supplied, try to find the device where
# the iso resides on
set -- $(IFS==; echo $o)
isoPath=$2
;;
esac
done
# Set hostid before modules are loaded.
# This is needed by the spl/zfs modules.
@setHostId@
# Load the required kernel modules.
mkdir -p /lib
ln -s @modulesClosure@/lib/modules /lib/modules
ln -s @modulesClosure@/lib/firmware /lib/firmware
echo @extraUtils@/bin/modprobe > /proc/sys/kernel/modprobe
for i in @kernelModules@; do
info "loading module $(basename $i)..."
modprobe $i
done
# Create device nodes in /dev.
@preDeviceCommands@
ln -sfn /proc/self/fd /dev/fd
ln -sfn /proc/self/fd/0 /dev/stdin
ln -sfn /proc/self/fd/1 /dev/stdout
ln -sfn /proc/self/fd/2 /dev/stderr
mkdir -p /dev/.mdadm
info "running mdevd..."
echo '$MODALIAS=.* 0:0 660 @modprobe --quiet "$MODALIAS"' >/etc/mdev.conf
mdevd -O4 -v 3 &
mdevd_pid=$!
mdevd-coldplug -O4 -v 3
@preLVMCommands@
if test -n "$debug1devices"; then fail; fi
@postDeviceCommands@
# Check the specified file system, if appropriate.
checkFS() {
local device="$1"
local fsType="$2"
# Only check block devices.
if [ ! -b "$device" ]; then return 0; fi
# Don't check ROM filesystems.
if [ "$fsType" = iso9660 -o "$fsType" = udf ]; then return 0; fi
# Don't check resilient COWs as they validate the fs structures at mount time
if [ "$fsType" = btrfs -o "$fsType" = zfs -o "$fsType" = bcachefs ]; then return 0; fi
# Skip fsck for apfs as the fsck utility does not support repairing the filesystem (no -a option)
if [ "$fsType" = apfs ]; then return 0; fi
# Skip fsck for nilfs2 - not needed by design and no fsck tool for this filesystem.
if [ "$fsType" = nilfs2 ]; then return 0; fi
# Skip fsck for inherently readonly filesystems.
if [ "$fsType" = squashfs ]; then return 0; fi
# Skip fsck.erofs because it is still experimental.
if [ "$fsType" = erofs ]; then return 0; fi
# If we couldn't figure out the FS type, then skip fsck.
if [ "$fsType" = auto ]; then
echo 'cannot check filesystem with type "auto"!'
return 0
fi
# Device might be already mounted manually
# e.g. NBD-device or the host filesystem of the file which contains encrypted root fs
if mount | grep -q "^$device on "; then
echo "skip checking already mounted $device"
return 0
fi
# Optionally, skip fsck on journaling filesystems. This option is
# a hack - it's mostly because e2fsck on ext3 takes much longer to
# recover the journal than the ext3 implementation in the kernel
# does (minutes versus seconds).
if test -z "@checkJournalingFS@" -a \
\( "$fsType" = ext3 -o "$fsType" = ext4 -o "$fsType" = reiserfs \
-o "$fsType" = xfs -o "$fsType" = jfs -o "$fsType" = f2fs \)
then
return 0
fi
echo "checking $device..."
fsck -V -a "$device"
fsckResult=$?
if test $(($fsckResult | 2)) = $fsckResult; then
echo "fsck finished, rebooting..."
sleep 3
reboot -f
fi
if test $(($fsckResult | 4)) = $fsckResult; then
echo "$device has unrepaired errors, please fix them manually."
fail
fi
if test $fsckResult -ge 8; then
echo "fsck on $device failed."
fail
fi
return 0
}
escapeFstab() {
local original="$1"
# Replace space
local escaped="${original// /\\040}"
# Replace tab
echo "${escaped//$'\t'/\\011}"
}
# Function for mounting a file system.
mountFS() {
local device="$1"
local mountPoint="$2"
local options="$3"
local fsType="$4"
if [ "$fsType" = auto ]; then
fsType=$(blkid -o value -s TYPE "$device")
if [ -z "$fsType" ]; then fsType=auto; fi
fi
# Filter out x- options, which busybox doesn't do yet.
local optionsFiltered="$(IFS=,; for i in $options; do if [ "${i:0:2}" != "x-" ]; then echo -n $i,; fi; done)"
# Prefix (lower|upper|work)dir with /mnt-root (overlayfs)
local optionsPrefixed="$( echo "$optionsFiltered" | sed -E 's#\<(lowerdir|upperdir|workdir)=#\1=/mnt-root#g' )"
echo "$device /mnt-root$mountPoint $fsType $optionsPrefixed" >> /etc/fstab
checkFS "$device" "$fsType"
# Create backing directories for overlayfs
if [ "$fsType" = overlay ]; then
for i in upper work; do
dir="$( echo "$optionsPrefixed" | grep -o "${i}dir=[^,]*" )"
mkdir -m 0700 -p "${dir##*=}"
done
fi
info "mounting $device on $mountPoint..."
mkdir -p "/mnt-root$mountPoint"
# For ZFS and CIFS mounts, retry a few times before giving up.
# We do this for ZFS as a workaround for issue NixOS/nixpkgs#25383.
local n=0
while true; do
mount "/mnt-root$mountPoint" && break
if [ \( "$fsType" != cifs -a "$fsType" != zfs \) -o "$n" -ge 10 ]; then fail; break; fi
echo "retrying..."
sleep 1
n=$((n + 1))
done
# For bind mounts, busybox has a tendency to ignore options, which can be a
# security issue (e.g. "nosuid"). Remounting the partition seems to fix the
# issue.
mount "/mnt-root$mountPoint" -o "remount,$optionsPrefixed"
[ "$mountPoint" == "/" ] &&
[ -f "/mnt-root/etc/NIXOS_LUSTRATE" ] &&
lustrateRoot "/mnt-root"
true
}
lustrateRoot () {
local root="$1"
echo
echo -e "\e[1;33m<<< @distroName@ is now lustrating the root filesystem (cruft goes to /old-root) >>>\e[0m"
echo
mkdir -m 0755 -p "$root/old-root.tmp"
echo
echo "Moving impurities out of the way:"
for d in "$root"/*
do
[ "$d" == "$root/nix" ] && continue
[ "$d" == "$root/boot" ] && continue # Don't render the system unbootable
[ "$d" == "$root/old-root.tmp" ] && continue
mv -v "$d" "$root/old-root.tmp"
done
# Use .tmp to make sure subsequent invocations don't clash
mv -v "$root/old-root.tmp" "$root/old-root"
mkdir -m 0755 -p "$root/etc"
touch "$root/etc/NIXOS"
exec 4< "$root/old-root/etc/NIXOS_LUSTRATE"
echo
echo "Restoring selected impurities:"
while read -u 4 keeper; do
dirname="$(dirname "$keeper")"
mkdir -m 0755 -p "$root/$dirname"
cp -av "$root/old-root/$keeper" "$root/$keeper"
done
exec 4>&-
}
# If we have a path to an iso file, find the iso and link it to /dev/root
if [ -n "$isoPath" ]; then
mkdir -p /findiso
for delay in 5 10; do
blkid | while read -r line; do
device=$(echo "$line" | sed 's/:.*//')
type=$(echo "$line" | sed 's/.*TYPE="\([^"]*\)".*/\1/')
mount -t "$type" "$device" /findiso
if [ -e "/findiso$isoPath" ]; then
ln -sf "/findiso$isoPath" /dev/root
break 2
else
umount /findiso
fi
done
sleep "$delay"
done
fi
# Try to find and mount the root device.
mkdir -p $targetRoot
exec 3< @fsInfo@
while read -u 3 mountPoint; do
read -u 3 device
read -u 3 fsType
read -u 3 options
# !!! Really quick hack to support bind mounts, i.e., where the
# "device" should be taken relative to /mnt-root, not /. Assume
# that every device that starts with / but doesn't start with /dev
# is a bind mount.
pseudoDevice=
case $device in
/dev/*)
;;
//*)
# Don't touch SMB/CIFS paths.
pseudoDevice=1
;;
/*)
device=/mnt-root$device
;;
*)
# Not an absolute path; assume that it's a pseudo-device
# like an NFS path (e.g. "server:/path").
pseudoDevice=1
;;
esac
if test -z "$pseudoDevice" && ! waitDevice "$device"; then
# If it doesn't appear, try to mount it anyway (and
# probably fail). This is a fallback for non-device "devices"
# that we don't properly recognise.
echo "Timed out waiting for device $device, trying to mount anyway."
fi
# If copytoram is enabled: skip mounting the ISO and copy its content to a tmpfs.
if [ -n "$copytoram" ] && [ "$device" = /dev/root ] && [ "$mountPoint" = /iso ]; then
fsType=$(blkid -o value -s TYPE "$device")
fsSize=$(blockdev --getsize64 "$device" || stat -Lc '%s' "$device")
mkdir -p /tmp-iso
mount -t "$fsType" /dev/root /tmp-iso
mountFS tmpfs /iso size="$fsSize" tmpfs
cp -r /tmp-iso/* /mnt-root/iso/
umount /tmp-iso
rmdir /tmp-iso
if [ -n "$isoPath" ] && [ $fsType = "iso9660" ] && mountpoint -q /findiso; then
umount /findiso
fi
continue
fi
if [ "$mountPoint" = / ] && [ "$device" = tmpfs ] && [ ! -z "$persistence" ]; then
echo persistence...
waitDevice "$persistence"
echo enabling persistence...
mountFS "$persistence" "$mountPoint" "$persistence_opt" "auto"
continue
fi
mountFS "$device" "$mountPoint" "$options" "$fsType"
done
exec 3>&-
@postMountCommands@
# Stop mdevd.
kill $mdevd_pid
info "killed early mdevd"
# Reset the logging file descriptors.
# Do this just before pkill, which will kill the tee process.
exec 1>&$logOutFd 2>&$logErrFd
eval "exec $logOutFd>&- $logErrFd>&-"
# Kill any remaining processes, just to be sure we're not taking any
# with us into stage 2. But keep storage daemons like unionfs-fuse.
#
# Storage daemons are distinguished by an @ in front of their command line:
# https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
for pid in $(pgrep -v -f '^@'); do
# Make sure we don't kill kernel processes, see #15226 and:
# http://stackoverflow.com/questions/12213445/identifying-kernel-threads
readlink "/proc/$pid/exe" &> /dev/null || continue
# Try to avoid killing ourselves.
[ $pid -eq $$ ] && continue
kill -9 "$pid"
done
if test -n "$debug1mounts"; then fail; fi
# Restore /proc/sys/kernel/modprobe to its original value.
echo /sbin/modprobe > /proc/sys/kernel/modprobe
# Start stage 2. `switch_root' deletes all files in the ramfs on the
# current root. The path has to be valid in the chroot not outside.
if [ ! -e "$targetRoot/$stage2Init" ]; then
stage2Check=${stage2Init}
while [ "$stage2Check" != "${stage2Check%/*}" ] && [ ! -L "$targetRoot/$stage2Check" ]; do
stage2Check=${stage2Check%/*}
done
if [ ! -L "$targetRoot/$stage2Check" ]; then
echo "stage 2 init script ($targetRoot/$stage2Init) not found"
fail
fi
fi
mkdir -m 0755 -p $targetRoot/proc $targetRoot/sys $targetRoot/dev $targetRoot/run
mount --move /proc $targetRoot/proc
mount --move /sys $targetRoot/sys
mount --move /dev $targetRoot/dev
mount --move /run $targetRoot/run
exec env -i $(type -P switch_root) "$targetRoot" "$stage2Init"
fail # should never be reached

View File

@ -0,0 +1,431 @@
# This module builds the initial ramdisk, which contains an init
# script that performs the first stage of booting the system: it loads
# the modules necessary to mount the root file system, then calls the
# init in the root file system to start the second boot stage.
{ config, lib, utils, pkgs, ... }:
with lib;
let
udev = pkgs.eudev;
kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; };
firmware = config.hardware.firmware;
# Determine the set of modules that we need to mount the root FS.
modulesClosure = pkgs.makeModulesClosure {
rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
kernel = modulesTree;
firmware = firmware;
allowMissing = false;
};
# The initrd only has to mount `/` or any FS marked as necessary for
# booting (such as the FS containing `/nix/store`, or an FS needed for
# mounting `/`, like `/` on a loopback).
fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
# A utility for enumerating the shared-library dependencies of a program
findLibs = pkgs.buildPackages.writeShellScriptBin "find-libs" ''
set -euo pipefail
declare -A seen
left=()
patchelf="${pkgs.buildPackages.patchelf}/bin/patchelf"
function add_needed {
rpath="$($patchelf --print-rpath $1)"
dir="$(dirname $1)"
for lib in $($patchelf --print-needed $1); do
left+=("$lib" "$rpath" "$dir")
done
}
add_needed "$1"
while [ ''${#left[@]} -ne 0 ]; do
next=''${left[0]}
rpath=''${left[1]}
ORIGIN=''${left[2]}
left=("''${left[@]:3}")
if [ -z ''${seen[$next]+x} ]; then
seen[$next]=1
# Ignore the dynamic linker which for some reason appears as a DT_NEEDED of glibc but isn't in glibc's RPATH.
case "$next" in
ld*.so.?) continue;;
esac
IFS=: read -ra paths <<< $rpath
res=
for path in "''${paths[@]}"; do
path=$(eval "echo $path")
if [ -f "$path/$next" ]; then
res="$path/$next"
echo "$res"
add_needed "$res"
break
fi
done
if [ -z "$res" ]; then
echo "Couldn't satisfy dependency $next" >&2
exit 1
fi
fi
done
'';
# Some additional utilities needed in stage 1, like mount, fsck
# etc. We don't want to bring in all of those packages, so we just
# copy what we need. Instead of using statically linked binaries,
# we just copy what we need from Glibc and use patchelf to make it
# work.
extraUtils = pkgs.runCommandCC "extra-utils"
{ nativeBuildInputs = [pkgs.buildPackages.nukeReferences];
allowedReferences = [ "out" ]; # prevent accidents like glibc being included in the initrd
}
''
set +o pipefail
mkdir -p $out/bin $out/lib
ln -s $out/bin $out/sbin
copy_bin_and_libs () {
[ -f "$out/bin/$(basename $1)" ] && rm "$out/bin/$(basename $1)"
cp -pdv $1 $out/bin
}
# Copy BusyBox.
for BIN in ${pkgs.busybox}/{s,}bin/*; do
copy_bin_and_libs $BIN
done
# Copy some util-linux stuff.
copy_bin_and_libs ${pkgs.util-linux}/sbin/blkid
# Add RAID mdadm tool.
copy_bin_and_libs ${pkgs.mdadm}/sbin/mdadm
copy_bin_and_libs ${pkgs.mdadm}/sbin/mdmon
# Copy udev.
copy_bin_and_libs ${udev}/bin/udevd
copy_bin_and_libs ${udev}/bin/udevadm
for BIN in ${udev}/lib/udev/*_id; do
copy_bin_and_libs $BIN
done
# Copy mdevd.
for BIN in ${pkgs.mdevd}/bin/*; do
copy_bin_and_libs $BIN
done
# Copy modprobe.
copy_bin_and_libs ${pkgs.kmod}/bin/kmod
ln -sf kmod $out/bin/modprobe
# Dirty hack to make sure the kernel properly loads modules
# such as ext4 on demand (e.g. on a `mount(2)` syscall). This is necessary
# because `kmod` isn't linked against `libpthread.so.0` anymore (since
# it was merged into `libc.so.6` since version `2.34`), but still needs
# to access it for some reason. This is not an issue in stage-1 itself
# because of the `LD_LIBRARY_PATH`-variable and anytime later because the rpath of
# kmod/modprobe points to glibc's `$out/lib` where `libpthread.so.6` exists.
# However, this is a problem when the kernel calls `modprobe` inside
# the initial ramdisk because it doesn't know about the
# `LD_LIBRARY_PATH` and the rpath was nuked.
#
# Also, we can't use `makeWrapper` here because `kmod` only does
# `modprobe` functionality if `argv[0] == "modprobe"`.
cat >$out/bin/modprobe-kernel <<EOF
#!$out/bin/ash
export LD_LIBRARY_PATH=$out/lib
exec $out/bin/modprobe "\$@"
EOF
chmod +x $out/bin/modprobe-kernel
# Copy resize2fs if any ext* filesystems are to be resized
${optionalString (any (fs: fs.autoResize && (lib.hasPrefix "ext" fs.fsType)) fileSystems) ''
# We need mke2fs in the initrd.
copy_bin_and_libs ${pkgs.e2fsprogs}/sbin/resize2fs
''}
# Copy multipath.
${optionalString config.services.multipath.enable ''
copy_bin_and_libs ${config.services.multipath.package}/bin/multipath
copy_bin_and_libs ${config.services.multipath.package}/bin/multipathd
# Copy lib/multipath manually.
cp -rpv ${config.services.multipath.package}/lib/multipath $out/lib
''}
# Copy secrets if needed.
#
# TODO: move out to a separate script; see #85000.
${optionalString (!config.boot.loader.supportsInitrdSecrets)
(concatStringsSep "\n" (mapAttrsToList (dest: source:
let source' = if source == null then dest else source; in
''
mkdir -p $(dirname "$out/secrets/${dest}")
# Some programs (e.g. ssh) doesn't like secrets to be
# symlinks, so we use `cp -L` here to match the
# behaviour when secrets are natively supported.
cp -Lr ${source'} "$out/secrets/${dest}"
''
) config.boot.initrd.secrets))
}
${config.boot.initrd.extraUtilsCommands}
# Copy ld manually since it isn't detected correctly
cp -pv ${pkgs.stdenv.cc.libc.out}/lib/ld*.so.? $out/lib
# Copy all of the needed libraries
find $out/bin $out/lib -type f | while read BIN; do
echo "Copying libs for executable $BIN"
for LIB in $(${findLibs}/bin/find-libs $BIN); do
TGT="$out/lib/$(basename $LIB)"
if [ ! -f "$TGT" ]; then
SRC="$(readlink -e $LIB)"
cp -pdv "$SRC" "$TGT"
fi
done
done
# Strip binaries further than normal.
chmod -R u+w $out
stripDirs "$STRIP" "lib bin" "-s"
# Run patchelf to make the programs refer to the copied libraries.
find $out/bin $out/lib -type f | while read i; do
if ! test -L $i; then
nuke-refs -e $out $i
fi
done
find $out/bin -type f | while read i; do
if ! test -L $i; then
echo "patching $i..."
patchelf --set-interpreter $out/lib/ld*.so.? --set-rpath $out/lib $i || true
fi
done
if [ -z "${toString (pkgs.stdenv.hostPlatform != pkgs.stdenv.buildPlatform)}" ]; then
# Make sure that the patchelf'ed binaries still work.
echo "testing patched programs..."
$out/bin/ash -c 'echo hello world' | grep "hello world"
export LD_LIBRARY_PATH=$out/lib
$out/bin/mount --help 2>&1 | grep -q "BusyBox"
$out/bin/blkid -V 2>&1 | grep -q 'libblkid'
$out/bin/udevadm --version
$out/bin/mdadm --version
${optionalString config.services.multipath.enable ''
($out/bin/multipath || true) 2>&1 | grep -q 'need to be root'
($out/bin/multipathd || true) 2>&1 | grep -q 'need to be root'
''}
${config.boot.initrd.extraUtilsCommandsTest}
fi
''; # */
udevRules = pkgs.runCommand "udev-rules" {
allowedReferences = [ extraUtils ];
preferLocalBuild = true;
} ''
mkdir -p $out
echo 'ENV{LD_LIBRARY_PATH}="${extraUtils}/lib"' > $out/00-env.rules
cp -v ${udev}/var/lib/udev/rules.d/60-cdrom_id.rules $out/
cp -v ${udev}/var/lib/udev/rules.d/60-persistent-storage.rules $out/
cp -v ${udev}/var/lib/udev/rules.d/75-net-description.rules $out/
cp -v ${udev}/var/lib/udev/rules.d/80-drivers.rules $out/
cp -v ${udev}/var/lib/udev/rules.d/80-net-name-slot.rules $out/
${config.boot.initrd.extraUdevRulesCommands}
for i in $out/*.rules; do
substituteInPlace $i \
--replace ata_id ${extraUtils}/bin/ata_id \
--replace scsi_id ${extraUtils}/bin/scsi_id \
--replace cdrom_id ${extraUtils}/bin/cdrom_id \
--replace ${pkgs.coreutils}/bin/basename ${extraUtils}/bin/basename \
--replace ${pkgs.util-linux}/bin/blkid ${extraUtils}/bin/blkid \
--replace ${pkgs.mdadm}/sbin ${extraUtils}/sbin \
--replace ${pkgs.bash}/bin/sh ${extraUtils}/bin/sh \
--replace ${udev} ${extraUtils}
done
# Work around a bug in QEMU, which doesn't implement the "READ
# DISC INFORMATION" SCSI command:
# https://bugzilla.redhat.com/show_bug.cgi?id=609049
# As a result, `cdrom_id' doesn't print
# ID_CDROM_MEDIA_TRACK_COUNT_DATA, which in turn prevents the
# /dev/disk/by-label symlinks from being created. We need these
# in the NixOS installation CD, so use ID_CDROM_MEDIA in the
# corresponding udev rules for now. This was the behaviour in
# udev <= 154. See also
# http://www.spinics.net/lists/hotplug/msg03935.html
substituteInPlace $out/60-persistent-storage.rules \
--replace ID_CDROM_MEDIA_TRACK_COUNT_DATA ID_CDROM_MEDIA
''; # */
# The init script of boot stage 1 (loading kernel modules for
# mounting the root FS).
bootStage1 = pkgs.substituteAll {
src = ./stage-1-init.sh;
shell = "${extraUtils}/bin/ash";
isExecutable = true;
postInstall = ''
echo checking syntax
# check both with bash
${pkgs.buildPackages.bash}/bin/sh -n $target
# and with ash shell, just in case
${pkgs.buildPackages.busybox}/bin/ash -n $target
'';
inherit udevRules extraUtils modulesClosure;
inherit (config.boot) resumeDevice;
inherit (config.system.build) earlyMountScript;
inherit (config.boot.initrd) checkJournalingFS verbose
preDeviceCommands postDeviceCommands postMountCommands preFailCommands kernelModules preLVMCommands;
resumeDevices = map (sd: if sd ? device then sd.device else "/dev/disk/by-label/${sd.label}")
(filter (sd: hasPrefix "/dev/" sd.device && !sd.randomEncryption.enable
# Don't include zram devices
&& !(hasPrefix "/dev/zram" sd.device)
) config.swapDevices);
fsInfo =
let f = fs: [ fs.mountPoint (if fs.device != null then fs.device else "/dev/disk/by-label/${fs.label}") fs.fsType (builtins.concatStringsSep "," fs.options) ];
in pkgs.writeText "initrd-fsinfo" (concatStringsSep "\n" (concatMap f fileSystems));
setHostId = optionalString (config.networking.hostId != null) ''
hi="${config.networking.hostId}"
${if pkgs.stdenv.isBigEndian then ''
echo -ne "\x''${hi:0:2}\x''${hi:2:2}\x''${hi:4:2}\x''${hi:6:2}" > /etc/hostid
'' else ''
echo -ne "\x''${hi:6:2}\x''${hi:4:2}\x''${hi:2:2}\x''${hi:0:2}" > /etc/hostid
''}
'';
};
# The closure of the init script of boot stage 1 is what we put in
# the initial RAM disk.
initialRamdisk = pkgs.makeInitrd {
name = "initrd-${kernel-name}";
inherit (config.boot.initrd) compressor compressorArgs prepend;
contents =
[ { object = bootStage1;
symlink = "/init";
}
{ object = pkgs.writeText "mdadm.conf" config.boot.initrd.services.swraid.mdadmConf;
symlink = "/etc/mdadm.conf";
}
{ object = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" {
src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
preferLocalBuild = true;
} ''
target=$out
${pkgs.buildPackages.perl}/bin/perl -0pe 's/## file: iwlwifi.conf(.+?)##/##/s;' $src > $out
'';
symlink = "/etc/modprobe.d/ubuntu.conf";
}
{ object = config.environment.etc."modprobe.d/nixos.conf".source;
symlink = "/etc/modprobe.d/nixos.conf";
}
{ object = pkgs.kmod-debian-aliases;
symlink = "/etc/modprobe.d/debian.conf";
}
] ++ lib.optionals config.services.multipath.enable [
{ object = pkgs.runCommand "multipath.conf" {
src = config.environment.etc."multipath.conf".text;
preferLocalBuild = true;
} ''
target=$out
printf "$src" > $out
substituteInPlace $out \
--replace ${config.services.multipath.package}/lib ${extraUtils}/lib
'';
symlink = "/etc/multipath.conf";
}
] ++ (lib.mapAttrsToList
(symlink: options:
{
inherit symlink;
object = options.source;
}
)
config.boot.initrd.extraFiles);
};
# Script to add secret files to the initrd at bootloader update time
initialRamdiskSecretAppender =
let
compressorExe = initialRamdisk.compressorExecutableFunction pkgs;
in pkgs.writeScriptBin "append-initrd-secrets"
''
#!${pkgs.bash}/bin/bash -e
function usage {
echo "USAGE: $0 INITRD_FILE" >&2
echo "Appends this configuration's secrets to INITRD_FILE" >&2
}
if [ $# -ne 1 ]; then
usage
exit 1
fi
if [ "$1"x = "--helpx" ]; then
usage
exit 0
fi
${lib.optionalString (config.boot.initrd.secrets == {})
"exit 0"}
export PATH=${pkgs.coreutils}/bin:${pkgs.libarchive}/bin:${pkgs.gzip}/bin:${pkgs.findutils}/bin
function cleanup {
if [ -n "$tmp" -a -d "$tmp" ]; then
rm -fR "$tmp"
fi
}
trap cleanup EXIT
tmp=$(mktemp -d ''${TMPDIR:-/tmp}/initrd-secrets.XXXXXXXXXX)
${lib.concatStringsSep "\n" (mapAttrsToList (dest: source:
let source' = if source == null then dest else toString source; in
''
mkdir -p $(dirname "$tmp/.initrd-secrets/${dest}")
cp -a ${source'} "$tmp/.initrd-secrets/${dest}"
''
) config.boot.initrd.secrets)
}
(cd "$tmp" && find . -print0 | sort -z | bsdtar --uid 0 --gid 0 -cnf - -T - | bsdtar --null -cf - --format=newc @-) | \
${compressorExe} ${lib.escapeShellArgs initialRamdisk.compressorArgs} >> "$1"
'';
in
{
config = mkIf config.boot.initrd.enable {
system.build = lib.mapAttrs (_: lib.mkForce) { inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; };
};
}

View File

@ -0,0 +1,135 @@
#! @shell@
systemConfig=@systemConfig@
export HOME=/root PATH="@path@"
if [ "${IN_NIXOS_SYSTEMD_STAGE1:-}" != true ]; then
# Process the kernel command line.
for o in $(</proc/cmdline); do
case $o in
boot.debugtrace)
# Show each command.
set -x
;;
esac
done
# Print a greeting.
echo
echo -e "\e[1;32m<<< NixOS Stage 2 >>>\e[0m"
echo
# Normally, stage 1 mounts the root filesystem read/writable.
# However, in some environments, stage 2 is executed directly, and the
# root is read-only. So make it writable here.
if [ -z "$container" ]; then
mount -n -o remount,rw none /
fi
fi
# Likewise, stage 1 mounts /proc, /dev and /sys, so if we don't have a
# stage 1, we need to do that here.
if [ ! -e /proc/1 ]; then
specialMount() {
local device="$1"
local mountPoint="$2"
local options="$3"
local fsType="$4"
# We must not overwrite this mount because it's bind-mounted
# from stage 1's /run
if [ "${IN_NIXOS_SYSTEMD_STAGE1:-}" = true ] && [ "${mountPoint}" = /run ]; then
return
fi
install -m 0755 -d "$mountPoint"
mount -n -t "$fsType" -o "$options" "$device" "$mountPoint"
}
source @earlyMountScript@
fi
if [ "${IN_NIXOS_SYSTEMD_STAGE1:-}" = true ]; then
echo "booting system configuration ${systemConfig}"
else
echo "booting system configuration $systemConfig" > /dev/kmsg
fi
# Make /nix/store a read-only bind mount to enforce immutability of
# the Nix store. Note that we can't use "chown root:nixbld" here
# because users/groups might not exist yet.
# Silence chown/chmod to fail gracefully on a readonly filesystem
# like squashfs.
chown -f 0:30000 /nix/store
chmod -f 1775 /nix/store
if [ -n "@readOnlyNixStore@" ]; then
if ! [[ "$(findmnt --noheadings --output OPTIONS /nix/store)" =~ ro(,|$) ]]; then
if [ -z "$container" ]; then
mount --bind /nix/store /nix/store
else
mount --rbind /nix/store /nix/store
fi
mount -o remount,ro,bind /nix/store
fi
fi
# Log the script output to /dev/kmsg or /run/log/stage-2-init.log.
# Only at this point are all the necessary prerequisites ready for these commands.
exec {logOutFd}>&1 {logErrFd}>&2
if test -w /dev/kmsg; then
exec > >(tee -i /proc/self/fd/"$logOutFd" | while read -r line; do
if test -n "$line"; then
echo "<7>stage-2-init: $line" > /dev/kmsg
fi
done) 2>&1
else
mkdir -p /run/log
exec > >(tee -i /run/log/stage-2-init.log) 2>&1
fi
# Required by the activation script
install -m 0755 -d /etc /etc/nixos
install -m 01777 -d /tmp
# Run the script that performs all configuration activation that does
# not have to be done at boot time.
echo "running activation script..."
$systemConfig/activate
# Record the boot configuration.
ln -sfn "$systemConfig" /run/booted-system
# copy eudev rules over to /var
cp -ru @eudev@/var/lib/udev /var/lib/
# Run any user-specified commands.
@shell@ @postBootCommands@
# Reset the logging file descriptors.
exec 1>&$logOutFd 2>&$logErrFd
exec {logOutFd}>&- {logErrFd}>&-
mkdir -p \
/etc/syndicate/user-settings \
/run/etc/syndicate/core \
/run/etc/syndicate/services \
/run/etc/syndicate/machine \
# Start Synit.
echo "starting Synit..."
exec @synit_pid1@ \
--log @logProgram@ \
--server-path @syndicate_server@

View File

@ -0,0 +1,37 @@
{ config, lib, pkgs, ... }:
{
options.synit.logProgram = lib.mkOption {
type = lib.types.path;
default = pkgs.writeScript "synit-log" ''
#!${pkgs.execline}/bin/execlineb -P
foreground { ${pkgs.coreutils}/bin/mkdir -p /var/log/synit }
${pkgs.s6}/bin/s6-log t /var/log/synit
'';
defaultText = "s6-log";
};
config = {
system.build.bootStage2 = lib.mkForce (pkgs.substituteAll {
src = ./stage-2-init.sh;
shellDebug = "${pkgs.bashInteractive}/bin/bash";
shell = "${pkgs.bash}/bin/bash";
isExecutable = true;
inherit (config.boot) readOnlyNixStore;
inherit (config.system.build) earlyMountScript;
path = lib.makeBinPath [ pkgs.busybox pkgs.eudev pkgs.mdevd pkgs.util-linux ];
inherit (pkgs) eudev;
synit_pid1 = "${pkgs.synit-pid1}/bin/synit-pid1";
inherit (config.synit) logProgram;
syndicate_server = "${pkgs.syndicate-server}/bin/syndicate-server";
postBootCommands = pkgs.writeText "local-cmds" ''
${config.boot.postBootCommands}
${config.powerManagement.powerUpCommands}
'';
});
};
}

View File

@ -0,0 +1,11 @@
{
imports = [
./boot/default.nix
./boot/stage-1.nix
./boot/stage-2.nix
./etc
# ./filesystems
./networking.nix
./services
];
}

View File

@ -0,0 +1,2 @@
<require-service <daemon console-getty>>
<daemon console-getty "@getty@ 0 /dev/console -l @loginProgram@">

View File

@ -0,0 +1,19 @@
; Sending <exec SOMECOMMAND RESTARTPOLICY> causes the command to be run.
;
?? <exec ?argv ?restartPolicy> [
let ?id = timestamp
let ?facet = facet
let ?d = <temporary-exec $id $argv>
<run-service <daemon $d>>
<daemon $d {
argv: $argv,
readyOnStart: #f,
restart: $restartPolicy,
}>
? <service-state <daemon $d> complete> [$facet ! stop]
? <service-state <daemon $d> failed> [$facet ! stop]
]
; If the restart policy is not specified, it is defaulted to `on-error`.
;
?? <exec ?argv> ! <exec $argv on-error>

View File

@ -0,0 +1,10 @@
; To "run" a milestone service,
; - assert that it is both started and ready
; - that's it!
;
? <run-service <milestone ?m>> [
<service-state <milestone $m> started>
<service-state <milestone $m> ready>
$log ! <log "-" { service: <milestone $m> state: up }>
?- <log "-" { service: <milestone $m> state: down }>
]

View File

@ -0,0 +1,4 @@
; To the usual suite of service states we add `up`, meaning "either `ready` or `complete`".
;
? <service-state ?x ready> <service-state $x up>
? <service-state ?x complete> <service-state $x up>

View File

@ -0,0 +1,28 @@
; Attenuate `$config` by rewriting plain `require-service` assertions to `require-core-service`
; assertions. Allow all other assertions through.
;
let ?sys = <* $config [<or [
<rewrite <require-service ?s> <require-core-service $s>>
<accept _>
]>]>
; Give meaning to `require-core-service`: it is an ordinary `require-service`, plus a
; declaration that the `core` milestone depends on the service.
;
? <require-core-service ?s> [
<depends-on <milestone core> <service-state $s up>>
<require-service $s>
]
; Load config in the `core` directory, using the wrapped `config` so that all plain services
; required are changed to be *core* services.
;
<require-service <config-watcher "/etc/syndicate/core" {
config: $sys
gatekeeper: $gatekeeper
log: $log
}>>
; In addition, require the `core` milestone explicitly.
;
<require-service <milestone core>>

View File

@ -0,0 +1,26 @@
; Attenuate `$config` by rewriting plain `require-service` assertions to
; `require-basic-service` assertions. Allow all other assertions through.
;
let ?basic = <* $config [<or [
<rewrite <require-service ?s> <require-basic-service $s>>
<accept _>
]>]>
; Give meaning to `require-basic-service`: it is an ordinary `require-service`, plus a
; declaration that the service depends on the `core` milestone.
;
? <require-basic-service ?s> [
<depends-on $s <service-state <milestone core> up>>
<require-service $s>
]
; Once we see that the `core` milestone is ready, start processing the `services`
; directory.
;
? <service-state <milestone core> up> [
<require-service <config-watcher "/etc/syndicate/services" {
config: $basic
gatekeeper: $gatekeeper
log: $log
}>>
]

View File

@ -0,0 +1 @@
<require-service <config-watcher "/run/etc/syndicate/core" $.>>

View File

@ -0,0 +1,2 @@
<require-service <daemon hostname>>
<daemon hostname <one-shot "@hostnameOneShot@">>

View File

@ -0,0 +1,9 @@
let ?ds = dataspace
<machine-dataspace $ds>
$ds ? ?r [
$log ! <log "-" { line: "machine" |+++|: $r }>
?- $log ! <log "-" { line: "machine" |---|: $r }>
]
<require-service <config-watcher "/run/etc/syndicate/machine" { config: $ds }>>

View File

@ -0,0 +1,9 @@
<require-service <daemon mdevd>>
<daemon mdevd {
argv: ["@mdevd@/bin/mdevd" "-v" "9" "-f" "@mdevdConf@"]
env: { PATH: "@mdevdPath@" }
}>
<require-service <daemon mdevd-coldplug>>
<depends-on <daemonmdevd-coldplug> <service-state <daemon mdevd> up>>
<daemon mdevd-coldplug <one-shot ["@mdevd@/bin/mdevd-coldplug" "-v" "9"]>>

View File

@ -0,0 +1,32 @@
{ config, lib, pkgs, ... }:
let
substituteDirectory = args:
pkgs.stdenvNoCC.mkDerivation ({
name = if args ? name then args.name else baseNameOf (toString args.src);
builder = ./substitute-dir.sh;
preferLocalBuild = true;
allowSubstitutes = false;
} // args);
in {
environment.etc = {
"syndicate/boot".source = substituteDirectory {
src = ./boot;
getty = "${pkgs.busybox}/bin/busybox getty";
inherit (config.services.getty) loginProgram;
};
"syndicate/core".source = substituteDirectory {
src = ./core;
hostnameOneShot =
"${pkgs.busybox}/bin/busybox hostname ${config.networking.hostName}";
inherit (pkgs) mdevd;
mdevdConf = pkgs.writeText "mdevd.conf" ''
.* 0:0 660 &importas -i MDEV MDEV backtick -E mdevdir { basename -- $MDEV } if { mkdir -p /run/etc/syndicate/machine/''${mdevdir} } redirfd -w 1 /run/etc/syndicate/machine/''${MDEV}.pr ${pkgs.nimPackages.syndicate_utils}/bin/preserve_process_environment
'';
mdevdPath = lib.strings.makeBinPath [ pkgs.execline pkgs.busybox ];
};
};
}

View File

@ -0,0 +1,15 @@
source $stdenv/setup
eval "$preInstall"
pushd $src || exit
find ./* -type f | while read filepath
do
fileout=$out/$filepath
dirout=$(dirname "$fileout")
mkdir -p "$dirout"
substituteAll "$filepath" "$fileout"
done
popd || exit
eval "$postInstall"

View File

@ -0,0 +1,47 @@
{ config, lib, pkgs, ... }:
let
lib' = lib.extend (import ../../../lib.nix);
toPreserves = lib'.generators.toPreserves { };
in {
environment.etc = lib.pipe config.networking.interfaces [
(lib.attrsets.mapAttrs' (ifname:
{ ipv4, useDHCP, ... }: {
name = "syndicate/services/network-interface-${ifname}.pr";
value.text = let
addresses = map ({ address, prefixLength }: [
ifname
[ "${address}/${toString prefixLength}" { record = "static"; } ]
{ record = "configure-interface"; }
]) ipv4.addresses;
routes = map ({ address, options, prefixLength, type, via }: [
"ipv4"
"${address}/${toString prefixLength}"
ifname
via
{ record = "configure-route"; }
]) ipv4.routes;
dhcp = lib.optional
(useDHCP == true || (useDHCP == null && ipv4.addresses == [ ])) [
ifname
[{ record = "dhcp"; }]
{ record = "configure-interface"; }
];
in lib.pipe (addresses ++ routes ++ dhcp) [
(map toPreserves)
lib.strings.concatLines
];
}))
] // {
"syndicate/services/networking.pr".source = pkgs.substituteAll {
src = ./networking.pr;
ip = "${pkgs.busybox}/bin/ip";
interface_monitor =
"${pkgs.python3Packages.synit-daemons}/bin/interface-monitor";
udhcpc = "${pkgs.busybox}/bin/udhcpc";
};
};
}

View File

@ -0,0 +1,56 @@
<require-service <daemon interface-monitor>>
<require-service <milestone network>>
<depends-on <milestone network> <service-state <daemon interface-monitor> ready>>
<configure-interface "lo" <static "127.0.0.1/8">>
? <configure-interface ?ifname <static ?ipaddr>> [
! <exec ["@ip@" "address" "add" "dev" $ifname $ipaddr]>
?- ! <exec ["@ip@" "address" "del" "dev" $ifname $ipaddr] never>
]
? <configure-interface ?ifname <dhcp>> [
<require-service <daemon <udhcpc $ifname>>>
]
? <configure-route ?addressFamily ?prefix ?iface ?gateway> [
! <exec ["@ip@" "route" "add" $prefix "via" $gateway "dev" $iface]>
?- ! <exec ["@ip@" "route" "del" $prefix "via" $gateway "dev" $iface]>
]
? <run-service <daemon <udhcpc ?ifname>>> [
<daemon <udhcpc $ifname> ["@udhcpc@" "-i" $ifname "-fR"]>
]
<daemon interface-monitor {
argv: "@interface_monitor@"
protocol: application/syndicate
}>
? <machine-dataspace ?machine> [
? <service-object <daemon interface-monitor> ?cap> [
$cap {
machine: $machine
}
]
$machine ? <interface ?ifname _ _ _ _ _ _> [
$log ! <log "-" { saw-machine-interface: $ifname }>
$config [
! <exec ["@ip@" "link" "set" $ifname "up"]>
?- ! <exec ["@ip@" "link" "set" $ifname "down"] never>
]
]
$machine ? <interface ?ifname _ normal up up carrier _> [
$config <configure-interface $ifname <dhcp>>
]
$machine ? <interface ?ifname _ normal up unknown carrier _> [
$config <configure-interface $ifname <dhcp>>
]
$machine ? <route ?addressFamily default _ _ _ _> [
$config <default-route $addressFamily>
]
]

View File

@ -0,0 +1,6 @@
{
imports = [ ./ntpd.nix ./sshd.nix ./userSettings.nix ./wifi.nix ];
environment.etc."syndicate/services/configdirs.pr".text = ''
<require-service <config-watcher "/run/etc/syndicate/services" $.>>
'';
}

View File

@ -0,0 +1,58 @@
{ config, lib, pkgs, ... }:
let
lib' = lib.extend (import ../../../lib.nix);
toPreserves = lib'.generators.toPreserves { };
cfg = config.services.nncp;
pkg = config.programs.nncp.package;
in {
options = {
services.nncp = {
caller = {
enable = lib.mkEnableOption ''
croned NNCP TCP daemon caller.
The daemon will take configuration from
<xref linkend="opt-programs.nncp.settings"/>
'';
extraArgs = lib.mkOption {
type = with lib.types; listOf str;
description = "Extra command-line arguments to pass to caller.";
default = [ ];
example = [ "-autotoss" ];
};
};
daemon = {
enable = lib.mkEnableOption ''
NNCP TCP synronization daemon.
The daemon will take configuration from
<xref linkend="opt-programs.nncp.settings"/>
'';
extraArgs = lib.mkOption {
type = with lib.types; listOf str;
description = "Extra command-line arguments to pass to daemon.";
default = [ ];
example = [ "-autotoss" ];
};
};
};
};
config = (lib.mkIf cfg.daemon.enable {
environment.etc."syndicate/services/nncp-daemon.pr".text =
let exec = [ "${pkg}/bin/nncp-daemon" ] ++ cfg.daemon.extraArgs;
in ''
<require-service <daemon nncp-daemon>>
<daemon nncp-daemon ${toPreserves exec}>
'';
}) // (lib.mkIf cfg.caller.enable {
environment.etc."syndicate/services/nncp-caller.pr".text =
let exec = [ "${pkg}/bin/nncp-caller" ] ++ cfg.caller.extraArgs;
in ''
<require-service <daemon nncp-caller>>
<daemon nncp-caller ${toPreserves exec}>
'';
});
}

View File

@ -0,0 +1,15 @@
{ config, lib, pkgs, ... }:
{
config = lib.mkIf config.services.ntp.enable {
environment.etc."syndicate/services/ntpd.pr".text =
let serverList = map (s: " -p ${s}") config.networking.timeServers;
in ''
<require-service <daemon ntpd>>
<depends-on <daemon ntpd> <default-route ipv4>>
<daemon ntpd "${pkgs.busybox}/bin/busybox ntpd -d -n ${
toString serverList
}">
'';
};
}

View File

@ -0,0 +1,9 @@
{ config, lib, pkgs, ... }:
{
config = lib.mkIf config.services.openssh.enable {
environment.etc."syndicate/services/ssh-keygen.pr".text = ''
<daemon ssh-host-keys <one-shot "${pkgs.openssh}/bin/ssh-keygen -A">>
'';
};
}

View File

@ -0,0 +1,26 @@
{ config, lib, pkgs, ... }:
{
environment.etc."syndicate/services/userSettings.pr".text = ''
let ?settingsDir = "/etc/syndicate/user-settings"
let ?settings = <* $config [
<rewrite ?item <user-setting $item>>
]>
<require-service <config-watcher $settingsDir { config: $settings }>>
<require-service <daemon user-settings-daemon>>
<daemon user-settings-daemon {
argv: "${pkgs.python3Packages.synit-daemons}/bin/user-settings-daemon"
protocol: application/syndicate
}>
? <service-object <daemon user-settings-daemon> ?cap> [
$cap {
config: $config
settingsDir: $settingsDir
}
]
'';
}

View File

@ -0,0 +1,45 @@
{ config, lib, pkgs, ... }:
{
config = lib.mkIf config.networking.wireless.enable {
environment.etc."syndicate/services/userSettings.pr".text = ''
? <machine-dataspace ?machine> [
$machine ? <interface ?ifname _ wireless _ _ _ _> [
$config [
<require-service <daemon <wpa_supplicant $ifname>>>
<depends-on <daemon <wifi-daemon $ifname>> <service-state <daemon <wpa_supplicant $ifname>> up>>
<require-service <daemon <wifi-daemon $ifname>>>
]
]
$machine ? <interface ?ifname _ wireless up up carrier _> [
$config <configure-interface $ifname <dhcp>>
]
$config ? <user-setting <?s <selected-wifi-network _ _ _>>> [ $machine += $s ]
]
? <run-service <daemon <wifi-daemon ?ifname>>> [
<daemon <wifi-daemon $ifname> {
argv: "/usr/lib/synit/wifi-daemon"
protocol: application/syndicate
}>
? <machine-dataspace ?machine> [
? <service-object <daemon <wifi-daemon $ifname>> ?cap> [
$cap {
machine: $machine
ifname: $ifname
}
]
]
]
? <run-service <daemon <wpa_supplicant ?ifname>>> [
<daemon <wpa_supplicant $ifname> [
"${pkgs.wpa_supplicant}/bin/wpa_supplicant" "-Dnl80211,wext" "-C/run/wpa_supplicant" "-i" $ifname
]>
]
'';
};
}

View File

@ -1,47 +0,0 @@
{ lib, stdenv, buildNimPackage, fetchFromGitea, fetchFromGitHub, runCommand
, nim-unwrapped, preserves }:
let
compiler = runCommand "compiler.nimble" { inherit (nim-unwrapped) src; } ''
mkdir -p $out
tar xf $src --strip-components=1 -C $out
'';
npeg = fetchFromGitHub {
owner = "zevv";
repo = "npeg";
rev = "1.2.1";
hash = "sha256-kN91cp50ZL4INeRWqwrRK6CAkVXUq4rN4YlcN6WL/3Y=";
};
in buildNimPackage rec {
pname = "preserves";
version = "20230530";
src = fetchFromGitea {
domain = "git.syndicate-lang.org";
owner = "ehmry";
repo = "${pname}-nim";
rev = version;
sha256 = "sha256-IRIBGjv4po8VyL873v++ovqz8Vg6a9Qbh/M1fxpQXvY=";
};
propagatedBuildInputs = [ compiler npeg ];
propagatedNativeBuildInputs =
lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) preserves;
# propagate the utilities of this package for this build platform
preConfigure = "substituteInPlace preserves.nimble --replace '# bin ' 'bin '";
postInstall = ''
pushd $out/bin
for link in preserves_decode preserves_from_json preserves_to_json;
do ln -s preserves_encode $link
done
popd
'';
doCheck = true;
meta = src.meta // {
maintainers = [ lib.maintainers.ehmry ];
license = lib.licenses.unlicense;
};
}

View File

@ -1,36 +0,0 @@
{ lib, buildNimPackage, fetchFromGitea, fetchFromGitHub, preserves }:
let
hashlib = buildNimPackage rec {
pname = "hashlib";
version = "1.0.1";
src = fetchFromGitHub {
owner = "khchen";
repo = pname;
rev = "84e0247555e4488594975900401baaf5bbbfb531";
hash = "sha256-nWNThelCh0LPVU7ryZgS/23hRRvJDVL2xWbQibb+zN0=";
};
doCheck = true;
};
in buildNimPackage rec {
pname = "syndicate";
version = "20230530";
src = fetchFromGitea {
domain = "git.syndicate-lang.org";
owner = "ehmry";
repo = "${pname}-nim";
rev = version;
hash = "sha256-lUHoMSQwUlz9EDMvpFL9GlrwbwMvZDILSmuakONwe50=";
};
propagatedBuildInputs = [ hashlib preserves ];
doCheck = true;
meta = src.meta // {
maintainers = [ lib.maintainers.ehmry ];
license = lib.licenses.unlicense;
};
}

View File

@ -1,21 +1,21 @@
{ lib, buildNimPackage, fetchFromGitea, fetchFromGitHub, syndicate }:
{ lib, buildNimPackage, fetchFromGitea, illwill, syndicate }:
buildNimPackage rec {
buildNimPackage (final: prev: {
pname = "syndicate_utils";
version = "20230518";
version = "20230801";
src = fetchFromGitea {
domain = "git.syndicate-lang.org";
owner = "ehmry";
repo = pname;
rev = version;
hash = "sha256-syYxxsl7rbE1FdFsT6ib3eUbxG6nOxbavo04ZzlTFOs=";
repo = final.pname;
rev = final.version;
hash = "sha256-O9n9y0v0cQg5b2P4GjOEKYj6t1fGTMy4v/QpeAScv2Q=";
};
propagatedBuildInputs = [ syndicate ];
propagatedBuildInputs = [ illwill syndicate ];
meta = src.meta // {
meta = final.src.meta // {
maintainers = [ lib.maintainers.ehmry ];
license = lib.licenses.unlicense;
};
}
})

View File

@ -1,8 +1,8 @@
{ lib, buildNimPackage, fetchFromGitea, makeDesktopItem, pkg-config, pcre, syndicate }:
buildNimPackage rec {
pname = "xdg_open_ng";
version = "20230518";
pname = "syndicated-open";
version = "20230630";
nimBinOnly = true;
src = fetchFromGitea {
@ -10,7 +10,7 @@ buildNimPackage rec {
owner = "ehmry";
repo = pname;
rev = version;
hash = "sha256-xoUcX3Vwz0Gnishqpe2hWo9x7HiNwagMBLW9nk9ZVWY=";
hash = "sha256-BRSvsIkaingUhtaBKQYwFzr6uC4WFs2/BmfWWPfI/6s=";
};
nativeBuildInputs = [ pkg-config ];
@ -18,8 +18,8 @@ buildNimPackage rec {
propagatedBuildInputs = [ pcre syndicate ];
desktopItem = makeDesktopItem rec {
name = "xdg-open";
desktopName = "Syndicate URI dispatcher (xdg-open)";
name = "open";
desktopName = "Syndicated URI open";
exec = "${name} %U";
mimeTypes = [
"application/vnd.mozilla.xul+xml"
@ -32,12 +32,12 @@ buildNimPackage rec {
};
postInstall = ''
mv $out/bin/xdg_open $out/bin/xdg-open
ln -s open $out/bin/xdg-open
cp -a $desktopItem/* $out/
'';
meta = src.meta // {
description = "Replacement for xdg-open that uses Syndicate for messaging.";
description = "Syndicated open command";
maintainers = [ lib.maintainers.ehmry ];
license = lib.licenses.unlicense;
};

View File

@ -0,0 +1,33 @@
{ lib, buildPythonApplication, python, pyroute2, syndicate, synit-pid1 }:
buildPythonApplication rec {
pname = "interface-monitor";
inherit (synit-pid1) version src;
propagatedBuildInputs = [ pyroute2 syndicate ];
format = "other";
installPhase = ''
runHook preInstall
mkdir -p \
$out/bin \
$out/lib/${python.libPrefix}/site-packages/synit/daemon \
$out/protocols \
cp -r protocols/schema-bundle.bin $out/protocols
pushd packaging/packages/synit-config/files/usr/lib/synit
for f in *; do
[ -f $f ] && cp $f $out/bin
done
pushd python/synit/daemon
for f in *; do
substitute $f $out/lib/${python.libPrefix}/site-packages/synit/daemon/$f \
--replace "/home/tonyg/src/synit/protocols" "$out/protocols"
done
popd
popd
runHook postInstall
'';
}

View File

@ -13,24 +13,12 @@ rustPlatform.buildRustPackage rec {
hash = "sha256-VVMttlRIiH/aGytC9CutoaT0YDGkCGhO3GbipYXk3dg=";
};
patchPhase =
# Patch to take children and configuration from /run/booted-system.
''
runHook prePatch
substituteInPlace src/main.rs \
--replace '"/usr/bin/syndicate-server"' '"/run/booted-system/sw/bin/syndicate-server"' \
--replace '"/sbin/synit-log"' '"/run/booted-system/sw/bin/synit-log"' \
--replace '"/etc/syndicate/boot"' '"/run/booted-system/etc/syndicate/boot"' \
runHook postPatch
'';
sourceRoot = "source/${pname}";
cargoHash = "sha256-57ZUlR+lIwQf3/4pDeeEMUc1juNTddcHpzNQZTUWbtU=";
nativeBuildInputs = [ rust ];
meta = src.meta // {
description = "Synit pid 1 program (patched for NixOS)";
description = "Synit pid 1 program";
homepage = "https://synit.org/";
maintainers = with lib.maintainers; [ ehmry ];
};