seedrng #3

Open
lbailly wants to merge 57 commits from seedrng into main
74 changed files with 2319 additions and 147 deletions

View file

@ -0,0 +1,40 @@
name: build liminix
on:
pull_request:
types: [opened, synchronize, edited, reopened]
branches:
- main
push:
branches:
- main
jobs:
build_vm_qemu_mips:
runs-on: nix
steps:
- uses: actions/checkout@v3
- name: Build VM QEMU MIPS
run: |
# Enter the shell
nix-build ci.nix -A qemu
build_zyxel-nwa50ax_mips:
runs-on: nix
steps:
- uses: actions/checkout@v3
- name: Build VM QEMU MIPS
run: |
# Enter the shell
nix-build ci.nix -A qemu
test_hostapd:
runs-on: nix
steps:
- uses: actions/checkout@v3
- name: Build VM QEMU MIPS
run: |
# Enter the shell
nix-build ci.nix -A wlan

26
ci.nix
View file

@ -1,21 +1,15 @@
{ {
nixpkgs sources ? import ./lon.nix
, unstable , nixpkgs ? sources.nixpkgs
, liminix , unstable ? nixpkgs
, liminix ? ./.
, ... }: , ... }:
let let
inherit (builtins) map; pkgs = (import nixpkgs { });
pkgs = (import nixpkgs {});
borderVmConf = ./bordervm.conf-example.nix; borderVmConf = ./bordervm.conf-example.nix;
inherit (pkgs.lib.attrsets) genAttrs; inherit (pkgs.lib.attrsets) genAttrs mapAttrs;
devices = [ devices = [
"gl-ar750"
"gl-mt300a"
"gl-mt300n-v2"
"qemu" "qemu"
"qemu-aarch64"
"qemu-armv7l"
"tp-archer-ax23"
"zyxel-nwa50ax" "zyxel-nwa50ax"
]; ];
vanilla = ./vanilla-configuration.nix; vanilla = ./vanilla-configuration.nix;
@ -25,7 +19,7 @@ let
device = import (liminix + "/devices/${name}"); device = import (liminix + "/devices/${name}");
liminix-config = vanilla; liminix-config = vanilla;
}).outputs.default; }).outputs.default;
tests = import ./tests/ci.nix; tests = mapAttrs (_: v: v { inherit liminix nixpkgs; }) (import ./tests/ci.nix);
jobs = jobs =
(genAttrs devices for-device) // (genAttrs devices for-device) //
tests // tests //
@ -44,12 +38,6 @@ let
imports = [ ./modules/all-modules.nix ]; imports = [ ./modules/all-modules.nix ];
}; };
}).outputs.optionsJson; }).outputs.optionsJson;
installers = map (f: "system.outputs.${f}") [
"vmroot"
"mtdimage"
"ubimage"
];
inherit (pkgs.lib) concatStringsSep;
in pkgs.stdenv.mkDerivation { in pkgs.stdenv.mkDerivation {
name = "liminix-doc"; name = "liminix-doc";
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [

View file

@ -26,9 +26,13 @@ let
eval = evalModules { eval = evalModules {
modules = [ modules = [
{ {
nixpkgs.overlays = [ nixpkgs = {
overlay source = nixpkgs;
]; overlays = [ overlay ];
config.permittedInsecurePackages = [
"python-2.7.18.8"
];
};
} }
device.module device.module
liminix-config liminix-config

View file

@ -1,7 +1,7 @@
# This "device" generates images that can be used with the QEMU # This "device" generates images that can be used with the QEMU
# emulator. The default output is a directory containing separate # emulator. The default output is a directory containing separate
# kernel (uncompressed vmlinux) and initrd (squashfs) images # kernel (uncompressed vmlinux) and initrd (squashfs) images
{ rec {
system = { system = {
crossSystem = { crossSystem = {
config = "mips-unknown-linux-musl"; config = "mips-unknown-linux-musl";
@ -41,6 +41,9 @@
../../modules/arch/mipseb.nix ../../modules/arch/mipseb.nix
../families/qemu.nix ../families/qemu.nix
]; ];
nixpkgs.hostPlatform = system.crossSystem;
kernel = { kernel = {
config = { config = {
MIPS_MALTA= "y"; MIPS_MALTA= "y";

View file

@ -1,4 +1,4 @@
{ rec {
system = { system = {
crossSystem = { crossSystem = {
config = "mipsel-unknown-linux-musl"; config = "mipsel-unknown-linux-musl";
@ -135,6 +135,8 @@
../../modules/zyxel-dual-image ../../modules/zyxel-dual-image
]; ];
nixpkgs.hostPlatform = system.crossSystem;
filesystem = dir { filesystem = dir {
lib = dir { lib = dir {
firmware = dir { firmware = dir {
@ -170,7 +172,7 @@
maxLEBcount = "256"; maxLEBcount = "256";
}; };
flash.eraseBlockSize = 65536; flash.eraseBlockSize = 64 * 1024;
# This is a FIT containing a kernel padded and # This is a FIT containing a kernel padded and
# a UBI volume rootfs. # a UBI volume rootfs.
@ -181,8 +183,8 @@
# Aligned on 2kb. # Aligned on 2kb.
alignment = 2048; alignment = 2048;
rootDevice = "ubi:rootfs"; rootDevice = "ubi0:rootfs";
alternativeRootDevice = "ubi:rootfs"; alternativeRootDevice = "ubi1:rootfs";
# Auto-attach MTD devices: ubi_a then ubi_b. # Auto-attach MTD devices: ubi_a then ubi_b.
ubi.mtds = [ "ubi_a" "ubi_b" ]; ubi.mtds = [ "ubi_a" "ubi_b" ];
@ -222,8 +224,9 @@
imageFormat = "fit"; imageFormat = "fit";
tftp = { tftp = {
# 5MB is nice. # 20MB is pretty good on this device as we have plenty of RAM.
freeSpaceBytes = 5 * 1024 * 1024; freeSpaceBytes = 20 * 1024 * 1024;
appendDTB = true;
loadAddress = lim.parseInt "0x2000000"; loadAddress = lim.parseInt "0x2000000";
}; };
}; };

View file

@ -1,4 +1,4 @@
{ nixpkgs ? <nixpkgs>, pkgs ? (import <nixpkgs> {}), lib ? pkgs.lib }: { nixpkgs ? <nixpkgs>, pkgs ? (import nixpkgs {}), lib ? pkgs.lib }:
args: args:
let let
modulesPath = builtins.toString ../modules; modulesPath = builtins.toString ../modules;

15
lon.lock Normal file
View file

@ -0,0 +1,15 @@
{
"version": "1",
"sources": {
"nixpkgs": {
"type": "GitHub",
"fetchType": "tarball",
"owner": "nixos",
"repo": "nixpkgs",
"branch": "nixos-unstable-small",
"revision": "b6227cadb5123c7e4cb159bf6f9f5705ae081a47",
"url": "https://github.com/nixos/nixpkgs/archive/b6227cadb5123c7e4cb159bf6f9f5705ae081a47.tar.gz",
"hash": "sha256-KFR30GNFhjzXl0kVEn+KK4xrFr0gggb1NBroP8ukbxY="
}
}
}

41
lon.nix Normal file
View file

@ -0,0 +1,41 @@
# Generated by lon. Do not modify!
let
lock = builtins.fromJSON (builtins.readFile ./lon.lock);
# Override with a path defined in an environment variable. If no variable is
# set, the original path is used.
overrideFromEnv =
name: path:
let
replacement = builtins.getEnv "LON_OVERRIDE_${name}";
in
if replacement == "" then
path
else
# this turns the string into an actual Nix path (for both absolute and
# relative paths)
if builtins.substring 0 1 replacement == "/" then
/. + replacement
else
/. + builtins.getEnv "PWD" + "/${replacement}";
fetchSource =
args@{ fetchType, ... }:
if fetchType == "git" then
builtins.fetchGit {
url = args.url;
ref = args.branch;
rev = args.revision;
narHash = args.hash;
}
else if fetchType == "tarball" then
builtins.fetchTarball {
url = args.url;
sha256 = args.hash;
}
else
builtins.throw "Unsupported source type ${fetchType}";
in
builtins.mapAttrs (name: args: overrideFromEnv name (fetchSource args)) lock.sources

View file

@ -8,6 +8,7 @@
./bridge ./bridge
./busybox.nix ./busybox.nix
./dhcp6c ./dhcp6c
./jitter-rng
./dnsmasq ./dnsmasq
./firewall ./firewall
./hardware.nix ./hardware.nix

View file

@ -14,5 +14,8 @@
boot.commandLine = [ boot.commandLine = [
"console=ttyS0,115200" # true of all mips we've yet encountered "console=ttyS0,115200" # true of all mips we've yet encountered
]; ];
boot.tftp.commandLine = [
"console=ttyS0,115200" # true of all mips we've yet encountered
];
}; };
} }

View file

@ -69,6 +69,14 @@ in {
default = "uimage"; default = "uimage";
}; };
tftp = { tftp = {
commandLine = mkOption {
type = types.listOf types.str;
default = config.boot.commandLine;
description = ''
TFTP-specific command line.
Defaults to the classical one if unset.
'';
};
loadAddress = mkOption { loadAddress = mkOption {
type = types.ints.unsigned; type = types.ints.unsigned;
description = '' description = ''
@ -98,6 +106,9 @@ in {
}; };
}; };
config = { config = {
# By default, we enable cross-compilation support.
nixpkgs.buildPlatform = lib.mkDefault builtins.currentSystem;
defaultProfile.packages = with pkgs; defaultProfile.packages = with pkgs;
[ s6 s6-init-bin execline s6-linux-init s6-rc ]; [ s6 s6-init-bin execline s6-linux-init s6-rc ];
@ -109,7 +120,13 @@ in {
] ]
++ (map (mtd: "ubi.mtd=${mtd}") config.hardware.ubi.mtds) ++ (map (mtd: "ubi.mtd=${mtd}") config.hardware.ubi.mtds)
++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}" ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}"
++ lib.optional (config.hardware.alternativeRootDevice != null) "altroot=${config.hardware.alternativeRootDevice}"; ++ lib.optional (config.hardware.alternativeRootDevice != null) "rootalt=${config.hardware.alternativeRootDevice}";
boot.tftp.commandLine = [
"panic=10 oops=panic init=/bin/init loglevel=8"
"fw_devlink=off"
"rootfstype=${config.rootfsType}"
];
system.callService = path : parameters : system.callService = path : parameters :
let let

View file

@ -20,6 +20,7 @@ in
system.service.bridge = { system.service.bridge = {
primary = mkOption { type = liminix.lib.types.serviceDefn; }; primary = mkOption { type = liminix.lib.types.serviceDefn; };
members = mkOption { type = liminix.lib.types.serviceDefn; }; members = mkOption { type = liminix.lib.types.serviceDefn; };
ready = mkOption { type = liminix.lib.types.serviceDefn; };
}; };
}; };
config.system.service.bridge = { config.system.service.bridge = {
@ -28,6 +29,12 @@ in
type = types.str; type = types.str;
description = "bridge interface name to create"; description = "bridge interface name to create";
}; };
macAddressFromInterface = mkOption {
type = types.nullOr liminix.lib.types.service;
default = null;
description = "reuse mac address from an existing interface service";
};
}; };
members = config.system.callService ./members.nix { members = config.system.callService ./members.nix {
primary = mkOption { primary = mkOption {
@ -36,8 +43,33 @@ in
}; };
members = mkOption { members = mkOption {
type = types.listOf liminix.lib.types.interface; type = types.attrsOf (types.submodule ({ ... }: { options = {
description = "interfaces to add to the bridge"; member = mkOption {
type = liminix.lib.types.interface;
description = "interface to add";
};
dependencies = mkOption {
type = types.listOf liminix.lib.types.service;
default = [];
description = "extra dependencies before attaching this interface to the bridge";
};
}; }));
description = "set of bridge members";
};
};
# TODO: generalize it outside
ready = config.system.callService ./ready.nix {
primary = mkOption {
type = liminix.lib.types.service;
description = "primary bridge interface";
};
members = mkOption {
type = liminix.lib.types.service;
description = "members service";
}; };
}; };
}; };

View file

@ -7,26 +7,22 @@
{ members, primary } : { members, primary } :
let let
inherit (liminix.networking) interface; inherit (liminix.services) structuredBundle oneshot;
inherit (liminix.services) bundle oneshot; inherit (lib) mapAttrs;
inherit (lib) mkOption types; addif = name: { dependencies ? [ ], member }: oneshot {
addif = member : name = "${primary.name}.member.${name}";
# how do we get sight of services from here? maybe we need to up = ''
# implement ifwait as a regualr derivation instead of a echo "attaching $(output ${member} ifname) to $(output ${primary} ifname) bridge"
# servicedefinition ip link set dev $(output ${member} ifname) master $(output ${primary} ifname)
svc.ifwait.build { '';
state = "running"; down = ''
interface = member; echo "detaching $(output ${member} ifname) from any bridge"
dependencies = [ primary member ]; ip link set dev $(output ${member} ifname) nomaster
service = oneshot { '';
name = "${primary.name}.member.${member.name}";
up = '' dependencies = [ primary member ] ++ dependencies;
ip link set dev $(output ${member} ifname) master $(output ${primary} ifname) };
''; in structuredBundle {
down = "ip link set dev $(output ${member} ifname) nomaster";
};
};
in bundle {
name = "${primary.name}.members"; name = "${primary.name}.members";
contents = map addif members; contents = mapAttrs addif members;
} }

View file

@ -3,15 +3,24 @@
, ifwait , ifwait
, lib , lib
}: }:
{ ifname } : { ifname, macAddressFromInterface ? null } :
let let
inherit (liminix.services) bundle oneshot; inherit (liminix.services) bundle oneshot;
inherit (lib) mkOption types; inherit (lib) mkOption types optional;
in oneshot rec { in oneshot rec {
name = "${ifname}.link"; name = "${ifname}.link";
up = '' up = ''
ip link add name ${ifname} type bridge ${if macAddressFromInterface == null then
${liminix.networking.ifup name ifname} "ip link add name ${ifname} type bridge"
else
"ip link add name ${ifname} address $(output ${macAddressFromInterface} ether) type bridge"}
(in_outputs ${name}
echo ${ifname} > ifname
cat /sys/class/net/${ifname}/address > ether
)
''; '';
down = "ip link set down dev ${ifname}"; down = "ip link delete ${ifname}";
dependencies = optional (macAddressFromInterface != null) macAddressFromInterface;
} }

18
modules/bridge/ready.nix Normal file
View file

@ -0,0 +1,18 @@
{
liminix
, ifwait
, lib
}:
{ primary, members } :
let
inherit (liminix.services) oneshot;
in oneshot {
name = "${primary.name}.oper";
up = ''
ip link set up dev $(output ${primary} ifname)
${ifwait}/bin/ifwait -v $(output ${primary} ifname) running
'';
down = "ip link set down dev $(output ${primary} ifname)";
dependencies = [ members ];
}

View file

@ -20,15 +20,30 @@ in {
system.service.hostapd = mkOption { system.service.hostapd = mkOption {
type = liminix.lib.types.serviceDefn; type = liminix.lib.types.serviceDefn;
}; };
system.service.hostapd-ready = mkOption {
type = liminix.lib.types.serviceDefn;
};
}; };
config = { config = {
system.service.hostapd = liminix.callService ./service.nix { system.service.hostapd = liminix.callService ./service.nix {
interface = mkOption { interface = mkOption {
type = liminix.lib.types.service; type = liminix.lib.types.service;
}; };
package = mkOption {
type = types.package;
default = pkgs.hostapd;
};
params = mkOption { params = mkOption {
type = types.attrs; type = types.attrs;
}; };
}; };
system.service.hostapd-ready = liminix.callService ./ready.nix {
interface = mkOption {
type = liminix.lib.types.interface;
description = "Interface for which to wait that the oper state Master or Master (VLAN) has been reached.";
};
};
}; };
} }

16
modules/hostapd/ready.nix Normal file
View file

@ -0,0 +1,16 @@
{
liminix
, ifwait
, lib
}:
{ interface } :
let
inherit (liminix.services) oneshot;
in oneshot {
name = "${interface.name}.wlan-oper";
up = ''
${ifwait}/bin/ifbridgeable -v $(output ${interface} ifname)
'';
dependencies = [ interface ];
}

View file

@ -1,10 +1,9 @@
{ {
liminix liminix
, hostapd
, writeText , writeText
, lib , lib
}: }:
{ interface, params} : { package, interface, params } :
let let
inherit (liminix.services) longrun; inherit (liminix.services) longrun;
inherit (lib) concatStringsSep mapAttrsToList; inherit (lib) concatStringsSep mapAttrsToList;
@ -35,5 +34,5 @@ let
in longrun { in longrun {
inherit name; inherit name;
dependencies = [ interface ]; dependencies = [ interface ];
run = "${hostapd}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}.pid -S ${conf}"; run = "${package}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}.pid -S ${conf}";
} }

View file

@ -15,7 +15,7 @@ in {
}; };
config = { config = {
services.hostname = oneshot { services.hostname = oneshot {
name = "hostname"; name = "hostname-${builtins.substring 0 12 (builtins.hashString "sha256" config.hostname)}";
up = "echo ${config.hostname} > /proc/sys/kernel/hostname"; up = "echo ${config.hostname} > /proc/sys/kernel/hostname";
down = "true"; down = "true";
}; };

View file

@ -0,0 +1,21 @@
## CPU Jitter RNG
## ==============
##
## CPU Jitter RNG is a random number generator # providing non-physical true
## random generation # that works equally for kernel and user-land. It relies
## on the availability of a high-resolution timer.
{ lib, pkgs, ... }:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in {
options.system.service.jitter-rng = mkOption {
type = liminix.lib.types.serviceDefn;
};
config = {
system.service.jitter-rng = pkgs.liminix.callService ./jitter-rng.nix {
};
};
}

View file

@ -0,0 +1,18 @@
{
liminix
, lib
, jitterentropy-rngd
}:
{ }:
let
inherit (liminix.services) longrun;
name = "jitterentropy-rngd";
in
longrun {
# Does it need to be unique?
inherit name;
run = ''
mkdir -p /run/jitterentropy-rngd
${jitterentropy-rngd}/bin/jitterentropy-rngd -v -p /run/jitterentropy-rngd/${name}.pid
'';
}

View file

@ -17,7 +17,7 @@ let
ip address replace $ip/$mask dev $interface ip address replace $ip/$mask dev $interface
(in_outputs ${name} (in_outputs ${name}
for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do
printenv $i > $i (printenv $i || true) > $i
done) done)
} }
case $action in case $action in
@ -40,7 +40,7 @@ let
''; '';
in longrun { in longrun {
inherit name; inherit name;
run = "/bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}"; run = "exec /bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
notification-fd = 10; notification-fd = 10;
dependencies = [ interface ]; dependencies = [ interface ];
} }

View file

@ -83,11 +83,11 @@ let
localSystem = cfg.hostPlatform; localSystem = cfg.hostPlatform;
}; };
in in
import <nixpkgs> ({ import cfg.source ({
inherit (cfg) config overlays; inherit (cfg) config overlays;
} // systemArgs) } // systemArgs)
else else
import <nixpkgs> { import cfg.source {
inherit (cfg) config overlays localSystem crossSystem; inherit (cfg) config overlays localSystem crossSystem;
}; };
@ -97,6 +97,14 @@ in
{ {
options.nixpkgs = { options.nixpkgs = {
source = mkOption {
type = types.package // {
description = "Source of a nixpkgs repository";
};
default = <nixpkgs>;
defaultText = "<nixpkgs>";
};
pkgs = mkOption { pkgs = mkOption {
defaultText = literalExpression '' defaultText = literalExpression ''

View file

@ -36,22 +36,43 @@ in
kernel.config = { kernel.config = {
BLK_DEV_INITRD = "y"; BLK_DEV_INITRD = "y";
INITRAMFS_SOURCE = builtins.toJSON "${config.system.outputs.initramfs}"; INITRAMFS_SOURCE = builtins.toJSON "${config.system.outputs.initramfs}";
# INITRAMFS_COMPRESSION_LZO = "y"; INITRAMFS_COMPRESSION_ZSTD = "y";
}; };
system.outputs = { system.outputs = {
initramfs = initramfs =
let inherit (pkgs.pkgsBuildBuild) gen_init_cpio; let
inherit (pkgs.pkgsBuildBuild) gen_init_cpio cpio writeScript;
inherit (pkgs) busybox;
failsafe-init = writeScript "init" ''
#!/bin/sh
exec >/dev/console
echo Running in initramfs
PATH=${busybox}/bin:$PATH
export PATH
mount -t proc none /proc
mount -t sysfs none /sys
${busybox}/bin/sh
'';
refs = pkgs.writeReferencesToFile busybox;
in runCommand "initramfs.cpio" {} '' in runCommand "initramfs.cpio" {} ''
cat << SPECIALS | ${gen_init_cpio}/bin/gen_init_cpio /dev/stdin > $out cat << SPECIALS | ${gen_init_cpio}/bin/gen_init_cpio /dev/stdin > out
dir /proc 0755 0 0 dir /proc 0755 0 0
dir /dev 0755 0 0 dir /dev 0755 0 0
nod /dev/console 0600 0 0 c 5 1 nod /dev/console 0600 0 0 c 5 1
dir /target 0755 0 0 dir /target 0755 0 0
dir /target/persist 0755 0 0 dir /target/persist 0755 0 0
dir /target/nix 0755 0 0 dir /target/nix 0755 0 0
dir /nix 0755 0 0
dir /nix/store 0755 0 0
dir /bin 0755 0 0
file /bin/sh ${busybox}/bin/sh 0755 0 0
file /init ${pkgs.preinit}/bin/preinit 0755 0 0 file /init ${pkgs.preinit}/bin/preinit 0755 0 0
file /failsafe-init ${failsafe-init} 0755 0 0
SPECIALS SPECIALS
find $(cat ${refs}) | ${cpio}/bin/cpio -H newc -o -A -v -O out
mv out $out
''; '';
systemConfiguration = systemConfiguration =
pkgs.systemconfig config.filesystem.contents; pkgs.systemconfig config.filesystem.contents;

View file

@ -5,7 +5,8 @@
, ... , ...
}: }:
let let
inherit (lib) mkIf mkOption types; inherit (pkgs) liminix;
inherit (lib) mkIf;
o = config.system.outputs; o = config.system.outputs;
in in
{ {
@ -24,17 +25,10 @@ in
}; };
boot.initramfs.enable = true; boot.initramfs.enable = true;
system.outputs = { system.outputs = {
rootfs = rootfs = liminix.builders.jffs2 {
let bootableRootDirectory = o.bootablerootdir;
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils; inherit (config.hardware.flash) eraseBlockSize;
endian = if pkgs.stdenv.isBigEndian };
then "--big-endian" else "--little-endian";
in runCommand "make-jffs2" {
depsBuildBuild = [ mtdutils ];
} ''
tree=${o.bootablerootdir}
(cd $tree && mkfs.jffs2 --compression-mode=size ${endian} -e ${toString config.hardware.flash.eraseBlockSize} --enable-compressor=lzo --pad --root . --output $out --squash --faketime )
'';
}; };
}; };
} }

View file

@ -0,0 +1,14 @@
/dts-v1/;
/ {
description = "Liminix TFTP bootscript";
#address-cells = <1>;
images {
bootscript {
description = "Bootscript";
data = /incbin/("boot.scr");
type = "script";
compression = "none";
};
};

View file

@ -5,10 +5,23 @@
, ... , ...
}: }:
let let
inherit (lib) mkOption types concatStringsSep; inherit (lib) mkOption mkIf types concatStringsSep;
inherit (pkgs) liminix;
cfg = config.boot.tftp; cfg = config.boot.tftp;
hw = config.hardware; hw = config.hardware;
arch = pkgs.stdenv.hostPlatform.linuxArch; arch = pkgs.stdenv.hostPlatform.linuxArch;
# UBI cannot run on the top of phram.
needsJffs2 = config.rootfsType == "ubifs";
# squashfs doesn't work out for us because only `bootablerootdir`
# contain what we need to boot, not `config.filesystem.contents` alas.
rootfstype = if needsJffs2 then "jffs2" else config.rootfsType;
rootfs = if needsJffs2 then
liminix.builders.jffs2 {
bootableRootDirectory = config.system.outputs.bootablerootdir;
inherit (config.hardware.flash) eraseBlockSize;
}
else config.system.outputs.rootfs;
in { in {
imports = [ ../ramdisk.nix ]; imports = [ ../ramdisk.nix ];
options.boot.tftp = { options.boot.tftp = {
@ -51,11 +64,56 @@ in {
It uses the Linux `phram <https://github.com/torvalds/linux/blob/master/drivers/mtd/devices/phram.c>`_ driver to emulate a flash device using a segment of physical RAM. It uses the Linux `phram <https://github.com/torvalds/linux/blob/master/drivers/mtd/devices/phram.c>`_ driver to emulate a flash device using a segment of physical RAM.
''; '';
}; };
tftpboot-fit = mkOption {
type = types.package;
description = ''
tftpboot-fit
************
This output is a variant that encloses the `boot.scr` in a FIT
if that's simpler to transfer for you.
'';
};
}; };
config = { config = {
boot.ramdisk.enable = true; boot.ramdisk.enable = true;
kernel.config = mkIf needsJffs2 {
JFFS2_FS = "y";
JFFS2_LZO = "y";
JFFS2_RTIME = "y";
JFFS2_COMPRESSION_OPTIONS = "y";
JFFS2_ZLIB = "y";
JFFS2_CMODE_SIZE = "y";
};
system.outputs = rec { system.outputs = rec {
tftpboot-fit =
let
tftpboot-fit = pkgs.writeText "tftpboot.its" ''
/dts-v1/;
/ {
description = "Liminix TFTP bootscript";
#address-cells = <1>;
images {
bootscript {
description = "Bootscript";
data = /incbin/("${tftpboot}/boot.scr");
type = "script";
compression = "none";
};
};
};
'';
in
pkgs.runCommand "tftpboot-fit" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ ubootTools ]; } ''
mkdir -p $out/
cp -rf ${tftpboot}/* $out/
mkimage -f ${tftpboot-fit} $out/script.ub
'';
tftpboot = tftpboot =
let let
inherit (pkgs.lib.trivial) toHexString; inherit (pkgs.lib.trivial) toHexString;
@ -69,7 +127,7 @@ in {
zimage = "bootz"; zimage = "bootz";
}; in choices.${cfg.kernelFormat}; }; in choices.${cfg.kernelFormat};
cmdline = concatStringsSep " " config.boot.commandLine; cmdline = concatStringsSep " " config.boot.tftp.commandLine;
objcopy = "${pkgs.stdenv.cc.bintools.targetPrefix}objcopy"; objcopy = "${pkgs.stdenv.cc.bintools.targetPrefix}objcopy";
stripAndZip = '' stripAndZip = ''
${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin ${objcopy} -O binary -R .reginfo -R .notes -R .note -R .comment -R .mdebug -R .note.gnu.build-id -S vmlinux.elf vmlinux.bin
@ -84,7 +142,7 @@ in {
hex() { printf "0x%x" $1; } hex() { printf "0x%x" $1; }
rootfsStart=${toString cfg.loadAddress} rootfsStart=${toString cfg.loadAddress}
rootfsSize=$(binsize64k ${o.rootfs} ) rootfsSize=$(binsize64k ${rootfs} )
rootfsSize=$(($rootfsSize + ${toString cfg.freeSpaceBytes} )) rootfsSize=$(($rootfsSize + ${toString cfg.freeSpaceBytes} ))
ln -s ${o.manifest} manifest ln -s ${o.manifest} manifest
@ -98,13 +156,13 @@ in {
dtbStart=$(($rootfsStart + $rootfsSize)) dtbStart=$(($rootfsStart + $rootfsSize))
${if cfg.compressRoot ${if cfg.compressRoot
then '' then ''
lzma -z9cv ${o.rootfs} > rootfs.lz lzma -z9cv ${rootfs} > rootfs.lz
rootfsLzStart=$dtbStart rootfsLzStart=$dtbStart
rootfsLzSize=$(binsize rootfs.lz) rootfsLzSize=$(binsize rootfs.lz)
dtbStart=$(($dtbStart + $rootfsLzSize)) dtbStart=$(($dtbStart + $rootfsLzSize))
'' ''
else '' else ''
ln -s ${o.rootfs} rootfs ln -s ${rootfs} rootfs
'' ''
} }
@ -121,7 +179,7 @@ in {
fdtput -p -t s dtb /reserved-memory/$node compatible phram fdtput -p -t s dtb /reserved-memory/$node compatible phram
fdtput -p -t lx dtb /reserved-memory/$node reg $ac_prefix $(hex $rootfsStart) $sz_prefix $(hex $rootfsSize) fdtput -p -t lx dtb /reserved-memory/$node reg $ac_prefix $(hex $rootfsStart) $sz_prefix $(hex $rootfsSize)
cmd="liminix ${cmdline} mtdparts=phram0:''${rootfsSize}(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsSize},${toString config.hardware.flash.eraseBlockSize} root=/dev/mtdblock0"; cmd="liminix ${cmdline} mtdparts=phram0:''${rootfsSize}(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsSize},${toString config.hardware.flash.eraseBlockSize} rootfstype=${rootfstype} root=/dev/mtdblock0";
fdtput -t s dtb /chosen ${config.boot.commandLineDtbNode} "$cmd" fdtput -t s dtb /chosen ${config.boot.commandLineDtbNode} "$cmd"
dtbSize=$(binsize ./dtb ) dtbSize=$(binsize ./dtb )

83
modules/pki/default.nix Normal file
View file

@ -0,0 +1,83 @@
{ config, lib, pkgs, ... }:
# Inspired from nixpkgs/NixOS.
with lib;
let
inherit (pkgs.pseudofile) dir symlink;
cfg = config.security.pki;
cacertPackage = pkgs.cacert.override {
blacklist = [ ];
extraCertificateFiles = cfg.certificateFiles;
extraCertificateStrings = cfg.certificates;
};
caBundleName = "ca-bundle.crt";
caBundle = "${cacertPackage}/etc/ssl/certs/${caBundleName}";
in
{
options = {
security.pki.installCACerts = mkEnableOption "installing CA certificates to the system" // {
default = false;
};
security.pki.certificateFiles = mkOption {
type = types.listOf types.path;
default = [];
example = literalExpression ''[ "''${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" ]'';
description = ''
A list of files containing trusted root certificates in PEM
format. These are concatenated to form
{file}`/etc/ssl/certs/ca-certificates.crt`, which is
used by many programs that use OpenSSL, such as
{command}`curl` and {command}`git`.
'';
};
security.pki.certificates = mkOption {
type = types.listOf types.str;
default = [];
example = literalExpression ''
[ '''
NixOS.org
=========
-----BEGIN CERTIFICATE-----
MIIGUDCCBTigAwIBAgIDD8KWMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
...
-----END CERTIFICATE-----
'''
]
'';
description = ''
A list of trusted root certificates in PEM format.
'';
};
};
config = mkIf cfg.installCACerts {
# NixOS canonical location + Debian/Ubuntu/Arch/Gentoo compatibility.
filesystem = dir {
etc = dir {
ssl = dir {
certs = dir {
"ca-certificates.crt" = symlink caBundle;
"ca-bundle.crt" = symlink caBundle;
};
};
# CentOS/Fedora compatibility.
pki = dir {
certs = dir {
"ca-bundle.crt" = symlink caBundle;
};
};
};
};
};
}

View file

@ -1,4 +1,4 @@
{ config, pkgs, ... }: { config, pkgs, lib, ... }:
let let
inherit (pkgs) inherit (pkgs)
execline execline
@ -31,6 +31,8 @@ let
mkdir $out mkdir $out
cp -r $src $out/scripts cp -r $src $out/scripts
chmod -R +w $out chmod -R +w $out
substituteInPlace $out/scripts/{rc.init,rc.shutdown} \
--replace-warn 'pkgs.seedrng' "${lib.getExe' pkgs.seedrng "seedrng"}"
''; '';
}; };
service = dir { service = dir {

View file

@ -32,6 +32,8 @@ else
mkdir -m 0751 -p /run/services/state mkdir -m 0751 -p /run/services/state
fi fi
pkgs.seedrng
### If your services are managed by s6-rc: ### If your services are managed by s6-rc:
### (replace /run/service with your scandir) ### (replace /run/service with your scandir)
s6-rc-init -d -c /etc/s6-rc/compiled /run/service s6-rc-init -d -c /etc/s6-rc/compiled /run/service

View file

@ -4,6 +4,7 @@
### Things to do before hardware halt/reboot/poweroff. ### Things to do before hardware halt/reboot/poweroff.
### Ideally, it should be a single call to the service manager, ### Ideally, it should be a single call to the service manager,
### telling it to bring all the services down. ### telling it to bring all the services down.
pkgs.seedrng
### If your s6-linux-init-maker invocation was made with the -1 ### If your s6-linux-init-maker invocation was made with the -1
### option, messages from rc.shutdown will appear on /dev/console ### option, messages from rc.shutdown will appear on /dev/console

24
modules/ubus/default.nix Normal file
View file

@ -0,0 +1,24 @@
## ubus
## ====
##
## ubus is a micro-bus à la D-Bus for all your needs.
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in {
options = {
system.service.ubus = mkOption {
type = liminix.lib.types.serviceDefn;
};
};
config = {
system.service.ubus = liminix.callService ./service.nix {
package = mkOption {
type = types.package;
default = pkgs.ubus;
};
};
};
}

16
modules/ubus/service.nix Normal file
View file

@ -0,0 +1,16 @@
{
liminix
, writeText
, lib
}:
{ package } :
let
inherit (liminix.services) longrun;
in longrun {
# Long term: make it unique so that user can spawn multiple buses if they want.
name = "ubus";
run = ''
mkdir -p /run/ubus
${package}/bin/ubusd -s /run/ubus/ubus.sock
'';
}

View file

@ -1,23 +1,24 @@
final: prev: final: prev:
let let
isCross = final.stdenv.buildPlatform != final.stdenv.hostPlatform;
crossOnly = pkg : amendFn : if isCross then (amendFn pkg) else pkg;
extraPkgs = import ./pkgs/default.nix { extraPkgs = import ./pkgs/default.nix {
inherit (final) lib callPackage; inherit (final) lib callPackage;
}; };
inherit (final) fetchpatch; inherit (final) fetchpatch;
lua_no_readline = prev.lua5_3; luaHost = prev.lua5_3.overrideAttrs(o: {
# lua_no_readline = prev.lua5_3.overrideAttrs(o: { name = "lua-tty";
# name = "lua-tty"; preBuild = ''
# preBuild = '' makeFlagsArray+=(PLAT="posix" SYSLIBS="-Wl,-E -ldl" CFLAGS="-O2 -fPIC -DLUA_USE_POSIX -DLUA_USE_DLOPEN")
# makeFlagsArray+=(PLAT="posix" SYSLIBS="-Wl,-E -ldl" CFLAGS="-O2 -fPIC -DLUA_USE_POSIX -DLUA_USE_DLOPEN") '';
# ''; # lua in nixpkgs has a postInstall stanza that assumes only
# # lua in nixpkgs has a postInstall stanza that assumes only # one output, we need to override that if we're going to
# # one output, we need to override that if we're going to # convert to multi-output
# # convert to multi-output # outputs = ["bin" "man" "out"];
# # outputs = ["bin" "man" "out"]; makeFlags =
# makeFlags = builtins.filter (x: (builtins.match "(PLAT|MYLIBS).*" x) == null)
# builtins.filter (x: (builtins.match "(PLAT|MYLIBS).*" x) == null) o.makeFlags;
# o.makeFlags; });
# });
s6 = prev.s6.overrideAttrs(o: s6 = prev.s6.overrideAttrs(o:
let let
@ -42,7 +43,6 @@ let
(if o ? patches then o.patches else []) ++ (if o ? patches then o.patches else []) ++
(if patch_needed then [ patch ] else []); (if patch_needed then [ patch ] else []);
}); });
lua = let s = lua_no_readline.override { self = s; }; in s;
in in
extraPkgs // { extraPkgs // {
# liminix library functions # liminix library functions
@ -130,9 +130,18 @@ extraPkgs // {
"CONFIG_LIBNL32=y" "CONFIG_LIBNL32=y"
"CONFIG_PKCS12=y" "CONFIG_PKCS12=y"
"CONFIG_RSN_PREAUTH=y" "CONFIG_RSN_PREAUTH=y"
"CONFIG_UBUS=y"
"CONFIG_TLS=internal" "CONFIG_TLS=internal"
]; ];
h = prev.hostapd.overrideAttrs(o: { h = prev.hostapd.overrideAttrs(o: {
buildInputs = o.buildInputs ++ [ final.libubox final.ubus ];
src = final.fetchFromGitea {
domain = "git.dgnum.eu";
owner = "DGNum";
repo = "hostapd";
rev = "hostap-liminix-integration";
hash = "sha256-5Xi90keCHxvuKR5Q7STuZDzuM9h9ac6aWoXVQYvqkQI=";
};
extraConfig = ""; extraConfig = "";
configurePhase = '' configurePhase = ''
cat > hostapd/defconfig <<EOF cat > hostapd/defconfig <<EOF
@ -143,6 +152,54 @@ extraPkgs // {
}); });
in h.override { openssl = null; sqlite = null; }; in h.override { openssl = null; sqlite = null; };
hostapd-radius =
let
config = [
"CONFIG_DRIVER_NL80211=y"
"CONFIG_DRIVER_WIRED=y"
"CONFIG_EAP=y"
"CONFIG_EAP_PEAP=y"
"CONFIG_RADIUS_SERVER=y"
"CONFIG_FULL_DYNAMIC_VLAN=y"
"CONFIG_IAPP=y"
"CONFIG_IEEE80211AC=y"
"CONFIG_IEEE80211AX=y"
"CONFIG_IEEE80211N=y"
"CONFIG_IEEE80211W=y"
"CONFIG_INTERNAL_LIBTOMMATH=y"
"CONFIG_INTERNAL_LIBTOMMATH_FAST=y"
"CONFIG_IPV6=y"
"CONFIG_LIBNL32=y"
"CONFIG_PKCS12=y"
"CONFIG_RSN_PREAUTH=y"
"CONFIG_UBUS=y"
"CONFIG_TLS=internal"
];
h = prev.hostapd.overrideAttrs(o: {
buildInputs = o.buildInputs ++ [ final.libubox final.ubus ];
src = final.fetchFromGitea {
domain = "git.dgnum.eu";
owner = "DGNum";
repo = "hostapd";
rev = "hostap-liminix-integration";
hash = "sha256-5Xi90keCHxvuKR5Q7STuZDzuM9h9ac6aWoXVQYvqkQI=";
};
extraConfig = "";
configurePhase = ''
cat > hostapd/defconfig <<EOF
${builtins.concatStringsSep "\n" config}
EOF
${o.configurePhase}
'';
});
in h.override { openssl = null; sqlite = null; };
wpa_supplicant = prev.wpa_supplicant.override {
dbusSupport = false;
withPcsclite = false;
wpa_supplicant_gui = null;
};
kexec-tools-static = prev.kexec-tools.overrideAttrs(o: { kexec-tools-static = prev.kexec-tools.overrideAttrs(o: {
# For kexecboot we copy kexec into a ramdisk on the system being # For kexecboot we copy kexec into a ramdisk on the system being
# upgraded from. This is more likely to work if kexec is # upgraded from. This is more likely to work if kexec is
@ -161,13 +218,17 @@ extraPkgs // {
]; ];
}); });
luaFull = prev.lua; lua = crossOnly prev.lua5_3 (_: luaHost);
inherit lua;
mtdutils = prev.mtdutils.overrideAttrs(o: { mtdutils = prev.mtdutils.overrideAttrs(o: {
patches = (if o ? patches then o.patches else []) ++ [ patches = (if o ? patches then o.patches else []) ++ [
./pkgs/mtdutils/0001-mkfs.jffs2-add-graft-option.patch ./pkgs/mtdutils/0001-mkfs.jffs2-add-graft-option.patch
]; ];
postInstall = ''
# Testing programs which we don't need. We save a lot of space!
rm -rf $out/libexec
'';
}); });
nftables = prev.nftables.overrideAttrs(o: { nftables = prev.nftables.overrideAttrs(o: {

View file

@ -1,5 +1,5 @@
default: fs.lua init.lua nl.lua svc.lua net/constants.lua default: wlan.lua fs.lua init.lua nl.lua svc.lua net/constants.lua
test: test:
ln -s . anoia ln -s . anoia

View file

@ -4,6 +4,7 @@
, linotify , linotify
, lua , lua
, lualinux , lualinux
, iwinfo
, cpio , cpio
}: }:
let pname = "anoia"; let pname = "anoia";
@ -12,7 +13,7 @@ in stdenv.mkDerivation {
version = "0.1"; version = "0.1";
src = ./.; src = ./.;
nativeBuildInputs = [ fennel cpio ]; nativeBuildInputs = [ fennel cpio ];
buildInputs = with lua.pkgs; [ linotify lualinux ]; buildInputs = with lua.pkgs; [ linotify lualinux iwinfo ];
outputs = [ "out" "dev" ]; outputs = [ "out" "dev" ];
doCheck = true; doCheck = true;

8
pkgs/anoia/wlan.fnl Normal file
View file

@ -0,0 +1,8 @@
(local { : nl80211 } (require :iwinfo))
(fn is-bridgeable [ifname]
(let [mode (nl80211.mode ifname)]
(or (= mode "Master") (= mode "Master (VLAN)"))
))
{ : is-bridgeable }

View file

@ -13,6 +13,7 @@ in {
liminix = { liminix = {
builders = { builders = {
squashfs = callPackage ./liminix-tools/builders/squashfs.nix {}; squashfs = callPackage ./liminix-tools/builders/squashfs.nix {};
jffs2 = callPackage ./liminix-tools/builders/jffs2.nix {};
dtb = callPackage ./kernel/dtb.nix {}; dtb = callPackage ./kernel/dtb.nix {};
uimage = callPackage ./kernel/uimage.nix {}; uimage = callPackage ./kernel/uimage.nix {};
kernel = callPackage ./kernel {}; kernel = callPackage ./kernel {};
@ -98,6 +99,7 @@ in {
run-liminix-vm = callPackage ./run-liminix-vm {}; run-liminix-vm = callPackage ./run-liminix-vm {};
s6-init-bin = callPackage ./s6-init-bin {}; s6-init-bin = callPackage ./s6-init-bin {};
s6-rc-database = callPackage ./s6-rc-database {}; s6-rc-database = callPackage ./s6-rc-database {};
seedrng = callPackage ./seedrng {};
# schnapps is written by Turris and provides a high-level interface # schnapps is written by Turris and provides a high-level interface
# to btrfs snapshots. It may be useful on the Turris Omnia to # to btrfs snapshots. It may be useful on the Turris Omnia to
@ -109,6 +111,9 @@ in {
swconfig = callPackage ./swconfig {}; swconfig = callPackage ./swconfig {};
systemconfig = callPackage ./systemconfig {}; systemconfig = callPackage ./systemconfig {};
tufted = callPackage ./tufted {}; tufted = callPackage ./tufted {};
libubox = callPackage ./libubox {};
ubus = callPackage ./ubus {};
iwinfo = callPackage ./iwinfo {};
uevent-watch = callPackage ./uevent-watch {}; uevent-watch = callPackage ./uevent-watch {};
usb-modeswitch = callPackage ./usb-modeswitch {}; usb-modeswitch = callPackage ./usb-modeswitch {};
writeAshScript = callPackage ./write-ash-script {}; writeAshScript = callPackage ./write-ash-script {};

View file

@ -10,6 +10,7 @@
, linotify , linotify
, anoia , anoia
, netlink-lua , netlink-lua
, iwinfo
, fennel , fennel
}: }:
let packages = [ let packages = [
@ -18,7 +19,7 @@ let packages = [
fennel fennel
lualinux lualinux
netlink-lua netlink-lua
lua.pkgs.readline iwinfo
]; ];
join = ps: builtins.concatStringsSep ";" ps; join = ps: builtins.concatStringsSep ";" ps;
luapath = join (builtins.map (f: luapath = join (builtins.map (f:

View file

@ -1,11 +1,14 @@
{ {
lua lua
, netlink-lua , netlink-lua
, lualinux
, iwinfo
, writeFennelScript , writeFennelScript
, runCommand , runCommand
, anoia , anoia
}: }:
runCommand "ifwait" {} '' runCommand "ifwait" {} ''
mkdir -p $out/bin mkdir -p $out/bin
cp -p ${writeFennelScript "ifwait" [anoia netlink-lua] ./ifwait.fnl} $out/bin/ifwait cp -p ${writeFennelScript "ifwait" [ anoia netlink-lua ] ./ifwait.fnl} $out/bin/ifwait
cp -p ${writeFennelScript "ifbridgeable" [ anoia lualinux iwinfo ] ./ifbridgeable.fnl} $out/bin/ifbridgeable
'' ''

View file

@ -0,0 +1,30 @@
(local wlan (require :anoia.wlan))
(local { : assoc } (require :anoia))
(local { : msleep } (require :lualinux))
(fn parse-args [args]
(match args
["-v" & rest] (assoc (parse-args rest) :verbose true)
[linkname] {:link linkname}
_ nil))
(fn run [args poll-fn]
(let [parameters
(assert (parse-args args)
(.. "Usage: ifbridgeable [-v] ifname"))]
(when parameters.verbose
(print (.. "ifbridgeable: waiting for "
parameters.link " to be bridgeable")))
(while (not (poll-fn parameters.link))
(when parameters.verbose
(print (.. "ifbridgeable: waiting for " parameters.link " to be bridgeable")))
(msleep 500)
)
)
)
(when (not (= (. arg 0) "test"))
(run arg wlan.is-bridgeable))
{ : run }

View file

@ -19,11 +19,11 @@
(match v (match v
;; - up: Reflects the administrative state of the interface (IFF_UP) ;; - up: Reflects the administrative state of the interface (IFF_UP)
;; - running: Reflects the operational state (IFF_RUNNING). ;; - running: Reflects the operational state (IFF_RUNNING).
{:event "newlink" :name params.link :up :yes :running :yes} {:event "newlink" :name params.link :up "yes" :running "yes"}
{:present true :up true :running true} {:present true :up true :running true}
{:event "newlink" :name params.link :up :yes} {:event "newlink" :name params.link :up "yes"}
{:present :true :up true} {:present true :up true}
{:event "newlink" :name params.link} {:event "newlink" :name params.link}
{:present true } {:present true }

59
pkgs/iwinfo/default.nix Normal file
View file

@ -0,0 +1,59 @@
{
lib,
stdenv,
fetchFromGitea,
ubus,
libubox,
lua5_3,
libnl-tiny,
backend ? "nl80211"
}:
let
lua = lua5_3;
in
stdenv.mkDerivation rec {
pname = "iwinfo";
version = "unstable-07-09-2024";
src = fetchFromGitea {
domain = "git.dgnum.eu";
owner = "DGNum";
repo = "iwinfo";
rev = "14685a26805155aa5c137993b9a4861a0bc585d5";
hash = "sha256-lg4sBoYcFFLhcUv+wKR6u1OCartjtnAoF9M5FdfO6JE=";
};
BACKENDS = backend;
buildInputs = [
ubus
libubox
lua
libnl-tiny
];
CFLAGS = "-I${libnl-tiny}/include/libnl-tiny -D_GNU_SOURCE";
installPhase = ''
runHook preInstall
install -Dm755 iwinfo $out/bin/iwinfo
install -Dm755 iwinfo.so $out/lib/lua/${lua.luaversion}/iwinfo.so
install -Dm755 libiwinfo.so $out/lib/libiwinfo.so
install -Dm755 libiwinfo.so.0 $out/lib/libiwinfo.so.0
mkdir -p $out/include
cp -r include/* $out/include
runHook postInstall
'';
meta = {
description = "Library to access wireless devices";
homepage = "https://github.com/openwrt/iwinfo";
license = lib.licenses.gpl2Only;
maintainers = with lib.maintainers; [ raitobezarius ];
mainProgram = "iwinfo";
platforms = lib.platforms.all;
};
}

View file

@ -23,7 +23,7 @@ stdenv.mkDerivation rec {
nativeBuildInputs = [buildPackages.stdenv.cc] ++ nativeBuildInputs = [buildPackages.stdenv.cc] ++
(with buildPackages.pkgs; [ (with buildPackages.pkgs; [
rsync bc bison flex pkg-config rsync bc bison flex pkg-config
openssl ncurses.all perl openssl ncurses.all perl zstd
]); ]);
CC = "${stdenv.cc.bintools.targetPrefix}gcc"; CC = "${stdenv.cc.bintools.targetPrefix}gcc";
HOSTCC = with buildPackages.pkgs; HOSTCC = with buildPackages.pkgs;

49
pkgs/libubox/default.nix Normal file
View file

@ -0,0 +1,49 @@
{
lib,
stdenv,
fetchFromGitea,
cmake,
lua,
json_c
}:
stdenv.mkDerivation rec {
pname = "libubox";
version = "unstable-2024-04-09";
src = fetchFromGitea {
domain = "git.dgnum.eu";
owner = "DGNum";
repo = "libubox";
rev = "1c4b2dc4c12848e1b70b11e1cb2139ca8f19c860";
hash = "sha256-aPhGJ7viXQmnoQRY8DuRvtwtxSy+S4qPj1fBsK066Yc=";
};
nativeBuildInputs = [
cmake
lua
];
buildInputs = [
lua
json_c
];
# Otherwise, CMake cannot find jsoncpp?
env.NIX_CFLAGS_COMPILE = toString [ "-I${json_c.dev}/include/json-c" "-D JSONC" "-D LUA_COMPAT_MODULE" ];
cmakeFlags = [
"-DBUILD_EXAMPLES=off"
# TODO: it explode at install phase.
"-DBUILD_LUA=on"
"-DLUAPATH=${placeholder "out"}/lib/lua/${lua.luaversion}/"
];
meta = {
description = "";
homepage = "https://git.openwrt.org/project/libubox.git";
maintainers = with lib.maintainers; [ raitobezarius ];
mainProgram = "libubox";
platforms = lib.platforms.all;
};
}

View file

@ -0,0 +1,17 @@
{
stdenv
, busybox
, buildPackages
, callPackage
, pseudofile
, runCommand
, writeText
} : { eraseBlockSize, bootableRootDirectory }:
let
endian = if stdenv.isBigEndian then "--big-endian" else "--little-endian";
in runCommand "frob-jffs2" {
depsBuildBuild = [ buildPackages.mtdutils ];
} ''
tree=${bootableRootDirectory}
(cd $tree && mkfs.jffs2 --compression-mode=size ${endian} -e ${toString eraseBlockSize} --enable-compressor=lzo --pad --root . --output $out --squash --faketime)
''

View file

@ -9,6 +9,7 @@
ip link set up dev ${ifname} ip link set up dev ${ifname}
(in_outputs ${name} (in_outputs ${name}
echo ${ifname} > ifname echo ${ifname} > ifname
cat /sys/class/net/${ifname}/address > ether
) )
''; '';
} }

View file

@ -39,6 +39,7 @@ let
, contents ? [] , contents ? []
, buildInputs ? [] , buildInputs ? []
, isTrigger ? false , isTrigger ? false
, passthru ? {}
} @ args: } @ args:
stdenvNoCC.mkDerivation { stdenvNoCC.mkDerivation {
# we use stdenvNoCC to avoid generating derivations with names # we use stdenvNoCC to avoid generating derivations with names
@ -50,6 +51,8 @@ let
dependencies = builtins.map (d: d.name) dependencies; dependencies = builtins.map (d: d.name) dependencies;
contents = builtins.map (d: d.name) contents; contents = builtins.map (d: d.name) contents;
builder = ./builder.sh; builder = ./builder.sh;
inherit passthru;
}; };
longrun = { longrun = {
@ -100,7 +103,18 @@ let
serviceType = "bundle"; serviceType = "bundle";
inherit contents dependencies; inherit contents dependencies;
}); });
structuredBundle = {
name
, contents ? {}
, dependencies ? []
, ...
} @ args: service (args // {
serviceType = "bundle";
contents = builtins.attrValues contents;
inherit dependencies;
passthru.components = contents;
});
target = bundle; target = bundle;
in { in {
inherit target bundle oneshot longrun output; inherit target bundle oneshot longrun output structuredBundle;
} }

View file

@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -Eeuo pipefail
ssh_command=${SSH_COMMAND-ssh} ssh_command=${SSH_COMMAND-ssh}
@ -13,19 +14,24 @@ case "$1" in
reboot="soft" reboot="soft"
shift shift
;; ;;
"--root")
root_prefix="$2"
shift
shift
;;
esac esac
target_host=$1 target_host=$1
shift shift
if [ -z "$target_host" ] ; then if [ -z "$target_host" ] ; then
echo Usage: liminix-rebuild \[--no-reboot\] target-host params echo Usage: liminix-rebuild \[--no-reboot\] \[--fast\] target-host params
exit 1 exit 1
fi fi
if toplevel=$(nix-build "$@" -A outputs.systemConfiguration --no-out-link); then if toplevel=$(nix-build "$@" -A outputs.systemConfiguration --no-out-link); then
echo systemConfiguration $toplevel echo systemConfiguration $toplevel
min-copy-closure $target_host $toplevel min-copy-closure --root "$root_prefix" $target_host $toplevel
$ssh_command $target_host $toplevel/bin/install $ssh_command $target_host $toplevel/bin/install
case "$reboot" in case "$reboot" in
reboot) reboot)

View file

@ -65,13 +65,18 @@ static char * eat_param(char *p, char *param_name, char **out)
return p; return p;
} }
#define SCAN_CMDLINE(cmdline, identifier, field) do { \
for (char* p = strdup(cmdline); *p; p++) { \
p = eat_param(p, identifier, &(opts->field)); \
} \
} while(0)
void parseopts(char * cmdline, struct root_opts *opts) { void parseopts(char * cmdline, struct root_opts *opts) {
for(char *p = cmdline; *p; p++) { SCAN_CMDLINE(cmdline, "root=", device);
p = eat_param(p, "root=", &(opts->device)); SCAN_CMDLINE(cmdline, "rootfstype=", fstype);
p = eat_param(p, "rootfstype=", &(opts->fstype)); SCAN_CMDLINE(cmdline, "rootflags=", mount_opts);
p = eat_param(p, "rootflags=", &(opts->mount_opts)); SCAN_CMDLINE(cmdline, "rootalt=", altdevice);
p = eat_param(p, "altroot=", &(opts->altdevice));
};
} }
#ifdef TESTS #ifdef TESTS
@ -85,6 +90,8 @@ void parseopts(char * cmdline, struct root_opts *opts) {
#define S(x) #x #define S(x) #x
#define expect_equal(actual, expected) \ #define expect_equal(actual, expected) \
if(!actual || strcmp(actual, expected)) die("%d: expected \"%s\", got \"%s\"", __LINE__, expected, actual); if(!actual || strcmp(actual, expected)) die("%d: expected \"%s\", got \"%s\"", __LINE__, expected, actual);
#define expect_null(actual) \
if (actual) die("%d: expected null, got \"%s\"", __LINE__, actual);
int main() int main()
@ -92,6 +99,7 @@ int main()
struct root_opts opts = { struct root_opts opts = {
.device = "/dev/hda1", .device = "/dev/hda1",
.fstype = "xiafs", .fstype = "xiafs",
.altdevice = NULL,
.mount_opts = NULL .mount_opts = NULL
}; };
char *buf; char *buf;
@ -103,14 +111,22 @@ int main()
expect_equal(opts.fstype, "ubifs"); expect_equal(opts.fstype, "ubifs");
expect_equal(opts.mount_opts, "subvol=1"); expect_equal(opts.mount_opts, "subvol=1");
// finds altroot= options // finds rootalt= options
buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs rootflags=subvol=1 fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0 altroot=/dev/mtdblock6 foo"); buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs rootflags=subvol=1 fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0 rootalt=/dev/mtdblock6 foo");
memset(&opts, '\0', sizeof opts); parseopts(buf, &opts); memset(&opts, '\0', sizeof opts); parseopts(buf, &opts);
expect_equal(opts.device, "/dev/mtdblock0"); expect_equal(opts.device, "/dev/mtdblock0");
expect_equal(opts.altdevice, "/dev/mtdblock6"); expect_equal(opts.altdevice, "/dev/mtdblock6");
expect_equal(opts.fstype, "ubifs"); expect_equal(opts.fstype, "ubifs");
expect_equal(opts.mount_opts, "subvol=1"); expect_equal(opts.mount_opts, "subvol=1");
// Ensure that `altdevice` is NULL.
buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 fw_devlink=off rootfstype=ubifs mtdparts=phram0:19791872(rootfs) phram.phram=phram0,33554432,19791872,65536 rootfstype=jffs2 root=/dev/mtdblock0");
memset(&opts, '\0', sizeof opts); parseopts(buf, &opts);
expect_equal(opts.device, "/dev/mtdblock0");
expect_null(opts.altdevice);
expect_equal(opts.fstype, "jffs2");
expect_null(opts.mount_opts);
// in case of duplicates, chooses the latter // in case of duplicates, chooses the latter
// also: works if the option is end of string // also: works if the option is end of string
buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0"); buf = strdup("liminix console=ttyS0,115200 panic=10 oops=panic init=/bin/init loglevel=8 root=/dev/ubi0_4 rootfstype=ubifs fw_devlink=off mtdparts=phram0:18M(rootfs) phram.phram=phram0,0x40400000,18874368,65536 root=/dev/mtdblock0");
@ -146,12 +162,12 @@ int main()
if(opts.altdevice) die("expected null altdevice, got \"%s\"", opts.altdevice); if(opts.altdevice) die("expected null altdevice, got \"%s\"", opts.altdevice);
// provides empty strings for empty options // provides empty strings for empty options
buf = strdup("liminix rootfstype= fw_devlink=off root= altroot= /dev/hda1"); buf = strdup("liminix rootfstype= fw_devlink=off root= rootalt= /dev/hda1");
memset(&opts, '\0', sizeof opts); parseopts(buf, &opts); memset(&opts, '\0', sizeof opts); parseopts(buf, &opts);
if(strlen(opts.fstype)) die("expected empty rootfstype, got \"%s\"", opts.fstype); if(strlen(opts.fstype)) die("expected empty rootfstype, got \"%s\"", opts.fstype);
if(strlen(opts.device)) die("expected empty root, got \"%s\"", opts.device); if(strlen(opts.device)) die("expected empty root, got \"%s\"", opts.device);
if(strlen(opts.altdevice)) die("expected empty altroot, got \"%s\"", opts.altdevice); if(strlen(opts.altdevice)) die("expected empty rootalt, got \"%s\"", opts.altdevice);
expect_equal("01", pr_u32(1)); expect_equal("01", pr_u32(1));
expect_equal("ab", pr_u32(0xab)); expect_equal("ab", pr_u32(0xab));

View file

@ -4,9 +4,13 @@
#include <fcntl.h> #include <fcntl.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/stat.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <errno.h> #include <errno.h>
#include <stdio.h>
#include <dirent.h>
#include <asm/setup.h> /* for COMMAND_LINE_SIZE */ #include <asm/setup.h> /* for COMMAND_LINE_SIZE */
@ -44,6 +48,25 @@ static int fork_exec(char * command, char *args[])
return execve(command, args, NULL); return execve(command, args, NULL);
} }
static void debug_listdir(const char * path)
{
DIR *mydir;
struct dirent *myfile;
struct stat mystat;
char buf[512];
mydir = opendir(path);
while((myfile = readdir(mydir)) != NULL)
{
sprintf(buf, "%s/%s", path, myfile->d_name);
stat(buf, &mystat);
printf("%llu", mystat.st_size);
printf(" %s\n", myfile->d_name);
}
closedir(mydir);
}
char banner[] = "Running pre-init...\n"; char banner[] = "Running pre-init...\n";
char buf[COMMAND_LINE_SIZE]; char buf[COMMAND_LINE_SIZE];
@ -98,6 +121,7 @@ int main(int argc, char *argv[], char *envp[])
AVER(mount(opts.device, "/target/persist", opts.fstype, 0, opts.mount_opts)); AVER(mount(opts.device, "/target/persist", opts.fstype, 0, opts.mount_opts));
} else { } else {
if(mount(opts.device, "/target/persist", opts.fstype, 0, opts.mount_opts) < 0) { if(mount(opts.device, "/target/persist", opts.fstype, 0, opts.mount_opts) < 0) {
ERR("failed to mount primary device, mount the alternative device\n");
AVER(mount(opts.altdevice, "/target/persist", opts.fstype, 0, opts.mount_opts)); AVER(mount(opts.altdevice, "/target/persist", opts.fstype, 0, opts.mount_opts));
} }
} }
@ -107,7 +131,12 @@ int main(int argc, char *argv[], char *envp[])
"bind", MS_BIND, NULL)); "bind", MS_BIND, NULL));
char *exec_args[] = { "activate", "/target", NULL }; char *exec_args[] = { "activate", "/target", NULL };
AVER(fork_exec("/target/persist/activate", exec_args)); if (fork_exec("/target/persist/activate", exec_args) < 0) {
ERR("failed to activate the system\n");
pr_u32(errno); ERR ( " - "); ERR(strerror(errno)); ERR("\n");
goto failsafe;
}
AVER(chdir("/target")); AVER(chdir("/target"));
AVER(mount("/target", "/", "bind", MS_BIND | MS_REC, NULL)); AVER(mount("/target", "/", "bind", MS_BIND | MS_REC, NULL));
@ -118,5 +147,23 @@ int main(int argc, char *argv[], char *envp[])
AVER(execve("/persist/init", argv, envp)); AVER(execve("/persist/init", argv, envp));
} }
failsafe:
debug_listdir("/");
debug_listdir("/target");
ERR("failed to mount the rootfs\n");
ERR("final stand using the failsafe initialization method\n");
ERR("the boot process is manual from now on\n");
argv[0] = "init";
argv[1] = NULL;
// Attempt to unmount the /target mount-bind.
AVER(umount("/target"));
AVER(execve("/failsafe-init", argv, envp));
debug_listdir("/");
debug_listdir("/target");
die(); die();
} }

