1#! @bash@/bin/sh -e
2
3shopt -s nullglob
4
5export PATH=/empty
6for i in @path@; do PATH=$PATH:$i/bin; done
7
8usage() {
9 echo "usage: $0 -t <timeout> -c <path-to-default-configuration> [-d <boot-dir>] [-g <num-generations>]" >&2
10 exit 1
11}
12
13timeout= # Timeout in centiseconds
14default= # Default configuration
15target=/boot # Target directory
16numGenerations=0 # Number of other generations to include in the menu
17
18while getopts "t:c:d:g:" opt; do
19 case "$opt" in
20 t) # U-Boot interprets '0' as infinite and negative as instant boot
21 if [ "$OPTARG" -lt 0 ]; then
22 timeout=0
23 elif [ "$OPTARG" = 0 ]; then
24 timeout=-10
25 else
26 timeout=$((OPTARG * 10))
27 fi
28 ;;
29 c) default="$OPTARG" ;;
30 d) target="$OPTARG" ;;
31 g) numGenerations="$OPTARG" ;;
32 \?) usage ;;
33 esac
34done
35
36[ "$timeout" = "" -o "$default" = "" ] && usage
37
38mkdir -p $target/nixos
39mkdir -p $target/extlinux
40
41# Convert a path to a file in the Nix store such as
42# /nix/store/<hash>-<name>/file to <hash>-<name>-<file>.
43cleanName() {
44 local path="$1"
45 echo "$path" | sed 's|^/nix/store/||' | sed 's|/|-|g'
46}
47
48# Copy a file from the Nix store to $target/nixos.
49declare -A filesCopied
50
51copyToKernelsDir() {
52 local src=$(readlink -f "$1")
53 local dst="$target/nixos/$(cleanName $src)"
54 # Don't copy the file if $dst already exists. This means that we
55 # have to create $dst atomically to prevent partially copied
56 # kernels or initrd if this script is ever interrupted.
57 if ! test -e $dst; then
58 local dstTmp=$dst.tmp.$$
59 cp -r $src $dstTmp
60 mv $dstTmp $dst
61 fi
62 filesCopied[$dst]=1
63 result=$dst
64}
65
66# Copy its kernel, initrd and dtbs to $target/nixos, and echo out an
67# extlinux menu entry
68addEntry() {
69 local path=$(readlink -f "$1")
70 local tag="$2" # Generation number or 'default'
71
72 if ! test -e $path/kernel -a -e $path/initrd; then
73 return
74 fi
75
76 copyToKernelsDir "$path/kernel"; kernel=$result
77 copyToKernelsDir "$path/initrd"; initrd=$result
78 # XXX UGLY: maybe the system config should have a top-level "dtbs" entry?
79 dtbDir=$(readlink -m "$path/kernel/../dtbs")
80 if [ -d "$dtbDir" ]; then
81 copyToKernelsDir "$dtbDir"; dtbs=$result
82 fi
83
84 timestampEpoch=$(stat -L -c '%Z' $path)
85
86 timestamp=$(date "+%Y-%m-%d %H:%M" -d @$timestampEpoch)
87 nixosLabel="$(cat $path/nixos-version)"
88 extraParams="$(cat $path/kernel-params)"
89
90 echo
91 echo "LABEL nixos-$tag"
92 if [ "$tag" = "default" ]; then
93 echo " MENU LABEL NixOS - Default"
94 else
95 echo " MENU LABEL NixOS - Configuration $tag ($timestamp - $nixosLabel)"
96 fi
97 echo " LINUX ../nixos/$(basename $kernel)"
98 echo " INITRD ../nixos/$(basename $initrd)"
99 if [ -d "$dtbDir" ]; then
100 echo " FDTDIR ../nixos/$(basename $dtbs)"
101 fi
102 echo " APPEND systemConfig=$path init=$path/init $extraParams"
103}
104
105tmpFile="$target/extlinux/extlinux.conf.tmp.$$"
106
107cat > $tmpFile <<EOF
108# Generated file, all changes will be lost on nixos-rebuild!
109
110# Change this to e.g. nixos-42 to temporarily boot to an older configuration.
111DEFAULT nixos-default
112
113MENU TITLE ------------------------------------------------------------
114TIMEOUT $timeout
115EOF
116
117addEntry $default default >> $tmpFile
118
119if [ "$numGenerations" -gt 0 ]; then
120 # Add up to $numGenerations generations of the system profile to the menu,
121 # in reverse (most recent to least recent) order.
122 for generation in $(
123 (cd /nix/var/nix/profiles && ls -d system-*-link) \
124 | sed 's/system-\([0-9]\+\)-link/\1/' \
125 | sort -n -r \
126 | head -n $numGenerations); do
127 link=/nix/var/nix/profiles/system-$generation-link
128 addEntry $link $generation
129 done >> $tmpFile
130fi
131
132mv -f $tmpFile $target/extlinux/extlinux.conf
133
134# Remove obsolete files from $target/nixos.
135for fn in $target/nixos/*; do
136 if ! test "${filesCopied[$fn]}" = 1; then
137 echo "Removing no longer needed boot file: $fn"
138 chmod +w -- "$fn"
139 rm -rf -- "$fn"
140 fi
141done