Unverified Commit 93cda47a authored by r-vdp's avatar r-vdp
Browse files

nixos/switchable-system: introduce a standard pre-switch check to prevent...

nixos/switchable-system: introduce a standard pre-switch check to prevent switching under certain conditions

This commit introduces "switch inhibitors" which are derivations that
prevent a switch of a system to a new configuration if those derivations
don't have the same hash in both configurations.
This means that we can for instance add the systemd and dbus derivations
such that users will be instructed to reboot their system when those
derivations have changed instead of switching.

This feature should be used sparingly, but it can make NixOS more robust
by avoiding users switching to a configuration that can make their
system unstable (like major updates of systemd, or new versions of dbus
since the dbus and dbus-broker daemons cannot be restarted).

The user can still force the switch by setting an env var.
parent 0738aa49
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -174,6 +174,10 @@ in
        permissions = "u+rx,g+rx,o-rx";
      };

      system.switch.inhibitors = [
        cfg.dbusPackage
      ];

      systemd.services.dbus = {
        aliases = [
          # hack aiding to prevent dbus from restarting when switching from dbus-broker back to dbus
@@ -212,6 +216,10 @@ in
        cfg.brokerPackage
      ];

      system.switch.inhibitors = [
        cfg.brokerPackage
      ];

      # Just to be sure we don't restart through the unit alias
      systemd.services.dbus.reloadIfChanged = true;
      systemd.user.services.dbus.reloadIfChanged = true;
+119 −28
Original line number Diff line number Diff line
@@ -16,7 +16,8 @@
    '')
  ];

  options.system.switch.enable = lib.mkOption {
  options.system.switch = {
    enable = lib.mkOption {
      type = lib.types.bool;
      default = true;
      description = ''
@@ -30,10 +31,22 @@
      '';
    };

    inhibitors = lib.mkOption {
      type = lib.types.listOf lib.types.pathInStore;
      default = [ ];
      description = ''
        List of derivations that will prevent switching into a configuration when
        they change.
        This can be manually overridden on the command line if required.
      '';
    };
  };

  config = lib.mkIf config.system.switch.enable {
    # Use a subshell so we can source makeWrapper's setup hook without
    # affecting the rest of activatableSystemBuilderCommands.
    system.activatableSystemBuilderCommands = ''
    system = {
      activatableSystemBuilderCommands = ''
        (
          source ${pkgs.buildPackages.makeWrapper}/nix-support/setup-hook

@@ -49,5 +62,83 @@
            --set SYSTEMD ${config.systemd.package}
        )
      '';

      systemBuilderCommands = ''
        ln -s ${config.system.build.inhibitSwitch} $out/switch-inhibitors
      '';

      build.inhibitSwitch = pkgs.writeTextFile {
        name = "switch-inhibitors";
        text = lib.concatMapStringsSep "\n" (drv: drv.outPath) config.system.switch.inhibitors;
      };

      preSwitchChecks.switchInhibitors =
        let
          realpath = lib.getExe' pkgs.coreutils "realpath";
          sha256sum = lib.getExe' pkgs.coreutils "sha256sum";
          diff = lib.getExe' pkgs.diffutils "diff";
        in
        # bash
        ''
          incoming="''${1-}"
          action="''${2-}"

          if [ "$action" == "boot" ]; then
            echo "Not checking switch inhibitors (action = $action)"
            exit
          fi

          echo -n "Checking switch inhibitors..."

          booted_inhibitors="$(${realpath} /run/booted-system)/switch-inhibitors"
          booted_inhibitors_sha="$(
            if [ -f "$booted_inhibitors" ]; then
              ${sha256sum} - < "$booted_inhibitors"
            else
              echo 'none'
            fi
          )"

          if [ "$booted_inhibitors_sha" == "none" ]; then
            echo
            echo "The previous configuration did not specify switch inhibitors, nothing to check."
            exit
          fi

          new_inhibitors="$(${realpath} "$incoming")/switch-inhibitors"
          new_inhibitors_sha="$(
            if [ -f "$new_inhibitors" ]; then
              ${sha256sum} - < "$new_inhibitors"
            else
              echo 'none'
            fi
          )"

          if [ "$new_inhibitors_sha" == "none" ]; then
            echo
            echo "The new configuration does not specify switch inhibitors, nothing to check."
            exit
          fi

          if [ "$new_inhibitors_sha" != "$booted_inhibitors_sha" ]; then
            echo
            echo "Found diff in switch inhibitors:"
            echo
            ${diff} --color "$booted_inhibitors" "$new_inhibitors"
            echo
            echo "The new configuration contains changes to packages that were"
            echo "listed as switch inhibitors."
            echo
            echo "If you really want to switch into this configuration directly, then"
            echo "you can set NIXOS_NO_CHECK=1 to ignore these pre-switch checks."
            echo
            echo "WARNING: doing so might cause the switch to fail or your system to become unstable."
            echo
            exit 1
          else
            echo " done"
          fi
        '';
    };
  };
}
+4 −0
Original line number Diff line number Diff line
@@ -567,6 +567,10 @@ in
      );
    };

    system.switch.inhibitors = [
      cfg.package
    ];

    environment.systemPackages = [ cfg.package ];

    environment.etc =