View file

@ -2,14 +2,15 @@
qemuLim qemuLim
, socat , socat
, writeShellScript , writeShellScript
, writeFennel
, runCommand , runCommand
, fennel
, lib , lib
, lua , lua
, pkgsBuildBuild , pkgsBuildBuild
}: let }: let
run-liminix-vm = pkgsBuildBuild.writeFennel "run-liminix-vm" { writeFennel = pkgsBuildBuild.writeFennel.override { inherit lua; };
packages = [ qemuLim pkgsBuildBuild.lua.pkgs.luaposix pkgsBuildBuild.lua.pkgs.fennel ]; run-liminix-vm = writeFennel "run-liminix-vm" {
packages = [ qemuLim lua.pkgs.luaposix fennel ];
} ./run-liminix-vm.fnl; } ./run-liminix-vm.fnl;
connect = writeShellScript "connect-vm" '' connect = writeShellScript "connect-vm" ''
export PATH="${lib.makeBinPath [socat]}:$PATH" export PATH="${lib.makeBinPath [socat]}:$PATH"

712
pkgs/seedrng/COPYING Normal file
View file

@ -0,0 +1,712 @@
This project is licensed under any one of:
- GPL-2.0
- Apache-2.0
- MIT
- BSD-1-Clause
- CC0-1.0
Choose which one works best for you. They are reproduced below.
-------------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
-------------------------------------------------------------------------------
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-------------------------------------------------------------------------------
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-------------------------------------------------------------------------------
BSD 1-Clause License
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------------
Creative Commons Legal Code - CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.
-------------------------------------------------------------------------------

