From 611b9239211aaab49339a40b610d0767efee4263 Mon Sep 17 00:00:00 2001 From: guochao Date: Tue, 31 Oct 2023 15:23:46 +0800 Subject: [PATCH] first commit --- .gitignore | 1 + default.nix | 20 ++++++ flake.lock | 25 ++++++++ flake.nix | 10 +++ nixos-kexec-profile.sh | 141 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 .gitignore create mode 100644 default.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 nixos-kexec-profile.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2be92b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +result diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..fdbf311 --- /dev/null +++ b/default.nix @@ -0,0 +1,20 @@ +{ substituteAll, lib, coreutils, jq, kexec-tools, runtimeShell, installShellFiles, ... }: substituteAll { + name = "nixos-kexec-profile"; + src = ./nixos-kexec-profile.sh; + dir = "bin"; + isExecutable = true; + + path = lib.makeBinPath [ coreutils jq kexec-tools ]; + inherit runtimeShell; + + nativeBuildInputs = [ + installShellFiles + ]; + + meta = { + description = "kexec into a NixOS profile"; + homepage = "https://github.com/jeffguorg/nixos-kexec-profile"; + licenses = lib.license.gpl; + mainProgram = "nixos-kexec-profile"; + }; +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..3cca03b --- /dev/null +++ b/flake.lock @@ -0,0 +1,25 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1698553279, + "narHash": "sha256-T/9P8yBSLcqo/v+FTOBK+0rjzjPMctVymZydbvR/Fak=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "90e85bc7c1a6fc0760a94ace129d3a1c61c3d035", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..d079966 --- /dev/null +++ b/flake.nix @@ -0,0 +1,10 @@ +{ + description = "kexec into nixos profile"; + + outputs = { self, nixpkgs }: { + packages.x86_64-linux = rec { + nixos-kexec-profile = nixpkgs.legacyPackages.x86_64-linux.pkgs.callPackage ./default.nix {}; + default = nixos-kexec-profile; + }; + }; +} diff --git a/nixos-kexec-profile.sh b/nixos-kexec-profile.sh new file mode 100644 index 0000000..b54db2f --- /dev/null +++ b/nixos-kexec-profile.sh @@ -0,0 +1,141 @@ +#!@runtimeShell@ +# shellcheck shell=bash + +if [ -x "@runtimeShell@" ]; then export SHELL="@runtimeShell@"; fi; + +set -euo pipefail + +export PATH=@path@:$PATH + +build-configuration-legacy() { + run nix-build '' -A system -I nixos-config="$1" +} + +build-configuration-flake() { + run nix build "$1.out" --no-link --print-out-paths +} + +type nix > /dev/null +type nix-build > /dev/null +type jq > /dev/null +type kexec > /dev/null +type id > /dev/null + +DEFAULT_TARGET="${BUILD:-/nix/var/nix/profiles/system}" + +TYPE="" +BUILD="" +BOOT=n + +msg() { + echo "=====> msg: $@" >&2 +} + +fail() { + echo "=====> fail: $@" >&2 + exit 1 +} + +run() { + echo "=====> run: $@" >&2 + "$@" +} + +set-target() { + if ! [ -z "$BUILD" ]; then + fail "Target is already set once" + fi + + if [[ ( "$1" =~ '.*#.*' ) || ( "${2:-}" = flake ) ]]; then + BUILD="$1" + TYPE=flake + elif [ -r "$1/flake.nix" ]; then + BUILD="$1#nixosConfigurations."$HOSTNAME".config.system.build.toplevel" + TYPE=flake + elif [ -r "$1/configuration.nix" ]; then + BUILD="$1/configuration.nix" + TYPE=legacy + elif [ -r "$1/boot.json" ]; then + TARGET=$1 + TYPE=built + elif [ ! -e "$BUILD" ]; then + fail "nothing is found $BUILD" + elif [ -d "$BUILD" ]; then + fail "not a valid directory. it should either contains a flake.nix to be a flake build, a configuration.nix to be a legacy build, or a boot.json to be a nixos top level directory" + else + return 1 + fi +} + +while [[ $# -gt 0 ]]; do + opt="$1" + shift + case "$opt" in + --flake) + set-target "$1" flake + shift + ;; + -b|--boot) + BOOT=y + ;; + *) + if set-target "$opt"; then + : + else + fail "unrecognized argument: $opt" + fi + ;; + esac +done + +if [ -z "$BUILD" ]; then + msg "Target not set. using $DEFAULT_TARGET" + msg + if set-target "$DEFAULT_TARGET"; then + : + else + fail "weird...how am i getting this? $opt" + fi +fi + +if [[ ( ! -z "$BUILD" ) && (( $TYPE = flake ) || ( $TYPE = legacy ) ) ]]; then + if [ $TYPE = flake ]; then + TARGET=$(build-configuration-flake "$BUILD") + else + TARGET=$(build-configuration-legacy "$BUILD") + fi + RET=$? + if [ $RET -ne 0 ]; then + exit $RET + fi +fi + +if [ -z "$TARGET" ]; then + fail "this is unreachable" +fi + +LABEL="$(jq '."org.nixos.bootspec.v1".label' /nix/var/nix/profiles/system/boot.json -r)" + +INITRD="$(jq '."org.nixos.bootspec.v1".initrd' /nix/var/nix/profiles/system/boot.json -r)" +KERNEL="$(jq '."org.nixos.bootspec.v1".kernel' /nix/var/nix/profiles/system/boot.json -r)" +INIT="$(jq '."org.nixos.bootspec.v1".init' /nix/var/nix/profiles/system/boot.json -r)" + +CMDLINE="init=${INIT} $(jq '."org.nixos.bootspec.v1".kernelParams | join(" ")' /nix/var/nix/profiles/system/boot.json -r)" + +msg "Loading: $LABEL" +msg "Toplevel: $TARGET" +msg +msg "Kernel: $KERNEL" +msg "Initrd: $INITRD" +msg "Cmdline: $CMDLINE" +msg + +if [ $(id -u) -ne 0 ]; then + msg "Warn: You are not running as root. kexec might fail. it is normal." +fi + +run kexec -l --initrd=$INITRD --command-line="$CMDLINE" $KERNEL + +if [ $BOOT == y ]; then + run kexec -e +fi