lol
at 23.11-beta 1212 lines 38 kB view raw
1{ lib 2, pkgs 3, kernel ? pkgs.linux 4, img ? pkgs.stdenv.hostPlatform.linux-kernel.target 5, storeDir ? builtins.storeDir 6, rootModules ? 7 [ "virtio_pci" "virtio_mmio" "virtio_blk" "virtio_balloon" "virtio_rng" "ext4" "unix" "9p" "9pnet_virtio" "crc32c_generic" ] 8 ++ pkgs.lib.optional pkgs.stdenv.hostPlatform.isx86 "rtc_cmos" 9}: 10 11let 12 inherit (pkgs) bash bashInteractive busybox cpio coreutils e2fsprogs fetchurl kmod rpm 13 stdenv util-linux 14 buildPackages writeScript writeText runCommand; 15in 16rec { 17 qemu-common = import ../../../nixos/lib/qemu-common.nix { inherit lib pkgs; }; 18 19 qemu = buildPackages.qemu_kvm; 20 21 modulesClosure = pkgs.makeModulesClosure { 22 inherit kernel rootModules; 23 firmware = kernel; 24 }; 25 26 27 hd = "vda"; # either "sda" or "vda" 28 29 initrdUtils = runCommand "initrd-utils" 30 { nativeBuildInputs = [ buildPackages.nukeReferences ]; 31 allowedReferences = [ "out" modulesClosure ]; # prevent accidents like glibc being included in the initrd 32 } 33 '' 34 mkdir -p $out/bin 35 mkdir -p $out/lib 36 37 # Copy what we need from Glibc. 38 cp -p \ 39 ${pkgs.stdenv.cc.libc}/lib/ld-*.so.? \ 40 ${pkgs.stdenv.cc.libc}/lib/libc.so.* \ 41 ${pkgs.stdenv.cc.libc}/lib/libm.so.* \ 42 ${pkgs.stdenv.cc.libc}/lib/libresolv.so.* \ 43 $out/lib 44 45 # Copy BusyBox. 46 cp -pd ${pkgs.busybox}/bin/* $out/bin 47 48 # Run patchelf to make the programs refer to the copied libraries. 49 for i in $out/bin/* $out/lib/*; do if ! test -L $i; then nuke-refs $i; fi; done 50 51 for i in $out/bin/*; do 52 if [ -f "$i" -a ! -L "$i" ]; then 53 echo "patching $i..." 54 patchelf --set-interpreter $out/lib/ld-*.so.? --set-rpath $out/lib $i || true 55 fi 56 done 57 ''; # */ 58 59 60 stage1Init = writeScript "vm-run-stage1" '' 61 #! ${initrdUtils}/bin/ash -e 62 63 export PATH=${initrdUtils}/bin 64 65 mkdir /etc 66 echo -n > /etc/fstab 67 68 mount -t proc none /proc 69 mount -t sysfs none /sys 70 71 echo 2 > /proc/sys/vm/panic_on_oom 72 73 for o in $(cat /proc/cmdline); do 74 case $o in 75 mountDisk=1) 76 mountDisk=1 77 ;; 78 command=*) 79 set -- $(IFS==; echo $o) 80 command=$2 81 ;; 82 out=*) 83 set -- $(IFS==; echo $o) 84 export out=$2 85 ;; 86 esac 87 done 88 89 echo "loading kernel modules..." 90 for i in $(cat ${modulesClosure}/insmod-list); do 91 insmod $i || echo "warning: unable to load $i" 92 done 93 94 mount -t devtmpfs devtmpfs /dev 95 ln -s /proc/self/fd /dev/fd 96 ln -s /proc/self/fd/0 /dev/stdin 97 ln -s /proc/self/fd/1 /dev/stdout 98 ln -s /proc/self/fd/2 /dev/stderr 99 100 ifconfig lo up 101 102 mkdir /fs 103 104 if test -z "$mountDisk"; then 105 mount -t tmpfs none /fs 106 else 107 mount /dev/${hd} /fs 108 fi 109 110 mkdir -p /fs/dev 111 mount -o bind /dev /fs/dev 112 113 mkdir -p /fs/dev/shm /fs/dev/pts 114 mount -t tmpfs -o "mode=1777" none /fs/dev/shm 115 mount -t devpts none /fs/dev/pts 116 117 echo "mounting Nix store..." 118 mkdir -p /fs${storeDir} 119 mount -t 9p store /fs${storeDir} -o trans=virtio,version=9p2000.L,cache=loose,msize=131072 120 121 mkdir -p /fs/tmp /fs/run /fs/var 122 mount -t tmpfs -o "mode=1777" none /fs/tmp 123 mount -t tmpfs -o "mode=755" none /fs/run 124 ln -sfn /run /fs/var/run 125 126 echo "mounting host's temporary directory..." 127 mkdir -p /fs/tmp/xchg 128 mount -t 9p xchg /fs/tmp/xchg -o trans=virtio,version=9p2000.L,msize=131072 129 130 mkdir -p /fs/proc 131 mount -t proc none /fs/proc 132 133 mkdir -p /fs/sys 134 mount -t sysfs none /fs/sys 135 136 mkdir -p /fs/etc 137 ln -sf /proc/mounts /fs/etc/mtab 138 echo "127.0.0.1 localhost" > /fs/etc/hosts 139 # Ensures tools requiring /etc/passwd will work (e.g. nix) 140 if [ ! -e /fs/etc/passwd ]; then 141 echo "root:x:0:0:System administrator:/root:/bin/sh" > /fs/etc/passwd 142 fi 143 144 echo "starting stage 2 ($command)" 145 exec switch_root /fs $command $out 146 ''; 147 148 149 initrd = pkgs.makeInitrd { 150 contents = [ 151 { object = stage1Init; 152 symlink = "/init"; 153 } 154 ]; 155 }; 156 157 158 stage2Init = writeScript "vm-run-stage2" '' 159 #! ${bash}/bin/sh 160 source /tmp/xchg/saved-env 161 162 # Set the system time from the hardware clock. Works around an 163 # apparent KVM > 1.5.2 bug. 164 ${util-linux}/bin/hwclock -s 165 166 export NIX_STORE=${storeDir} 167 export NIX_BUILD_TOP=/tmp 168 export TMPDIR=/tmp 169 export PATH=/empty 170 out="$1" 171 cd "$NIX_BUILD_TOP" 172 173 if ! test -e /bin/sh; then 174 ${coreutils}/bin/mkdir -p /bin 175 ${coreutils}/bin/ln -s ${bash}/bin/sh /bin/sh 176 fi 177 178 # Set up automatic kernel module loading. 179 export MODULE_DIR=${kernel}/lib/modules/ 180 ${coreutils}/bin/cat <<EOF > /run/modprobe 181 #! ${bash}/bin/sh 182 export MODULE_DIR=$MODULE_DIR 183 exec ${kmod}/bin/modprobe "\$@" 184 EOF 185 ${coreutils}/bin/chmod 755 /run/modprobe 186 echo /run/modprobe > /proc/sys/kernel/modprobe 187 188 # For debugging: if this is the second time this image is run, 189 # then don't start the build again, but instead drop the user into 190 # an interactive shell. 191 if test -n "$origBuilder" -a ! -e /.debug; then 192 exec < /dev/null 193 ${coreutils}/bin/touch /.debug 194 $origBuilder $origArgs 195 echo $? > /tmp/xchg/in-vm-exit 196 197 ${busybox}/bin/mount -o remount,ro dummy / 198 199 ${busybox}/bin/poweroff -f 200 else 201 export PATH=/bin:/usr/bin:${coreutils}/bin 202 echo "Starting interactive shell..." 203 echo "(To run the original builder: \$origBuilder \$origArgs)" 204 exec ${busybox}/bin/setsid ${bashInteractive}/bin/bash < /dev/${qemu-common.qemuSerialDevice} &> /dev/${qemu-common.qemuSerialDevice} 205 fi 206 ''; 207 208 209 qemuCommandLinux = '' 210 ${qemu-common.qemuBinary qemu} \ 211 -nographic -no-reboot \ 212 -device virtio-rng-pci \ 213 -virtfs local,path=${storeDir},security_model=none,mount_tag=store \ 214 -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \ 215 ''${diskImage:+-drive file=$diskImage,if=virtio,cache=unsafe,werror=report} \ 216 -kernel ${kernel}/${img} \ 217 -initrd ${initrd}/initrd \ 218 -append "console=${qemu-common.qemuSerialDevice} panic=1 command=${stage2Init} out=$out mountDisk=$mountDisk loglevel=4" \ 219 $QEMU_OPTS 220 ''; 221 222 223 vmRunCommand = qemuCommand: writeText "vm-run" '' 224 export > saved-env 225 226 PATH=${coreutils}/bin 227 mkdir xchg 228 mv saved-env xchg/ 229 230 eval "$preVM" 231 232 if [ "$enableParallelBuilding" = 1 ]; then 233 if [ ''${NIX_BUILD_CORES:-0} = 0 ]; then 234 QEMU_OPTS+=" -smp cpus=$(nproc)" 235 else 236 QEMU_OPTS+=" -smp cpus=$NIX_BUILD_CORES" 237 fi 238 fi 239 240 # Write the command to start the VM to a file so that the user can 241 # debug inside the VM if the build fails (when Nix is called with 242 # the -K option to preserve the temporary build directory). 243 cat > ./run-vm <<EOF 244 #! ${bash}/bin/sh 245 ''${diskImage:+diskImage=$diskImage} 246 TMPDIR=$TMPDIR 247 cd $TMPDIR 248 ${qemuCommand} 249 EOF 250 251 mkdir -p -m 0700 $out 252 253 chmod +x ./run-vm 254 source ./run-vm 255 256 if ! test -e xchg/in-vm-exit; then 257 echo "Virtual machine didn't produce an exit code." 258 exit 1 259 fi 260 261 exitCode="$(cat xchg/in-vm-exit)" 262 if [ "$exitCode" != "0" ]; then 263 exit "$exitCode" 264 fi 265 266 eval "$postVM" 267 ''; 268 269 /* 270 A bash script fragment that produces a disk image at `destination`. 271 */ 272 createEmptyImage = { 273 # Disk image size in MiB 274 size, 275 # Name that will be written to ${destination}/nix-support/full-name 276 fullName, 277 # Where to write the image files, defaulting to $out 278 destination ? "$out" 279 }: '' 280 mkdir -p ${destination} 281 diskImage=${destination}/disk-image.qcow2 282 ${qemu}/bin/qemu-img create -f qcow2 $diskImage "${toString size}M" 283 284 mkdir ${destination}/nix-support 285 echo "${fullName}" > ${destination}/nix-support/full-name 286 ''; 287 288 289 defaultCreateRootFS = '' 290 mkdir /mnt 291 ${e2fsprogs}/bin/mkfs.ext4 /dev/${hd} 292 ${util-linux}/bin/mount -t ext4 /dev/${hd} /mnt 293 294 if test -e /mnt/.debug; then 295 exec ${bash}/bin/sh 296 fi 297 touch /mnt/.debug 298 299 mkdir /mnt/proc /mnt/dev /mnt/sys 300 ''; 301 302 303 /* Run a derivation in a Linux virtual machine (using Qemu/KVM). By 304 default, there is no disk image; the root filesystem is a tmpfs, 305 and the nix store is shared with the host (via the 9P protocol). 306 Thus, any pure Nix derivation should run unmodified, e.g. the 307 call 308 309 runInLinuxVM patchelf 310 311 will build the derivation `patchelf' inside a VM. The attribute 312 `preVM' can optionally contain a shell command to be evaluated 313 *before* the VM is started (i.e., on the host). The attribute 314 `memSize' specifies the memory size of the VM in megabytes, 315 defaulting to 512. The attribute `diskImage' can optionally 316 specify a file system image to be attached to /dev/sda. (Note 317 that currently we expect the image to contain a filesystem, not a 318 full disk image with a partition table etc.) 319 320 If the build fails and Nix is run with the `-K' option, a script 321 `run-vm' will be left behind in the temporary build directory 322 that allows you to boot into the VM and debug it interactively. */ 323 324 runInLinuxVM = drv: lib.overrideDerivation drv ({ memSize ? 512, QEMU_OPTS ? "", args, builder, ... }: { 325 requiredSystemFeatures = [ "kvm" ]; 326 builder = "${bash}/bin/sh"; 327 args = ["-e" (vmRunCommand qemuCommandLinux)]; 328 origArgs = args; 329 origBuilder = builder; 330 QEMU_OPTS = "${QEMU_OPTS} -m ${toString memSize}"; 331 passAsFile = []; # HACK fix - see https://github.com/NixOS/nixpkgs/issues/16742 332 }); 333 334 335 extractFs = {file, fs ? null} : 336 runInLinuxVM ( 337 stdenv.mkDerivation { 338 name = "extract-file"; 339 buildInputs = [ util-linux ]; 340 buildCommand = '' 341 ln -s ${kernel}/lib /lib 342 ${kmod}/bin/modprobe loop 343 ${kmod}/bin/modprobe ext4 344 ${kmod}/bin/modprobe hfs 345 ${kmod}/bin/modprobe hfsplus 346 ${kmod}/bin/modprobe squashfs 347 ${kmod}/bin/modprobe iso9660 348 ${kmod}/bin/modprobe ufs 349 ${kmod}/bin/modprobe cramfs 350 351 mkdir -p $out 352 mkdir -p tmp 353 mount -o loop,ro,ufstype=44bsd ${lib.optionalString (fs != null) "-t ${fs} "}${file} tmp || 354 mount -o loop,ro ${lib.optionalString (fs != null) "-t ${fs} "}${file} tmp 355 cp -Rv tmp/* $out/ || exit 0 356 ''; 357 }); 358 359 360 extractMTDfs = {file, fs ? null} : 361 runInLinuxVM ( 362 stdenv.mkDerivation { 363 name = "extract-file-mtd"; 364 buildInputs = [ pkgs.util-linux pkgs.mtdutils ]; 365 buildCommand = '' 366 ln -s ${kernel}/lib /lib 367 ${kmod}/bin/modprobe mtd 368 ${kmod}/bin/modprobe mtdram total_size=131072 369 ${kmod}/bin/modprobe mtdchar 370 ${kmod}/bin/modprobe mtdblock 371 ${kmod}/bin/modprobe jffs2 372 ${kmod}/bin/modprobe zlib 373 374 mkdir -p $out 375 mkdir -p tmp 376 377 dd if=${file} of=/dev/mtd0 378 mount ${lib.optionalString (fs != null) "-t ${fs} "}/dev/mtdblock0 tmp 379 380 cp -R tmp/* $out/ 381 ''; 382 }); 383 384 385 /* Like runInLinuxVM, but run the build not using the stdenv from 386 the Nix store, but using the tools provided by /bin, /usr/bin 387 etc. from the specified filesystem image, which typically is a 388 filesystem containing a non-NixOS Linux distribution. */ 389 390 runInLinuxImage = drv: runInLinuxVM (lib.overrideDerivation drv (attrs: { 391 mountDisk = true; 392 393 /* Mount `image' as the root FS, but use a temporary copy-on-write 394 image since we don't want to (and can't) write to `image'. */ 395 preVM = '' 396 diskImage=$(pwd)/disk-image.qcow2 397 origImage=${attrs.diskImage} 398 if test -d "$origImage"; then origImage="$origImage/disk-image.qcow2"; fi 399 ${qemu}/bin/qemu-img create -F ${attrs.diskImageFormat} -b "$origImage" -f qcow2 $diskImage 400 ''; 401 402 /* Inside the VM, run the stdenv setup script normally, but at the 403 very end set $PATH and $SHELL to the `native' paths for the 404 distribution inside the VM. */ 405 postHook = '' 406 PATH=/usr/bin:/bin:/usr/sbin:/sbin 407 SHELL=/bin/sh 408 eval "$origPostHook" 409 ''; 410 411 origPostHook = lib.optionalString (attrs ? postHook) attrs.postHook; 412 413 /* Don't run Nix-specific build steps like patchelf. */ 414 fixupPhase = "true"; 415 })); 416 417 418 /* Create a filesystem image of the specified size and fill it with 419 a set of RPM packages. */ 420 421 fillDiskWithRPMs = 422 { size ? 4096, rpms, name, fullName, preInstall ? "", postInstall ? "" 423 , runScripts ? true, createRootFS ? defaultCreateRootFS 424 , QEMU_OPTS ? "", memSize ? 512 425 , unifiedSystemDir ? false 426 }: 427 428 runInLinuxVM (stdenv.mkDerivation { 429 inherit name preInstall postInstall rpms QEMU_OPTS memSize; 430 preVM = createEmptyImage {inherit size fullName;}; 431 432 buildCommand = '' 433 ${createRootFS} 434 435 chroot=$(type -tP chroot) 436 437 # Make the Nix store available in /mnt, because that's where the RPMs live. 438 mkdir -p /mnt${storeDir} 439 ${util-linux}/bin/mount -o bind ${storeDir} /mnt${storeDir} 440 441 # Newer distributions like Fedora 18 require /lib etc. to be 442 # symlinked to /usr. 443 ${lib.optionalString unifiedSystemDir '' 444 mkdir -p /mnt/usr/bin /mnt/usr/sbin /mnt/usr/lib /mnt/usr/lib64 445 ln -s /usr/bin /mnt/bin 446 ln -s /usr/sbin /mnt/sbin 447 ln -s /usr/lib /mnt/lib 448 ln -s /usr/lib64 /mnt/lib64 449 ${util-linux}/bin/mount -t proc none /mnt/proc 450 ''} 451 452 echo "unpacking RPMs..." 453 set +o pipefail 454 for i in $rpms; do 455 echo "$i..." 456 ${rpm}/bin/rpm2cpio "$i" | chroot /mnt ${cpio}/bin/cpio -i --make-directories --unconditional 457 done 458 459 eval "$preInstall" 460 461 echo "initialising RPM DB..." 462 PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \ 463 ldconfig -v || true 464 PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \ 465 rpm --initdb 466 467 ${util-linux}/bin/mount -o bind /tmp /mnt/tmp 468 469 echo "installing RPMs..." 470 PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \ 471 rpm -iv --nosignature ${lib.optionalString (!runScripts) "--noscripts"} $rpms 472 473 echo "running post-install script..." 474 eval "$postInstall" 475 476 rm /mnt/.debug 477 478 ${util-linux}/bin/umount /mnt${storeDir} /mnt/tmp ${lib.optionalString unifiedSystemDir "/mnt/proc"} 479 ${util-linux}/bin/umount /mnt 480 ''; 481 482 passthru = { inherit fullName; }; 483 }); 484 485 486 /* Generate a script that can be used to run an interactive session 487 in the given image. */ 488 489 makeImageTestScript = image: writeScript "image-test" '' 490 #! ${bash}/bin/sh 491 if test -z "$1"; then 492 echo "Syntax: $0 <copy-on-write-temp-file>" 493 exit 1 494 fi 495 diskImage="$1" 496 if ! test -e "$diskImage"; then 497 ${qemu}/bin/qemu-img create -b ${image}/disk-image.qcow2 -f qcow2 -F qcow2 "$diskImage" 498 fi 499 export TMPDIR=$(mktemp -d) 500 export out=/dummy 501 export origBuilder= 502 export origArgs= 503 mkdir $TMPDIR/xchg 504 export > $TMPDIR/xchg/saved-env 505 mountDisk=1 506 ${qemuCommandLinux} 507 ''; 508 509 510 /* Build RPM packages from the tarball `src' in the Linux 511 distribution installed in the filesystem `diskImage'. The 512 tarball must contain an RPM specfile. */ 513 514 buildRPM = attrs: runInLinuxImage (stdenv.mkDerivation ({ 515 prePhases = [ "prepareImagePhase" "sysInfoPhase" ]; 516 dontConfigure = true; 517 518 outDir = "rpms/${attrs.diskImage.name}"; 519 520 prepareImagePhase = '' 521 if test -n "$extraRPMs"; then 522 for rpmdir in $extraRPMs ; do 523 rpm -iv $(ls $rpmdir/rpms/*/*.rpm | grep -v 'src\.rpm' | sort | head -1) 524 done 525 fi 526 ''; 527 528 sysInfoPhase = '' 529 echo "System/kernel: $(uname -a)" 530 if test -e /etc/fedora-release; then echo "Fedora release: $(cat /etc/fedora-release)"; fi 531 if test -e /etc/SuSE-release; then echo "SUSE release: $(cat /etc/SuSE-release)"; fi 532 echo "installed RPM packages" 533 rpm -qa --qf "%{Name}-%{Version}-%{Release} (%{Arch}; %{Distribution}; %{Vendor})\n" 534 ''; 535 536 buildPhase = '' 537 eval "$preBuild" 538 539 srcName="$(rpmspec --srpm -q --qf '%{source}' *.spec)" 540 cp "$src" "$srcName" # `ln' doesn't work always work: RPM requires that the file is owned by root 541 542 export HOME=/tmp/home 543 mkdir $HOME 544 545 rpmout=/tmp/rpmout 546 mkdir $rpmout $rpmout/SPECS $rpmout/BUILD $rpmout/RPMS $rpmout/SRPMS 547 548 echo "%_topdir $rpmout" >> $HOME/.rpmmacros 549 550 if [ `uname -m` = i686 ]; then extra="--target i686-linux"; fi 551 rpmbuild -vv $extra -ta "$srcName" 552 553 eval "$postBuild" 554 ''; 555 556 installPhase = '' 557 eval "$preInstall" 558 559 mkdir -p $out/$outDir 560 find $rpmout -name "*.rpm" -exec cp {} $out/$outDir \; 561 562 for i in $out/$outDir/*.rpm; do 563 echo "Generated RPM/SRPM: $i" 564 rpm -qip $i 565 done 566 567 eval "$postInstall" 568 ''; # */ 569 } // attrs)); 570 571 572 /* Create a filesystem image of the specified size and fill it with 573 a set of Debian packages. `debs' must be a list of list of 574 .deb files, namely, the Debian packages grouped together into 575 strongly connected components. See deb/deb-closure.nix. */ 576 577 fillDiskWithDebs = 578 { size ? 4096, debs, name, fullName, postInstall ? null, createRootFS ? defaultCreateRootFS 579 , QEMU_OPTS ? "", memSize ? 512 }: 580 581 runInLinuxVM (stdenv.mkDerivation { 582 inherit name postInstall QEMU_OPTS memSize; 583 584 debs = (lib.intersperse "|" debs); 585 586 preVM = createEmptyImage {inherit size fullName;}; 587 588 buildCommand = '' 589 ${createRootFS} 590 591 PATH=$PATH:${lib.makeBinPath [ pkgs.dpkg pkgs.glibc pkgs.xz ]} 592 593 # Unpack the .debs. We do this to prevent pre-install scripts 594 # (which have lots of circular dependencies) from barfing. 595 echo "unpacking Debs..." 596 597 for deb in $debs; do 598 if test "$deb" != "|"; then 599 echo "$deb..." 600 dpkg-deb --extract "$deb" /mnt 601 fi 602 done 603 604 # Make the Nix store available in /mnt, because that's where the .debs live. 605 mkdir -p /mnt/inst${storeDir} 606 ${util-linux}/bin/mount -o bind ${storeDir} /mnt/inst${storeDir} 607 ${util-linux}/bin/mount -o bind /proc /mnt/proc 608 ${util-linux}/bin/mount -o bind /dev /mnt/dev 609 610 # Misc. files/directories assumed by various packages. 611 echo "initialising Dpkg DB..." 612 touch /mnt/etc/shells 613 touch /mnt/var/lib/dpkg/status 614 touch /mnt/var/lib/dpkg/available 615 touch /mnt/var/lib/dpkg/diversions 616 617 # Now install the .debs. This is basically just to register 618 # them with dpkg and to make their pre/post-install scripts 619 # run. 620 echo "installing Debs..." 621 622 export DEBIAN_FRONTEND=noninteractive 623 624 oldIFS="$IFS" 625 IFS="|" 626 for component in $debs; do 627 IFS="$oldIFS" 628 echo 629 echo ">>> INSTALLING COMPONENT: $component" 630 debs= 631 for i in $component; do 632 debs="$debs /inst/$i"; 633 done 634 chroot=$(type -tP chroot) 635 636 # Create a fake start-stop-daemon script, as done in debootstrap. 637 mv "/mnt/sbin/start-stop-daemon" "/mnt/sbin/start-stop-daemon.REAL" 638 echo "#!/bin/true" > "/mnt/sbin/start-stop-daemon" 639 chmod 755 "/mnt/sbin/start-stop-daemon" 640 641 PATH=/usr/bin:/bin:/usr/sbin:/sbin $chroot /mnt \ 642 /usr/bin/dpkg --install --force-all $debs < /dev/null || true 643 644 # Move the real start-stop-daemon back into its place. 645 mv "/mnt/sbin/start-stop-daemon.REAL" "/mnt/sbin/start-stop-daemon" 646 done 647 648 echo "running post-install script..." 649 eval "$postInstall" 650 ln -sf dash /mnt/bin/sh 651 652 rm /mnt/.debug 653 654 ${util-linux}/bin/umount /mnt/inst${storeDir} 655 ${util-linux}/bin/umount /mnt/proc 656 ${util-linux}/bin/umount /mnt/dev 657 ${util-linux}/bin/umount /mnt 658 ''; 659 660 passthru = { inherit fullName; }; 661 }); 662 663 664 /* Generate a Nix expression containing fetchurl calls for the 665 closure of a set of top-level RPM packages from the 666 `primary.xml.gz' file of a Fedora or openSUSE distribution. */ 667 668 rpmClosureGenerator = 669 {name, packagesLists, urlPrefixes, packages, archs ? []}: 670 assert (builtins.length packagesLists) == (builtins.length urlPrefixes); 671 runCommand "${name}.nix" { 672 nativeBuildInputs = [ buildPackages.perl buildPackages.perlPackages.XMLSimple ]; 673 inherit archs; 674 } '' 675 ${lib.concatImapStrings (i: pl: '' 676 gunzip < ${pl} > ./packages_${toString i}.xml 677 '') packagesLists} 678 perl -w ${rpm/rpm-closure.pl} \ 679 ${lib.concatImapStrings (i: pl: "./packages_${toString i}.xml ${pl.snd} " ) (lib.zipLists packagesLists urlPrefixes)} \ 680 ${toString packages} > $out 681 ''; 682 683 684 /* Helper function that combines rpmClosureGenerator and 685 fillDiskWithRPMs to generate a disk image from a set of package 686 names. */ 687 688 makeImageFromRPMDist = 689 { name, fullName, size ? 4096 690 , urlPrefix ? "", urlPrefixes ? [urlPrefix] 691 , packagesList ? "", packagesLists ? [packagesList] 692 , packages, extraPackages ? [] 693 , preInstall ? "", postInstall ? "", archs ? ["noarch" "i386"] 694 , runScripts ? true, createRootFS ? defaultCreateRootFS 695 , QEMU_OPTS ? "", memSize ? 512 696 , unifiedSystemDir ? false }: 697 698 fillDiskWithRPMs { 699 inherit name fullName size preInstall postInstall runScripts createRootFS unifiedSystemDir QEMU_OPTS memSize; 700 rpms = import (rpmClosureGenerator { 701 inherit name packagesLists urlPrefixes archs; 702 packages = packages ++ extraPackages; 703 }) { inherit fetchurl; }; 704 }; 705 706 707 /* Like `rpmClosureGenerator', but now for Debian/Ubuntu releases 708 (i.e. generate a closure from a Packages.bz2 file). */ 709 710 debClosureGenerator = 711 {name, packagesLists, urlPrefix, packages}: 712 713 runCommand "${name}.nix" 714 { nativeBuildInputs = [ buildPackages.perl buildPackages.dpkg ]; } '' 715 for i in ${toString packagesLists}; do 716 echo "adding $i..." 717 case $i in 718 *.xz | *.lzma) 719 xz -d < $i >> ./Packages 720 ;; 721 *.bz2) 722 bunzip2 < $i >> ./Packages 723 ;; 724 *.gz) 725 gzip -dc < $i >> ./Packages 726 ;; 727 esac 728 done 729 730 # Work around this bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=452279 731 sed -i ./Packages -e s/x86_64-linux-gnu/x86-64-linux-gnu/g 732 733 perl -w ${deb/deb-closure.pl} \ 734 ./Packages ${urlPrefix} ${toString packages} > $out 735 ''; 736 737 738 /* Helper function that combines debClosureGenerator and 739 fillDiskWithDebs to generate a disk image from a set of package 740 names. */ 741 742 makeImageFromDebDist = 743 { name, fullName, size ? 4096, urlPrefix 744 , packagesList ? "", packagesLists ? [packagesList] 745 , packages, extraPackages ? [], postInstall ? "" 746 , extraDebs ? [], createRootFS ? defaultCreateRootFS 747 , QEMU_OPTS ? "", memSize ? 512 }: 748 749 let 750 expr = debClosureGenerator { 751 inherit name packagesLists urlPrefix; 752 packages = packages ++ extraPackages; 753 }; 754 in 755 (fillDiskWithDebs { 756 inherit name fullName size postInstall createRootFS QEMU_OPTS memSize; 757 debs = import expr {inherit fetchurl;} ++ extraDebs; 758 }) // {inherit expr;}; 759 760 761 /* The set of supported RPM-based distributions. */ 762 763 rpmDistros = { 764 765 # Note: no i386 release for Fedora >= 26 766 fedora26x86_64 = 767 let version = "26"; 768 in { 769 name = "fedora-${version}-x86_64"; 770 fullName = "Fedora ${version} (x86_64)"; 771 packagesList = fetchurl rec { 772 url = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os/repodata/${sha256}-primary.xml.gz"; 773 sha256 = "880055a50c05b20641530d09b23f64501a000b2f92fe252417c530178730a95e"; 774 }; 775 urlPrefix = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os"; 776 archs = ["noarch" "x86_64"]; 777 packages = commonFedoraPackages ++ [ "cronie" "util-linux" ]; 778 unifiedSystemDir = true; 779 }; 780 781 fedora27x86_64 = 782 let version = "27"; 783 in { 784 name = "fedora-${version}-x86_64"; 785 fullName = "Fedora ${version} (x86_64)"; 786 packagesList = fetchurl rec { 787 url = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os/repodata/${sha256}-primary.xml.gz"; 788 sha256 = "48986ce4583cd09825c6d437150314446f0f49fa1a1bd62dcfa1085295030fe9"; 789 }; 790 urlPrefix = "mirror://fedora/linux/releases/${version}/Everything/x86_64/os"; 791 archs = ["noarch" "x86_64"]; 792 packages = commonFedoraPackages ++ [ "cronie" "util-linux" ]; 793 unifiedSystemDir = true; 794 }; 795 796 centos6i386 = 797 let version = "6.9"; 798 in rec { 799 name = "centos-${version}-i386"; 800 fullName = "CentOS ${version} (i386)"; 801 urlPrefix = "mirror://centos/${version}/os/i386"; 802 packagesList = fetchurl rec { 803 url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz"; 804 sha256 = "b826a45082ef68340325c0855f3d2e5d5a4d0f77d28ba3b871791d6f14a97aeb"; 805 }; 806 archs = ["noarch" "i386"]; 807 packages = commonCentOSPackages ++ [ "procps" ]; 808 }; 809 810 centos6x86_64 = 811 let version = "6.9"; 812 in rec { 813 name = "centos-${version}-x86_64"; 814 fullName = "CentOS ${version} (x86_64)"; 815 urlPrefix = "mirror://centos/${version}/os/x86_64"; 816 packagesList = fetchurl rec { 817 url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz"; 818 sha256 = "ed2b2d4ac98d774d4cd3e91467e1532f7e8b0275cfc91a0d214b532dcaf1e979"; 819 }; 820 archs = ["noarch" "x86_64"]; 821 packages = commonCentOSPackages ++ [ "procps" ]; 822 }; 823 824 # Note: no i386 release for 7.x 825 centos7x86_64 = 826 let version = "7.4.1708"; 827 in rec { 828 name = "centos-${version}-x86_64"; 829 fullName = "CentOS ${version} (x86_64)"; 830 urlPrefix = "mirror://centos/${version}/os/x86_64"; 831 packagesList = fetchurl rec { 832 url = "${urlPrefix}/repodata/${sha256}-primary.xml.gz"; 833 sha256 = "b686d3a0f337323e656d9387b9a76ce6808b26255fc3a138b1a87d3b1cb95ed5"; 834 }; 835 archs = ["noarch" "x86_64"]; 836 packages = commonCentOSPackages ++ [ "procps-ng" ]; 837 }; 838 }; 839 840 841 /* The set of supported Dpkg-based distributions. */ 842 843 debDistros = { 844 ubuntu1404i386 = { 845 name = "ubuntu-14.04-trusty-i386"; 846 fullName = "Ubuntu 14.04 Trusty (i386)"; 847 packagesLists = 848 [ (fetchurl { 849 url = "mirror://ubuntu/dists/trusty/main/binary-i386/Packages.bz2"; 850 sha256 = "1d5y3v3v079gdq45hc07ja0bjlmzqfwdwwlq0brwxi8m75k3iz7x"; 851 }) 852 (fetchurl { 853 url = "mirror://ubuntu/dists/trusty/universe/binary-i386/Packages.bz2"; 854 sha256 = "03x9w92by320rfklrqhcl3qpwmnxds9c8ijl5zhcb21d6dcz5z1a"; 855 }) 856 ]; 857 urlPrefix = "mirror://ubuntu"; 858 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 859 }; 860 861 ubuntu1404x86_64 = { 862 name = "ubuntu-14.04-trusty-amd64"; 863 fullName = "Ubuntu 14.04 Trusty (amd64)"; 864 packagesLists = 865 [ (fetchurl { 866 url = "mirror://ubuntu/dists/trusty/main/binary-amd64/Packages.bz2"; 867 sha256 = "1hhzbyqfr5i0swahwnl5gfp5l9p9hspywb1vpihr3b74p1z935bh"; 868 }) 869 (fetchurl { 870 url = "mirror://ubuntu/dists/trusty/universe/binary-amd64/Packages.bz2"; 871 sha256 = "04560ba8s4z4v5iawknagrkn9q1nzvpn081ycmqvhh73p3p3g1jm"; 872 }) 873 ]; 874 urlPrefix = "mirror://ubuntu"; 875 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 876 }; 877 878 ubuntu1604i386 = { 879 name = "ubuntu-16.04-xenial-i386"; 880 fullName = "Ubuntu 16.04 Xenial (i386)"; 881 packagesLists = 882 [ (fetchurl { 883 url = "mirror://ubuntu/dists/xenial/main/binary-i386/Packages.xz"; 884 sha256 = "13r75sp4slqy8w32y5dnr7pp7p3cfvavyr1g7gwnlkyrq4zx4ahy"; 885 }) 886 (fetchurl { 887 url = "mirror://ubuntu/dists/xenial/universe/binary-i386/Packages.xz"; 888 sha256 = "14fid1rqm3sc0wlygcvn0yx5aljf51c2jpd4x0zxij4019316hsh"; 889 }) 890 ]; 891 urlPrefix = "mirror://ubuntu"; 892 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 893 }; 894 895 ubuntu1604x86_64 = { 896 name = "ubuntu-16.04-xenial-amd64"; 897 fullName = "Ubuntu 16.04 Xenial (amd64)"; 898 packagesLists = 899 [ (fetchurl { 900 url = "mirror://ubuntu/dists/xenial/main/binary-amd64/Packages.xz"; 901 sha256 = "110qnkhjkkwm316fbig3aivm2595ydz6zskc4ld5cr8ngcrqm1bn"; 902 }) 903 (fetchurl { 904 url = "mirror://ubuntu/dists/xenial/universe/binary-amd64/Packages.xz"; 905 sha256 = "0mm7gj491yi6q4v0n4qkbsm94s59bvqir6fk60j73w7y4la8rg68"; 906 }) 907 ]; 908 urlPrefix = "mirror://ubuntu"; 909 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 910 }; 911 912 ubuntu1804i386 = { 913 name = "ubuntu-18.04-bionic-i386"; 914 fullName = "Ubuntu 18.04 Bionic (i386)"; 915 packagesLists = 916 [ (fetchurl { 917 url = "mirror://ubuntu/dists/bionic/main/binary-i386/Packages.xz"; 918 sha256 = "0f0v4131kwf7m7f8j3288rlqdxk1k3vqy74b7fcfd6jz9j8d840i"; 919 }) 920 (fetchurl { 921 url = "mirror://ubuntu/dists/bionic/universe/binary-i386/Packages.xz"; 922 sha256 = "1v75c0dqr0wp0dqd4hnci92qqs4hll8frqdbpswadgxm5chn91bw"; 923 }) 924 ]; 925 urlPrefix = "mirror://ubuntu"; 926 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 927 }; 928 929 ubuntu1804x86_64 = { 930 name = "ubuntu-18.04-bionic-amd64"; 931 fullName = "Ubuntu 18.04 Bionic (amd64)"; 932 packagesLists = 933 [ (fetchurl { 934 url = "mirror://ubuntu/dists/bionic/main/binary-amd64/Packages.xz"; 935 sha256 = "1ls81bjyvmfz6i919kszl7xks1ibrh1xqhsk6698ackndkm0wp39"; 936 }) 937 (fetchurl { 938 url = "mirror://ubuntu/dists/bionic/universe/binary-amd64/Packages.xz"; 939 sha256 = "1832nqpn4ap95b3sj870xqayrza9in4kih9jkmjax27pq6x15v1r"; 940 }) 941 ]; 942 urlPrefix = "mirror://ubuntu"; 943 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 944 }; 945 946 ubuntu2004i386 = { 947 name = "ubuntu-20.04-focal-i386"; 948 fullName = "Ubuntu 20.04 Focal (i386)"; 949 packagesLists = 950 [ (fetchurl { 951 url = "mirror://ubuntu/dists/focal/main/binary-i386/Packages.xz"; 952 sha256 = "sha256-7RAYURoN3RKYQAHpwBS9TIV6vCmpURpphyMJQmV4wLc="; 953 }) 954 (fetchurl { 955 url = "mirror://ubuntu/dists/focal/universe/binary-i386/Packages.xz"; 956 sha256 = "sha256-oA551xVE80volUPgkMyvzpQ1d+GhuZd4DAe7dXZnULM="; 957 }) 958 ]; 959 urlPrefix = "mirror://ubuntu"; 960 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 961 }; 962 963 ubuntu2004x86_64 = { 964 name = "ubuntu-20.04-focal-amd64"; 965 fullName = "Ubuntu 20.04 Focal (amd64)"; 966 packagesLists = 967 [ (fetchurl { 968 url = "mirror://ubuntu/dists/focal/main/binary-amd64/Packages.xz"; 969 sha256 = "sha256-d1eSH/j+7Zw5NKDJk21EG6SiOL7j6myMHfXLzUP8mGE="; 970 }) 971 (fetchurl { 972 url = "mirror://ubuntu/dists/focal/universe/binary-amd64/Packages.xz"; 973 sha256 = "sha256-RqdG2seJvZU3rKVNsWgLnf9RwkgVMRE1A4IZnX2WudE="; 974 }) 975 ]; 976 urlPrefix = "mirror://ubuntu"; 977 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 978 }; 979 980 ubuntu2204i386 = { 981 name = "ubuntu-22.04-jammy-i386"; 982 fullName = "Ubuntu 22.04 Jammy (i386)"; 983 packagesLists = 984 [ (fetchurl { 985 url = "mirror://ubuntu/dists/jammy/main/binary-i386/Packages.xz"; 986 sha256 = "sha256-iZBmwT0ep4v+V3sayybbOgZBOFFZwPGpOKtmuLMMVPQ="; 987 }) 988 (fetchurl { 989 url = "mirror://ubuntu/dists/jammy/universe/binary-i386/Packages.xz"; 990 sha256 = "sha256-DO2LdpZ9rDDBhWj2gvDWd0TJJVZHxKsYTKTi6GXjm1E="; 991 }) 992 ]; 993 urlPrefix = "mirror://ubuntu"; 994 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 995 }; 996 997 ubuntu2204x86_64 = { 998 name = "ubuntu-22.04-jammy-amd64"; 999 fullName = "Ubuntu 22.04 Jammy (amd64)"; 1000 packagesLists = 1001 [ (fetchurl { 1002 url = "mirror://ubuntu/dists/jammy/main/binary-amd64/Packages.xz"; 1003 sha256 = "sha256-N8tX8VVMv6ccWinun/7hipqMF4K7BWjgh0t/9M6PnBE="; 1004 }) 1005 (fetchurl { 1006 url = "mirror://ubuntu/dists/jammy/universe/binary-amd64/Packages.xz"; 1007 sha256 = "sha256-0pyyTJP+xfQyVXBrzn60bUd5lSA52MaKwbsUpvNlXOI="; 1008 }) 1009 ]; 1010 urlPrefix = "mirror://ubuntu"; 1011 packages = commonDebPackages ++ [ "diffutils" "libc-bin" ]; 1012 }; 1013 1014 debian10i386 = { 1015 name = "debian-10.13-buster-i386"; 1016 fullName = "Debian 10.13 Buster (i386)"; 1017 packagesList = fetchurl { 1018 url = "https://snapshot.debian.org/archive/debian/20221126T084953Z/dists/buster/main/binary-i386/Packages.xz"; 1019 hash = "sha256-n9JquhtZgxw3qr9BX0MQoY3ZTIHN0dit+iru3DC31UY="; 1020 }; 1021 urlPrefix = "https://snapshot.debian.org/archive/debian/20221126T084953Z"; 1022 packages = commonDebianPackages; 1023 }; 1024 1025 debian10x86_64 = { 1026 name = "debian-10.13-buster-amd64"; 1027 fullName = "Debian 10.13 Buster (amd64)"; 1028 packagesList = fetchurl { 1029 url = "https://snapshot.debian.org/archive/debian/20221126T084953Z/dists/buster/main/binary-amd64/Packages.xz"; 1030 hash = "sha256-YukIIB3u87jgp9oudwklsxyKVKjSL618wFgDSXiFmjU="; 1031 }; 1032 urlPrefix = "https://snapshot.debian.org/archive/debian/20221126T084953Z"; 1033 packages = commonDebianPackages; 1034 }; 1035 1036 debian11i386 = { 1037 name = "debian-11.6-bullseye-i386"; 1038 fullName = "Debian 11.6 Bullseye (i386)"; 1039 packagesList = fetchurl { 1040 url = "https://snapshot.debian.org/archive/debian/20230131T034648Z/dists/bullseye/main/binary-i386/Packages.xz"; 1041 hash = "sha256-z9eG7RlvelEnZAaeCfIO+XxTZVL3d+zTA7ShU43l/pw="; 1042 }; 1043 urlPrefix = "https://snapshot.debian.org/archive/debian/20230131T034648Z"; 1044 packages = commonDebianPackages; 1045 }; 1046 1047 debian11x86_64 = { 1048 name = "debian-11.6-bullseye-amd64"; 1049 fullName = "Debian 11.6 Bullseye (amd64)"; 1050 packagesList = fetchurl { 1051 url = "https://snapshot.debian.org/archive/debian/20230131T034648Z/dists/bullseye/main/binary-amd64/Packages.xz"; 1052 hash = "sha256-mz0eCWdn6uWt40OxsSPheHzEnMeLE52yR/vpb48/VF0="; 1053 }; 1054 urlPrefix = "https://snapshot.debian.org/archive/debian/20230131T034648Z"; 1055 packages = commonDebianPackages; 1056 }; 1057 }; 1058 1059 1060 /* Common packages for Fedora images. */ 1061 commonFedoraPackages = [ 1062 "autoconf" 1063 "automake" 1064 "basesystem" 1065 "bzip2" 1066 "curl" 1067 "diffutils" 1068 "fedora-release" 1069 "findutils" 1070 "gawk" 1071 "gcc-c++" 1072 "gzip" 1073 "make" 1074 "patch" 1075 "perl" 1076 "pkgconf-pkg-config" 1077 "rpm" 1078 "rpm-build" 1079 "tar" 1080 "unzip" 1081 ]; 1082 1083 commonCentOSPackages = [ 1084 "autoconf" 1085 "automake" 1086 "basesystem" 1087 "bzip2" 1088 "curl" 1089 "diffutils" 1090 "centos-release" 1091 "findutils" 1092 "gawk" 1093 "gcc-c++" 1094 "gzip" 1095 "make" 1096 "patch" 1097 "perl" 1098 "pkgconfig" 1099 "rpm" 1100 "rpm-build" 1101 "tar" 1102 "unzip" 1103 ]; 1104 1105 commonRHELPackages = [ 1106 "autoconf" 1107 "automake" 1108 "basesystem" 1109 "bzip2" 1110 "curl" 1111 "diffutils" 1112 "findutils" 1113 "gawk" 1114 "gcc-c++" 1115 "gzip" 1116 "make" 1117 "patch" 1118 "perl" 1119 "pkgconfig" 1120 "procps-ng" 1121 "rpm" 1122 "rpm-build" 1123 "tar" 1124 "unzip" 1125 ]; 1126 1127 /* Common packages for openSUSE images. */ 1128 commonOpenSUSEPackages = [ 1129 "aaa_base" 1130 "autoconf" 1131 "automake" 1132 "bzip2" 1133 "curl" 1134 "diffutils" 1135 "findutils" 1136 "gawk" 1137 "gcc-c++" 1138 "gzip" 1139 "make" 1140 "patch" 1141 "perl" 1142 "pkg-config" 1143 "rpm" 1144 "tar" 1145 "unzip" 1146 "util-linux" 1147 "gnu-getopt" 1148 ]; 1149 1150 1151 /* Common packages for Debian/Ubuntu images. */ 1152 commonDebPackages = [ 1153 "base-passwd" 1154 "dpkg" 1155 "libc6-dev" 1156 "perl" 1157 "bash" 1158 "dash" 1159 "gzip" 1160 "bzip2" 1161 "tar" 1162 "grep" 1163 "mawk" 1164 "sed" 1165 "findutils" 1166 "g++" 1167 "make" 1168 "curl" 1169 "patch" 1170 "locales" 1171 "coreutils" 1172 # Needed by checkinstall: 1173 "util-linux" 1174 "file" 1175 "dpkg-dev" 1176 "pkg-config" 1177 # Needed because it provides /etc/login.defs, whose absence causes 1178 # the "passwd" post-installs script to fail. 1179 "login" 1180 "passwd" 1181 ]; 1182 1183 commonDebianPackages = commonDebPackages ++ [ "sysvinit" "diff" ]; 1184 1185 1186 /* A set of functions that build the Linux distributions specified 1187 in `rpmDistros' and `debDistros'. For instance, 1188 `diskImageFuns.ubuntu1004x86_64 { }' builds an Ubuntu 10.04 disk 1189 image containing the default packages specified above. Overrides 1190 of the default image parameters can be given. In particular, 1191 `extraPackages' specifies the names of additional packages from 1192 the distribution that should be included in the image; `packages' 1193 allows the entire set of packages to be overridden; and `size' 1194 sets the size of the disk in megabytes. E.g., 1195 `diskImageFuns.ubuntu1004x86_64 { extraPackages = ["firefox"]; 1196 size = 8192; }' builds an 8 GiB image containing Firefox in 1197 addition to the default packages. */ 1198 diskImageFuns = 1199 (lib.mapAttrs (name: as: as2: makeImageFromRPMDist (as // as2)) rpmDistros) // 1200 (lib.mapAttrs (name: as: as2: makeImageFromDebDist (as // as2)) debDistros); 1201 1202 1203 /* Shorthand for `diskImageFuns.<attr> { extraPackages = ... }'. */ 1204 diskImageExtraFuns = 1205 lib.mapAttrs (name: f: extraPackages: f { inherit extraPackages; }) diskImageFuns; 1206 1207 1208 /* Default disk images generated from the `rpmDistros' and 1209 `debDistros' sets. */ 1210 diskImages = lib.mapAttrs (name: f: f {}) diskImageFuns; 1211 1212}