18
pkgs/seedrng/Makefile Normal file
View file

@ -0,0 +1,18 @@
PREFIX ?= /usr
DESTDIR ?=
SBINDIR ?= $(PREFIX)/sbin
LOCALSTATEDIR ?= /var/lib
CFLAGS ?= -O2 -pipe
CFLAGS += -Wall -Wextra -pedantic
CFLAGS += -DLOCALSTATEDIR="\"$(LOCALSTATEDIR)\""
seedrng: seedrng.c
install: seedrng
install -v -d "$(DESTDIR)$(SBINDIR)" && install -v -m 0755 seedrng "$(DESTDIR)$(SBINDIR)/seedrng"
clean:
rm -f seedrng
.PHONY: clean

72
pkgs/seedrng/README.md Normal file
View file

@ -0,0 +1,72 @@
## SeedRNG &mdash; `seedrng(8)`
##### by [Jason A. Donenfeld](mailto:Jason@zx2c4.com)
SeedRNG is a simple program made for seeding the Linux kernel random number
generator from seed files. The program takes no arguments, must be run as root,
and always attempts to do something useful.
This program is useful in light of the fact that the Linux kernel RNG cannot be
initialized from shell scripts, and new seeds cannot be safely generated from
boot time shell scripts either.
It should be run once at init time and once at shutdown time. It can be run at
other times without detriment as well. Whenever it is run, it writes existing
seed files into the RNG pool, and then creates a new seed file. If the RNG is
initialized at the time of creating a new seed file, then that new seed file is
marked as "creditable", which means it can be used to initialize the RNG.
Otherwise, it is marked as "non-creditable", in which case it is still used to
seed the RNG's pool, but will not initialize the RNG.
In order to ensure that entropy only ever stays the same or increases from one
seed file to the next, old seed values are hashed together with new seed values
when writing new seed files:
```
new_seed = new_seed[:-32] || HASH(fixed_prefix || real_time || boot_time || old_seed_len || old_seed || new_seed_len || new_seed)
```
The seed is stored in `LOCALSTATEDIR/seedrng/`, which can be adjusted at
compile time. If the `SEEDRNG_SKIP_CREDIT` environment variable is set to `1`,
`true`, `yes`, or `y`, then seeds never credit the RNG, even if the seed file
is creditable.
Being a single C file, `seedrng.c`, SeedRNG is meant to be copy and pasted
verbatim into various minimal init system projects and tweaked as needed.
**Please do not package this repo as a standalone program**: it is intended as
utility code meant to be imported into existing projects instead.
### Building &amp; Installing
```
$ make
$ sudo make install
```
In addition to the usual compiler environment variables (`CFLAGS`, etc), the
following environment variable is respected during compilation:
* `LOCALSTATEDIR` default: `/var/lib`
The following environment variables are respected during installation:
* `PREFIX` default: `/usr`
* `DESTDIR` default:
* `SBINDIR` default: `$(PREFIX)/sbin`
### Usage
```
# seedrng
```
However, this invocation should generally come from init and shutdown scripts.
### License
This program is licensed under any one of the the following licenses, so that it can be incorporated into other software projects as needed:
- GPL-2.0
- Apache-2.0
- MIT
- BSD-1-Clause
- CC0-1.0

