nixfmt-rfc-style

There is nothing in this commit except for the changes made by
nix-shell -p nixfmt-rfc-style --run "nixfmt ."

If this has mucked up your open branches then sorry about that. You
can probably nixfmt them to match before merging
This commit is contained in:
Daniel Barlow
2025-02-10 21:55:08 +00:00
parent 13cc5a8992
commit 7e2b0068e6
211 changed files with 6049 additions and 4355 deletions

View File

@@ -4,35 +4,35 @@
{
imports = [
./base.nix
./bridge
./busybox.nix
./dhcp6c
./dnsmasq
./firewall
./hardware.nix
./hostapd
./hostname.nix
./kernel
./mdevd.nix
./mount
./network
./ntp
./outputs.nix
./outputs/ext4fs.nix
./outputs/initramfs.nix
./outputs/jffs2.nix
./outputs/mtdimage.nix
./outputs/tftpboot.nix
./outputs/ubifs.nix
./outputs/ubimage.nix
./outputs/vmroot.nix
./ppp
./ramdisk.nix
./ssh
./users.nix
./vlan
./watchdog
./wlan.nix
];
./base.nix
./bridge
./busybox.nix
./dhcp6c
./dnsmasq
./firewall
./hardware.nix
./hostapd
./hostname.nix
./kernel
./mdevd.nix
./mount
./network
./ntp
./outputs.nix
./outputs/ext4fs.nix
./outputs/initramfs.nix
./outputs/jffs2.nix
./outputs/mtdimage.nix
./outputs/tftpboot.nix
./outputs/ubifs.nix
./outputs/ubimage.nix
./outputs/vmroot.nix
./ppp
./ramdisk.nix
./ssh
./users.nix
./vlan
./watchdog
./wlan.nix
];
}

View File

