Compare commits

..

62 commits
main ... main

Author SHA1 Message Date
6dd34b97a9 Merge pull request 'feat(ci): shell-customization' (#32) from lbailly/liminix:shell-customization into main
Reviewed-on: DGNum/liminix#32
2024-10-04 11:22:43 +02:00
7eff028b02
fix: hostname at early boot
All checks were successful
build liminix / build_zyxel-nwa50ax_mips (pull_request) Successful in 20s
build liminix / build_vm_qemu_mips (pull_request) Successful in 20s
build liminix / test_shell_customization (pull_request) Successful in 22s
build liminix / test_hostapd (pull_request) Successful in 22s
2024-10-04 11:21:46 +02:00
89d2d34ad7
feat(ci): prompt checking 2024-10-04 11:21:46 +02:00
eec7a6e985
fix PS1 2024-10-04 11:21:46 +02:00
a56936f1d3 Merge pull request 'feat: add environment variables and prompt customization in login shells' (#29) from shell-customization into main
Reviewed-on: DGNum/liminix#29
2024-10-04 11:12:05 +02:00
Raito Bezarius
562b050341 feat: add environment variables and prompt customization in login shells
This way, we can configure a bit our prompt.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-10-04 11:12:05 +02:00
dbe6b1b135 Merge pull request 'feat(ci): test with wpa_supplicant' (#24) from lbailly/liminix:CI into main
Reviewed-on: DGNum/liminix#24
Reviewed-by: Ryan Lahfa <ryan@dgnum.eu>
2024-10-04 11:11:47 +02:00
c5e48f5c9f
feat(ci-wlan): use wpa_supplicant 2024-09-30 13:16:57 +02:00
0dd72b31f4
feat(ci): use ci.nix 2024-09-30 13:16:57 +02:00
Raito Bezarius
013e4c396c feat: repair CI and cleanup cross-compilation mechanism
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-16 18:14:39 +02:00
Raito Bezarius
036f91d2f0 feat(ubus): support for Lua 5.3
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-08 01:08:59 +02:00
Raito Bezarius
54db2ad006 feat(libubox): support for Lua 5.3
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-08 01:08:54 +02:00
Raito Bezarius
8ac3e32b8b fix(modules/hostname): hash the hostname to avoid duplicate services in the db
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-08 00:31:15 +02:00
Raito Bezarius
6970d811e8 chore(min-copy-closure): improve liminix-rebuild for maintenance
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 23:59:29 +02:00
Raito Bezarius
b468275f53 fix(tftpboot): revert squashfs use and go back to JFFS2
squashfs doesn't copy all the files we need.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 23:48:13 +02:00
Raito Bezarius
3ed1564235 feat(recovery): bump to 20MB the TFTP phram size
Otherwise, this will fail to boot on a modern closure.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 23:39:22 +02:00
Raito Bezarius
9fcfae3eae feat(recovery): strengthen debugging capabilities of preinit
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 23:30:05 +02:00
Raito Bezarius
73ea02b982 feat(modules/nixpkgs): introduce source parameter
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 23:05:03 +02:00
Raito Bezarius
c942b2be09 feat(initramfs): enable zstd compression
Busybox is still 3MB.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 22:49:25 +02:00
Raito Bezarius
95850a44c2 feat(recovery): implement failsafe boot
for TFTP or anything, really.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 22:49:25 +02:00
Raito Bezarius
bc1f54e701 Merge branch 'bridgeability' into 'main' 2024-09-07 21:53:20 +02:00
Raito Bezarius
0ee2ce4183 fix(overlay): adopt upstream fix for Lua readline
9f58e7b926
("maybe fix nixpkgs-unstable lua")

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 21:52:48 +02:00
Raito Bezarius
94a5b19c77 fix(evalConfig): fix pkgs relation with nixpkgs
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 21:52:48 +02:00
Raito Bezarius
13069415fd feat(modules/hostapd): offer readiness oneshot
WLAN oper wait until the WLAN interface is ready.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 21:15:31 +02:00
Raito Bezarius
5590fea16e feat(ifwait): add ifbridgeable script
This enable to wait for bridgeability of a WLAN interface via
`iwinfo`.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 21:09:17 +02:00
Raito Bezarius
c39bfc2bb5 fix(write-fennel): escape binary path for PATH
otherwise, escapeShellArg might just do nothing!

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 21:03:57 +02:00
Raito Bezarius
0c363be423 fennelrepl: add iwinfo to it
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 20:03:02 +02:00
Raito Bezarius
26d2812aa4 fennerepl: remove readline
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 20:03:02 +02:00
Raito Bezarius
b57df3f288 iwinfo: use Lua 5.3 port
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 20:03:02 +02:00
Raito Bezarius
4cf10c2e75 iwinfo: init at unstable-07-09-2024
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 19:26:14 +02:00
Raito Bezarius
0d36000d9f overlay: update hostapd with readiness support
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 18:31:57 +02:00
ce17fea337 Merge pull request 'feat(hostapd): ubus support' (#16) from hostapd-ubus into main
Reviewed-on: DGNum/liminix#16
2024-09-07 17:53:21 +02:00
Raito Bezarius
1a770910a6 fix(ubus): rendez vous URL for the unix socket
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 17:48:06 +02:00
Raito Bezarius
5444059b63 feat(hostapd): enable ubus on RADIUS variant
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 17:18:59 +02:00
Raito Bezarius
ffc6492365 fix(ubus): set the socket path properly
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 17:15:31 +02:00
Raito Bezarius
71813a1f8f feat(hostapd): disable openssl to save space
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 17:03:50 +02:00
Raito Bezarius
032e57b34c feat(hostapd): ubus support
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-07 15:56:53 +02:00
Raito Bezarius
0fb671023c feat(services): introduce structured bundles
Structured bundles keep their original contents as a `passthru` field
named `components`.

This enable users to depend on a specific piece of the bundle instead of
the whole bundle.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-05 14:54:12 +02:00
Raito Bezarius
ebcdbf76bc fix(bridge): members are now granular services
They are still part of the bundle, but we can wait on each of them
independently now.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-05 14:54:12 +02:00
Raito Bezarius
78d0088b65 ubus: build with Lua
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-04 16:30:44 +02:00
Raito Bezarius
6d4237ff87 libubox: build with Lua
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-04 16:29:59 +02:00
Raito Bezarius
c24c659ee1 modules/ubus: init
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-04 16:08:56 +02:00
Raito Bezarius
4287a05182 ubus: init at unstable-04-09-2024
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-04 15:53:26 +02:00
Raito Bezarius
eb083bee20 libubox: init at unstable-04-09-2024
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-04 15:53:19 +02:00
Raito Bezarius
1bd9af1e9d fix(bridge): reorder initialization for bridge dependents
Consider the scenario where you run DHCPv4 on the primary bridge
interface.

You have no real interface to "wait upon", so it's OK. Nonetheless,
anything depending on successful completion of DHCPv4, e.g. adding a
default route, will block `s6-rc -v2 up change default`.

The way new interfaces are attached to the bridge is via `s6-rc -b -u
change $attach-oneshot-service`, this introduce in turn a deadlock.

At some point, DHCPv4 will timeout, unblocking the deadlock and
attaching the members to the primary bridge interface, making it ready
to send L2 broadcast packets for DHCP, unblocking DHCP in turn again.

This is not satisfying because we really want to have a no-hiccups
bring-up.

To fix this, we proceed to multiple changes:

- we remove `svc.ifwait.build` out of band `s6-rc -b -u $oneshot-attach`
  call, which is, by design, wrong here.
- users can now depend on the members service to know when a bridge is
  fully operational (we could make it more granular and let them depend
  on the LAN member joining rather than WLAN, etc.)
- users can also depend on the primary service being brought up rather
  than just being present, this is useful if you need to bring it up
  when it has AT LEAST one member to get link local address or MAC
  addresses (fixing DHCPv6 bring up as well because `ff02::1` is used
  there).

One thing is not addressed yet, if you are running a WLAN service using
RADIUS attached to the bridge, at bring up time, it will try to reach
out the external RADIUS server and *fail*.

To solve this, granular dependency on the DHCPv4 once LAN is joined.
Then the hostapd can wait on defaultroute4 completion so that
connectivity is available to reach RADIUS server.

It can join the bridge later on without any hiccup as well.

This is left as a TODO as hostapd can survive RADIUS authentication
failure and retry later.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 18:15:28 +02:00
Raito Bezarius
21ff11503e fix(bridge): pick up MAC from another interface
This avoids the OPERSTATE unknown when the bridge is brought up but the
members are not ready yet.

This will make OPERSTATE to down, enabling us to wait until we have
brought up completely all the members.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
f0b4e826cb fix(ifwait): match over strings and not symbols
Are they the same in Fennel?

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
ca9642a61e fix(ifwait): return :present if newlink is up/yes
Urgh…

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
1d0fc24111 fix(bridge/members): log attach/detach
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Daniel Barlow
36f3015acd dhcpc handle case when env vars are missing
the notify-script should continue and signal readiness even if one or
more of the outputs it writes are mssing in the environment
2024-09-01 17:48:54 +02:00
Raito Bezarius
664624a478 feat(jitterentropy): introduce a jitterentropy module
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
f34a63d1c8 feat(pki): init TLS PKI module
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
eaa8c089a8 feat(mtd-utils): save more space
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
3d528a71e9 feat(hostapd): make the package configurable to enable RADIUS
The default hostapd disable too many things, we need a bit more for
RADIUS.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
17355c3911 fix(preinit): scan multiple times the cmdline and rename altroot in rootalt
The way the parsing works is examining one character at a time.

First, if we had `rootfstype=... root=...`, the parsing would jump and
ignore `root=...`, which sucks.

To fix this, we scan multiple times a copy of the cmdline.

Now, we have a new problem: `root=... altroot=...` lead to opts.device
being equal to the altroot as we are looking one char at a time, so we
will arrive at a moment looking at `root=...` for `altroot=...`.

To avoid this, we rename `altroot` in `rootalt`, cheap, I know.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
c1e61d6af5 fix(zyxel/nwa50ax): ubi cannot run on phram
Discovered the hard way.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
85bfe94429 fix(zyxel/nwa50ax): ensure the DTB is in the FIT
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
ea740bbfaf fix(zyxel/nwa50ax): make altroot useful
Let's use `ubi1` if it exist, as it should be the second device
containing a rootfs.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
8f5ea94765 chore(zyxel/nwa50ax): write flash erase block size as kb size
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
86e81efbd6 tftp: introduce an alternative command line for TFTP
Normal command line and TFTP command line can be sometimes very
different.

e.g. We don't want to load UBI filesystems for a TFTP boot as it may
interfere with our root device loading.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
Raito Bezarius
2d6414ea41 tftp: introduce the FIT enclosing boot.scr
This simplify TFTP.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-01 17:48:54 +02:00
65de896019
feat(ci): build VM QEMU MIPS
All checks were successful
build liminix / build_vm_qemu_mips (pull_request) Successful in 1h24m6s
2024-08-27 11:05:43 +02:00
72 changed files with 1104 additions and 155 deletions

View file

@ -0,0 +1,50 @@
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
test_shell_customization:
runs-on: nix
steps:
- uses: actions/checkout@v3
- name: Build VM QEMU MIPS
run: |
# Enter the shell
nix-build ci.nix -A custom-shell

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

@ -4,11 +4,13 @@
{ lib, pkgs, config, ...}: { lib, pkgs, config, ...}:
let let
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ; inherit (lib) mkEnableOption mkOption types isDerivation hasAttr concatStringsSep mapAttrsToList;
inherit (pkgs.pseudofile) dir symlink; inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.networking) address interface; inherit (pkgs.liminix.networking) address interface;
inherit (pkgs.liminix.services) bundle; inherit (pkgs.liminix.services) bundle;
# TODO: escape shell argument.
exportVar = name: value: "export ${name}=\"${value}\"";
type_service = pkgs.liminix.lib.types.service; type_service = pkgs.liminix.lib.types.service;
in { in {
@ -22,6 +24,24 @@ in {
/run/current-system, we just add the paths in /etc/profile /run/current-system, we just add the paths in /etc/profile
''; '';
}; };
environmentVariables = mkOption {
type = types.attrsOf types.str;
description = ''
Attribute set of environment variables to make available
in a login shell.
The value is assumed to be escaped and the name to be valid.
'';
};
prompt = mkOption {
type = types.str;
default = "$(whoami)@$(hostname) # ";
description = ''
Prompt string (PS1) for the shell.
'';
};
}; };
services = mkOption { services = mkOption {
type = types.attrsOf type_service; type = types.attrsOf type_service;
@ -69,6 +89,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,8 +126,13 @@ 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 ];
# Set the useful PS1 prompt by default.
defaultProfile.environmentVariables.PS1 = lib.mkDefault config.defaultProfile.prompt;
boot.commandLine = [ boot.commandLine = [
"panic=10 oops=panic init=/bin/init loglevel=8" "panic=10 oops=panic init=/bin/init loglevel=8"
@ -109,7 +142,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
@ -164,9 +203,10 @@ in {
etc = let etc = let
profile = symlink profile = symlink
(pkgs.writeScript ".profile" '' (pkgs.writeScript ".profile" ''
PATH=${lib.makeBinPath config.defaultProfile.packages}:/bin PATH=${lib.makeBinPath config.defaultProfile.packages}:/bin
export PATH export PATH
''); ${concatStringsSep "\n" (mapAttrsToList exportVar config.defaultProfile.environmentVariables)}
'');
in dir { in dir {
inherit profile; inherit profile;
ashrc = profile; ashrc = profile;

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

@ -1,7 +1,6 @@
{ lib, pkgs, config, ...}: { lib, pkgs, config, ...}:
let let
inherit (lib) mkOption types; inherit (lib) mkOption types;
inherit (pkgs.liminix.services) oneshot;
in { in {
options = { options = {
hostname = mkOption { hostname = mkOption {
@ -12,12 +11,21 @@ in {
default = "liminix"; default = "liminix";
type = types.nonEmptyStr; type = types.nonEmptyStr;
}; };
}; hostname-script = mkOption {
config = { description = ''
services.hostname = oneshot { Script that outputs the system hostname on stdin.
name = "hostname"; '';
up = "echo ${config.hostname} > /proc/sys/kernel/hostname"; default = pkgs.writeScript "hostname-gen" ''
down = "true"; #!/bin/sh
echo ${config.hostname}
'';
defaultText = ''
pkgs.writeScript "hostname-gen" '''
#!/bin/sh
echo ''${config.hostname}
'''
'';
type = types.package;
}; };
}; };
} }

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

@ -30,6 +30,8 @@ let
installPhase = '' installPhase = ''
mkdir $out mkdir $out
cp -r $src $out/scripts cp -r $src $out/scripts
substituteInPlace $out/scripts/rc.init \
--replace-fail 'config.hostname' "${config.hostname-script}"
chmod -R +w $out chmod -R +w $out
''; '';
}; };

View file

@ -36,6 +36,7 @@ fi
### (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
config.hostname > /proc/sys/kernel/hostname
### 2. Starting the wanted set of services ### 2. Starting the wanted set of services
### This is also called every time you change runlevels with telinit. ### This is also called every time you change runlevels with telinit.

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 {};
@ -109,6 +110,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"

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

@ -10,4 +10,5 @@
tftpboot = import ./tftpboot/test.nix; tftpboot = import ./tftpboot/test.nix;
updown = import ./updown/test.nix; updown = import ./updown/test.nix;
inout = import ./inout/test.nix; inout = import ./inout/test.nix;
custom-shell = import ./custom-shell/test.nix;
} }

View file

@ -0,0 +1,7 @@
set timeout 60
spawn socat unix-connect:vm/console -
expect {
"root@liminix blah blah > " { exit 0 }
timeout { exit 1 }
}

View file

@ -0,0 +1,13 @@
{ config, pkgs, lib, ... } :
let
inherit (pkgs.liminix.networking) interface address hostapd route dnsmasq;
inherit (pkgs.liminix.services) oneshot longrun bundle target;
in rec {
imports = [
../../modules/network
];
defaultProfile.prompt = "$(whoami)@$(hostname) blah blah > ";
defaultProfile.packages = with pkgs; [ ];
}

View file

@ -0,0 +1,21 @@
{
liminix
, nixpkgs
}:
let img = (import liminix {
inherit nixpkgs;
device = import "${liminix}/devices/qemu/";
liminix-config = ./configuration.nix;
}).outputs.default;
pkgs = import nixpkgs { overlays = [(import ../../overlay.nix)]; };
in pkgs.runCommand "check" {
nativeBuildInputs = with pkgs; [
expect socat
] ;
} ''
. ${../test-helpers.sh}
mkdir vm
${img}/run.sh --background ./vm
expect ${./check-prompt.expect} |tee output && mv output $out
''

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;
};
};
}