13
pkgs/seedrng/default.nix Normal file
View file

@ -0,0 +1,13 @@
{ stdenv }:
stdenv.mkDerivation {
pname = "seedrng";
version = "2022.04";
src = ./.;
makeFlags = [
"PREFIX=${placeholder "out"}"
"SBINDIR=${placeholder "out"}/bin"
"LOCALSTATEDIR=/persist"
];
}

488
pkgs/seedrng/seedrng.c Normal file
View file

@ -0,0 +1,488 @@
// SPDX-License-Identifier: (GPL-2.0 OR Apache-2.0 OR MIT OR BSD-1-Clause OR CC0-1.0)
/*
* Copyright (C) 2022 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
*/
#include <linux/random.h>
#include <sys/random.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <endian.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef LOCALSTATEDIR
#define LOCALSTATEDIR "/var/lib"
#endif
#define SEED_DIR LOCALSTATEDIR "/seedrng"
#define CREDITABLE_SEED "seed.credit"
#define NON_CREDITABLE_SEED "seed.no-credit"
enum blake2s_lengths {
BLAKE2S_BLOCK_LEN = 64,
BLAKE2S_HASH_LEN = 32,
BLAKE2S_KEY_LEN = 32
};
enum seedrng_lengths {
MAX_SEED_LEN = 512,
MIN_SEED_LEN = BLAKE2S_HASH_LEN
};
struct blake2s_state {
uint32_t h[8];
uint32_t t[2];
uint32_t f[2];
uint8_t buf[BLAKE2S_BLOCK_LEN];
unsigned int buflen;
unsigned int outlen;
};
#define le32_to_cpup(a) le32toh(*(a))
#define cpu_to_le32(a) htole32(a)
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#endif
#ifndef DIV_ROUND_UP
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#endif
static inline void cpu_to_le32_array(uint32_t *buf, unsigned int words)
{
while (words--) {
*buf = cpu_to_le32(*buf);
++buf;
}
}
static inline void le32_to_cpu_array(uint32_t *buf, unsigned int words)
{
while (words--) {
*buf = le32_to_cpup(buf);
++buf;
}
}
static inline uint32_t ror32(uint32_t word, unsigned int shift)
{
return (word >> (shift & 31)) | (word << ((-shift) & 31));
}
static const uint32_t blake2s_iv[8] = {
0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
};
static const uint8_t blake2s_sigma[10][16] = {
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 },
};
static void blake2s_set_lastblock(struct blake2s_state *state)
{
state->f[0] = -1;
}
static void blake2s_increment_counter(struct blake2s_state *state, const uint32_t inc)
{
state->t[0] += inc;
state->t[1] += (state->t[0] < inc);
}
static void blake2s_init_param(struct blake2s_state *state, const uint32_t param)
{
int i;
memset(state, 0, sizeof(*state));
for (i = 0; i < 8; ++i)
state->h[i] = blake2s_iv[i];
state->h[0] ^= param;
}
static void blake2s_init(struct blake2s_state *state, const size_t outlen)
{
blake2s_init_param(state, 0x01010000 | outlen);
state->outlen = outlen;
}
static void blake2s_compress(struct blake2s_state *state, const uint8_t *block, size_t nblocks, const uint32_t inc)
{
uint32_t m[16];
uint32_t v[16];
int i;
while (nblocks > 0) {
blake2s_increment_counter(state, inc);
memcpy(m, block, BLAKE2S_BLOCK_LEN);
le32_to_cpu_array(m, ARRAY_SIZE(m));
memcpy(v, state->h, 32);
v[ 8] = blake2s_iv[0];
v[ 9] = blake2s_iv[1];
v[10] = blake2s_iv[2];
v[11] = blake2s_iv[3];
v[12] = blake2s_iv[4] ^ state->t[0];
v[13] = blake2s_iv[5] ^ state->t[1];
v[14] = blake2s_iv[6] ^ state->f[0];
v[15] = blake2s_iv[7] ^ state->f[1];
#define G(r, i, a, b, c, d) do { \
a += b + m[blake2s_sigma[r][2 * i + 0]]; \
d = ror32(d ^ a, 16); \
c += d; \
b = ror32(b ^ c, 12); \
a += b + m[blake2s_sigma[r][2 * i + 1]]; \
d = ror32(d ^ a, 8); \
c += d; \
b = ror32(b ^ c, 7); \
} while (0)
#define ROUND(r) do { \
G(r, 0, v[0], v[ 4], v[ 8], v[12]); \
G(r, 1, v[1], v[ 5], v[ 9], v[13]); \
G(r, 2, v[2], v[ 6], v[10], v[14]); \
G(r, 3, v[3], v[ 7], v[11], v[15]); \
G(r, 4, v[0], v[ 5], v[10], v[15]); \
G(r, 5, v[1], v[ 6], v[11], v[12]); \
G(r, 6, v[2], v[ 7], v[ 8], v[13]); \
G(r, 7, v[3], v[ 4], v[ 9], v[14]); \
} while (0)
ROUND(0);
ROUND(1);
ROUND(2);
ROUND(3);
ROUND(4);
ROUND(5);
ROUND(6);
ROUND(7);
ROUND(8);
ROUND(9);
#undef G
#undef ROUND
for (i = 0; i < 8; ++i)
state->h[i] ^= v[i] ^ v[i + 8];
block += BLAKE2S_BLOCK_LEN;
--nblocks;
}
}
static void blake2s_update(struct blake2s_state *state, const void *inp, size_t inlen)
{
const size_t fill = BLAKE2S_BLOCK_LEN - state->buflen;
const uint8_t *in = inp;
if (!inlen)
return;
if (inlen > fill) {
memcpy(state->buf + state->buflen, in, fill);
blake2s_compress(state, state->buf, 1, BLAKE2S_BLOCK_LEN);
state->buflen = 0;
in += fill;
inlen -= fill;
}
if (inlen > BLAKE2S_BLOCK_LEN) {
const size_t nblocks = DIV_ROUND_UP(inlen, BLAKE2S_BLOCK_LEN);
blake2s_compress(state, in, nblocks - 1, BLAKE2S_BLOCK_LEN);
in += BLAKE2S_BLOCK_LEN * (nblocks - 1);
inlen -= BLAKE2S_BLOCK_LEN * (nblocks - 1);
}
memcpy(state->buf + state->buflen, in, inlen);
state->buflen += inlen;
}
static void blake2s_final(struct blake2s_state *state, uint8_t *out)
{
blake2s_set_lastblock(state);
memset(state->buf + state->buflen, 0, BLAKE2S_BLOCK_LEN - state->buflen);
blake2s_compress(state, state->buf, 1, state->buflen);
cpu_to_le32_array(state->h, ARRAY_SIZE(state->h));
memcpy(out, state->h, state->outlen);
}
static ssize_t getrandom_full(void *buf, size_t count, unsigned int flags)
{
ssize_t ret, total = 0;
uint8_t *p = buf;
do {
ret = getrandom(p, count, flags);
if (ret < 0 && errno == EINTR)
continue;
else if (ret < 0)
return ret;
total += ret;
p += ret;
count -= ret;
} while (count);
return total;
}
static ssize_t read_full(int fd, void *buf, size_t count)
{
ssize_t ret, total = 0;
uint8_t *p = buf;
do {
ret = read(fd, p, count);
if (ret < 0 && errno == EINTR)
continue;
else if (ret < 0)
return ret;
else if (ret == 0)
break;
total += ret;
p += ret;
count -= ret;
} while (count);
return total;
}
static ssize_t write_full(int fd, const void *buf, size_t count)
{
ssize_t ret, total = 0;
const uint8_t *p = buf;
do {
ret = write(fd, p, count);
if (ret < 0 && errno == EINTR)
continue;
else if (ret < 0)
return ret;
total += ret;
p += ret;
count -= ret;
} while (count);
return total;
}
static size_t determine_optimal_seed_len(void)
{
size_t ret = 0;
char poolsize_str[11] = { 0 };
int fd = open("/proc/sys/kernel/random/poolsize", O_RDONLY);
if (fd < 0 || read_full(fd, poolsize_str, sizeof(poolsize_str) - 1) < 0) {
perror("Unable to determine pool size, falling back to 256 bits");
ret = MIN_SEED_LEN;
} else
ret = DIV_ROUND_UP(strtoul(poolsize_str, NULL, 10), 8);
if (fd >= 0)
close(fd);
if (ret < MIN_SEED_LEN)
ret = MIN_SEED_LEN;
else if (ret > MAX_SEED_LEN)
ret = MAX_SEED_LEN;
return ret;
}
static int read_new_seed(uint8_t *seed, size_t len, bool *is_creditable)
{
ssize_t ret;
int urandom_fd;
*is_creditable = false;
ret = getrandom_full(seed, len, GRND_NONBLOCK);
if (ret == (ssize_t)len) {
*is_creditable = true;
return 0;
} else if (ret < 0 && errno == ENOSYS) {
struct pollfd random_fd = {
.fd = open("/dev/random", O_RDONLY),
.events = POLLIN
};
if (random_fd.fd < 0)
return -errno;
*is_creditable = poll(&random_fd, 1, 0) == 1;
close(random_fd.fd);
} else if (getrandom_full(seed, len, GRND_INSECURE) == (ssize_t)len)
return 0;
urandom_fd = open("/dev/urandom", O_RDONLY);
if (urandom_fd < 0)
return -1;
ret = read_full(urandom_fd, seed, len);
if (ret == (ssize_t)len)
ret = 0;
else
ret = -errno ? -errno : -EIO;
close(urandom_fd);
errno = -ret;
return ret ? -1 : 0;
}
static int seed_rng(uint8_t *seed, size_t len, bool credit)
{
struct {
int entropy_count;
int buf_size;
uint8_t buffer[MAX_SEED_LEN];
} req = {
.entropy_count = credit ? len * 8 : 0,
.buf_size = len
};
int random_fd, ret;
if (len > sizeof(req.buffer)) {
errno = EFBIG;
return -1;
}
memcpy(req.buffer, seed, len);
random_fd = open("/dev/urandom", O_RDONLY);
if (random_fd < 0)
return -1;
ret = ioctl(random_fd, RNDADDENTROPY, &req);
if (ret)
ret = -errno ? -errno : -EIO;
close(random_fd);
errno = -ret;
return ret ? -1 : 0;
}
static int seed_from_file_if_exists(const char *filename, int dfd, bool credit, struct blake2s_state *hash)
{
uint8_t seed[MAX_SEED_LEN];
ssize_t seed_len;
int fd = -1, ret = 0;
fd = openat(dfd, filename, O_RDONLY);
if (fd < 0 && errno == ENOENT)
return 0;
else if (fd < 0) {
ret = -errno;
perror("Unable to open seed file");
goto out;
}
seed_len = read_full(fd, seed, sizeof(seed));
if (seed_len < 0) {
ret = -errno;
perror("Unable to read seed file");
goto out;
}
if ((unlinkat(dfd, filename, 0) < 0 || fsync(dfd) < 0) && seed_len) {
ret = -errno;
perror("Unable to remove seed after reading, so not seeding");
goto out;
}
if (!seed_len)
goto out;
blake2s_update(hash, &seed_len, sizeof(seed_len));
blake2s_update(hash, seed, seed_len);
printf("Seeding %zd bits %s crediting\n", seed_len * 8, credit ? "and" : "without");
if (seed_rng(seed, seed_len, credit) < 0) {
ret = -errno;
perror("Unable to seed");
}
out:
if (fd >= 0)
close(fd);
errno = -ret;
return ret ? -1 : 0;
}
static bool skip_credit(void)
{
const char *skip = getenv("SEEDRNG_SKIP_CREDIT");
return skip && (!strcmp(skip, "1") || !strcasecmp(skip, "true") ||
!strcasecmp(skip, "yes") || !strcasecmp(skip, "y"));
}
int main(int argc __attribute__((unused)), char *argv[] __attribute__((unused)))
{
static const char seedrng_prefix[] = "SeedRNG v1 Old+New Prefix";
static const char seedrng_failure[] = "SeedRNG v1 No New Seed Failure";
int fd = -1, dfd = -1, program_ret = 0;
uint8_t new_seed[MAX_SEED_LEN];
size_t new_seed_len;
bool new_seed_creditable;
struct timespec realtime = { 0 }, boottime = { 0 };
struct blake2s_state hash;
umask(0077);
if (getuid()) {
errno = EACCES;
perror("This program requires root");
return 1;
}
blake2s_init(&hash, BLAKE2S_HASH_LEN);
blake2s_update(&hash, seedrng_prefix, strlen(seedrng_prefix));
clock_gettime(CLOCK_REALTIME, &realtime);
clock_gettime(CLOCK_BOOTTIME, &boottime);
blake2s_update(&hash, &realtime, sizeof(realtime));
blake2s_update(&hash, &boottime, sizeof(boottime));
if (mkdir(SEED_DIR, 0700) < 0 && errno != EEXIST) {
perror("Unable to create seed directory");
return 1;
}
dfd = open(SEED_DIR, O_DIRECTORY | O_RDONLY);
if (dfd < 0 || flock(dfd, LOCK_EX) < 0) {
perror("Unable to lock seed directory");
program_ret = 1;
goto out;
}
if (seed_from_file_if_exists(NON_CREDITABLE_SEED, dfd, false, &hash) < 0)
program_ret |= 1 << 1;
if (seed_from_file_if_exists(CREDITABLE_SEED, dfd, !skip_credit(), &hash) < 0)
program_ret |= 1 << 2;
new_seed_len = determine_optimal_seed_len();
if (read_new_seed(new_seed, new_seed_len, &new_seed_creditable) < 0) {
perror("Unable to read new seed");
new_seed_len = BLAKE2S_HASH_LEN;
strncpy((char *)new_seed, seedrng_failure, new_seed_len);
program_ret |= 1 << 3;
}
blake2s_update(&hash, &new_seed_len, sizeof(new_seed_len));
blake2s_update(&hash, new_seed, new_seed_len);
blake2s_final(&hash, new_seed + new_seed_len - BLAKE2S_HASH_LEN);
printf("Saving %zu bits of %s seed for next boot\n", new_seed_len * 8, new_seed_creditable ? "creditable" : "non-creditable");
fd = openat(dfd, NON_CREDITABLE_SEED, O_WRONLY | O_CREAT | O_TRUNC, 0400);
if (fd < 0) {
perror("Unable to open seed file for writing");
program_ret |= 1 << 4;
goto out;
}
if (write_full(fd, new_seed, new_seed_len) != (ssize_t)new_seed_len || fsync(fd) < 0) {
perror("Unable to write seed file");
program_ret |= 1 << 5;
goto out;
}
if (new_seed_creditable && renameat(dfd, NON_CREDITABLE_SEED, dfd, CREDITABLE_SEED) < 0) {
perror("Unable to make new seed creditable");
program_ret |= 1 << 6;
}
out:
if (fd >= 0)
close(fd);
if (dfd >= 0)
close(dfd);
return program_ret;
}