@@ -1,12 +1,17 @@
{ lim, pkgs, config, ...}:
{
lim,
pkgs,
config,
...
}:
{
config = {
kernel.config = {
CPU_LITTLE_ENDIAN= "y";
CPU_BIG_ENDIAN= "n";
CPU_LITTLE_ENDIAN = "y";
CPU_BIG_ENDIAN = "n";
# CMDLINE_FROM_BOOTLOADER availability is conditional
# on CMDLINE being set to something non-empty
CMDLINE="\"empty=false\"";
CMDLINE = "\"empty=false\"";
CMDLINE_FROM_BOOTLOADER = "y";
OF = "y";

View File

@@ -1,10 +1,15 @@
{ lim, pkgs, config, ...}:
{
lim,
pkgs,
config,
...
}:
{
config = {
kernel.config = {
OF = "y";
};
kernel.makeTargets = ["arch/arm/boot/zImage"];
kernel.makeTargets = [ "arch/arm/boot/zImage" ];
hardware.ram.startAddress = lim.parseInt "0x40000000";
system.outputs.u-boot = pkgs.ubootQemuArm;
};

View File

@@ -1,4 +1,4 @@
{ config, lim, ...}:
{ config, lim, ... }:
{
config = {
kernel.config = {

View File

@@ -1,4 +1,4 @@
{ pkgs, config, ...}:
{ pkgs, config, ... }:
{
imports = [ ./mips.nix ];
config = {

View File

@@ -1,4 +1,4 @@
{ config, ...}:
{ config, ... }:
{
imports = [ ./mips.nix ];
config = {

View File

@@ -1,15 +1,20 @@
## Base options
## ============
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs.pseudofile) dir symlink;
type_service = pkgs.liminix.lib.types.service;
in {
in
{
options = {
defaultProfile = {
packages = mkOption {
@@ -109,8 +114,13 @@ in {
};
};
config = {
defaultProfile.packages = with pkgs;
[ s6 s6-init-bin execline s6-linux-init s6-rc ];
defaultProfile.packages = with pkgs; [
s6
s6-init-bin
execline
s6-linux-init
s6-rc
];
boot.commandLine = [
"panic=10 oops=panic init=/bin/init loglevel=8"
@@ -119,69 +129,98 @@ in {
"fw_devlink=off"
] ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}";
system.callService = path : parameters :
system.callService =
path: parameters:
let
typeChecked = caller: type: value:
typeChecked =
caller: type: value:
let
inherit (lib) types mergeDefinitions;
defs = [{ file = caller; inherit value; }];
defs = [
{
file = caller;
inherit value;
}
];
type' = types.submodule { options = type; };
in (mergeDefinitions [] type' defs).mergedValue;
cp = lib.callPackageWith(pkgs // { svc = config.system.service; });
pkg = cp path {};
checkTypes = t : p : typeChecked (builtins.toString path) t p;
in {
in
(mergeDefinitions [ ] type' defs).mergedValue;
cp = lib.callPackageWith (pkgs // { svc = config.system.service; });
pkg = cp path { };
checkTypes = t: p: typeChecked (builtins.toString path) t p;
in
{
inherit parameters;
build = { dependencies ? [], ... } @ args :
build =
{
dependencies ? [ ],
...
}@args:
let
s = pkg (checkTypes parameters
(builtins.removeAttrs args ["dependencies"]));
in s.overrideAttrs (o: {
s = pkg (checkTypes parameters (builtins.removeAttrs args [ "dependencies" ]));
in
s.overrideAttrs (o: {
dependencies = dependencies ++ o.dependencies;
buildInputs = dependencies ++ o.buildInputs;
});
};
users.root = {
uid = 0; gid= 0; gecos = "Root of all evaluation";
uid = 0;
gid = 0;
gecos = "Root of all evaluation";
dir = "/home/root/";
passwd = lib.mkDefault "";
shell = "/bin/sh";
};
groups = {
root = {
gid = 0; usernames = ["root"];
gid = 0;
usernames = [ "root" ];
};
system = {
gid = 1; usernames = ["root"];
gid = 1;
usernames = [ "root" ];
};
};
filesystem = dir {
dev =
let node = type: major: minor: mode : { inherit type major minor mode; };
in dir {
null = node "c" "1" "3" "0666";
zero = node "c" "1" "5" "0666";
tty = node "c" "5" "0" "0666";
let
node = type: major: minor: mode: {
inherit
type
major
minor
mode
;
};
in
dir {
null = node "c" "1" "3" "0666";
zero = node "c" "1" "5" "0666";
tty = node "c" "5" "0" "0666";
console = node "c" "5" "1" "0600";
pts = dir {};
pts = dir { };
};
etc =
let
profile = symlink (
pkgs.writeScript ".profile" ''
PATH=${lib.makeBinPath config.defaultProfile.packages}:/bin
export PATH
''
);
in
dir {
inherit profile;
ashrc = profile;
};
etc = let
profile = symlink
(pkgs.writeScript ".profile" ''
PATH=${lib.makeBinPath config.defaultProfile.packages}:/bin
export PATH
'');
in dir {
inherit profile;
ashrc = profile;
};
proc = dir {};
run = dir {};
sys = dir {};
tmp = dir {};
proc = dir { };
run = dir { };
sys = dir { };
tmp = dir { };
};
};
}

View File

@@ -6,8 +6,12 @@
## with one or more WLANs so that several local devices appear to be
## on the same network.
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
@@ -40,13 +44,15 @@ in
};
};
};
config.kernel.config = {
BRIDGE = "y";
BRIDGE_IGMP_SNOOPING = "y";
} // lib.optionalAttrs (config.system.service ? vlan) {
# depends on bridge _and_ vlan. I would like there to be
# a better way to test for the existence of vlan config:
# maybe the module should set an `enabled` attribute?
BRIDGE_VLAN_FILTERING = "y";
};
config.kernel.config =
{
BRIDGE = "y";
BRIDGE_IGMP_SNOOPING = "y";
}
// lib.optionalAttrs (config.system.service ? vlan) {
# depends on bridge _and_ vlan. I would like there to be
# a better way to test for the existence of vlan config:
# maybe the module should set an `enabled` attribute?
BRIDGE_VLAN_FILTERING = "y";
};
}

View File

@@ -1,21 +1,25 @@
{
liminix
, ifwait
, svc
liminix,
ifwait,
svc,
}:
{ members, primary } :
{ members, primary }:
let
inherit (liminix.networking) interface;
inherit (liminix.services) bundle oneshot;
addif = member :
addif =
member:
# how do we get sight of services from here? maybe we need to
# implement ifwait as a regualr derivation instead of a
# servicedefinition
svc.ifwait.build {
state = "running";
interface = member;
dependencies = [ primary member ];
dependencies = [
primary
member
];
service = oneshot {
name = "${primary.name}.member.${member.name}";
up = ''
@@ -24,7 +28,8 @@ let
down = "ip link set dev $(output ${member} ifname) nomaster";
};
};
in bundle {
in
bundle {
name = "${primary.name}.members";
contents = map addif members;
}

View File

@@ -1,11 +1,12 @@
{
liminix
, lib
liminix,
lib,
}:
{ ifname } :
{ ifname }:
let
inherit (liminix.services) oneshot;
in oneshot rec {
in
oneshot rec {
name = "${ifname}.link";
up = ''
ip link add name ${ifname} type bridge

View File

@@ -6,19 +6,26 @@
## the commands (termed "applets") required by the user or
## by other included modules.
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types mapAttrsToList;
inherit (pkgs.pseudofile) dir symlink;
inherit (lib.strings) toUpper;
attrs = { options, applets } :
attrs =
{ options, applets }:
let
extraOptions = builtins.concatStringsSep "\n"
(mapAttrsToList (n: v: "CONFIG_${toUpper n} ${toString v}") options);
appletOptions = builtins.concatStringsSep "\n"
(map (n: "CONFIG_${toUpper n} y") applets);
in {
extraOptions = builtins.concatStringsSep "\n" (
mapAttrsToList (n: v: "CONFIG_${toUpper n} ${toString v}") options
);
appletOptions = builtins.concatStringsSep "\n" (map (n: "CONFIG_${toUpper n} y") applets);
in
{
enableMinimal = true;
extraConfig = ''
${extraOptions}
@@ -26,44 +33,171 @@ let
'';
};
cfg = config.programs.busybox;
busybox = pkgs.busybox.override (attrs { inherit (cfg) applets options; });
makeLinks = lib.attrsets.genAttrs
cfg.applets
(a: symlink "${busybox}/bin/busybox");
busybox = pkgs.busybox.override (attrs {
inherit (cfg) applets options;
});
makeLinks = lib.attrsets.genAttrs cfg.applets (a: symlink "${busybox}/bin/busybox");
minimalApplets = [
# this is probably less minimal than it could be
"arch" "ash" "base64" "basename" "bc" "brctl" "bunzip2" "bzcat" "bzip2"
"cal" "cat" "chattr" "chgrp" "chmod" "chown" "chpst" "chroot" "clear" "cmp"
"comm" "cp" "cpio" "cut" "date" "dhcprelay" "dd" "df" "dirname" "dmesg"
"du" "echo" "egrep" "env" "expand" "expr" "false" "fdisk" "fgrep" "find"
"free" "fuser" "grep" "gunzip" "gzip" "head" "hexdump" "hostname" "hwclock"
"ifconfig" "ip" "ipaddr" "iplink" "ipneigh" "iproute" "iprule" "kill"
"killall" "killall5" "less" "ln" "ls" "lsattr" "lsof" "md5sum" "mkdir"
"mknod" "mktemp" "mount" "mv" "nc" "netstat" "nohup" "od" "pgrep" "pidof"
"ping" "ping6" "pkill" "pmap" "printenv" "printf" "ps" "pwd" "readlink"
"realpath" "reset" "rm" "rmdir" "route" "sed" "seq" "setsid" "sha1sum"
"sha256sum" "sha512sum" "sleep" "sort" "stat" "strings" "stty" "su" "sum"
"swapoff" "swapon" "sync" "tail" "tee" "test" "time" "touch" "tr"
"traceroute" "traceroute6" "true" "truncate" "tty" "udhcpc" "umount"
"uname" "unexpand" "uniq" "unlink" "unlzma" "unxz" "unzip" "uptime" "watch"
"wc" "whoami" "xargs" "xxd" "xz" "xzcat" "yes" "zcat"
"arch"
"ash"
"base64"
"basename"
"bc"
"brctl"
"bunzip2"
"bzcat"
"bzip2"
"cal"
"cat"
"chattr"
"chgrp"
"chmod"
"chown"
"chpst"
"chroot"
"clear"
"cmp"
"comm"
"cp"
"cpio"
"cut"
"date"
"dhcprelay"
"dd"
"df"
"dirname"
"dmesg"
"du"
"echo"
"egrep"
"env"
"expand"
"expr"
"false"
"fdisk"
"fgrep"
"find"
"free"
"fuser"
"grep"
"gunzip"
"gzip"
"head"
"hexdump"
"hostname"
"hwclock"
"ifconfig"
"ip"
"ipaddr"
"iplink"
"ipneigh"
"iproute"
"iprule"
"kill"
"killall"
"killall5"
"less"
"ln"
"ls"
"lsattr"
"lsof"
"md5sum"
"mkdir"
"mknod"
"mktemp"
"mount"
"mv"
"nc"
"netstat"
"nohup"
"od"
"pgrep"
"pidof"
"ping"
"ping6"
"pkill"
"pmap"
"printenv"
"printf"
"ps"
"pwd"
"readlink"
"realpath"
"reset"
"rm"
"rmdir"
"route"
"sed"
"seq"
"setsid"
"sha1sum"
"sha256sum"
"sha512sum"
"sleep"
"sort"
"stat"
"strings"
"stty"
"su"
"sum"
"swapoff"
"swapon"
"sync"
"tail"
"tee"
"test"
"time"
"touch"
"tr"
"traceroute"
"traceroute6"
"true"
"truncate"
"tty"
"udhcpc"
"umount"
"uname"
"unexpand"
"uniq"
"unlink"
"unlzma"
"unxz"
"unzip"
"uptime"
"watch"
"wc"
"whoami"
"xargs"
"xxd"
"xz"
"xzcat"
"yes"
"zcat"
];
in {
in
{
options = {
programs.busybox = {
applets = mkOption {
applets = mkOption {
type = types.listOf types.str;
description = "Applets required";
default = [];
example = ["sh" "getty" "login"];
default = [ ];
example = [
"sh"
"getty"
"login"
];
};
options = mkOption {
# mostly the values are y n or m, but sometimes
# other strings are also used
description = "Other busybox config flags that do not map directly to applet names (often prefixed FEATURE_)";
type = types.attrsOf types.nonEmptyStr;
default = { };
example = { FEATURE_DD_IBS_OBS = "y"; };
default = { };
example = {
FEATURE_DD_IBS_OBS = "y";
};
};
};
};

View File

@@ -1,10 +1,14 @@
{
writeFennel
, linotify
, anoia
, lualinux
writeFennel,
linotify,
anoia,
lualinux,
}:
writeFennel "acquire-delegated-prefix" {
packages = [ linotify anoia lualinux ];
packages = [
linotify
anoia
lualinux
];
mainFunction = "run";
} ./acquire-delegated-prefix.fnl

View File

@@ -1,10 +1,14 @@
{
writeFennel
, linotify
, anoia
, lualinux
writeFennel,
linotify,
anoia,
lualinux,
}:
writeFennel "acquire-wan-address" {
packages = [ linotify anoia lualinux ];
packages = [
linotify
anoia
lualinux
];
mainFunction = "run";
} ./acquire-wan-address.fnl

View File

@@ -1,14 +1,18 @@
{
liminix
, callPackage
liminix,
callPackage,
}:
{ client, interface } :
{ client, interface }:
let
inherit (liminix.services) longrun;
name = "dhcp6c.addr.${client.name}.${interface.name}";
script = callPackage ./acquire-wan-address.nix { };
in longrun {
script = callPackage ./acquire-wan-address.nix { };
in
longrun {
inherit name;
run = "${script} $SERVICE_OUTPUTS/${client.name} $(output ${interface} ifname)";
dependencies = [ client interface ];
dependencies = [
client
interface
];
}

View File

@@ -1,13 +1,14 @@
{
liminix
, odhcp6c
, odhcp-script
liminix,
odhcp6c,
odhcp-script,
}:
{ interface } :
{ interface }:
let
inherit (liminix.services) longrun;
name = "dhcp6c.${interface.name}";
in longrun {
in
longrun {
inherit name;
notification-fd = 10;
run = ''

View File

@@ -9,7 +9,12 @@
## addresses of network interfaces that you want to assign those
## prefixes to
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;

View File

@@ -1,14 +1,18 @@
{
liminix
, callPackage
liminix,
callPackage,
}:
{ client, interface } :
{ client, interface }:
let
inherit (liminix.services) longrun;
name = "dhcp6c.prefix.${client.name}.${interface.name}";
script = callPackage ./acquire-delegated-prefix.nix { };
in longrun {
script = callPackage ./acquire-delegated-prefix.nix { };
in
longrun {
inherit name;
run = "${script} $SERVICE_OUTPUTS/${client.name} $(output ${interface} ifname)";
dependencies = [ client interface ];
dependencies = [
client
interface
];
}

View File

@@ -4,12 +4,17 @@
## This module includes a service to provide DNS, DHCP, and IPv6
## router advertisement for the local network.
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in {
in
{
options = {
system.service.dnsmasq = mkOption {
type = liminix.lib.types.serviceDefn;
@@ -20,12 +25,12 @@ in {
user = mkOption {
type = types.str;
default = "dnsmasq";
description = "Specifies the unix user which dnsmasq will run as" ;
description = "Specifies the unix user which dnsmasq will run as";
};
group = mkOption {
type = types.str;
default = "dnsmasq";
description = "Specifies the unix group which dnsmasq will run as" ;
description = "Specifies the unix group which dnsmasq will run as";
};
resolvconf = mkOption {
type = types.nullOr liminix.lib.types.service;
@@ -37,42 +42,47 @@ in {
};
upstreams = mkOption {
type = types.listOf types.str;
default = [];
default = [ ];
};
ranges = mkOption {
type = types.listOf types.str;
};
hosts = mkOption {
default = {};
type = types.attrsOf (types.submodule {
options = {
mac = mkOption {
description = ''
MAC or other hardware address to match on. For Ethernet
this is a 48 bit address represented as colon-separated
hex bytes, or "id:clientid" to match a presented
client id (IPv6 DUID)
'';
type = types.str;
example = "01:20:31:4a:50";
default = { };
type = types.attrsOf (
types.submodule {
options = {
mac = mkOption {
description = ''
MAC or other hardware address to match on. For Ethernet
this is a 48 bit address represented as colon-separated
hex bytes, or "id:clientid" to match a presented
client id (IPv6 DUID)
'';
type = types.str;
example = "01:20:31:4a:50";
};
v4 = mkOption {
description = "IPv4 address to assign to this client";
example = "192.0.2.1";
type = types.str;
};
v6 = mkOption {
type = types.listOf types.str;
description = "IPv6 addresses or interface-ids to assign to this client";
default = [ ];
example = [
"fe80::42:1eff:fefd:b341"
"::1234"
];
};
leasetime = mkOption {
type = types.int;
default = 86400;
};
};
v4 = mkOption {
description = "IPv4 address to assign to this client";
example = "192.0.2.1";
type = types.str;
};
v6 = mkOption {
type = types.listOf types.str;
description = "IPv6 addresses or interface-ids to assign to this client";
default = [];
example = [ "fe80::42:1eff:fefd:b341" "::1234"];
};
leasetime = mkOption {
type = types.int;
default = 86400;
};
};
});
}
);
};
domain = mkOption {
# this can be given multiple times so probably should be
@@ -83,13 +93,16 @@ in {
};
};
users.dnsmasq = {
uid = 51; gid= 51; gecos = "DNS/DHCP service user";
uid = 51;
gid = 51;
gecos = "DNS/DHCP service user";
dir = "/run/dnsmasq";
shell = "/bin/false";
};
groups.dnsmasq = {
gid = 51; usernames = ["dnsmasq"];
gid = 51;
usernames = [ "dnsmasq" ];
};
groups.system.usernames = ["dnsmasq"];
groups.system.usernames = [ "dnsmasq" ];
};
}

View File

@@ -1,26 +1,35 @@
{
liminix
, dnsmasq
, serviceFns
, lib
liminix,
dnsmasq,
serviceFns,
lib,
}:
{
interface
, user
, domain
, group
, ranges
, hosts
, upstreams
, resolvconf
interface,
user,
domain,
group,
ranges,
hosts,
upstreams,
resolvconf,
}:
let
name = "${interface.name}.dnsmasq";
inherit (liminix.services) longrun;
inherit (lib) concatStrings concatStringsSep mapAttrsToList;
hostOpt = name : { mac, v4, v6, leasetime }:
let v6s = concatStrings (map (a : ",[${a}]") v6);
in "--dhcp-host=${mac},${v4}${v6s},${name},${builtins.toString leasetime}";
hostOpt =
name:
{
mac,
v4,
v6,
leasetime,
}:
let
v6s = concatStrings (map (a: ",[${a}]") v6);
in
"--dhcp-host=${mac},${v4}${v6s},${name},${builtins.toString leasetime}";
in
longrun {
inherit name;
@@ -35,7 +44,12 @@ longrun {
${lib.concatStringsSep " " (builtins.map (r: "--server=${r}") upstreams)} \
--keep-in-foreground \
--dhcp-authoritative \
${if resolvconf != null then "--resolv-file=$(output_path ${resolvconf} resolv.conf)" else "--no-resolv"} \
${
if resolvconf != null then
"--resolv-file=$(output_path ${resolvconf} resolv.conf)"
else
"--no-resolv"
} \
${lib.concatStringsSep " " (mapAttrsToList hostOpt hosts)} \
--no-hosts \
--log-dhcp \
@@ -44,7 +58,7 @@ longrun {
--dhcp-leasefile=$(mkstate ${name})/leases \
--pid-file=/run/${name}.pid
'';
# --log-debug \
# --log-queries \
# --log-debug \
# --log-queries \
}

View File

@@ -1,9 +1,10 @@
let
drop = expr : "${expr} drop";
accept = expr : "${expr} accept";
drop = expr: "${expr} drop";
accept = expr: "${expr} accept";
mcast-scope = 8;
allow-incoming = false;
in {
in
{
bogons-ip6 = {
type = "filter";
family = "ip6";
@@ -44,7 +45,7 @@ in {
rules = [
(drop "ip6 saddr ::1/128") # loopback address [RFC4291]
(drop "ip6 daddr ::1/128")
(drop "ip6 saddr ::FFFF:0:0/96")# IPv4-mapped addresses
(drop "ip6 saddr ::FFFF:0:0/96") # IPv4-mapped addresses
(drop "ip6 daddr ::FFFF:0:0/96")
(drop "ip6 saddr fe80::/10") # link-local unicast
(drop "ip6 daddr fe80::/10")
@@ -60,7 +61,8 @@ in {
(drop
# dest addr first byte 0xff, low nibble of second byte <= scope
# https://www.mankier.com/8/nft#Payload_Expressions-Raw_Payload_Expression
"@nh,192,8 eq 0xff @nh,204,4 le ${toString mcast-scope}")
"@nh,192,8 eq 0xff @nh,204,4 le ${toString mcast-scope}"
)
(accept "oifname @lan iifname @wan meta l4proto udp ct state established,related")
(accept "iifname @lan oifname @wan meta l4proto udp")
@@ -72,7 +74,7 @@ in {
# does this ever get used or does the preceding general udp accept
# already grab anything that might get here?
(accept "oifname @wan udp dport 500") # IKE Protocol [RFC5996]. haha zyxel
(accept "ip6 nexthdr 139") # Host Identity Protocol
(accept "ip6 nexthdr 139") # Host Identity Protocol
## FIXME no support yet for recs 27-30 Mobility Header
@@ -88,9 +90,11 @@ in {
# we can allow all reasonable inbound, or we can use an explicit
# allowlist to enumerate the endpoints that are allowed to
# accept inbound from the WAN
(if allow-incoming
then accept "oifname @lan iifname @wan"
else "iifname @wan jump incoming-allowed-ip6"
(
if allow-incoming then
accept "oifname @lan iifname @wan"
else
"iifname @wan jump incoming-allowed-ip6"
)
# allow all outbound and any inbound that's part of a
# recognised (outbound-initiated) flow
@@ -130,10 +134,7 @@ in {
(accept "meta l4proto icmpv6")
"iifname @lan jump input-ip6-lan"
"iifname @wan jump input-ip6-wan"
(if allow-incoming
then accept "iifname @wan"
else "iifname @wan jump incoming-allowed-ip6"
)
(if allow-incoming then accept "iifname @wan" else "iifname @wan jump incoming-allowed-ip6")
# how does this even make sense in an input chain?
(accept "iifname @wan ct state established,related")
(accept "iifname @lan ")
@@ -185,9 +186,9 @@ in {
family = "ip";
rules = [
(accept "udp dport 67") # dhcp
(accept "udp dport 53") # dns
(accept "tcp dport 22") # ssh
(accept "udp dport 67") # dhcp
(accept "udp dport 53") # dns
(accept "tcp dport 22") # ssh
];
};

View File

@@ -4,7 +4,12 @@
## Provides a service to create an nftables ruleset based on
## configuration supplied to it.
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
@@ -54,37 +59,44 @@ in
};
config = {
system.service.firewall =
let svc = config.system.callService ./service.nix {
extraRules = mkOption {
type = types.attrsOf types.attrs;
description = "firewall ruleset";
default = {};
};
zones = mkOption {
type = types.attrsOf (types.listOf liminix.lib.types.service);
default = {};
example = lib.literalExpression ''
{
lan = with config.hardware.networkInterfaces; [ int ];
wan = [ config.services.ppp0 ];
}
'';
};
rules = mkOption {
type = types.attrsOf types.attrs; # we could usefully tighten this a bit :-)
default = import ./default-rules.nix;
description = "firewall ruleset";
};
let
svc = config.system.callService ./service.nix {
extraRules = mkOption {
type = types.attrsOf types.attrs;
description = "firewall ruleset";
default = { };
};
in svc // {
build = args :
let args' = args // {
dependencies = (args.dependencies or []) ++ [kmodules];
};
in svc.build args' ;
zones = mkOption {
type = types.attrsOf (types.listOf liminix.lib.types.service);
default = { };
example = lib.literalExpression ''
{
lan = with config.hardware.networkInterfaces; [ int ];
wan = [ config.services.ppp0 ];
}
'';
};
rules = mkOption {
type = types.attrsOf types.attrs; # we could usefully tighten this a bit :-)
default = import ./default-rules.nix;
description = "firewall ruleset";
};
};
in
svc
// {
build =
args:
let
args' = args // {
dependencies = (args.dependencies or [ ]) ++ [ kmodules ];
};
in
svc.build args';
};
programs.busybox.applets = [
"insmod" "rmmod"
"insmod"
"rmmod"
];
kernel.config = {
NETFILTER = "y";
@@ -94,7 +106,7 @@ in
NETLINK_DIAG = "y";
IP6_NF_IPTABLES= "m";
IP6_NF_IPTABLES = "m";
IP_NF_IPTABLES = "m";
IP_NF_NAT = "m";
IP_NF_TARGET_MASQUERADE = "m";

View File

@@ -1,37 +1,44 @@
{
liminix
, lib
, firewallgen
, nftables
, writeFennel
, anoia
, lualinux
, linotify
liminix,
lib,
firewallgen,
nftables,
writeFennel,
anoia,
lualinux,
linotify,
}:
{
rules,
extraRules,
zones,
}:
{ rules, extraRules, zones }:
let
inherit (liminix.services) longrun;
inherit (lib.attrsets) mapAttrs' nameValuePair mapAttrsToList;
inherit (lib.strings) concatStringsSep;
inherit (lib.lists) flatten;
mkSet = family : name :
nameValuePair
"${name}-set-${family}"
{
kind = "set";
inherit name family;
type = "ifname";
};
sets = (mapAttrs' (n : _ : mkSet "ip" n) zones) //
(mapAttrs' (n : _ : mkSet "ip6" n) zones);
mkSet =
family: name:
nameValuePair "${name}-set-${family}" {
kind = "set";
inherit name family;
type = "ifname";
};
sets = (mapAttrs' (n: _: mkSet "ip" n) zones) // (mapAttrs' (n: _: mkSet "ip6" n) zones);
allRules = lib.recursiveUpdate extraRules (lib.recursiveUpdate (builtins.trace sets sets) rules);
script = firewallgen "firewall1.nft" allRules;
ifwatch = writeFennel "ifwatch" {
packages = [anoia lualinux linotify];
packages = [
anoia
lualinux
linotify
];
mainFunction = "run";
} ./ifwatch.fnl ;
watchArg = z : intfs : map (i: "${z}:${i}/.outputs") intfs;
in longrun {
} ./ifwatch.fnl;
watchArg = z: intfs: map (i: "${z}:${i}/.outputs") intfs;
in
longrun {
name = "firewall";
run = ''
${script}

View File

@@ -75,17 +75,27 @@ in
type = types.ints.unsigned;
};
};
loadAddress = mkOption { type = types.ints.unsigned; default = null; };
loadAddress = mkOption {
type = types.ints.unsigned;
default = null;
};
entryPoint = mkOption { type = types.ints.unsigned; };
alignment = mkOption { type = types.nullOr types.ints.unsigned; default = null; description = "Alignment passed to `mkimage` for FIT"; };
alignment = mkOption {
type = types.nullOr types.ints.unsigned;
default = null;
description = "Alignment passed to `mkimage` for FIT";
};
radios = mkOption {
description = ''
Kernel modules (from mac80211 package) required for the
wireless devices on this board
'';
type = types.listOf types.str;
default = [];
example = ["ath9k" "ath10k"];
default = [ ];
example = [
"ath9k"
"ath10k"
];
};
rootDevice = mkOption {
description = "Full path to preferred root device";

View File

@@ -6,13 +6,18 @@
## have other behaviours by e.g. combining this service with a round-robin
## for failover)
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in
# inherit (pkgs.liminix.services) longrun;
in {
{
options = {
system.service.health-check = mkOption {
description = "run a service while periodically checking it is healthy";
@@ -23,13 +28,13 @@ in {
service = mkOption {
type = liminix.lib.types.service;
};
interval = mkOption {
interval = mkOption {
description = "interval between checks, in seconds";
type = types.int;
default = 10;
example = 10;
};
threshold = mkOption {
threshold = mkOption {
description = "number of consecutive failures required for the service to be kicked";
type = types.int;
example = 3;
@@ -39,5 +44,5 @@ in {
type = types.path;
};
};
config.programs.busybox.applets = ["expr"];
config.programs.busybox.applets = [ "expr" ];
}

View File

@@ -1,37 +1,50 @@
{
liminix, lib, lim, s6
liminix,
lib,
lim,
s6,
}:
{
service,
interval,
threshold,
healthCheck,
}:
{ service, interval, threshold, healthCheck } :
let
inherit (liminix.services) oneshot longrun;
inherit (builtins) toString;
inherit (service) name;
checker = let name' = "check-${name}"; in longrun {
name = name';
run = ''
fails=0
echo waiting for /run/service/${name}
${s6}/bin/s6-svwait -U /run/service/${name} || exit
while sleep ${toString interval} ; do
${healthCheck}
if test $? -gt 0; then
fails=$(expr $fails + 1)
else
fails=0
fi
echo fails $fails/${toString threshold} for ${name}
if test "$fails" -gt "${toString threshold}" ; then
echo time to die
${s6}/bin/s6-svc -r /run/service/${name}
echo bounced
fails=0
echo waiting for /run/service/${name}
${s6}/bin/s6-svwait -U /run/service/${name}
fi
done
'';
};
in service.overrideAttrs(o: {
buildInputs = (lim.orEmpty o.buildInputs) ++ [ checker ];
checker =
let
name' = "check-${name}";
in
longrun {
name = name';
run = ''
fails=0
echo waiting for /run/service/${name}
${s6}/bin/s6-svwait -U /run/service/${name} || exit
while sleep ${toString interval} ; do
${healthCheck}
if test $? -gt 0; then
fails=$(expr $fails + 1)
else
fails=0
fi
echo fails $fails/${toString threshold} for ${name}
if test "$fails" -gt "${toString threshold}" ; then
echo time to die
${s6}/bin/s6-svc -r /run/service/${name}
echo bounced
fails=0
echo waiting for /run/service/${name}
${s6}/bin/s6-svwait -U /run/service/${name}
fi
done
'';
};
in
service.overrideAttrs (o: {
buildInputs = (lim.orEmpty o.buildInputs) ++ [ checker ];
dependencies = (lim.orEmpty o.dependencies) ++ [ checker ];
})

View File

@@ -11,11 +11,17 @@
## If you have more than one wireless network interface (e.g.
## wlan0, wlan1) you can run an instance of hostapd on each of them.
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in {
in
{
imports = [ ../secrets ];
options = {
system.service.hostapd = mkOption {

View File

@@ -1,16 +1,23 @@
{
liminix
, svc
, hostapd
, output-template
, writeText
, lib
liminix,
svc,
hostapd,
output-template,
writeText,
lib,
}:
{ interface, params} :
{ interface, params }:
let
inherit (liminix.services) longrun;
inherit (lib) concatStringsSep mapAttrsToList unique ;
inherit (builtins) map filter attrValues length head typeOf;
inherit (lib) concatStringsSep mapAttrsToList unique;
inherit (builtins)
map
filter
attrValues
length
head
typeOf
;
# This is not a friendly interface to configuring a wireless AP: it
# just passes everything straight through to the hostapd config.
@@ -19,27 +26,31 @@ let
# extraParams
name = "${interface.name}.hostapd";
defaults = {
defaults = {
driver = "nl80211";
logger_syslog = "-1";
logger_syslog_level = 1;
ctrl_interface = "/run/${name}";
ctrl_interface_group = 0;
};
attrs = defaults // params ;
literal_or_output = o: ({
string = builtins.toJSON;
int = builtins.toJSON;
lambda = (o: "output(${builtins.toJSON (o "service")}, ${builtins.toJSON (o "path")})");
}.${builtins.typeOf o}) o;
attrs = defaults // params;
literal_or_output =
o:
(
{
string = builtins.toJSON;
int = builtins.toJSON;
lambda = (o: "output(${builtins.toJSON (o "service")}, ${builtins.toJSON (o "path")})");
}
.${builtins.typeOf o}
)
o;
conf =
(writeText "hostapd.conf.in"
((concatStringsSep
"\n"
(mapAttrsToList
(n : v : "${n}={{ ${literal_or_output v} }}")
attrs)) + "\n"));
conf = (
writeText "hostapd.conf.in" (
(concatStringsSep "\n" (mapAttrsToList (n: v: "${n}={{ ${literal_or_output v} }}") attrs)) + "\n"
)
);
service = longrun {
inherit name;
dependencies = [ interface ];
@@ -51,7 +62,8 @@ let
'';
};
watch = filter (f: typeOf f == "lambda") (attrValues attrs);
in svc.secrets.subscriber.build {
in
svc.secrets.subscriber.build {
inherit service watch;
action = "restart-all";
}

View File

@@ -1,8 +1,14 @@
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs.liminix.services) oneshot;
in {
in
{
options = {
hostname = mkOption {
description = ''

View File

@@ -1,10 +1,15 @@
{ config, pkgs, lib, ... } :
{
config,
pkgs,
lib,
...
}:
let
inherit (pkgs) liminix;
inherit (lib) mkOption types;
in {
options.system.service.ifwait =
mkOption { type = liminix.lib.types.serviceDefn; };
in
{
options.system.service.ifwait = mkOption { type = liminix.lib.types.serviceDefn; };
config.system.service.ifwait = config.system.callService ./ifwait.nix {
state = mkOption { type = types.str; };

View File

@@ -1,12 +1,13 @@
{ ifwait, liminix } :
{ ifwait, liminix }:
{
state
, interface
, service
state,
interface,
service,
}:
let
inherit (liminix.services) longrun;
in longrun {
in
longrun {
name = "ifwait.${interface.name}";
buildInputs = [ service ];
restart-on-upgrade = true;

View File

@@ -3,26 +3,36 @@
##
##
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types ;
inherit (lib) mkOption types;
inherit (pkgs) liminix openwrt;
mergeConditionals = conf : conditions :
mergeConditionals =
conf: conditions:
# for each key in conditions, if it is present in conf
# then merge the associated value into conf
lib.foldlAttrs
(acc: name: value:
if (conf ? ${name}) && (conf.${name} != "n")
then acc // value
else acc)
conf
conditions;
in {
lib.foldlAttrs (
acc: name: value:
if (conf ? ${name}) && (conf.${name} != "n") then acc // value else acc
) conf conditions;
in
{
options = {
kernel = {
src = mkOption { type = types.path; default = openwrt.kernelSrc; } ;
version = mkOption { type = types.str; default = openwrt.kernelVersion;} ;
src = mkOption {
type = types.path;
default = openwrt.kernelSrc;
};
version = mkOption {
type = types.str;
default = openwrt.kernelVersion;
};
modular = mkOption {
type = types.bool;
default = true;
@@ -54,7 +64,7 @@ in {
some other option is present.
'';
type = types.attrsOf (types.attrsOf types.nonEmptyStr);
default = {};
default = { };
example = {
USB = {
USB_XHCI_MVEBU = "y";
@@ -70,10 +80,9 @@ in {
config = {
system.outputs.kernel =
let
mergedConfig = mergeConditionals
config.kernel.config
config.kernel.conditionalConfig;
in liminix.builders.kernel.override {
mergedConfig = mergeConditionals config.kernel.config config.kernel.conditionalConfig;
in
liminix.builders.kernel.override {
config = mergedConfig;
inherit (config.kernel) version src extraPatchPhase;
targets = config.kernel.makeTargets;
@@ -81,7 +90,7 @@ in {
kernel = rec {
modular = true; # disabling this is not yet supported
makeTargets = ["vmlinux"];
makeTargets = [ "vmlinux" ];
config = {
IKCONFIG = "y";
IKCONFIG_PROC = "y";
@@ -96,10 +105,10 @@ in {
UNIX = "y";
INET = "y";
IPV6 = "y";
PACKET = "y"; # for ppp, tcpdump ...
SYSVIPC= "y";
PACKET = "y"; # for ppp, tcpdump ...
SYSVIPC = "y";
NETDEVICES = "y"; # even PPP needs this
NETDEVICES = "y"; # even PPP needs this
# disabling this option causes the kernel to use an "empty"
# initramfs instead: it has a /dev/console node and not much

View File

@@ -1,7 +1,13 @@
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (pkgs.liminix.services) longrun;
in {
in
{
config.services.klogd = longrun {
name = "klogd";
run = ''

View File

@@ -1,7 +1,13 @@
{ config, lib, ... }:
let
inherit (lib) mkIf mkEnableOption mkOption types;
in {
inherit (lib)
mkIf
mkEnableOption
mkOption
types
;
in
{
options = {
logging = {
persistent = {
@@ -9,11 +15,11 @@ in {
};
};
};
config = {
kernel.config = mkIf config.logging.persistent.enable {
PSTORE = "y";
PSTORE_PMSG = "y";
PSTORE_RAM = "y";
};
};
config = {
kernel.config = mkIf config.logging.persistent.enable {
PSTORE = "y";
PSTORE_PMSG = "y";
PSTORE_RAM = "y";
};
};
}

View File

@@ -1,6 +1,8 @@
{ config, pkgs, ...} :
let inherit (pkgs.liminix.services) oneshot longrun;
in {
{ config, pkgs, ... }:
let
inherit (pkgs.liminix.services) oneshot longrun;
in
{
config = {
services = rec {
mdevd = longrun {

View File

@@ -2,53 +2,74 @@
##
## Mount filesystems
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in {
in
{
options = {
system.service.mount = mkOption {
type = liminix.lib.types.serviceDefn;
};
};
imports = [ ../mdevd.nix ../uevent-rule ];
imports = [
../mdevd.nix
../uevent-rule
];
config.system.service.mount =
let svc = config.system.callService ./service.nix {
partlabel = mkOption {
type = types.str;
example = "my-usb-stick";
};
mountpoint = mkOption {
type = types.str;
example = "/mnt/media";
};
options = mkOption {
type = types.listOf types.str;
default = [];
example = ["noatime" "ro" "sync"];
};
fstype = mkOption {
type = types.str;
default = "auto";
example = "vfat";
};
let
svc = config.system.callService ./service.nix {
partlabel = mkOption {
type = types.str;
example = "my-usb-stick";
};
in svc // {
build = args:
let args' = args // {
dependencies = (args.dependencies or []) ++ [
config.services.mdevd
config.services.devout
];
};
in svc.build args' ;
mountpoint = mkOption {
type = types.str;
example = "/mnt/media";
};
options = mkOption {
type = types.listOf types.str;
default = [ ];
example = [
"noatime"
"ro"
"sync"
];
};
fstype = mkOption {
type = types.str;
default = "auto";
example = "vfat";
};
};
in
svc
// {
build =
args:
let
args' = args // {
dependencies = (args.dependencies or [ ]) ++ [
config.services.mdevd
config.services.devout
];
};
in
svc.build args';
};
config.programs.busybox = {
applets = ["blkid" "findfs"];
config.programs.busybox = {
applets = [
"blkid"
"findfs"
];
options = {
FEATURE_BLKID_TYPE = "y";
FEATURE_MOUNT_FLAGS = "y";

View File

@@ -1,15 +1,19 @@
{
liminix
, lib
, svc
liminix,
lib,
svc,
}:
{
partlabel,
mountpoint,
options,
fstype,
}:
{ partlabel, mountpoint, options, fstype }:
let
inherit (liminix.services) oneshot;
device = "/dev/disk/by-partlabel/${partlabel}";
name = "mount.${lib.strings.sanitizeDerivationName (lib.escapeURL mountpoint)}";
options_string =
if options == [] then "" else "-o ${lib.concatStringsSep "," options}";
options_string = if options == [ ] then "" else "-o ${lib.concatStringsSep "," options}";
controller = svc.uevent-rule.build {
serviceName = name;
symlink = device;
@@ -18,7 +22,8 @@ let
devtype = "partition";
};
};
in oneshot {
in
oneshot {
inherit name;
timeout-up = 3600;
up = "mount -t ${fstype} ${options_string} ${device} ${mountpoint}";

View File

@@ -1,9 +1,14 @@
{
liminix
, serviceFns
, lib
liminix,
serviceFns,
lib,
}:
{
interface,
family,
address,
prefixLength,
}:
{interface, family, address, prefixLength} :
let
inherit (liminix.services) oneshot;
# rather depending on the assumption that nobody will
@@ -20,8 +25,9 @@ let
echo $dev > ifname
)
'';
in oneshot {
in
oneshot {
inherit name up;
down = "true"; # this has been broken for ~ ages
down = "true"; # this has been broken for ~ ages
dependencies = [ interface ];
}

View File

@@ -4,13 +4,18 @@
## Basic network services for creating hardware ethernet devices
## and adding addresses
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
inherit (pkgs.liminix.services) bundle;
in {
in
{
options = {
system.service.network = {
link = mkOption {
@@ -42,17 +47,18 @@ in {
lo =
let
net = config.system.service.network;
iface = net.link.build { ifname = "lo";};
in bundle {
iface = net.link.build { ifname = "lo"; };
in
bundle {
name = "loopback";
contents = [
( net.address.build {
(net.address.build {
interface = iface;
family = "inet";
address ="127.0.0.1";
address = "127.0.0.1";
prefixLength = 8;
})
( net.address.build {
(net.address.build {
interface = iface;
family = "inet6";
address = "::1";
@@ -82,7 +88,8 @@ in {
Path to the sysfs node of the device. If you provide this
and the ifname option, the device will be renamed to the
name given by ifname.
''; };
'';
};
# other "ip link add" options could go here as well
mtu = mkOption {
type = types.nullOr types.int;
@@ -94,7 +101,10 @@ in {
type = liminix.lib.types.service;
};
family = mkOption {
type = types.enum [ "inet" "inet6" ];
type = types.enum [
"inet"
"inet6"
];
};
address = mkOption {
type = types.str;

View File

@@ -1,14 +1,14 @@
{
liminix
, writeAshScript
, serviceFns
, lib
} :
liminix,
writeAshScript,
serviceFns,
lib,
}:
{ interface }:
let
inherit (liminix.services) longrun;
name = "${interface.name}.dhcpc";
script = writeAshScript "dhcp-notify" { } ''
script = writeAshScript "dhcp-notify" { } ''
. ${serviceFns}
exec 2>&1
action=$1
@@ -38,7 +38,8 @@ let
;;
esac
'';
in longrun {
in
longrun {
inherit name;
run = "exec /bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
notification-fd = 10;

View File

@@ -1,6 +1,6 @@
{
liminix
, lib
liminix,
lib,
}:
{ enableIPv4, enableIPv6 }:
let
@@ -8,11 +8,9 @@ let
ip4 = "/proc/sys/net/ipv4/conf/all/forwarding";
ip6 = "/proc/sys/net/ipv6/conf/all/forwarding";
opt = lib.optionalString;
sysctls = b :
""
+ opt enableIPv4 "echo ${b} > ${ip4}\n"
+ opt enableIPv6 "echo ${b} > ${ip6}\n";
in oneshot {
sysctls = b: "" + opt enableIPv4 "echo ${b} > ${ip4}\n" + opt enableIPv6 "echo ${b} > ${ip6}\n";
in
oneshot {
name = "forwarding${opt enableIPv4 "4"}${opt enableIPv6 "6"}";
up = sysctls "1";
down = sysctls "0";

View File

@@ -1,23 +1,27 @@
{
liminix
, lib
liminix,
lib,
}:
{
ifname
, devpath ? null
, mtu} :
ifname,
devpath ? null,
mtu,
}:
# if devpath is supplied, we rename the interface at that
# path to have the specified name.
let
inherit (liminix.services) oneshot;
name = "${ifname}.link";
rename = if devpath != null
then ''
oldname=$(cd /sys${devpath} && cd net/ && echo *)
ip link set ''${oldname} name ${ifname}
''
else "";
in oneshot {
rename =
if devpath != null then
''
oldname=$(cd /sys${devpath} && cd net/ && echo *)
ip link set ''${oldname} name ${ifname}
''
else
"";
in
oneshot {
inherit name;
up = ''
${rename}

View File

@@ -1,20 +1,30 @@
{
liminix
, lib
liminix,
lib,
}:
{
target,
via,
interface ? null,
metric,
}:
{ target, via, interface ? null, metric }:
let
inherit (liminix.services) oneshot;
with_dev = if interface != null then "dev $(output ${interface} ifname)" else "";
target_hash = builtins.substring 0 12 (builtins.hashString "sha256" target);
via_hash = builtins.substring 0 12 (builtins.hashString "sha256" via);
in oneshot {
name = "route-${target_hash}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via_hash}-${if interface!=null then interface.name else ""}")}";
in
oneshot {
name = "route-${target_hash}-${
builtins.substring 0 12 (
builtins.hashString "sha256" "${via_hash}-${if interface != null then interface.name else ""}"
)
}";
up = ''
ip route add ${target} via ${via} metric ${toString metric} ${with_dev}
'';
down = ''
ip route del ${target} via ${via} ${with_dev}
'';
dependencies = [] ++ lib.optional (interface != null) interface;
dependencies = [ ] ++ lib.optional (interface != null) interface;
}

View File

@@ -6,12 +6,18 @@
## optionally also provide time service to its peers. The
## implementation used in Liminix is Chrony
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
serverOpts = types.listOf types.str;
in {
in
{
options = {
system.service.ntp = mkOption {
type = liminix.lib.types.serviceDefn;
@@ -23,23 +29,36 @@ in {
type = types.str;
default = "ntp";
};
servers = mkOption { type = types.attrsOf serverOpts; default = {}; };
pools = mkOption { type = types.attrsOf serverOpts; default = {}; };
peers = mkOption { type = types.attrsOf serverOpts; default = {}; };
servers = mkOption {
type = types.attrsOf serverOpts;
default = { };
};
pools = mkOption {
type = types.attrsOf serverOpts;
default = { };
};
peers = mkOption {
type = types.attrsOf serverOpts;
default = { };
};
makestep = mkOption {
default = null;
type = types.nullOr
(types.submodule {
type = types.nullOr (
types.submodule {
options = {
threshold = mkOption { type = types.number; default = null;};
threshold = mkOption {
type = types.number;
default = null;
};
limit = mkOption { type = types.number; };
};
});
}
);
};
allow = mkOption {
description = "subnets from which NTP clients are allowed to access the server";
type = types.listOf types.str;
default = [];
default = [ ];
};
bindaddress = mkOption {
type = types.nullOr types.str;
@@ -60,7 +79,9 @@ in {
};
};
users.ntp = {
uid = 52; gid= 52; gecos = "Unprivileged NTP user";
uid = 52;
gid = 52;
gecos = "Unprivileged NTP user";
dir = "/run/ntp";
shell = "/bin/false";
};

View File

@@ -1,25 +1,23 @@
{
liminix
, chrony
, lib
, writeText
liminix,
chrony,
lib,
writeText,
}:
params:
let
name = "ntp"; # bad name, needs to be unique
inherit (liminix.services) longrun;
inherit (lib) concatStringsSep mapAttrsToList;
configFile = p:
(mapAttrsToList (name: opts: "server ${name} ${concatStringsSep "" opts}")
p.servers)
++
(mapAttrsToList (name: opts: "pool ${name} ${concatStringsSep "" opts}")
p.pools)
++
(mapAttrsToList (name: opts: "peer ${name} ${concatStringsSep "" opts}")
p.peers)
configFile =
p:
(mapAttrsToList (name: opts: "server ${name} ${concatStringsSep "" opts}") p.servers)
++ (mapAttrsToList (name: opts: "pool ${name} ${concatStringsSep "" opts}") p.pools)
++ (mapAttrsToList (name: opts: "peer ${name} ${concatStringsSep "" opts}") p.peers)
++ lib.optional (p.user != null) "user ${p.user}"
++ (lib.optional (p.makestep != null) "makestep ${toString p.makestep.threshold} ${toString p.makestep.limit}")
++ (lib.optional (
p.makestep != null
) "makestep ${toString p.makestep.threshold} ${toString p.makestep.limit}")
++ (map (n: "allow ${n}") p.allow)
++ (lib.optional (p.bindaddress != null) "bindaddress ${p.bindaddress}")
++ (lib.optional (p.binddevice != null) "binddevice ${p.binddevice}")
@@ -28,11 +26,11 @@ let
"bindcmdaddress /" # disable unix socket
"pidfile /run/${name}.pid"
]
++ [p.extraConfig];
++ [ p.extraConfig ];
config = writeText "chrony.conf"
(concatStringsSep "\n" (configFile params));
in longrun {
config = writeText "chrony.conf" (concatStringsSep "\n" (configFile params));
in
longrun {
inherit name;
run = "${chrony}/bin/chronyd -f ${config} -d";
}

View File

@@ -97,7 +97,7 @@ in
system.outputs = rec {
dtb = liminix.builders.dtb {
inherit (config.boot) commandLine;
dts = [config.hardware.dts.src] ++ config.hardware.dts.includes;
dts = [ config.hardware.dts.src ] ++ config.hardware.dts.includes;
includes = config.hardware.dts.includePaths ++ [
"${o.kernel.headers}/include"
];
@@ -105,7 +105,8 @@ in
rootdir =
let
inherit (pkgs.pkgsBuildBuild) runCommand;
in runCommand "mktree" { } ''
in
runCommand "mktree" { } ''
mkdir -p $out/nix/store/ $out/secrets $out/boot
cp ${o.systemConfiguration}/bin/activate $out/activate
ln -s ${pkgs.s6-init-bin}/bin/init $out/init
@@ -115,14 +116,18 @@ in
done
'';
bootablerootdir =
let inherit (pkgs.pkgsBuildBuild) runCommand;
in runCommand "add-slash-boot" { } ''
let
inherit (pkgs.pkgsBuildBuild) runCommand;
in
runCommand "add-slash-boot" { } ''
cp -a ${o.rootdir} $out
${if o.bootfiles != null
then "(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.bootfiles} boot)"
else ""
}
'';
${
if o.bootfiles != null then
"(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.bootfiles} boot)"
else
""
}
'';
manifest = writeText "manifest.json" (builtins.toJSON config.filesystem.contents);
};
};

View File

@@ -1,21 +1,28 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
inherit (lib)
mkIf
mkEnableOption
mkOption
types
concatStringsSep
;
inherit (pkgs.pseudofile) dir symlink;
cfg = config.boot.loader.extlinux;
o = config.system.outputs;
cmdline = concatStringsSep " " config.boot.commandLine;
wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null;
in {
in
{
options.boot.loader.extlinux.enable = mkEnableOption "extlinux";
config = mkIf cfg.enable {
system.outputs.bootfiles = pkgs.runCommand "extlinux" {} ''
system.outputs.bootfiles = pkgs.runCommand "extlinux" { } ''
mkdir $out
cd $out
${if wantsDtb then "cp ${o.dtb} dtb" else "true"}

View File

@@ -1,21 +1,28 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
inherit (lib)
mkIf
mkEnableOption
mkOption
types
concatStringsSep
;
inherit (pkgs.pseudofile) dir symlink;
cfg = config.boot.loader.fit;
o = config.system.outputs;
cmdline = concatStringsSep " " config.boot.commandLine;
wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null;
in {
in
{
options.boot.loader.fit.enable = mkEnableOption "FIT in /boot";
config = mkIf cfg.enable {
system.outputs.bootfiles = pkgs.runCommand "boot-fit" {} ''
system.outputs.bootfiles = pkgs.runCommand "boot-fit" { } ''
mkdir $out
cd $out
cp ${o.uimage} fit

View File

@@ -1,8 +1,8 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf;
@@ -21,17 +21,20 @@ in
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand e2fsprogs;
in runCommand "mkfs.btrfs" {
depsBuildBuild = [ e2fsprogs ];
} ''
tree=${o.bootablerootdir}
size=$(du -s --apparent-size --block-size 1024 $tree |cut -f1)
# add 25% for filesystem overhead
size=$(( 5 * $size / 4))
dd if=/dev/zero of=$out bs=1024 count=$size
echo "not implemented" ; exit 1
# mke2fs -t ext4 -j -d $tree $out
'';
in
runCommand "mkfs.btrfs"
{
depsBuildBuild = [ e2fsprogs ];
}
''
tree=${o.bootablerootdir}
size=$(du -s --apparent-size --block-size 1024 $tree |cut -f1)
# add 25% for filesystem overhead
size=$(( 5 * $size / 4))
dd if=/dev/zero of=$out bs=1024 count=$size
echo "not implemented" ; exit 1
# mke2fs -t ext4 -j -d $tree $out
'';
};
};
}

View File

@@ -1,8 +1,8 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf;
@@ -23,16 +23,19 @@ in
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand e2fsprogs;
in runCommand "mkfs.ext4" {
depsBuildBuild = [ e2fsprogs ];
} ''
tree=${o.bootablerootdir}
size=$(du -s --apparent-size --block-size 1024 $tree |cut -f1)
# add 25% for filesystem overhead
size=$(( 5 * $size / 4))
dd if=/dev/zero of=$out bs=1024 count=$size
mke2fs -t ext4 -j -d $tree $out
'';
in
runCommand "mkfs.ext4"
{
depsBuildBuild = [ e2fsprogs ];
}
''
tree=${o.bootablerootdir}
size=$(du -s --apparent-size --block-size 1024 $tree |cut -f1)
# add 25% for filesystem overhead
size=$(( 5 * $size / 4))
dd if=/dev/zero of=$out bs=1024 count=$size
mke2fs -t ext4 -j -d $tree $out
'';
};
};
}

View File

@@ -1,11 +1,16 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkEnableOption mkOption mkIf types;
inherit (lib)
mkEnableOption
mkOption
mkIf
types
;
inherit (pkgs) runCommand;
in
{
@@ -29,13 +34,15 @@ in
kernel.config = {
BLK_DEV_INITRD = "y";
INITRAMFS_SOURCE = builtins.toJSON "${config.system.outputs.initramfs}";
# INITRAMFS_COMPRESSION_LZO = "y";
# INITRAMFS_COMPRESSION_LZO = "y";
};
system.outputs = {
initramfs =
let inherit (pkgs.pkgsBuildBuild) gen_init_cpio;
in runCommand "initramfs.cpio" {} ''
let
inherit (pkgs.pkgsBuildBuild) gen_init_cpio;
in
runCommand "initramfs.cpio" { } ''
cat << SPECIALS | ${gen_init_cpio}/bin/gen_init_cpio /dev/stdin > $out
dir /proc 0755 0 0
dir /dev 0755 0 0

View File

@@ -1,8 +1,8 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf;
@@ -27,14 +27,16 @@ in
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
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 )
'';
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

@@ -1,14 +1,15 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkOption types;
o = config.system.outputs;
phram_address = lib.toHexString (config.hardware.ram.startAddress + 256 * 1024 * 1024);
in {
in
{
options.system.outputs = {
mbrimage = mkOption {
type = types.package;
@@ -20,7 +21,7 @@ in {
the contents of ``outputs.rootfs`` as its only partition.
'';
};
vmdisk = mkOption { type = types.package; };
vmdisk = mkOption { type = types.package; };
};
config = {
@@ -28,15 +29,18 @@ in {
mbrimage =
let
o = config.system.outputs;
in pkgs.runCommand "mbrimage" {
depsBuildBuild = [ pkgs.pkgsBuildBuild.util-linux ];
} ''
# leave 4 sectors at start for partition table
# and alignment to 2048 bytes (does that help?)
dd if=${o.rootfs} of=$out bs=512 seek=4 conv=sync
echo '4,-,L,*' | sfdisk $out
'';
vmdisk = pkgs.runCommand "vmdisk" {} ''
in
pkgs.runCommand "mbrimage"
{
depsBuildBuild = [ pkgs.pkgsBuildBuild.util-linux ];
}
''
# leave 4 sectors at start for partition table
# and alignment to 2048 bytes (does that help?)
dd if=${o.rootfs} of=$out bs=512 seek=4 conv=sync
echo '4,-,L,*' | sfdisk $out
'';
vmdisk = pkgs.runCommand "vmdisk" { } ''
mkdir $out
cd $out
ln -s ${o.mbrimage} ./mbrimage

View File

@@ -1,17 +1,18 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkOption types concatStringsSep;
inherit (config.boot) tftp;
in {
in
{
options.system.outputs = {
firmware = mkOption {
type = types.package;
internal = true; # component of mtdimage
internal = true; # component of mtdimage
description = ''
Binary image (combining kernel, FDT, rootfs, initramfs
if needed, etc) for the target device.
@@ -19,7 +20,7 @@ in {
};
flash-scr = mkOption {
type = types.package;
internal = true; # component of mtdimage
internal = true; # component of mtdimage
description = ''
Copy-pastable U-Boot commands to TFTP download the
image and write it to flash
@@ -60,13 +61,15 @@ in {
config = {
kernel = {
config = {
# this needs to be conditional on "not qemu"
MTD_SPLIT_UIMAGE_FW = "y";
} // lib.optionalAttrs (pkgs.stdenv.isMips) {
# https://stackoverflow.com/questions/26466470/can-the-logical-erase-block-size-of-an-mtd-device-be-increased
MTD_SPI_NOR_USE_4K_SECTORS = "n";
};
config =
{
# this needs to be conditional on "not qemu"
MTD_SPLIT_UIMAGE_FW = "y";
}
// lib.optionalAttrs (pkgs.stdenv.isMips) {
# https://stackoverflow.com/questions/26466470/can-the-logical-erase-block-size-of-an-mtd-device-be-increased
MTD_SPI_NOR_USE_4K_SECTORS = "n";
};
};
programs.busybox.applets = [
@@ -78,14 +81,17 @@ in {
let
o = config.system.outputs;
bs = toString config.hardware.flash.eraseBlockSize;
in pkgs.runCommand "firmware" {} ''
in
pkgs.runCommand "firmware" { } ''
dd if=${o.uimage} of=$out bs=${bs} conv=sync
dd if=${o.rootfs} of=$out bs=${bs} conv=sync,nocreat,notrunc oflag=append
'';
mtdimage =
let o = config.system.outputs; in
let
o = config.system.outputs;
in
# could use trivial-builders.linkFarmFromDrvs here?
pkgs.runCommand "mtdimage" {} ''
pkgs.runCommand "mtdimage" { } ''
mkdir $out
cd $out
ln -s ${o.firmware} firmware.bin
@@ -96,24 +102,24 @@ in {
ln -s ${o.uimage} uimage
ln -s ${o.dtb} dtb
ln -s ${o.flash-scr} flash.scr
'';
'';
flash-scr =
let
inherit (pkgs.lib.trivial) toHexString;
inherit (config.hardware) flash;
in
pkgs.buildPackages.runCommand "" {} ''
imageSize=$(stat -L -c %s ${config.system.outputs.firmware})
cat > $out << EOF
setenv serverip ${tftp.serverip}
setenv ipaddr ${tftp.ipaddr}
tftp 0x${toHexString tftp.loadAddress} result/firmware.bin
erase 0x${toHexString flash.address} +0x${toHexString flash.size}
cp.b 0x${toHexString tftp.loadAddress} 0x${toHexString flash.address} \''${filesize}
echo command line was ${builtins.toJSON (concatStringsSep " " config.boot.commandLine)}
EOF
'';
pkgs.buildPackages.runCommand "" { } ''
imageSize=$(stat -L -c %s ${config.system.outputs.firmware})
cat > $out << EOF
setenv serverip ${tftp.serverip}
setenv ipaddr ${tftp.ipaddr}
tftp 0x${toHexString tftp.loadAddress} result/firmware.bin
erase 0x${toHexString flash.address} +0x${toHexString flash.size}
cp.b 0x${toHexString tftp.loadAddress} 0x${toHexString flash.address} \''${filesize}
echo command line was ${builtins.toJSON (concatStringsSep " " config.boot.commandLine)}
EOF
'';
};
};
}

View File

@@ -10,8 +10,7 @@ let
in
{
config = mkIf (config.rootfsType == "squashfs") {
system.outputs.rootfs =
liminix.builders.squashfs config.filesystem.contents;
system.outputs.rootfs = liminix.builders.squashfs config.filesystem.contents;
kernel.config = {
SQUASHFS = "y";
SQUASHFS_XZ = "y";

View File

@@ -1,11 +1,16 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkEnableOption mkOption mkIf types;
inherit (lib)
mkEnableOption
mkOption
mkIf
types
;
inherit (pkgs) runCommand;
in
{
@@ -22,7 +27,6 @@ in
};
};
config = {
system.outputs.systemConfiguration =
pkgs.systemconfig config.filesystem.contents;
system.outputs.systemConfiguration = pkgs.systemconfig config.filesystem.contents;
};
}

View File

@@ -1,15 +1,16 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkOption types concatStringsSep;
cfg = config.boot.tftp;
hw = config.hardware;
arch = pkgs.stdenv.hostPlatform.linuxArch;
in {
in
{
imports = [ ../ramdisk.nix ];
options.boot.tftp = {
freeSpaceBytes = mkOption {
@@ -17,7 +18,10 @@ in {
default = 0;
};
kernelFormat = mkOption {
type = types.enum [ "zimage" "uimage" ];
type = types.enum [
"zimage"
"uimage"
];
default = "uimage";
};
compressRoot = mkOption {
@@ -61,14 +65,22 @@ in {
assert config.rootfsType != "ubifs";
let
o = config.system.outputs;
image = let choices = {
uimage = o.uimage;
zimage = o.kernel.zImage;
}; in choices.${cfg.kernelFormat};
bootCommand = let choices = {
uimage = "bootm";
zimage = "bootz";
}; in choices.${cfg.kernelFormat};
image =
let
choices = {
uimage = o.uimage;
zimage = o.kernel.zImage;
};
in
choices.${cfg.kernelFormat};
bootCommand =
let
choices = {
uimage = "bootm";
zimage = "bootz";
};
in
choices.${cfg.kernelFormat};
cmdline = concatStringsSep " " config.boot.commandLine;
objcopy = "${pkgs.stdenv.cc.bintools.targetPrefix}objcopy";
@@ -77,7 +89,16 @@ in {
rm -f vmlinux.bin.lzma ; lzma -k -z vmlinux.bin
'';
in
pkgs.runCommand "tftpboot" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ lzma dtc pkgs.stdenv.cc ubootTools ]; } ''
pkgs.runCommand "tftpboot"
{
nativeBuildInputs = with pkgs.pkgsBuildBuild; [
lzma
dtc
pkgs.stdenv.cc
ubootTools
];
}
''
mkdir $out
cd $out
binsize() { local s=$(stat -L -c %s $1); echo $(($s + 0x1000 &(~0xfff))); }
@@ -97,17 +118,19 @@ in {
# end of the kernel is free
dtbStart=$(($rootfsStart + $rootfsSize))
${if cfg.compressRoot
then ''
lzma -z9cv ${o.rootfs} > rootfs.lz
rootfsLzStart=$dtbStart
rootfsLzSize=$(binsize rootfs.lz)
dtbStart=$(($dtbStart + $rootfsLzSize))
''
else ''
ln -s ${o.rootfs} rootfs
''
}
${
if cfg.compressRoot then
''
lzma -z9cv ${o.rootfs} > rootfs.lz
rootfsLzStart=$dtbStart
rootfsLzSize=$(binsize rootfs.lz)
dtbStart=$(($dtbStart + $rootfsLzSize))
''
else
''
ln -s ${o.rootfs} rootfs
''
}
cat ${o.dtb} > dtb
address_cells=$(fdtget dtb / '#address-cells')
@@ -128,37 +151,40 @@ in {
dtbSize=$(binsize ./dtb )
${if cfg.appendDTB then ''
imageStart=$dtbStart
# re-package image with updated dtb
cat ${o.kernel} > vmlinux.elf
${objcopy} --update-section .appended_dtb=dtb vmlinux.elf
${stripAndZip}
mkimage -A ${arch} -O linux -T kernel -C lzma -a $(hex ${toString hw.loadAddress}) -e $(hex ${toString hw.entryPoint}) -n '${lib.toUpper arch} Liminix Linux tftpboot' -d vmlinux.bin.lzma image
# dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1
tftpcmd="tftpboot $(hex $imageStart) result/image "
bootcmd="bootm $(hex $imageStart)"
'' else ''
imageStart=$(($dtbStart + $dtbSize))
tftpcmd="tftpboot $(hex $imageStart) result/image; tftpboot $(hex $dtbStart) result/dtb "
ln -s ${image} image
bootcmd="${bootCommand} $(hex $imageStart) - $(hex $dtbStart)"
''}
${
if cfg.appendDTB then
''
imageStart=$dtbStart
# re-package image with updated dtb
cat ${o.kernel} > vmlinux.elf
${objcopy} --update-section .appended_dtb=dtb vmlinux.elf
${stripAndZip}
mkimage -A ${arch} -O linux -T kernel -C lzma -a $(hex ${toString hw.loadAddress}) -e $(hex ${toString hw.entryPoint}) -n '${lib.toUpper arch} Liminix Linux tftpboot' -d vmlinux.bin.lzma image
# dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1
tftpcmd="tftpboot $(hex $imageStart) result/image "
bootcmd="bootm $(hex $imageStart)"
''
else
''
imageStart=$(($dtbStart + $dtbSize))
tftpcmd="tftpboot $(hex $imageStart) result/image; tftpboot $(hex $dtbStart) result/dtb "
ln -s ${image} image
bootcmd="${bootCommand} $(hex $imageStart) - $(hex $dtbStart)"
''
}
cat > boot.scr << EOF
setenv serverip ${cfg.serverip}
setenv ipaddr ${cfg.ipaddr}
${
if cfg.compressRoot
then "tftpboot $(hex $rootfsLzStart) result/rootfs.lz"
else "tftpboot $(hex $rootfsStart) result/rootfs"
if cfg.compressRoot then
"tftpboot $(hex $rootfsLzStart) result/rootfs.lz"
else
"tftpboot $(hex $rootfsStart) result/rootfs"
}; $tftpcmd
${if cfg.compressRoot
then "lzmadec $(hex $rootfsLzStart) $(hex $rootfsStart); "
else ""
} $bootcmd
${if cfg.compressRoot then "lzmadec $(hex $rootfsLzStart) $(hex $rootfsStart); " else ""} $bootcmd
EOF
'';
'';
};
};

View File

@@ -1,14 +1,15 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkOption types;
o = config.system.outputs;
cfg = config.tplink-safeloader;
in {
in
{
options.tplink-safeloader = {
board = mkOption {
type = types.str;
@@ -53,9 +54,10 @@ in {
config = {
system.outputs = rec {
tplink-safeloader =
pkgs.runCommand "tplink" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ firmware-utils ]; } ''
tplink-safeloader -B "${cfg.board}" -k "${o.uimage}" -r "${o.rootfs}" -o $out
'';
pkgs.runCommand "tplink" { nativeBuildInputs = with pkgs.pkgsBuildBuild; [ firmware-utils ]; }
''
tplink-safeloader -B "${cfg.board}" -k "${o.uimage}" -r "${o.rootfs}" -o $out
'';
};
};
}

View File

@@ -1,8 +1,8 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf mkOption types;
@@ -15,7 +15,7 @@ in
config = mkIf (config.rootfsType == "ubifs") {
kernel.config = {
MTD_UBI="y";
MTD_UBI = "y";
UBIFS_FS = "y";
UBIFS_FS_SECURITY = "n";
};
@@ -25,13 +25,16 @@ in
let
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
cfg = config.hardware.ubi;
in runCommand "mkfs.ubifs" {
depsBuildBuild = [ mtdutils ];
} ''
mkdir tmp
tree=${o.bootablerootdir}
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.logicalEraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
'';
in
runCommand "mkfs.ubifs"
{
depsBuildBuild = [ mtdutils ];
}
''
mkdir tmp
tree=${o.bootablerootdir}
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.logicalEraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
'';
};
};
}

View File

@@ -1,8 +1,8 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf mkOption types;
@@ -13,108 +13,111 @@ let
setenv loadaddr ${lib.toHexString cfg.loadAddress}
tftpboot $loadaddr result/rootfs
ubi write $loadaddr liminix $filesize
'';
in {
'';
in
{
options.system.outputs = {
ubimage = mkOption {
type = types.package;
description = ''
ubimage
*******
ubimage
*******
This output provides a UBIFS filesystem image and a small U-Boot script
to make the manual installation process very slightly simpler. You will
need a serial connection and a network connection to a TFTP server
containing the filesystem image it creates.
This output provides a UBIFS filesystem image and a small U-Boot script
to make the manual installation process very slightly simpler. You will
need a serial connection and a network connection to a TFTP server
containing the filesystem image it creates.
.. warning:: These steps were tested on a Belkin RT3200 (also known as
Linksys E8450). Other devices may be set up differently,
so use them as inspiration and don't just paste them
blindly.
.. warning:: These steps were tested on a Belkin RT3200 (also known as
Linksys E8450). Other devices may be set up differently,
so use them as inspiration and don't just paste them
blindly.
1) determine which MTD device is being used for UBI, and the partition name:
1) determine which MTD device is being used for UBI, and the partition name:
.. code-block:: console
.. code-block:: console
uboot> ubi part
Device 0: ubi0, MTD partition ubi
uboot> ubi part
Device 0: ubi0, MTD partition ubi
In this case the important value is ``ubi0``
In this case the important value is ``ubi0``
2) list the available volumes and create a new one on which to install Liminix
2) list the available volumes and create a new one on which to install Liminix
.. code-block:: console
.. code-block:: console
uboot> ubi info l
[ copious output scrolls past ]
uboot> ubi info l
[ copious output scrolls past ]
Expect there to be existing volumes and for some or all of them to be
important. Unless you know what you're doing, don't remove anything
whose name suggests it's related to uboot, or any kind of backup or
recovery partition. To see how much space is free:
Expect there to be existing volumes and for some or all of them to be
important. Unless you know what you're doing, don't remove anything
whose name suggests it's related to uboot, or any kind of backup or
recovery partition. To see how much space is free:
.. code-block:: console
.. code-block:: console
uboot> ubi info
[ ... ]
UBI: available PEBs: 823
uboot> ubi info
[ ... ]
UBI: available PEBs: 823
Now we can make our new root volume
Now we can make our new root volume
.. code-block:: console
.. code-block:: console
uboot> ubi create liminix -
uboot> ubi create liminix -
3) transfer the root filesystem from the build system and write it to
the new volume. Paste the contents of :file:`result/flash.scr` one line at a time
into U-Boot:
3) transfer the root filesystem from the build system and write it to
the new volume. Paste the contents of :file:`result/flash.scr` one line at a time
into U-Boot:
.. code-block:: console
.. code-block:: console
uboot> setenv serverip 10.0.0.1
uboot> setenv ipaddr 10.0.0.8
uboot> setenv loadaddr 4007FF28
uboot> tftpboot $loadaddr result/rootfs
uboot> ubi write $loadaddr liminix $filesize
uboot> setenv serverip 10.0.0.1
uboot> setenv ipaddr 10.0.0.8
uboot> setenv loadaddr 4007FF28
uboot> tftpboot $loadaddr result/rootfs
uboot> ubi write $loadaddr liminix $filesize
Now we have the root filesystem installed on the device. You
can even mount it and poke around using :command:`ubifsmount ubi0:liminix; ubifsls /`
Now we have the root filesystem installed on the device. You
can even mount it and poke around using :command:`ubifsmount ubi0:liminix; ubifsls /`
4) optional: before you configure the device to boot into Liminix
automatically, you can try booting it by hand to see if it works:
4) optional: before you configure the device to boot into Liminix
automatically, you can try booting it by hand to see if it works:
.. code-block:: console
.. code-block:: console
uboot> ubifsmount ubi0:liminix
uboot> ubifsload ''${loadaddr} boot/fit
uboot> bootm ''${loadaddr}
uboot> ubifsmount ubi0:liminix
uboot> ubifsload ''${loadaddr} boot/fit
uboot> bootm ''${loadaddr}
Once you've done this and you're happy with it, reset the device to
return to U-Boot.
Once you've done this and you're happy with it, reset the device to
return to U-Boot.
5) Instructions for configuring autoboot are likely to be very
device-dependent and you should consult the Liminix documentation for
your device. (If you're bringing up a new device, some detective work
may be needed. Try running `printenv` and trace through the flow of
execution from (probably) :command:`$bootcmd` and look for a suitable
variable to change)
5) Instructions for configuring autoboot are likely to be very
device-dependent and you should consult the Liminix documentation for
your device. (If you're bringing up a new device, some detective work
may be needed. Try running `printenv` and trace through the flow of
execution from (probably) :command:`$bootcmd` and look for a suitable
variable to change)
6) Now you can reboot the device into Liminix
6) Now you can reboot the device into Liminix
.. code-block:: console
.. code-block:: console
uboot> reset
uboot> reset
'';
};
};
config.system.outputs.ubimage =
assert config.rootfsType == "ubifs";
let o = config.system.outputs; in
pkgs.runCommand "ubimage" {} ''
let
o = config.system.outputs;
in
pkgs.runCommand "ubimage" { } ''
mkdir $out
cd $out
ln -s ${o.rootfs} rootfs
ln -s ${instructions} flash.scr
'';
'';
}

View File

@@ -1,93 +1,112 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf mkOption types concatStringsSep optionalString;
inherit (lib)
mkIf
mkOption
types
concatStringsSep
optionalString
;
in
{
imports = [
./initramfs.nix
./ubifs.nix
];
{
imports = [
./initramfs.nix
./ubifs.nix
];
options.hardware.ubi = {
minIOSize = mkOption { type = types.str; };
eraseBlockSize = mkOption { type = types.str; }; # LEB
maxLEBcount = mkOption { type = types.str; }; # LEB
};
options.system.outputs.ubivolume = mkOption {
type = types.package;
options.hardware.ubi = {
minIOSize = mkOption { type = types.str; };
eraseBlockSize = mkOption { type = types.str; }; # LEB
maxLEBcount = mkOption { type = types.str; }; # LEB
};
options.system.outputs.ubivolume = mkOption {
type = types.package;
};
config = mkIf (config.rootfsType == "ubifs") {
kernel.config = {
MTD_UBI = "y";
UBIFS_FS = "y";
UBIFS_FS_SECURITY = "n";
};
boot.initramfs.enable = true;
config = mkIf (config.rootfsType == "ubifs") {
kernel.config = {
MTD_UBI="y";
UBIFS_FS = "y";
UBIFS_FS_SECURITY = "n";
};
boot.initramfs.enable = true;
system.outputs.ubivolume =
system.outputs.ubivolume =
let
inherit (pkgs.pkgsBuildBuild) runCommand;
ubiVolume = ({ name, volumeId, image, flags ? [] }:
''
[${name}]
mode=ubi
vol_id=${toString volumeId}
vol_type=dynamic
vol_name=${name}
vol_alignment=1
${optionalString (image != null) ''
image=${image}
''}
${optionalString (image == null) ''
vol_size=1MiB
''}
${optionalString (flags != []) ''
vol_flags=${concatStringsSep "," flags}
''}
'');
ubiVolume = (
{
name,
volumeId,
image,
flags ? [ ],
}:
''
[${name}]
mode=ubi
vol_id=${toString volumeId}
vol_type=dynamic
vol_name=${name}
vol_alignment=1
${optionalString (image != null) ''
image=${image}
''}
${optionalString (image == null) ''
vol_size=1MiB
''}
${optionalString (flags != [ ]) ''
vol_flags=${concatStringsSep "," flags}
''}
''
);
ubiImage = (volumes:
let
ubinizeConfig = pkgs.writeText "ubinize.conf" (concatStringsSep "\n" volumes);
inherit (pkgs.pkgsBuildBuild) mtdutils;
in
runCommand "ubinize" {
depsBuildBuild = [ mtdutils ];
# block size := 128kb
# page size := 2048
# ubninize opts := -E 5
} ''
ubinize -Q "$SOURCE_DATE_EPOCH" -o $out \
-p ${config.hardware.ubi.physicalEraseBlockSize} -m ${config.hardware.ubi.minIOSize} \
-e ${config.hardware.ubi.logicalEraseBlockSize} \
${ubinizeConfig}
'');
ubiImage = (
volumes:
let
ubinizeConfig = pkgs.writeText "ubinize.conf" (concatStringsSep "\n" volumes);
inherit (pkgs.pkgsBuildBuild) mtdutils;
in
runCommand "ubinize"
{
depsBuildBuild = [ mtdutils ];
# block size := 128kb
# page size := 2048
# ubninize opts := -E 5
}
''
ubinize -Q "$SOURCE_DATE_EPOCH" -o $out \
-p ${config.hardware.ubi.physicalEraseBlockSize} -m ${config.hardware.ubi.minIOSize} \
-e ${config.hardware.ubi.logicalEraseBlockSize} \
${ubinizeConfig}
''
);
ubiDisk = ({ initramfs }:
let
initramfsUbi = ubiVolume {
name = "rootfs";
volumeId = 0;
image = initramfs;
flags = [ "autoresize" ];
};
in
ubiDisk = (
{ initramfs }:
let
initramfsUbi = ubiVolume {
name = "rootfs";
volumeId = 0;
image = initramfs;
flags = [ "autoresize" ];
};
in
ubiImage [
initramfsUbi
]);
]
);
disk = ubiDisk {
initramfs = config.system.outputs.rootfs; # ???
};
in
disk;
disk;
};
}

View File

@@ -1,8 +1,8 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf;
@@ -25,13 +25,12 @@ in
'';
};
config.system.outputs.updater =
runCommand "buildUpdater" { } ''
mkdir -p $out/bin $out/etc
cp ${o.kernel.config} $out/etc/kconfig
substitute ${./update.sh} $out/bin/update.sh \
--subst-var-by toplevel ${o.systemConfiguration} \
--subst-var-by min_copy_closure ${min-copy-closure}
chmod +x $out/bin/update.sh
'';
config.system.outputs.updater = runCommand "buildUpdater" { } ''
mkdir -p $out/bin $out/etc
cp ${o.kernel.config} $out/etc/kconfig
substitute ${./update.sh} $out/bin/update.sh \
--subst-var-by toplevel ${o.systemConfiguration} \
--subst-var-by min_copy_closure ${min-copy-closure}
chmod +x $out/bin/update.sh
'';
}

View File

@@ -1,8 +1,8 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkOption types concatStringsSep;
@@ -49,12 +49,15 @@ in
let
inherit (config.system.outputs) rootfs kernel manifest;
cmdline = builtins.toJSON (concatStringsSep " " config.boot.commandLine);
makeBootableImage = pkgs.runCommandCC "objcopy" {}
(if pkgs.stdenv.hostPlatform.isAarch
then "${pkgs.stdenv.cc.targetPrefix}objcopy -O binary -R .comment -S ${kernel} $out"
else "cp ${kernel} $out");
makeBootableImage = pkgs.runCommandCC "objcopy" { } (
if pkgs.stdenv.hostPlatform.isAarch then
"${pkgs.stdenv.cc.targetPrefix}objcopy -O binary -R .comment -S ${kernel} $out"
else
"cp ${kernel} $out"
);
phram_address = lib.toHexString (config.hardware.ram.startAddress + 256 * 1024 * 1024);
in pkgs.runCommand "vmroot" {} ''
in
pkgs.runCommand "vmroot" { } ''
mkdir $out
cd $out
ln -s ${rootfs} rootfs
@@ -67,7 +70,7 @@ in
${pkgs.pkgsBuildBuild.run-liminix-vm}/bin/run-liminix-vm --command-line ${cmdline} --arch ${pkgs.stdenv.hostPlatform.qemuArch} --phram-address 0x${phram_address} \$* ${makeBootableImage} ${config.system.outputs.rootfs}
EOF
chmod +x run.sh
'';
'';
};
};
}

View File

@@ -1,25 +1,26 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkIf mkOption types;
models = "6b e1 6f e1 ff ff ff ff ff ff";
in {
in
{
options.system.outputs = {
zyxel-nwa-fit = mkOption {
type = types.package;
description = ''
zyxel-nwa-fit
*************
zyxel-nwa-fit
*************
This output provides a FIT image for Zyxel NWA series
containing a kernel image and an UBIFS rootfs.
This output provides a FIT image for Zyxel NWA series
containing a kernel image and an UBIFS rootfs.
It can usually be used as a factory image to install Liminix
on a system with pre-existing firmware and OS.
It can usually be used as a factory image to install Liminix
on a system with pre-existing firmware and OS.
'';
};
};
@@ -34,38 +35,43 @@ on a system with pre-existing firmware and OS.
let
o = config.system.outputs;
# 8129kb padding.
paddedKernel = pkgs.runCommand "padded-kernel" {} ''
paddedKernel = pkgs.runCommand "padded-kernel" { } ''
cp --no-preserve=mode ${o.uimage} $out
dd if=/dev/zero of=$out bs=1 count=1 seek=8388607
'';
firmwareImage = pkgs.runCommand "firmware-image" {} ''
firmwareImage = pkgs.runCommand "firmware-image" { } ''
cat ${paddedKernel} ${o.ubivolume} > $out
'';
dts = pkgs.writeText "image.its" ''
/dts-v1/;
/dts-v1/;
/ {
description = "Zyxel FIT (Flattened Image Tree)";
compat-models = [${models}];
#address-cells = <1>;
/ {
description = "Zyxel FIT (Flattened Image Tree)";
compat-models = [${models}];
#address-cells = <1>;
images {
firmware {
data = /incbin/("${firmwareImage}");
type = "firmware";
compression = "none";
hash@1 {
algo = "sha1";
images {
firmware {
data = /incbin/("${firmwareImage}");
type = "firmware";
compression = "none";
hash@1 {
algo = "sha1";
};
};
};
};
};
'';
'';
in
pkgs.runCommand "zyxel-nwa-fit-${config.boot.imageType}" {
nativeBuildInputs = [ pkgs.pkgsBuildBuild.ubootTools pkgs.pkgsBuildBuild.dtc ];
} ''
mkimage -f ${dts} $out
'';
pkgs.runCommand "zyxel-nwa-fit-${config.boot.imageType}"
{
nativeBuildInputs = [
pkgs.pkgsBuildBuild.ubootTools
pkgs.pkgsBuildBuild.dtc
];
}
''
mkimage -f ${dts} $out
'';
};
}

View File

@@ -1,20 +1,32 @@
{ writeAshScript, liminix, svc, lib, serviceFns, output-template }:
{
writeAshScript,
liminix,
svc,
lib,
serviceFns,
output-template,
}:
{
command,
name,
debug
, username,
debug,
username,
password,
lcpEcho,
ppp-options,
dependencies ? []
} :
dependencies ? [ ],
}:
let
inherit (lib) optional optionals escapeShellArgs concatStringsSep;
inherit (lib)
optional
optionals
escapeShellArgs
concatStringsSep
;
inherit (liminix.services) longrun;
inherit (builtins) toJSON toString typeOf;
ip-up = writeAshScript "ip-up" {} ''
ip-up = writeAshScript "ip-up" { } ''
exec >&5 2>&5
. ${serviceFns}
in_outputs ${name}
@@ -28,7 +40,7 @@ let
if test -n "''${DNS2}" ;then echo ''${DNS2} > ns2 ; fi
test -e ipv6-address && echo >/proc/self/fd/10
'';
ip6-up = writeAshScript "ip6-up" {} ''
ip6-up = writeAshScript "ip6-up" { } ''
exec >&5 2>&5
. ${serviceFns}
in_outputs ${name}
@@ -37,35 +49,61 @@ let
test -e ifname && echo >/proc/self/fd/10
'';
literal_or_output =
let v = o: ({
string = toJSON;
int = toJSON;
lambda = (o: "output(${toJSON (o "service")}, ${toJSON (o "path")})");
}.${typeOf o}) o;
in o: "{{ ${v o} }}";
let
v =
o:
(
{
string = toJSON;
int = toJSON;
lambda = (o: "output(${toJSON (o "service")}, ${toJSON (o "path")})");
}
.${typeOf o}
)
o;
in
o: "{{ ${v o} }}";
ppp-options' =
["+ipv6" "noauth"]
[
"+ipv6"
"noauth"
]
++ optional debug "debug"
++ optionals (username != null) ["name" (literal_or_output username)]
++ optionals (password != null) ["password" (literal_or_output password)]
++ optionals (username != null) [
"name"
(literal_or_output username)
]
++ optionals (password != null) [
"password"
(literal_or_output password)
]
++ optional lcpEcho.adaptive "lcp-echo-adaptive"
++ optionals (lcpEcho.interval != null)
["lcp-echo-interval" (toString lcpEcho.interval)]
++ optionals (lcpEcho.failure != null)
["lcp-echo-failure" (toString lcpEcho.failure)]
++ optionals (lcpEcho.interval != null) [
"lcp-echo-interval"
(toString lcpEcho.interval)
]
++ optionals (lcpEcho.failure != null) [
"lcp-echo-failure"
(toString lcpEcho.failure)
]
++ ppp-options
++ ["ip-up-script" ip-up
"ipv6-up-script" ip6-up
"ipparam" name
"nodetach"
# usepeerdns requests DNS servers from peer (which is good),
# then attempts to write them to /nix/store/xxxx/ppp/resolv.conf
# which causes an unsightly but inconsequential error message
"usepeerdns"
"nodefaultroute"
"logfd" "2"
];
++ [
"ip-up-script"
ip-up
"ipv6-up-script"
ip6-up
"ipparam"
name
"nodetach"
# usepeerdns requests DNS servers from peer (which is good),
# then attempts to write them to /nix/store/xxxx/ppp/resolv.conf
# which causes an unsightly but inconsequential error message
"usepeerdns"
"nodefaultroute"
"logfd"
"2"
];
service = longrun {
inherit name;
run = ''
@@ -77,12 +115,15 @@ let
${command}
'';
notification-fd = 10;
timeout-up = if lcpEcho.failure != null
then (10 + lcpEcho.failure * lcpEcho.interval) * 1000
else 60 * 1000;
timeout-up =
if lcpEcho.failure != null then (10 + lcpEcho.failure * lcpEcho.interval) * 1000 else 60 * 1000;
inherit dependencies;
};
in svc.secrets.subscriber.build {
watch = lib.filter (n: typeOf n=="lambda") [ username password ];
in
svc.secrets.subscriber.build {
watch = lib.filter (n: typeOf n == "lambda") [
username
password
];
inherit service;
}

View File

@@ -13,18 +13,24 @@
## conjunction with a DHCP uplink, or other more creative forms of
## network connection
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
mkStringOption =
description: mkOption {
description:
mkOption {
type = types.nullOr types.str;
default = null;
inherit description;
};
in {
in
{
imports = [ ../secrets ];
options = {
system.service.pppoe = mkOption {
@@ -61,7 +67,7 @@ in {
default = 3;
description = "send an LCP echo-request frame to the peer every n seconds";
};
failure = mkOption {
failure = mkOption {
type = types.nullOr types.int;
default = 3;
description = "terminate connection if n LCP echo-requests are sent without receiving a valid LCP echo-reply";
@@ -75,7 +81,7 @@ in {
ppp-options = mkOption {
type = types.listOf types.str;
description = "options supplied on ppp command line";
default = [];
default = [ ];
};
};
system.service.l2tp = config.system.callService ./l2tp.nix {
@@ -104,7 +110,7 @@ in {
default = 3;
description = "send an LCP echo-request frame to the peer every n seconds";
};
failure = mkOption {
failure = mkOption {
type = types.nullOr types.int;
default = 3;
description = "terminate connection if n LCP echo-requests are sent without receiving a valid LCP echo-reply";
@@ -117,7 +123,7 @@ in {
};
ppp-options = mkOption {
type = types.listOf types.str;
default = [];
default = [ ];
description = "options supplied on ppp command line";
};
};

View File

@@ -7,14 +7,15 @@
writeAshScript,
writeText,
xl2tpd,
callPackage
} :
{ lns,
callPackage,
}:
{
lns,
ppp-options,
lcpEcho,
username,
password,
debug
debug,
}:
let
name = "${lns}.l2tp";
@@ -31,8 +32,16 @@ let
max redials = 2 # this gives 1 actual retry, as xl2tpd can't count
'';
control = "/run/${name}/control";
in common {
inherit name debug username password lcpEcho ppp-options;
in
common {
inherit
name
debug
username
password
lcpEcho
ppp-options
;
command = ''
touch ${control}
exec ${xl2tpd}/bin/xl2tpd -D -p /run/${name}/${name}.pid -c ${conf} -C ${control}

View File

@@ -7,22 +7,32 @@
serviceFns,
svc,
writeAshScript,
callPackage
} :
{ interface,
callPackage,
}:
{
interface,
ppp-options,
lcpEcho,
username,
password,
debug
debug,
}:
let
name = "${interface.name}.pppoe";
common = callPackage ./common.nix { inherit svc; };
timeoutOpt = if lcpEcho.interval != null then "-T ${builtins.toString (4 * lcpEcho.interval)}" else "";
in common {
inherit name debug username password lcpEcho ppp-options;
timeoutOpt =
if lcpEcho.interval != null then "-T ${builtins.toString (4 * lcpEcho.interval)}" else "";
in
common {
inherit
name
debug
username
password
lcpEcho
ppp-options
;
command = ''
exec ${ppp}/bin/pppd pty "${pppoe}/bin/pppoe ${timeoutOpt} -I $(output ${interface} ifname)" file /run/${name}/ppp-options
'';

View File

@@ -1,8 +1,18 @@
{ config, pkgs, lib, ... } :
{
config,
pkgs,
lib,
...
}:
let
svc = config.system.service;
cfg = config.profile.gateway;
inherit (lib) mkOption mkEnableOption mkIf types;
inherit (lib)
mkOption
mkEnableOption
mkIf
types
;
inherit (pkgs) liminix serviceFns;
inherit (liminix.services) bundle oneshot;
hostaps =
@@ -14,24 +24,27 @@ let
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
in lib.mapAttrs'
(name : value :
let
attrs = defaults // { ssid = name; } // value;
in lib.nameValuePair
"hostap-${name}"
(svc.hostapd.build {
interface = attrs.interface;
params = lib.filterAttrs (k: v: k != "interface") attrs;
}))
cfg.wireless.networks;
in {
in
lib.mapAttrs' (
name: value:
let
attrs = defaults // { ssid = name; } // value;
in
lib.nameValuePair "hostap-${name}" (
svc.hostapd.build {
interface = attrs.interface;
params = lib.filterAttrs (k: v: k != "interface") attrs;
}
)
) cfg.wireless.networks;
in
{
options.profile.gateway = {
lan = {
interfaces = mkOption {
type = types.listOf liminix.lib.types.interface;
default = [];
default = [ ];
};
address = mkOption {
type = types.attrs;
@@ -49,7 +62,7 @@ in {
enable = mkEnableOption "firewall";
rules = mkOption { type = types.attrsOf types.attrs; };
zones = mkOption {
type = types.attrsOf (types.listOf liminix.lib.types.service);
type = types.attrsOf (types.listOf liminix.lib.types.service);
default = {
lan = [ config.services.int ];
wan = [ config.services.wan ];
@@ -82,11 +95,14 @@ in {
];
config = {
services.int = svc.network.address.build ({
interface = svc.bridge.primary.build { ifname = "int"; };
} // cfg.lan.address);
services.int = svc.network.address.build (
{
interface = svc.bridge.primary.build { ifname = "int"; };
}
// cfg.lan.address
);
services.bridge = svc.bridge.members.build {
services.bridge = svc.bridge.members.build {
primary = config.services.int;
members = cfg.lan.interfaces;
};
@@ -113,12 +129,15 @@ in {
})
];
};
in mkIf cfg.wan.dhcp6.enable bundl;
in
mkIf cfg.wan.dhcp6.enable bundl;
services.dns =
let interface = config.services.int;
dcfg = cfg.lan.dhcp;
in svc.dnsmasq.build {
let
interface = config.services.int;
dcfg = cfg.lan.dhcp;
in
svc.dnsmasq.build {
resolvconf = config.services.resolvconf;
inherit interface;
ranges = [
@@ -147,11 +166,12 @@ in {
interface = config.services.wan;
};
services.firewall = mkIf cfg.firewall.enable
(svc.firewall.build {
services.firewall = mkIf cfg.firewall.enable (
svc.firewall.build {
extraRules = cfg.firewall.rules;
inherit (cfg.firewall) zones;
});
}
);
services.resolvconf = oneshot rec {
dependencies = [ config.services.wan ];
@@ -166,11 +186,13 @@ in {
};
filesystem =
let inherit (pkgs.pseudofile) dir symlink;
in dir {
let
inherit (pkgs.pseudofile) dir symlink;
in
dir {
etc = dir {
"resolv.conf" = symlink "${config.services.resolvconf}/.outputs/resolv.conf";
};
};
};
}
}

View File

@@ -3,9 +3,10 @@
pkgs,
lib,
...
}: let
}:
let
inherit (pkgs) liminix;
inherit (lib) mkOption types ;
inherit (lib) mkOption types;
inherit (pkgs.liminix.services) oneshot target;
inherit (pkgs.pseudofile) dir symlink;
@@ -22,19 +23,22 @@
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
in lib.mapAttrs'
(name : value :
let
attrs = defaults // { ssid = name; } // value;
in lib.nameValuePair
"hostap-${name}"
(svc.hostapd.build {
interface = attrs.interface;
params = lib.filterAttrs (k: v: k != "interface") attrs;
}))
cfg.wireless.networks;
in
lib.mapAttrs' (
name: value:
let
attrs = defaults // { ssid = name; } // value;
in
lib.nameValuePair "hostap-${name}" (
svc.hostapd.build {
interface = attrs.interface;
params = lib.filterAttrs (k: v: k != "interface") attrs;
}
)
) cfg.wireless.networks;
in {
in
{
imports = [
../wlan.nix
../network
@@ -46,7 +50,7 @@ in {
options.profile.wap = {
interfaces = mkOption {
type = types.listOf liminix.lib.types.interface;
default = [];
default = [ ];
};
wireless = mkOption {
type = types.attrsOf types.anything;
@@ -71,7 +75,7 @@ in {
services.defaultroute4 = svc.network.route.build {
via = "$(output ${config.services.dhcpc} router)";
target = "default";
dependencies = [config.services.dhcpc];
dependencies = [ config.services.dhcpc ];
};
services.resolvconf = oneshot rec {

View File

@@ -1,7 +1,8 @@
{ config, lib, ... }:
let
inherit (lib) mkIf mkEnableOption; # types concatStringsSep;
in {
in
{
options = {
boot = {
ramdisk = {

View File

@@ -3,13 +3,18 @@
## Given a list of services, run each in turn until it exits, then
## runs the next.
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
inherit (pkgs.liminix.services) longrun;
in {
in
{
options = {
system.service.round-robin = mkOption {
description = "run services one at a time and failover to next";
@@ -18,9 +23,9 @@ in {
};
config.system.service.round-robin = config.system.callService ./service.nix {
services = mkOption {
type = types.listOf liminix.lib.types.service;
type = types.listOf liminix.lib.types.service;
};
name = mkOption {
name = mkOption {
type = types.str;
};
};

View File

@@ -1,22 +1,30 @@
{
liminix, lib, s6-rc-round-robin
liminix,
lib,
s6-rc-round-robin,
}:
{ services, name} :
{ services, name }:
let
inherit (liminix.services) oneshot longrun;
controlled-services = builtins.map
(s: s.overrideAttrs(o: { inherit controller; }))
services;
controller = let name' = "control-${name}"; in longrun {
name = name';
run = ''
in_outputs ${name'}
exec ${s6-rc-round-robin}/bin/s6-rc-round-robin \
-p ${proxy.name} \
${lib.concatStringsSep " "
(builtins.map (f: f.name) controlled-services)}
'';
};
inherit (liminix.services) oneshot longrun;
controlled-services = builtins.map (
s:
s.overrideAttrs (o: {
inherit controller;
})
) services;
controller =
let
name' = "control-${name}";
in
longrun {
name = name';
run = ''
in_outputs ${name'}
exec ${s6-rc-round-robin}/bin/s6-rc-round-robin \
-p ${proxy.name} \
${lib.concatStringsSep " " (builtins.map (f: f.name) controlled-services)}
'';
};
proxy = oneshot rec {
inherit name;
inherit controller;
@@ -29,4 +37,5 @@ let
)
'';
};
in proxy
in
proxy

View File

@@ -1,27 +1,39 @@
{ config, pkgs, lib, lim, ... }:
{
config,
pkgs,
lib,
lim,
...
}:
let
inherit (pkgs)
execline
s6
s6-init-bin
s6-linux-init
stdenvNoCC;
stdenvNoCC
;
inherit (lib.lists) unique concatMap;
inherit (lib) concatStrings;
inherit (builtins) map;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.services) oneshot bundle longrun;
inherit (lib) mkIf mkEnableOption mkOption types;
inherit (lib)
mkIf
mkEnableOption
mkOption
types
;
cfg = config.logging;
logger =
let pipecmds =
["${s6}/bin/s6-log -bpd3 -- ${cfg.script} 1"] ++
(lib.optional (cfg ? persistent && cfg.persistent.enable)
"/bin/tee /dev/pmsg0") ++
(lib.optional cfg.shipping.enable
"${pkgs.logshipper}/bin/logtap ${cfg.shipping.socket} logshipper-socket-event");
in ''
let
pipecmds =
[ "${s6}/bin/s6-log -bpd3 -- ${cfg.script} 1" ]
++ (lib.optional (cfg ? persistent && cfg.persistent.enable) "/bin/tee /dev/pmsg0")
++ (lib.optional cfg.shipping.enable "${pkgs.logshipper}/bin/logtap ${cfg.shipping.socket} logshipper-socket-event");
in
''
#!${execline}/bin/execlineb -P
${execline}/bin/redirfd -w 1 /dev/null
${execline}/bin/redirfd -rnb 0 fifo
@@ -40,14 +52,12 @@ let
# services and all services that have a controlled service as
# dependency
isControlled = s : s ? controller && s.controller != null;
deps = s : s.dependencies ++
lib.optional (isControlled s) s.controller;
flatDeps = s : [s] ++ concatMap flatDeps (deps s);
isControlled = s: s ? controller && s.controller != null;
deps = s: s.dependencies ++ lib.optional (isControlled s) s.controller;
flatDeps = s: [ s ] ++ concatMap flatDeps (deps s);
allServices = unique (concatMap flatDeps (builtins.attrValues config.services));
isDependentOnControlled = s :
isControlled s ||
(lib.lists.any isDependentOnControlled s.dependencies);
isDependentOnControlled =
s: isControlled s || (lib.lists.any isDependentOnControlled s.dependencies);
# all controlled services depend on this oneshot, which
# makes a list of them so we can identify them at runtime
@@ -62,42 +72,45 @@ let
down = "rm -r /run/services/controlled";
};
defaultStart =
builtins.filter
(s: !(isDependentOnControlled s)) allServices;
defaultStart = builtins.filter (s: !(isDependentOnControlled s)) allServices;
defaultDefaultTarget = bundle {
name = "default";
contents = defaultStart ++ [controlled];
contents = defaultStart ++ [ controlled ];
};
servicesAttrs = {
default = defaultDefaultTarget;
} // config.services;
in
pkgs.s6-rc-database.override {
services = builtins.attrValues servicesAttrs;
};
pkgs.s6-rc-database.override {
services = builtins.attrValues servicesAttrs;
};
s6-init-scripts = stdenvNoCC.mkDerivation {
name = "s6-scripts";
src = ./scripts;
phases = ["unpackPhase" "installPhase" ];
buildInputs = [];
phases = [
"unpackPhase"
"installPhase"
];
buildInputs = [ ];
installPhase = ''
mkdir $out
cp -r $src $out/scripts
chmod -R +w $out
'';
};
service = dir {
service = dir {
s6-linux-init-runleveld = dir {
notification-fd = { file = "3"; };
notification-fd = {
file = "3";
};
run = {
file = ''
#!${execline}/bin/execlineb -P
${execline}/bin/fdmove -c 2 1
${execline}/bin/fdmove 1 3
${s6}/bin/s6-ipcserver -1 -a 0700 -c 1 -- s
${s6}/bin/s6-sudod -dt30000 -- "/etc/s6-linux-init/current"/scripts/runlevel
'';
#!${execline}/bin/execlineb -P
${execline}/bin/fdmove -c 2 1
${execline}/bin/fdmove 1 3
${s6}/bin/s6-ipcserver -1 -a 0700 -c 1 -- s
${s6}/bin/s6-sudod -dt30000 -- "/etc/s6-linux-init/current"/scripts/runlevel
'';
mode = "0755";
};
};
@@ -108,12 +121,12 @@ let
};
run = {
file = ''
#!${execline}/bin/execlineb -P
importas PATH PATH
export PATH ${s6}/bin:''${PATH}
foreground { echo path is ''${PATH} }
${s6-linux-init}/bin/s6-linux-init-shutdownd -c "/etc/s6-linux-init/current" -g 3000
'';
#!${execline}/bin/execlineb -P
importas PATH PATH
export PATH ${s6}/bin:''${PATH}
foreground { echo path is ''${PATH} }
${s6-linux-init}/bin/s6-linux-init-shutdownd -c "/etc/s6-linux-init/current" -g 3000
'';
mode = "0755";
};
};
@@ -122,8 +135,13 @@ let
type = "i";
mode = "0600";
};
notification-fd = { file = "3"; };
run = { file = logger; mode = "0755"; };
notification-fd = {
file = "3";
};
run = {
file = logger;
mode = "0755";
};
};
getty = dir {
run = {
@@ -166,7 +184,8 @@ let
${s6-linux-init}/bin/s6-linux-init-shutdown -a #{action} -- now
'';
empty = "#!${execline}/bin/execlineb -P\n";
in dir {
in
dir {
crash = {
file = quit "s6-svscan crashed. Rebooting.";
mode = "0755";
@@ -212,13 +231,15 @@ let
};
};
in {
in
{
options = {
logging = {
shipping = {
enable = mkEnableOption "unix socket for log shipping";
socket = mkOption {
description = "socket pathname"; type = types.path;
description = "socket pathname";
type = types.path;
default = "/run/.log-shipping.sock";
};
service = mkOption {
@@ -239,31 +260,46 @@ in {
};
};
imports = [
( {config, pkgs, lib, ...}:
(
{
config,
pkgs,
lib,
...
}:
let
cfg = config.logging;
pipeline = shipper: bundle {
name = "log-shipping-pipe";
contents = let
eat = longrun {
name = "log-shipping-pipe-eat";
run = ''
fdmove -c 12 1 \
${pkgs.s6}/bin/s6-ipcserver ${cfg.shipping.socket} \
fdmove -c 1 12 \
cat
'';
producer-for = spew.name;
};
spew = shipper.override {
consumer-for ="log-shipping-pipe-eat";
};
in [ eat spew ];
};
in mkIf cfg.shipping.enable {
pipeline =
shipper:
bundle {
name = "log-shipping-pipe";
contents =
let
eat = longrun {
name = "log-shipping-pipe-eat";
run = ''
fdmove -c 12 1 \
${pkgs.s6}/bin/s6-ipcserver ${cfg.shipping.socket} \
fdmove -c 1 12 \
cat
'';
producer-for = spew.name;
};
spew = shipper.override {
consumer-for = "log-shipping-pipe-eat";
};
in
[
eat
spew
];
};
in
mkIf cfg.shipping.enable {
services.${cfg.shipping.service.name} = pipeline cfg.shipping.service;
}
)];
)
];
config = {
filesystem = dir {
@@ -274,9 +310,11 @@ in {
s6-linux-init = dir {
current = dir {
scripts = symlink "${s6-init-scripts}/scripts";
env = dir {};
env = dir { };
run-image = dir {
uncaught-logs = (dir {}) // {mode = "2750";};
uncaught-logs = (dir { }) // {
mode = "2750";
};
inherit service;
};
};

View File

@@ -1,4 +1,4 @@
{ config, pkgs, ... } :
{ config, pkgs, ... }:
{
config = {
programs.busybox = {
@@ -14,6 +14,6 @@
FEATURE_FANCY_ECHO = "y";
};
};
defaultProfile.packages = [ pkgs.schnapps ] ;
defaultProfile.packages = [ pkgs.schnapps ];
};
}

View File

@@ -3,12 +3,18 @@
## various ways to manage secrets without writing them to the
## nix store
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
inherit (pkgs.liminix.services) longrun;
in {
in
{
options.system.service.secrets = {
outboard = mkOption {
description = "fetch secrets from external vault with https";
@@ -42,7 +48,7 @@ in {
description = "service name";
type = types.str;
};
interval = mkOption {
interval = mkOption {
type = types.int;
default = 30;
description = "how often to check the source, in minutes";
@@ -57,7 +63,7 @@ in {
description = "service name";
type = types.str;
};
interval = mkOption {
interval = mkOption {
type = types.int;
default = 30;
description = "how often to check the source, in minutes";
@@ -76,9 +82,16 @@ in {
description = "how do we notify the service to regenerate its config";
default = "restart-all";
type = types.enum [
"restart" "restart-all"
"hup" "int" "quit" "kill" "term"
"winch" "usr1" "usr2"
"restart"
"restart-all"
"hup"
"int"
"quit"
"kill"
"term"
"winch"
"usr1"
"usr2"
];
};
};

View File

@@ -1,11 +1,21 @@
{
liminix, lib, json-to-fstree, serviceFns
liminix,
lib,
json-to-fstree,
serviceFns,
}:
{
name,
url,
interval,
username,
password,
}:
{ name, url, interval, username, password } :
let
inherit (liminix.services) oneshot longrun;
inherit (lib) optionalString;
in longrun {
in
longrun {
inherit name;
buildInputs = [ json-to-fstree ];
run = ''

View File

@@ -1,7 +1,16 @@
{
liminix, lib, lim, s6, s6-rc, watch-outputs
liminix,
lib,
lim,
s6,
s6-rc,
watch-outputs,
}:
{
watch,
service,
action,
}:
{ watch, service, action } :
let
inherit (liminix.services) oneshot longrun;
inherit (builtins) length head toString;
@@ -11,41 +20,51 @@ let
watched-services = unique (map (f: f "service") watch);
paths = unique (map (f: f "path") watch);
restart-flag = {
restart = "-r";
restart-all = "-R";
"hup" = "-s 1";
"int" = "-s 2";
"quit" = "-s 3";
"kill" = "-s 9";
"term" = "-s 15";
"winch" = "-s 28";
"usr1" = "-s 10";
"usr2" = "-s 12";
}.${action};
restart-flag =
{
restart = "-r";
restart-all = "-R";
"hup" = "-s 1";
"int" = "-s 2";
"quit" = "-s 3";
"kill" = "-s 9";
"term" = "-s 15";
"winch" = "-s 28";
"usr1" = "-s 10";
"usr2" = "-s 12";
}
.${action};
watched-service =
if length watched-services == 0
then null
else if length watched-services == 1
then head watched-services
else throw "cannot subscribe to more than one source service for secrets";
if length watched-services == 0 then
null
else if length watched-services == 1 then
head watched-services
else
throw "cannot subscribe to more than one source service for secrets";
watcher = let name' = "restart-${name}"; in longrun {
name = name';
run = ''
dir=/run/service/${name}
echo waiting for $dir
if test -e $dir/notification-fd; then flag="-U"; else flag="-u"; fi
${s6}/bin/s6-svwait $flag /run/service/${name} || exit
PATH=${s6-rc}/bin:${s6}/bin:$PATH
${watch-outputs}/bin/watch-outputs ${restart-flag} ${name} ${watched-service.name} ${lib.concatStringsSep " " paths}
'';
};
in service.overrideAttrs(o: {
buildInputs = (lim.orEmpty o.buildInputs) ++
optional (watched-service != null) watcher;
dependencies = (lim.orEmpty o.dependencies) ++
optionals (watched-service != null)
[ watcher watched-service ];
watcher =
let
name' = "restart-${name}";
in
longrun {
name = name';
run = ''
dir=/run/service/${name}
echo waiting for $dir
if test -e $dir/notification-fd; then flag="-U"; else flag="-u"; fi
${s6}/bin/s6-svwait $flag /run/service/${name} || exit
PATH=${s6-rc}/bin:${s6}/bin:$PATH
${watch-outputs}/bin/watch-outputs ${restart-flag} ${name} ${watched-service.name} ${lib.concatStringsSep " " paths}
'';
};
in
service.overrideAttrs (o: {
buildInputs = (lim.orEmpty o.buildInputs) ++ optional (watched-service != null) watcher;
dependencies =
(lim.orEmpty o.dependencies)
++ optionals (watched-service != null) [
watcher
watched-service
];
})

View File

@@ -1,11 +1,20 @@
{
liminix, lib, json-to-fstree, serviceFns, tangc
liminix,
lib,
json-to-fstree,
serviceFns,
tangc,
}:
{
name,
path,
interval,
}:
{ name, path, interval } :
let
inherit (liminix.services) longrun;
inherit (lib) optionalString;
in longrun {
in
longrun {
inherit name;
buildInputs = [ json-to-fstree ];
notification-fd = 10;

View File

@@ -3,18 +3,26 @@
##
## Provide SSH service using Dropbear
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
inherit (pkgs.pseudofile) dir file;
mkBoolOption = description : mkOption {
type = types.bool;
inherit description;
default = true;
};
mkBoolOption =
description:
mkOption {
type = types.bool;
inherit description;
default = true;
};
in {
in
{
options = {
system.service.ssh = mkOption {
type = liminix.lib.types.serviceDefn;
@@ -47,15 +55,16 @@ in {
allowLocalPortForward = mkBoolOption "Enable local port forwarding";
allowRemotePortForward = mkBoolOption "Enable remote port forwarding";
allowRemoteConnectionToForwardedPorts = mkOption {
type = types.bool; default = false;
type = types.bool;
default = false;
description = "Allow remote hosts to connect to local forwarded ports (by default they are bound to loopback)";
};
authorizedKeys = mkOption {
type = types.nullOr (liminix.lib.types.replacable (types.attrsOf (types.listOf types.nonEmptyStr)));
example = {
root = ["ssh-rsa AAAAB3N...aZaZ"];
alice = ["ssh-rsa AAAAB3N...qS4r"];
bob = [];
root = [ "ssh-rsa AAAAB3N...aZaZ" ];
alice = [ "ssh-rsa AAAAB3N...qS4r" ];
bob = [ ];
};
default = null;
description = "Authorized SSH public keys for each username. If this optin is provided it overrides any keys found in /home/{username}/.ssh";

View File

@@ -1,8 +1,8 @@
{
liminix
, dropbear
, lib
, watch-ssh-keys
liminix,
dropbear,
lib,
watch-ssh-keys,
}:
{
address,
@@ -14,7 +14,7 @@
allowRoot,
authorizedKeys,
port,
extraConfig
extraConfig,
}:
let
name = "sshd";
@@ -24,37 +24,39 @@ let
keydir = "/run/${name}/authorized_keys";
options =
[
"-e" # pass environment to child
"-E" # log to stderr
"-R" # create hostkeys if needed
"-e" # pass environment to child
"-E" # log to stderr
"-R" # create hostkeys if needed
"-P /run/dropbear.pid"
"-F" # don't fork into background
] ++
(lib.optional (! allowRoot) "-w") ++
(lib.optional (! allowPasswordLogin) "-s") ++
(lib.optional (! allowPasswordLoginForRoot) "-g") ++
(lib.optional (! allowLocalPortForward) "-j") ++
(lib.optional (! allowRemotePortForward) "-k") ++
(lib.optional (! allowRemoteConnectionToForwardedPorts) "-a") ++
(lib.optionals (authorizedKeys != null) ["-U" "${keydir}/%n"]) ++
[(if address != null
then "-p ${address}:${toString port}"
else "-p ${toString port}")] ++
[extraConfig];
"-F" # don't fork into background
]
++ (lib.optional (!allowRoot) "-w")
++ (lib.optional (!allowPasswordLogin) "-s")
++ (lib.optional (!allowPasswordLoginForRoot) "-g")
++ (lib.optional (!allowLocalPortForward) "-j")
++ (lib.optional (!allowRemotePortForward) "-k")
++ (lib.optional (!allowRemoteConnectionToForwardedPorts) "-a")
++ (lib.optionals (authorizedKeys != null) [
"-U"
"${keydir}/%n"
])
++ [
(if address != null then "-p ${address}:${toString port}" else "-p ${toString port}")
]
++ [ extraConfig ];
isKeyservice = typeOf authorizedKeys == "lambda";
authKeysConcat =
if authorizedKeys != null && !isKeyservice
then mapAttrs
(n : v : concatStringsSep "\\n" v)
authorizedKeys
else {};
if authorizedKeys != null && !isKeyservice then
mapAttrs (n: v: concatStringsSep "\\n" v) authorizedKeys
else
{ };
keyservice = longrun {
name = "${name}-watch-keys";
run = ''
mkdir -p ${keydir}
exec ${watch-ssh-keys}/bin/watch-ssh-keys -d ${keydir} ${authorizedKeys "service"} ${authorizedKeys "path"}
'';
dependencies = [ (authorizedKeys "service") ] ;
dependencies = [ (authorizedKeys "service") ];
};
in
longrun {
@@ -66,12 +68,9 @@ longrun {
run = ''
ln -s $(mkstate dropbear) /run
mkdir -p /run/${name}/authorized_keys
${concatStringsSep "\n"
(mapAttrsToList
(n : v : "echo -e '${v}' > /run/${name}/authorized_keys/${n} ")
authKeysConcat
)
}
${concatStringsSep "\n" (
mapAttrsToList (n: v: "echo -e '${v}' > /run/${name}/authorized_keys/${n} ") authKeysConcat
)}
. /etc/profile # sets PATH but do we need this? it's the same file as ashrc
exec env -i ENV=/etc/ashrc PATH=$PATH ${dropbear}/bin/dropbear ${concatStringsSep " " options}
'';

View File

@@ -1,9 +1,16 @@
{ liminix, certifix-client, svc, lib, writeText, serviceFns }:
{
liminix,
certifix-client,
svc,
lib,
writeText,
serviceFns,
}:
{
caCertificate,
secret,
subject,
serviceUrl
serviceUrl,
}:
let
inherit (builtins) filter isString split;
@@ -11,7 +18,8 @@ let
name = "certifix-${lib.strings.sanitizeDerivationName subject}";
caCertFile = writeText "ca.crt" caCertificate;
secretFile = writeText "secret" secret;
in oneshot {
in
oneshot {
inherit name;
up = ''
(in_outputs ${name}

View File

@@ -1,5 +1,9 @@
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
@@ -7,34 +11,35 @@ in
{
options = {
system.service.tls-certificate = {
certifix-client = mkOption {
certifix-client = mkOption {
type = liminix.lib.types.serviceDefn;
};
};
};
config.system.service.tls-certificate.certifix-client =
config.system.callService ./certifix-client.nix {
# this is probably read from files on the build machine,
# but are not named with ...File suffix because they are
# not files on the device (they get embedded into the store)
caCertificate = mkOption {
description = "CA certificate in PEM format. This must be the same CA as that which signed the certificate of the Certifix server";
type = types.str;
config.system.callService ./certifix-client.nix
{
# this is probably read from files on the build machine,
# but are not named with ...File suffix because they are
# not files on the device (they get embedded into the store)
caCertificate = mkOption {
description = "CA certificate in PEM format. This must be the same CA as that which signed the certificate of the Certifix server";
type = types.str;
};
secret = mkOption {
description = "The shared secret to embed in signing request. This must match the secret configured in the Certifix service, otherwise it will refuse to sign the CSR.";
type = types.str;
};
subject = mkOption {
description = "Subject of the certificate request, as an X509 DN. The CN ('Common Name') you provide here is also used as the value of the SubjectAlternativeName extension.";
type = types.str;
example = "C=GB,ST=London,O=Liminix,OU=IT,CN=myhostname";
};
serviceUrl = mkOption {
description = "Certifix server endpoint URL";
type = types.str;
example = "https://certifix.lan:19613/sign";
};
};
secret = mkOption {
description = "The shared secret to embed in signing request. This must match the secret configured in the Certifix service, otherwise it will refuse to sign the CSR.";
type = types.str;
};
subject = mkOption {
description = "Subject of the certificate request, as an X509 DN. The CN ('Common Name') you provide here is also used as the value of the SubjectAlternativeName extension.";
type = types.str;
example = "C=GB,ST=London,O=Liminix,OU=IT,CN=myhostname";
};
serviceUrl = mkOption {
description = "Certifix server endpoint URL";
type = types.str;
example = "https://certifix.lan:19613/sign";
};
};
}

View File

@@ -1,12 +1,18 @@
# this is unlikely to be the final form or location of this code, it's
# an interim module which wraps the uevent-watch command
{ lib, pkgs, config, ... }:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in
# inherit (pkgs.liminix.services) bundle;
in {
{
options = {
system.service.uevent-rule = mkOption {
description = "a service which starts other services based on device state (sysfs)";
@@ -25,7 +31,7 @@ in {
devtype = "usb_device";
attrs.idVendor = "8086";
};
default = {};
default = { };
};
symlink = mkOption {
description = "create symlink targeted on devpath";

View File

@@ -1,23 +1,30 @@
{
liminix
, uevent-watch
, lib }:
liminix,
uevent-watch,
lib,
}:
{
serviceName, terms, symlink
serviceName,
terms,
symlink,
}:
let
inherit (liminix.services) longrun;
inherit (lib.attrsets) collect mapAttrsRecursive;
inherit (lib.strings) concatStringsSep;
stringify = attrs :
concatStringsSep " "
(collect lib.isString
(mapAttrsRecursive
(path : value : "${concatStringsSep "." path}=${value}")
attrs));
stringify =
attrs:
concatStringsSep " " (
collect lib.isString (
mapAttrsRecursive (path: value: "${concatStringsSep "." path}=${value}") attrs
)
);
termsString = stringify terms;
in longrun {
in
longrun {
name = "watch-for-${serviceName}";
restart-on-upgrade = true;
run = "${uevent-watch}/bin/uevent-watch ${if symlink != null then "-n ${symlink}" else ""} -s ${serviceName} ${termsString}";
run = "${uevent-watch}/bin/uevent-watch ${
if symlink != null then "-n ${symlink}" else ""
} -s ${serviceName} ${termsString}";
}

View File

@@ -10,11 +10,20 @@
## the immutable store, so you can't e.g change a password with
## :command:`passwd`
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib)
concatStrings concatStringsSep mapAttrsToList mkOption types;
concatStrings
concatStringsSep
mapAttrsToList
mkOption
types
;
inherit (builtins) toString;
inherit (pkgs.pseudofile) dir;
passwd-file =
@@ -28,82 +37,98 @@ let
in
concatStrings lines;
group-file =
let lines = mapAttrsToList
(name: {gid, usernames ? []}:
"${name}:x:${toString gid}:${concatStringsSep "," usernames}\n" )
config.groups;
in concatStrings lines;
in {
let
lines = mapAttrsToList (
name:
{
gid,
usernames ? [ ],
}:
"${name}:x:${toString gid}:${concatStringsSep "," usernames}\n"
) config.groups;
in
concatStrings lines;
in
{
options = {
users = mkOption {
type = types.attrsOf (types.submodule {
options = {
passwd = mkOption {
type = types.str;
description = "encrypted password, as generated by mkpasswd -m sha512crypt";
example = "$6$RIYL.EgWOrtoJ0/7$Z53a8sc0o6AU/kuFOGiLJKhwVavTG/deoM7JTs6luNczYSUsh4UYmhvT8sVzm.l8F/LZXhhhkC7IHQs5UGAIM/";
default = "!!";
users = mkOption {
type = types.attrsOf (
types.submodule {
options = {
passwd = mkOption {
type = types.str;
description = "encrypted password, as generated by mkpasswd -m sha512crypt";
example = "$6$RIYL.EgWOrtoJ0/7$Z53a8sc0o6AU/kuFOGiLJKhwVavTG/deoM7JTs6luNczYSUsh4UYmhvT8sVzm.l8F/LZXhhhkC7IHQs5UGAIM/";
default = "!!";
};
uid = mkOption {
type = types.int;
};
gid = mkOption {
type = types.int;
};
gecos = mkOption {
type = types.str;
default = "";
example = "Jo Q User";
};
dir = mkOption {
type = types.str;
default = "/run";
};
shell = mkOption {
type = types.str;
default = "/bin/sh";
};
openssh.authorizedKeys.keys = mkOption {
type = types.listOf types.str;
default = [ ];
};
};
uid = mkOption {
type = types.int;
};
gid = mkOption {
type = types.int;
};
gecos = mkOption {
type = types.str;
default = "";
example = "Jo Q User";
};
dir = mkOption {
type = types.str;
default = "/run";
};
shell = mkOption {
type = types.str;
default = "/bin/sh";
};
openssh.authorizedKeys.keys = mkOption {
type = types.listOf types.str;
default = [];
};
};
});
}
);
};
groups = mkOption {
type = types.attrsOf (types.submodule {
options = {
gid = mkOption {
type = types.int;
groups = mkOption {
type = types.attrsOf (
types.submodule {
options = {
gid = mkOption {
type = types.int;
};
usernames = mkOption {
type = types.listOf types.str;
default = [ ];
};
};
usernames = mkOption {
type = types.listOf types.str;
default = [];
};
};
});
}
);
};
};
config =
let authorized_key_files =
lib.attrsets.mapAttrs
(name: val: dir {
".ssh" = dir {
authorized_keys = {
inherit (val) uid gid;
type = "f";
mode = "0400";
file = lib.concatStringsSep
"\n" val.openssh.authorizedKeys.keys;
};
};
})
config.users;
in {
let
authorized_key_files = lib.attrsets.mapAttrs (
name: val:
dir {
".ssh" = dir {
authorized_keys = {
inherit (val) uid gid;
type = "f";
mode = "0400";
file = lib.concatStringsSep "\n" val.openssh.authorizedKeys.keys;
};
};
}
) config.users;
in
{
filesystem = dir {
etc = dir {
passwd = { file = passwd-file; };
group = { file = group-file; };
passwd = {
file = passwd-file;
};
group = {
file = group-file;
};
};
home = dir authorized_key_files;
};

View File

@@ -10,7 +10,12 @@
## and require using VLAN in order to send different traffic to
## different ports (e.g. LAN vs WAN)
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;

View File

@@ -1,11 +1,16 @@
{
liminix
, lib
liminix,
lib,
}:
{
ifname,
primary,
vid,
}:
{ ifname, primary, vid } :
let
inherit (liminix.services) oneshot;
in oneshot rec {
in
oneshot rec {
name = "${ifname}.link";
up = ''
ip link add link $(output ${primary} ifname) name ${ifname} type vlan id ${vid}

View File

@@ -4,14 +4,19 @@
## feed it by checking the health of specified critical services.
## If the watchdog feeder stops, the device will reboot.
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in
{
options = {
system.service.watchdog = mkOption {
system.service.watchdog = mkOption {
type = liminix.lib.types.serviceDefn;
};
};

View File

@@ -1,13 +1,15 @@
{
liminix
, lib
, s6
liminix,
lib,
s6,
}:
{ watched, headStart } :
{ watched, headStart }:
let
inherit (liminix.services) longrun;
in longrun {
in
longrun {
name = "watchdog";
run =
"PATH=${s6}/bin:$PATH HEADSTART=${toString headStart} ${./gaspode.sh} ${lib.concatStringsSep " " (builtins.map (s: s.name) watched)}";
run = "PATH=${s6}/bin:$PATH HEADSTART=${toString headStart} ${./gaspode.sh} ${
lib.concatStringsSep " " (builtins.map (s: s.name) watched)
}";
}

View File

@@ -1,4 +1,9 @@
{ lib, pkgs, config, ...}:
{
lib,
pkgs,
config,
...
}:
let
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) stdenv wireless-regdb;
@@ -10,7 +15,8 @@ let
cp ${wireless-regdb}/lib/firmware/regulatory.db $out/
'';
};
in {
in
{
config = {
filesystem = dir {
lib = dir {
@@ -20,7 +26,8 @@ in {
};
};
programs.busybox.applets = [
"insmod" "rmmod"
"insmod"
"rmmod"
];
kernel = rec {
config = {

View File

@@ -1,12 +1,18 @@
{ config, pkgs, lib, ... }:
{
config,
pkgs,
lib,
...
}:
let
inherit (pkgs) liminix;
inherit (lib) mkOption types;
huawei-cdc-ncm = pkgs.kmodloader.override {
targets = ["huawei_cdc_ncm"];
targets = [ "huawei_cdc_ncm" ];
inherit (config.system.outputs) kernel;
};
in {
in
{
imports = [
../uevent-rule
../mdevd.nix
@@ -25,25 +31,35 @@ in {
USB_SERIAL_OPTION = "y";
};
programs.busybox.applets = [
"insmod" "rmmod"
"insmod"
"rmmod"
];
# https://www.0xf8.org/2017/01/flashing-a-huawei-e3372h-4g-lte-stick-from-hilink-to-stick-mode/
system.service.wwan.huawei-e3372 =
let svc = config.system.callService ./huawei-e3372.nix {
apn = mkOption { type = types.str; };
username = mkOption { type = types.str; };
password = mkOption { type = types.str; };
authType = mkOption { type = types.enum [ "pap" "chap" ]; };
let
svc = config.system.callService ./huawei-e3372.nix {
apn = mkOption { type = types.str; };
username = mkOption { type = types.str; };
password = mkOption { type = types.str; };
authType = mkOption {
type = types.enum [
"pap"
"chap"
];
};
in
svc // {
build = args :
let args' = args // {
dependencies = (args.dependencies or []) ++
[huawei-cdc-ncm];
};
in svc.build args' ;
};
in
svc
// {
build =
args:
let
args' = args // {
dependencies = (args.dependencies or [ ]) ++ [ huawei-cdc-ncm ];
};
in
svc.build args';
};
};
}

View File

@@ -1,12 +1,17 @@
{
liminix
, usb-modeswitch
, ppp
, lib
, svc
, uevent-watch
liminix,
usb-modeswitch,
ppp,
lib,
svc,
uevent-watch,
}:
{
apn,
username,
password,
authType,
}:
{ apn, username, password, authType }:
let
inherit (liminix.services) oneshot;
authTypeNum = if authType == "pap" then "1" else "2";
@@ -16,25 +21,36 @@ let
# kind is to be preferred, at least in principle, because it's
# faster. This initialization sequence works for the Huawei
# E3372, and took much swearing: the error messages are *awful*
"" "AT"
"OK" "ATZ"
""
"AT"
"OK"
"ATZ"
# create PDP context
"OK" "AT+CGDCONT=1,\"IP\",\"${apn}\""
"OK"
"AT+CGDCONT=1,\"IP\",\"${apn}\""
# activate PDP context
"OK" "AT+CGACT=1,1"
"OK"
"AT+CGACT=1,1"
# setup username and password per requirements of sim provider.
# (caret is special to chat, so needs escaping in AT commands)
"OK" "AT\\^AUTHDATA=1,${authTypeNum},\"\",\"${password}\",\"${username}\""
"OK"
"AT\\^AUTHDATA=1,${authTypeNum},\"\",\"${password}\",\"${username}\""
# start the thing (I am choosing to read this as "NDIS DialUP")
"OK" "AT\\^NDISDUP=1,1"
"OK"
"AT\\^NDISDUP=1,1"
"OK"
];
modeswitch = oneshot rec {
name = "modem-modeswitch";
controller = (svc.uevent-rule.build {
serviceName = name;
terms = { devtype = "usb_device"; product = "12d1/14fe/102"; };
});
controller = (
svc.uevent-rule.build {
serviceName = name;
terms = {
devtype = "usb_device";
product = "12d1/14fe/102";
};
}
);
up = ''
${usb-modeswitch}/bin/usb_modeswitch -v 12d1 -p 14fe --huawei-new-mode
'';
@@ -45,17 +61,19 @@ let
# is only running when the stick is in the wrong mode
dependencies = [ modeswitch.controller ];
buildInputs = [ modeswitch ];
controller = (svc.uevent-rule.build {
serviceName = name;
terms = {
subsystem = "tty";
attrs = {
idVendor = "12d1";
idProduct = "1506";
controller = (
svc.uevent-rule.build {
serviceName = name;
terms = {
subsystem = "tty";
attrs = {
idVendor = "12d1";
idProduct = "1506";
};
};
};
symlink = "/dev/modem";
});
symlink = "/dev/modem";
}
);
up = ''
ls -l /dev/modem
test -L /dev/modem || exit 1
@@ -64,7 +82,8 @@ let
down = "${ppp}/bin/chat -v '' ATZ OK 0<>/dev/modem 1>&0";
};
in svc.network.link.build {
in
svc.network.link.build {
ifname = "wwan0";
dependencies = [ atz ];
}

View File

@@ -10,7 +10,12 @@
## failure on the primary partition. The exact details are specifics to your device.
## See the Zyxel NWA50AX for an example.
## TODO: generalize this module.
{ config, lib, pkgs, ... }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
@@ -22,23 +27,31 @@ in
config.boot.zyxel-dual-image = config.system.callService ./service.nix {
ensureActiveImage = mkOption {
type = types.enum [ "primary" "secondary" ];
type = types.enum [
"primary"
"secondary"
];
default = "primary";
description = ''At boot, ensure that the active image is the one specified.
description = ''
At boot, ensure that the active image is the one specified.
If you are already on a broken image, you need to manually boot
into the right image via `atgo <image index>` in U-Boot.
If you are already on a broken image, you need to manually boot
into the right image via `atgo <image index>` in U-Boot.
'';
};
kernelCommandLineSource = mkOption {
type = types.enum [ "/proc/cmdline" "/proc/device-tree/chosen/bootargs" ];
type = types.enum [
"/proc/cmdline"
"/proc/device-tree/chosen/bootargs"
];
default = "/proc/device-tree/chosen/bootargs";
description = ''Kernel command line arguments source file.
On MIPS, Liminix embeds the kernel command line in /proc/device-tree/chosen/bootargs-override.
description = ''
Kernel command line arguments source file.
On MIPS, Liminix embeds the kernel command line in /proc/device-tree/chosen/bootargs-override.
In this instance, it does not get concatenated with `/proc/cmdline`.
Therefore you may prefer to source it from another place, like `/proc/device-tree/chosen/bootargs`.
In this instance, it does not get concatenated with `/proc/cmdline`.
Therefore you may prefer to source it from another place, like `/proc/device-tree/chosen/bootargs`.
'';
};

View File

@@ -1,13 +1,20 @@
{
liminix
, lib
, zyxel-bootconfig
liminix,
lib,
zyxel-bootconfig,
}:
{
ensureActiveImage,
primaryMtdPartition,
secondaryMtdPartition,
bootConfigurationMtdPartition,
kernelCommandLineSource,
}:
{ ensureActiveImage, primaryMtdPartition, secondaryMtdPartition, bootConfigurationMtdPartition, kernelCommandLineSource }:
let
inherit (liminix.services) oneshot;
activeImageIndex = if ensureActiveImage == "primary" then 0 else 1;
in oneshot {
in
oneshot {
name = "zyxel-boot-configure";
up = ''
set -- $(cat /proc/device-tree/chosen/bootargs)