35
pkgs/ubus/default.nix Normal file
View file

@ -0,0 +1,35 @@
{ stdenv, fetchFromGitea, lib, cmake, libubox, json_c, lua, defaultSocketLocation ? "/run/ubus/ubus.sock" }:
stdenv.mkDerivation {
pname = "ubus";
version = "unstable-04-09-2024";
src = fetchFromGitea {
domain = "git.dgnum.eu";
owner = "DGNum";
repo = "ubus";
rev = "ebb1dc92e4985538a8e18b7e926264118138f281";
hash = "sha256-fo4zleC9R6uzlcOJ/jQ0t0nSBHUAq/uqPVd9xJdkAM0=";
};
# We don't use /var/run/ in Liminix by default.
postPatch = ''
substituteInPlace CMakeLists.txt \
--replace-fail "/var/run/ubus/ubus.sock" "${defaultSocketLocation}"
'';
nativeBuildInputs = [
cmake
];
buildInputs = [
lua
libubox
json_c
];
cmakeFlags = [
"-DBUILD_LUA=on"
"-DLUAPATH=${placeholder "out"}/lib/lua/${lua.luaversion}"
"-DBUILD_EXAMPLES=off"
];
}

View file

@ -27,7 +27,7 @@ name :
echo "#!${lua}/bin/lua ${luaFlags}" echo "#!${lua}/bin/lua ${luaFlags}"
echo "package.path = ${lib.strings.escapeShellArg (builtins.concatStringsSep "" luapath)} .. package.path" echo "package.path = ${lib.strings.escapeShellArg (builtins.concatStringsSep "" luapath)} .. package.path"
echo "package.cpath = ${lib.strings.escapeShellArg (builtins.concatStringsSep "" luacpath)} .. package.cpath" echo "package.cpath = ${lib.strings.escapeShellArg (builtins.concatStringsSep "" luacpath)} .. package.cpath"
echo "local ok, stdlib = pcall(require,'posix.stdlib'); if ok then stdlib.setenv('PATH',${lib.escapeShellArg (lib.makeBinPath packages)} .. \":\" .. os.getenv('PATH')) end" echo "local ok, stdlib = pcall(require,'posix.stdlib'); if ok then stdlib.setenv('PATH', \"${lib.makeBinPath packages}\" .. \":\" .. os.getenv('PATH')) end"
fennel ${if correlate then "--correlate" else ""} --compile ${source} fennel ${if correlate then "--correlate" else ""} --compile ${source}
) > ${name}.lua ) > ${name}.lua
''; '';

View file

@ -6,7 +6,7 @@ let img = (import liminix {
device = import "${liminix}/devices/qemu/"; device = import "${liminix}/devices/qemu/";
liminix-config = ./configuration.nix; liminix-config = ./configuration.nix;
}).outputs.vmroot; }).outputs.vmroot;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; }; pkgs = import nixpkgs { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" { in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
expect expect

View file

@ -4,7 +4,7 @@
}: }:
let let
overlay = import "${liminix}/overlay.nix"; overlay = import "${liminix}/overlay.nix";
pkgs = import <nixpkgs> { overlays = [overlay]; }; pkgs = import nixpkgs { overlays = [overlay]; };
script = pkgs.writeFennelScript "foo" [] ./hello.fnl; script = pkgs.writeFennelScript "foo" [] ./hello.fnl;
inherit (pkgs.lua.pkgs) fifo; inherit (pkgs.lua.pkgs) fifo;
netlink = pkgs.netlink-lua; netlink = pkgs.netlink-lua;

View file

@ -6,7 +6,7 @@ let img = (import liminix {
device = import "${liminix}/devices/qemu/"; device = import "${liminix}/devices/qemu/";
liminix-config = ./configuration.nix; liminix-config = ./configuration.nix;
}).outputs.vmroot; }).outputs.vmroot;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; }; pkgs = import nixpkgs { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" { in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
expect expect

View file

@ -5,7 +5,6 @@ in {
imports = [ imports = [
../../vanilla-configuration.nix ../../vanilla-configuration.nix
../../modules/squashfs.nix ../../modules/squashfs.nix
../../modules/outputs/jffs2.nix
]; ];
config.rootfsType = "jffs2"; config.rootfsType = "jffs2";
config.filesystem = dir { config.filesystem = dir {

View file

@ -6,7 +6,7 @@ let img = (import liminix {
device = import "${liminix}/devices/qemu/"; device = import "${liminix}/devices/qemu/";
liminix-config = ./configuration.nix; liminix-config = ./configuration.nix;
}).outputs.vmroot; }).outputs.vmroot;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; }; pkgs = import nixpkgs { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" { in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
expect expect

View file

@ -13,7 +13,6 @@ let
in { in {
imports = [ imports = [
../../vanilla-configuration.nix ../../vanilla-configuration.nix
../../modules/outputs/jffs2.nix
]; ];
config = { config = {
services.sshd = longrun { services.sshd = longrun {

View file

@ -8,7 +8,7 @@ let lmx = (import liminix {
}); });
rogue = lmx.pkgs.rogue; rogue = lmx.pkgs.rogue;
img = lmx.outputs.vmroot; img = lmx.outputs.vmroot;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; }; pkgs = import nixpkgs { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" { in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
expect expect

View file

@ -6,7 +6,7 @@ let img = (import liminix {
device = import "${liminix}/devices/qemu"; device = import "${liminix}/devices/qemu";
liminix-config = ./configuration.nix; liminix-config = ./configuration.nix;
}).outputs.default; }).outputs.default;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; }; pkgs = import nixpkgs { overlays = [(import ../../overlay.nix)]; };
inherit (pkgs.pkgsBuildBuild) routeros; inherit (pkgs.pkgsBuildBuild) routeros;
in pkgs.runCommand "check" { in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [

View file

@ -1,5 +1,6 @@
{ {
liminix liminix,
...
}: }:
let check = deviceName : config : let check = deviceName : config :
let derivation = (import liminix { let derivation = (import liminix {

View file

@ -6,7 +6,7 @@ let img = (import liminix {
device = import "${liminix}/devices/qemu/"; device = import "${liminix}/devices/qemu/";
liminix-config = ./configuration.nix; liminix-config = ./configuration.nix;
}).outputs.vmroot; }).outputs.vmroot;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; }; pkgs = import nixpkgs { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" { in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
expect expect

View file

@ -7,6 +7,7 @@ in rec {
../../modules/wlan.nix ../../modules/wlan.nix
../../modules/hostapd ../../modules/hostapd
../../modules/network ../../modules/network
./wpa_supplicant.nix
]; ];
services.hostap = config.system.service.hostapd.build { services.hostap = config.system.service.hostapd.build {
@ -27,5 +28,21 @@ in rec {
}; };
}; };
defaultProfile.packages = with pkgs; [ tcpdump ] ; services.wpa_supplicant = config.system.service.wpa_supplicant.build {
interface = "wlan1";
driver = "nl80211";
config-file = pkgs.writeText "wpa_supplicant.conf" ''
country=us
update_config=1
ctrl_interface=/run/wpa_supplicant
network={
scan_ssid=1
ssid="liminix"
psk="colourless green ideas"
}
'';
};
defaultProfile.packages = with pkgs; [ tcpdump wpa_supplicant ];
} }

View file

@ -3,10 +3,11 @@
, nixpkgs , nixpkgs
}: }:
let img = (import liminix { let img = (import liminix {
device = import "${liminix}/devices/qemu-armv7l/"; inherit nixpkgs;
device = import "${liminix}/devices/qemu/";
liminix-config = ./configuration.nix; liminix-config = ./configuration.nix;
}).outputs.default; }).outputs.default;
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; }; pkgs = import nixpkgs { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" { in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
expect socat expect socat

View file

@ -14,10 +14,10 @@ expect {
} }
expect "#" expect "#"
while { $FINISHED < 10 } { while { $FINISHED < 10 } {
send "date && grep AP-ENABLED /run/uncaught-logs/* || echo \$NOT\r\n" send "date && grep CTRL-EVENT-CONNECTED /run/uncaught-logs/* || echo \$NOT\r\n"
expect { expect {
"wlan0: AP-ENABLED" { set FINISHED 999; set EXIT 0; } "wlan1: CTRL-EVENT-CONNECTED" { set FINISHED 999; set EXIT 0; }
"not_present" { send_user "waiting ...\n" ; sleep 5 } "not_present" { send_user "waiting ...\n" ; sleep 5 }
} }
set FINISHED [ expr $FINISHED + 1 ] set FINISHED [ expr $FINISHED + 1 ]

View file

@ -0,0 +1,21 @@
{
liminix,
wpa_supplicant,
lib,
}:
{
interface,
driver,
config-file,
}:
let
inherit (liminix.services) longrun;
inherit (lib.strings) escapeShellArg;
in
longrun {
name = "wpa_supplicant";
run =
''
${wpa_supplicant}/bin/wpa_supplicant -D${driver} -i${interface} -c ${config-file}
'';
}

View file

@ -0,0 +1,15 @@
{ config, lib, pkgs, ... }:
with lib; {
options.system.service.wpa_supplicant = mkOption { type = pkgs.liminix.lib.types.serviceDefn; };
config.system.service.wpa_supplicant = config.system.callService ./wpa_service.nix {
interface = mkOption {
type = types.str;
};
driver = mkOption {
type = types.str;
};
config-file = mkOption {
type = types.package;
};
};
}