treewide: remove Perl-based switch-to-configuration

Changed files
+45 -1127
nixos
pkgs
by-name
sw
switch-to-configuration-ng
+2
nixos/doc/manual/release-notes/rl-2511.section.md
···
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
+
- The Perl implementation of the `switch-to-configuration` program is removed. All switchable systems now use the Rust rewrite. Any prior usage of `system.switch.enableNg` must now be removed. If you have any outstanding issues with the new implementation, please open an issue on GitHub.
+
- The `services.polipo` module has been removed as `polipo` is unmaintained and archived upstream.
- The Pocket ID module ([`services.pocket-id`][#opt-services.pocket-id.enable]) and package (`pocket-id`) has been updated to 1.0.0. Some environment variables have been changed or removed, see the [migration guide](https://pocket-id.org/docs/setup/migrate-to-v1/).
+1 -1
nixos/modules/system/activation/activation-script.nix
···
description = ''
A program that writes a bootloader installation script to the path passed in the first command line argument.
-
See `nixos/modules/system/activation/switch-to-configuration.pl`.
+
See `pkgs/by-name/sw/switch-to-configuration-ng/src/src/main.rs`.
'';
type = types.unique {
message = ''
-1027
nixos/modules/system/activation/switch-to-configuration.pl
···
-
#! @perl@/bin/perl
-
-
# NOTE: This script has an alternative implementation at
-
# <nixpkgs/pkgs/by-name/sw/switch-to-configuration-ng>. Any behavioral
-
# modifications to this script should also be made to that implementation.
-
-
-
# Issue #166838 uncovered a situation in which a configuration not suitable
-
# for the target architecture caused a cryptic error message instead of
-
# a clean failure. Due to this mismatch, the perl interpreter in the shebang
-
# line wasn't able to be executed, causing this script to be misinterpreted
-
# as a shell script.
-
#
-
# Let's detect this situation to give a more meaningful error
-
# message. The following two lines are carefully written to be both valid Perl
-
# and Bash.
-
printf "Perl script erroneously interpreted as shell script,\ndoes target platform match nixpkgs.crossSystem platform?\n" && exit 1
-
if 0;
-
-
use strict;
-
use warnings;
-
use Config::IniFiles;
-
use File::Path qw(make_path);
-
use File::Basename;
-
use File::Slurp qw(read_file write_file edit_file);
-
use JSON::PP;
-
use IPC::Cmd;
-
use Sys::Syslog qw(:standard :macros);
-
use Cwd qw(abs_path);
-
use Fcntl ':flock';
-
-
## no critic(ControlStructures::ProhibitDeepNests)
-
## no critic(ErrorHandling::RequireCarping)
-
## no critic(CodeLayout::ProhibitParensWithBuiltins)
-
## no critic(Variables::ProhibitPunctuationVars, Variables::RequireLocalizedPunctuationVars)
-
## no critic(InputOutput::RequireCheckedSyscalls, InputOutput::RequireBracedFileHandleWithPrint, InputOutput::RequireBriefOpen)
-
## no critic(ValuesAndExpressions::ProhibitNoisyQuotes, ValuesAndExpressions::ProhibitMagicNumbers, ValuesAndExpressions::ProhibitEmptyQuotes, ValuesAndExpressions::ProhibitInterpolationOfLiterals)
-
## no critic(RegularExpressions::ProhibitEscapedMetacharacters)
-
-
# Location of activation scripts
-
my $out = "@out@";
-
# System closure path to switch to
-
my $toplevel = "@toplevel@";
-
-
# To be robust against interruption, record what units need to be started etc.
-
# We read these files again every time this script starts to make sure we continue
-
# where the old (interrupted) script left off.
-
my $start_list_file = "/run/nixos/start-list";
-
my $restart_list_file = "/run/nixos/restart-list";
-
my $reload_list_file = "/run/nixos/reload-list";
-
-
# Parse restart/reload requests by the activation script.
-
# Activation scripts may write newline-separated units to the restart
-
# file and switch-to-configuration will handle them. While
-
# `stopIfChanged = true` is ignored, switch-to-configuration will
-
# handle `restartIfChanged = false` and `reloadIfChanged = true`.
-
# This is the same as specifying a restart trigger in the NixOS module.
-
#
-
# The reload file asks the script to reload a unit. This is the same as
-
# specifying a reload trigger in the NixOS module and can be ignored if
-
# the unit is restarted in this activation.
-
my $restart_by_activation_file = "/run/nixos/activation-restart-list";
-
my $reload_by_activation_file = "/run/nixos/activation-reload-list";
-
my $dry_restart_by_activation_file = "/run/nixos/dry-activation-restart-list";
-
my $dry_reload_by_activation_file = "/run/nixos/dry-activation-reload-list";
-
-
# The action that is to be performed (like switch, boot, test, dry-activate)
-
# Also exposed via environment variable from now on
-
my $action = shift(@ARGV);
-
$ENV{NIXOS_ACTION} = $action;
-
-
# Expose the locale archive as an environment variable for systemctl and the activation script
-
if ("@localeArchive@" ne "") {
-
$ENV{LOCALE_ARCHIVE} = "@localeArchive@";
-
}
-
-
if (!defined($action) || ($action ne "switch" && $action ne "boot" && $action ne "test" && $action ne "dry-activate" && $action ne "check")) {
-
print STDERR <<"EOF";
-
Usage: $0 [check|switch|boot|test|dry-activate]
-
-
check: run pre-switch checks and exit
-
switch: make the configuration the boot default and activate now
-
boot: make the configuration the boot default
-
test: activate the configuration, but don\'t make it the boot default
-
dry-activate: show what would be done if this configuration were activated
-
EOF
-
exit(1);
-
}
-
-
# This is a NixOS installation if it has /etc/NIXOS or a proper
-
# /etc/os-release.
-
if (!-f "/etc/NIXOS" && (read_file("/etc/os-release", err_mode => "quiet") // "") !~ /^ID="?@distroId@"?/msx) {
-
die("This is not a NixOS installation!\n");
-
}
-
-
make_path("/run/nixos", { mode => oct(755) });
-
open(my $stc_lock, '>>', '/run/nixos/switch-to-configuration.lock') or die "Could not open lock - $!";
-
flock($stc_lock, LOCK_EX|LOCK_NB) or die "Could not acquire lock - $!";
-
openlog("nixos", "", LOG_USER);
-
-
# run pre-switch checks
-
if (($ENV{"NIXOS_NO_CHECK"} // "") ne "1") {
-
chomp(my $pre_switch_checks = <<'EOFCHECKS');
-
@preSwitchCheck@
-
EOFCHECKS
-
system("$pre_switch_checks $out $action") == 0 or exit 1;
-
if ($action eq "check") {
-
exit 0;
-
}
-
}
-
-
# Install or update the bootloader.
-
if ($action eq "switch" || $action eq "boot") {
-
chomp(my $install_boot_loader = <<'EOFBOOTLOADER');
-
@installBootLoader@
-
EOFBOOTLOADER
-
system("$install_boot_loader $toplevel") == 0 or exit 1;
-
}
-
-
# Just in case the new configuration hangs the system, do a sync now.
-
if (($ENV{"NIXOS_NO_SYNC"} // "") ne "1") {
-
system("@coreutils@/bin/sync", "-f", "/nix/store");
-
}
-
-
if ($action eq "boot") {
-
exit(0);
-
}
-
-
# Path to the directory containing systemd tools of the old system
-
# Needs to be after the "boot" action exits, as this directory will not exist when doing a NIXOS_LUSTRATE install
-
my $cur_systemd = abs_path("/run/current-system/sw/bin");
-
# Path to the systemd store path of the new system
-
my $new_systemd = "@systemd@";
-
-
# Check if we can activate the new configuration.
-
my $cur_init_interface_version = read_file("/run/current-system/init-interface-version", err_mode => "quiet") // "";
-
my $new_init_interface_version = read_file("$toplevel/init-interface-version");
-
-
if ($new_init_interface_version ne $cur_init_interface_version) {
-
print STDERR <<'EOF';
-
Warning: the new NixOS configuration has an ‘init’ that is
-
incompatible with the current configuration. The new configuration
-
won't take effect until you reboot the system.
-
EOF
-
exit(100);
-
}
-
-
# Ignore SIGHUP so that we're not killed if we're running on (say)
-
# virtual console 1 and we restart the "tty1" unit.
-
$SIG{PIPE} = "IGNORE";
-
-
# Replacement for Net::DBus that calls busctl of the current systemd, parses
-
# it's json output and returns the response using only core modules to reduce
-
# dependencies on perlPackages in baseSystem
-
sub busctl_call_systemd1_mgr {
-
my (@args) = @_;
-
my $cmd = [
-
"$cur_systemd/busctl", "--json=short", "call", "org.freedesktop.systemd1",
-
"/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager",
-
@args
-
];
-
-
my ($ok, $err, undef, $stdout) = IPC::Cmd::run(command => $cmd);
-
die $err unless $ok;
-
-
my $res = decode_json(join "", @$stdout);
-
return $res;
-
}
-
-
# Asks the currently running systemd instance via dbus which units are active.
-
# Returns a hash where the key is the name of each unit and the value a hash
-
# of load, state, substate.
-
sub get_active_units {
-
my $units = busctl_call_systemd1_mgr("ListUnitsByPatterns", "asas", 0, 0)->{data}->[0];
-
my $res = {};
-
for my $item (@{$units}) {
-
my ($id, $description, $load_state, $active_state, $sub_state,
-
$following, $unit_path, $job_id, $job_type, $job_path) = @{$item};
-
if ($following ne "") {
-
next;
-
}
-
if ($job_id == 0 and $active_state eq "inactive") {
-
next;
-
}
-
$res->{$id} = { load => $load_state, state => $active_state, substate => $sub_state };
-
}
-
return $res;
-
}
-
-
# Asks the currently running systemd instance whether a unit is currently active.
-
# Takes the name of the unit as an argument and returns a bool whether the unit is active or not.
-
sub unit_is_active {
-
my ($unit_name) = @_;
-
my $units = busctl_call_systemd1_mgr("ListUnitsByNames", "as", 1, , "--", $unit_name)->{data}->[0];
-
if (scalar(@{$units}) == 0) {
-
return 0;
-
}
-
my $active_state = $units->[0]->[3];
-
return $active_state eq "active" || $active_state eq "activating";
-
}
-
-
# Parse a fstab file, given its path.
-
# Returns a tuple of filesystems and swaps.
-
#
-
# Filesystems is a hash of mountpoint and { device, fsType, options }
-
# Swaps is a hash of device and { options }
-
sub parse_fstab {
-
my ($filename) = @_;
-
my ($fss, $swaps);
-
foreach my $line (read_file($filename, err_mode => "quiet")) {
-
chomp($line);
-
$line =~ s/^\s*\#.*//msx;
-
if ($line =~ /^\s*$/msx) {
-
next;
-
}
-
my @xs = split(/\s+/msx, $line);
-
if ($xs[2] eq "swap") {
-
$swaps->{$xs[0]} = { options => $xs[3] // "" };
-
} else {
-
$fss->{$xs[1]} = { device => $xs[0], fsType => $xs[2], options => $xs[3] // "" };
-
}
-
}
-
return ($fss, $swaps);
-
}
-
-
# This subroutine takes a single ini file that specified systemd configuration
-
# like unit configuration and parses it into a hash where the keys are the sections
-
# of the unit file and the values are hashes themselves. These hashes have the unit file
-
# keys as their keys (left side of =) and an array of all values that were set as their
-
# values. If a value is empty (for example `ExecStart=`), then all current definitions are
-
# removed.
-
#
-
# Instead of returning the hash, this subroutine takes a hashref to return the data in. This
-
# allows calling the subroutine multiple times with the same hash to parse override files.
-
sub parse_systemd_ini {
-
my ($unit_contents, $path) = @_;
-
# Tie the ini file to a hash for easier access
-
tie(my %file_contents, "Config::IniFiles", (-file => $path, -allowempty => 1, -allowcontinue => 1)); ## no critic(Miscellanea::ProhibitTies)
-
-
# Copy over all sections
-
foreach my $section_name (keys(%file_contents)) {
-
if ($section_name eq "Install") {
-
# Skip the [Install] section because it has no relevant keys for us
-
next;
-
}
-
# Copy over all keys
-
foreach my $ini_key (keys(%{$file_contents{$section_name}})) {
-
# Ensure the value is an array so it's easier to work with
-
my $ini_value = $file_contents{$section_name}{$ini_key};
-
my @ini_values;
-
if (ref($ini_value) eq "ARRAY") {
-
@ini_values = @{$ini_value};
-
} else {
-
@ini_values = $ini_value;
-
}
-
# Go over all values
-
for my $ini_value (@ini_values) {
-
# If a value is empty, it's an override that tells us to clean the value
-
if ($ini_value eq "") {
-
delete $unit_contents->{$section_name}->{$ini_key};
-
next;
-
}
-
push(@{$unit_contents->{$section_name}->{$ini_key}}, $ini_value);
-
}
-
}
-
}
-
return;
-
}
-
-
# This subroutine takes the path to a systemd configuration file (like a unit configuration),
-
# parses it, and returns a hash that contains the contents. The contents of this hash are
-
# explained in the `parse_systemd_ini` subroutine. Neither the sections nor the keys inside
-
# the sections are consistently sorted.
-
#
-
# If a directory with the same basename ending in .d exists next to the unit file, it will be
-
# assumed to contain override files which will be parsed as well and handled properly.
-
sub parse_unit {
-
my ($unit_path, $base_unit_path) = @_;
-
-
# Parse the main unit and all overrides
-
my %unit_data;
-
# Replace \ with \\ so glob() still works with units that have a \ in them
-
# Valid characters in unit names are ASCII letters, digits, ":", "-", "_", ".", and "\"
-
$base_unit_path =~ s/\\/\\\\/gmsx;
-
$unit_path =~ s/\\/\\\\/gmsx;
-
-
foreach (glob("${base_unit_path}{,.d/*.conf}")) {
-
parse_systemd_ini(\%unit_data, "$_")
-
}
-
# Handle drop-in template-unit instance overrides
-
if ($unit_path ne $base_unit_path) {
-
foreach (glob("${unit_path}.d/*.conf")) {
-
parse_systemd_ini(\%unit_data, "$_")
-
}
-
}
-
return %unit_data;
-
}
-
-
# Checks whether a specified boolean in a systemd unit is true
-
# or false, with a default that is applied when the value is not set.
-
sub parse_systemd_bool {
-
my ($unit_config, $section_name, $bool_name, $default) = @_;
-
-
my @values = @{$unit_config->{$section_name}{$bool_name} // []};
-
# Return default if value is not set
-
if ((scalar(@values) < 1) || (not defined($values[-1]))) {
-
return $default;
-
}
-
# If value is defined multiple times, use the last definition
-
my $last_value = $values[-1];
-
# These are valid values as of systemd.syntax(7)
-
return $last_value eq "1" || $last_value eq "yes" || $last_value eq "true" || $last_value eq "on";
-
}
-
-
# Writes a unit name into a given file to be more resilient against
-
# crashes of the script. Does nothing when the action is dry-activate.
-
sub record_unit {
-
my ($fn, $unit) = @_;
-
if ($action ne "dry-activate") {
-
write_file($fn, { append => 1 }, "$unit\n");
-
}
-
return;
-
}
-
-
# The opposite of record_unit, removes a unit name from a file
-
sub unrecord_unit {
-
my ($fn, $unit) = @_;
-
if ($action ne "dry-activate") {
-
edit_file(sub { s/^$unit\n//msx }, $fn);
-
}
-
return;
-
}
-
-
# Compare the contents of two unit files and return whether the unit
-
# needs to be restarted or reloaded. If the units differ, the service
-
# is restarted unless the only difference is `X-Reload-Triggers` in the
-
# `Unit` section. If this is the only modification, the unit is reloaded
-
# instead of restarted. If the only difference is `Options` in the
-
# `[Mount]` section, the unit is reloaded rather than restarted.
-
# Returns:
-
# - 0 if the units are equal
-
# - 1 if the units are different and a restart action is required
-
# - 2 if the units are different and a reload action is required
-
sub compare_units { ## no critic(Subroutines::ProhibitExcessComplexity)
-
my ($cur_unit, $new_unit) = @_;
-
my $ret = 0;
-
# Keys to ignore in the [Unit] section
-
my %unit_section_ignores = map { $_ => 1 } qw(
-
X-Reload-Triggers
-
Description Documentation
-
OnFailure OnSuccess OnFailureJobMode
-
IgnoreOnIsolate StopWhenUnneeded
-
RefuseManualStart RefuseManualStop
-
AllowIsolate CollectMode
-
SourcePath
-
);
-
-
my $comp_array = sub {
-
my ($a, $b) = @_;
-
return join("\0", @{$a}) eq join("\0", @{$b});
-
};
-
-
# Comparison hash for the sections
-
my %section_cmp = map { $_ => 1 } keys(%{$new_unit});
-
# Iterate over the sections
-
foreach my $section_name (keys(%{$cur_unit})) {
-
# Missing section in the new unit?
-
if (not exists($section_cmp{$section_name})) {
-
# If the [Unit] section was removed, make sure that only keys
-
# were in it that are ignored
-
if ($section_name eq "Unit") {
-
foreach my $ini_key (keys(%{$cur_unit->{"Unit"}})) {
-
if (not defined($unit_section_ignores{$ini_key})) {
-
return 1;
-
}
-
}
-
next; # check the next section
-
} else {
-
return 1;
-
}
-
if ($section_name eq "Unit" and %{$cur_unit->{"Unit"}} == 1 and defined(%{$cur_unit->{"Unit"}}{"X-Reload-Triggers"})) {
-
# If a new [Unit] section was removed that only contained X-Reload-Triggers,
-
# do nothing.
-
next;
-
} else {
-
return 1;
-
}
-
}
-
delete $section_cmp{$section_name};
-
# Comparison hash for the section contents
-
my %ini_cmp = map { $_ => 1 } keys(%{$new_unit->{$section_name}});
-
# Iterate over the keys of the section
-
foreach my $ini_key (keys(%{$cur_unit->{$section_name}})) {
-
delete $ini_cmp{$ini_key};
-
my @cur_value = @{$cur_unit->{$section_name}{$ini_key}};
-
# If the key is missing in the new unit, they are different...
-
if (not $new_unit->{$section_name}{$ini_key}) {
-
# ... unless the key that is now missing is one of the ignored keys
-
if ($section_name eq "Unit" and defined($unit_section_ignores{$ini_key})) {
-
next;
-
}
-
return 1;
-
}
-
my @new_value = @{$new_unit->{$section_name}{$ini_key}};
-
# If the contents are different, the units are different
-
if (not $comp_array->(\@cur_value, \@new_value)) {
-
# Check if only the reload triggers changed or one of the ignored keys
-
if ($section_name eq "Unit") {
-
if ($ini_key eq "X-Reload-Triggers") {
-
$ret = 2;
-
next;
-
} elsif (defined($unit_section_ignores{$ini_key})) {
-
next;
-
}
-
}
-
# If this is a mount unit, check if it was only `Options`
-
if ($section_name eq "Mount" and $ini_key eq "Options") {
-
$ret = 2;
-
next;
-
}
-
return 1;
-
}
-
}
-
# A key was introduced that was missing in the previous unit
-
if (%ini_cmp) {
-
if ($section_name eq "Unit") {
-
foreach my $ini_key (keys(%ini_cmp)) {
-
if ($ini_key eq "X-Reload-Triggers") {
-
$ret = 2;
-
} elsif (defined($unit_section_ignores{$ini_key})) {
-
next;
-
} else {
-
return 1;
-
}
-
}
-
} else {
-
return 1;
-
}
-
};
-
}
-
# A section was introduced that was missing in the previous unit
-
if (%section_cmp) {
-
if (%section_cmp == 1 and defined($section_cmp{"Unit"})) {
-
foreach my $ini_key (keys(%{$new_unit->{"Unit"}})) {
-
if (not defined($unit_section_ignores{$ini_key})) {
-
return 1;
-
} elsif ($ini_key eq "X-Reload-Triggers") {
-
$ret = 2;
-
}
-
}
-
} else {
-
return 1;
-
}
-
}
-
-
return $ret;
-
}
-
-
# Called when a unit exists in both the old systemd and the new system and the units
-
# differ. This figures out of what units are to be stopped, restarted, reloaded, started, and skipped.
-
sub handle_modified_unit { ## no critic(Subroutines::ProhibitManyArgs, Subroutines::ProhibitExcessComplexity)
-
my ($unit, $base_name, $new_unit_file, $new_base_unit_file, $new_unit_info, $active_cur, $units_to_stop, $units_to_start, $units_to_reload, $units_to_restart, $units_to_skip) = @_;
-
-
if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target" || $unit =~ /\.path$/msx || $unit =~ /\.slice$/msx) {
-
# Do nothing. These cannot be restarted directly.
-
-
# Slices and Paths don't have to be restarted since
-
# properties (resource limits and inotify watches)
-
# seem to get applied on daemon-reload.
-
} elsif ($unit =~ /\.mount$/msx) {
-
# Just restart the unit. We wouldn't have gotten into this subroutine
-
# if only `Options` was changed, in which case the unit would be reloaded.
-
# The only exception is / and /nix because it's very unlikely we can safely
-
# unmount them so we reload them instead. This means that we may not get
-
# all changes into the running system but it's better than crashing it.
-
if ($unit eq "-.mount" or $unit eq "nix.mount") {
-
$units_to_reload->{$unit} = 1;
-
record_unit($reload_list_file, $unit);
-
} else {
-
$units_to_restart->{$unit} = 1;
-
record_unit($restart_list_file, $unit);
-
}
-
} elsif ($unit =~ /\.socket$/msx) {
-
# FIXME: do something?
-
# Attempt to fix this: https://github.com/NixOS/nixpkgs/pull/141192
-
# Revert of the attempt: https://github.com/NixOS/nixpkgs/pull/147609
-
# More details: https://github.com/NixOS/nixpkgs/issues/74899#issuecomment-981142430
-
} else {
-
my %new_unit_info = $new_unit_info ? %{$new_unit_info} : parse_unit($new_unit_file, $new_base_unit_file);
-
if (parse_systemd_bool(\%new_unit_info, "Service", "X-ReloadIfChanged", 0) and not $units_to_restart->{$unit} and not $units_to_stop->{$unit}) {
-
$units_to_reload->{$unit} = 1;
-
record_unit($reload_list_file, $unit);
-
}
-
elsif ($unit eq "dbus.service" || $unit eq "dbus-broker.service") {
-
# dbus service should only ever be reloaded, not started/stoped/restarted as that would break the system.
-
}
-
elsif (!parse_systemd_bool(\%new_unit_info, "Service", "X-RestartIfChanged", 1) || parse_systemd_bool(\%new_unit_info, "Unit", "RefuseManualStop", 0) || parse_systemd_bool(\%new_unit_info, "Unit", "X-OnlyManualStart", 0)) {
-
$units_to_skip->{$unit} = 1;
-
} else {
-
# It doesn't make sense to stop and start non-services because
-
# they can't have ExecStop=
-
if (!parse_systemd_bool(\%new_unit_info, "Service", "X-StopIfChanged", 1) || $unit !~ /\.service$/msx) {
-
# This unit should be restarted instead of
-
# stopped and started.
-
$units_to_restart->{$unit} = 1;
-
record_unit($restart_list_file, $unit);
-
# Remove from units to reload so we don't restart and reload
-
if ($units_to_reload->{$unit}) {
-
delete $units_to_reload->{$unit};
-
unrecord_unit($reload_list_file, $unit);
-
}
-
} else {
-
# If this unit is socket-activated, then stop the
-
# socket unit(s) as well, and restart the
-
# socket(s) instead of the service.
-
my $socket_activated = 0;
-
if ($unit =~ /\.service$/msx) {
-
my @sockets = split(/\s+/msx, join(" ", @{$new_unit_info{Service}{Sockets} // []}));
-
if (scalar(@sockets) == 0) {
-
@sockets = ("$base_name.socket");
-
}
-
foreach my $socket (@sockets) {
-
if (defined($active_cur->{$socket})) {
-
# We can now be sure this is a socket-activate unit
-
-
$units_to_stop->{$socket} = 1;
-
# Only restart sockets that actually
-
# exist in new configuration:
-
if (-e "$toplevel/etc/systemd/system/$socket") {
-
$units_to_start->{$socket} = 1;
-
if ($units_to_start eq $units_to_restart) {
-
record_unit($restart_list_file, $socket);
-
} else {
-
record_unit($start_list_file, $socket);
-
}
-
$socket_activated = 1;
-
}
-
# Remove from units to reload so we don't restart and reload
-
if ($units_to_reload->{$unit}) {
-
delete $units_to_reload->{$unit};
-
unrecord_unit($reload_list_file, $unit);
-
}
-
}
-
}
-
}
-
-
if (parse_systemd_bool(\%new_unit_info, "Service", "X-NotSocketActivated", 0)) {
-
# If the unit explicitly opts out of socket
-
# activation, restart it as if it weren't (but do
-
# restart its sockets, that's fine):
-
$socket_activated = 0;
-
}
-
-
# If the unit is not socket-activated, record
-
# that this unit needs to be started below.
-
# We write this to a file to ensure that the
-
# service gets restarted if we're interrupted.
-
if (!$socket_activated) {
-
$units_to_start->{$unit} = 1;
-
if ($units_to_start eq $units_to_restart) {
-
record_unit($restart_list_file, $unit);
-
} else {
-
record_unit($start_list_file, $unit);
-
}
-
}
-
-
$units_to_stop->{$unit} = 1;
-
# Remove from units to reload so we don't restart and reload
-
if ($units_to_reload->{$unit}) {
-
delete $units_to_reload->{$unit};
-
unrecord_unit($reload_list_file, $unit);
-
}
-
}
-
}
-
}
-
return;
-
}
-
-
# Figure out what units need to be stopped, started, restarted or reloaded.
-
my (%units_to_stop, %units_to_skip, %units_to_start, %units_to_restart, %units_to_reload);
-
-
my %units_to_filter; # units not shown
-
-
%units_to_start = map { $_ => 1 }
-
split(/\n/msx, read_file($start_list_file, err_mode => "quiet") // "");
-
-
%units_to_restart = map { $_ => 1 }
-
split(/\n/msx, read_file($restart_list_file, err_mode => "quiet") // "");
-
-
%units_to_reload = map { $_ => 1 }
-
split(/\n/msx, read_file($reload_list_file, err_mode => "quiet") // "");
-
-
my $active_cur = get_active_units();
-
while (my ($unit, $state) = each(%{$active_cur})) {
-
my $cur_unit_file = "/etc/systemd/system/$unit";
-
my $new_unit_file = "$toplevel/etc/systemd/system/$unit";
-
-
my $base_unit = $unit;
-
my $cur_base_unit_file = $cur_unit_file;
-
my $new_base_unit_file = $new_unit_file;
-
-
# Detect template instances.
-
if (!-e $cur_unit_file && !-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
-
$base_unit = "$1\@.$2";
-
$cur_base_unit_file = "/etc/systemd/system/$base_unit";
-
$new_base_unit_file = "$toplevel/etc/systemd/system/$base_unit";
-
}
-
-
my $base_name = $base_unit;
-
$base_name =~ s/\.[[:lower:]]*$//msx;
-
-
if (-e $cur_base_unit_file && ($state->{state} eq "active" || $state->{state} eq "activating")) {
-
if (! -e $new_base_unit_file || abs_path($new_base_unit_file) eq "/dev/null") {
-
my %cur_unit_info = parse_unit($cur_unit_file, $cur_base_unit_file);
-
if (parse_systemd_bool(\%cur_unit_info, "Unit", "X-StopOnRemoval", 1)) {
-
$units_to_stop{$unit} = 1;
-
}
-
}
-
-
elsif ($unit =~ /\.target$/msx) {
-
my %new_unit_info = parse_unit($new_unit_file, $new_base_unit_file);
-
-
# Cause all active target units to be restarted below.
-
# This should start most changed units we stop here as
-
# well as any new dependencies (including new mounts and
-
# swap devices). FIXME: the suspend target is sometimes
-
# active after the system has resumed, which probably
-
# should not be the case. Just ignore it.
-
if ($unit ne "suspend.target" && $unit ne "hibernate.target" && $unit ne "hybrid-sleep.target") {
-
if (!(parse_systemd_bool(\%new_unit_info, "Unit", "RefuseManualStart", 0) || parse_systemd_bool(\%new_unit_info, "Unit", "X-OnlyManualStart", 0))) {
-
$units_to_start{$unit} = 1;
-
record_unit($start_list_file, $unit);
-
# Don't spam the user with target units that always get started.
-
if (($ENV{"STC_DISPLAY_ALL_UNITS"} // "") ne "1") {
-
$units_to_filter{$unit} = 1;
-
}
-
}
-
}
-
-
# Stop targets that have X-StopOnReconfiguration set.
-
# This is necessary to respect dependency orderings
-
# involving targets: if unit X starts after target Y and
-
# target Y starts after unit Z, then if X and Z have both
-
# changed, then X should be restarted after Z. However,
-
# if target Y is in the "active" state, X and Z will be
-
# restarted at the same time because X's dependency on Y
-
# is already satisfied. Thus, we need to stop Y first.
-
# Stopping a target generally has no effect on other units
-
# (unless there is a PartOf dependency), so this is just a
-
# bookkeeping thing to get systemd to do the right thing.
-
if (parse_systemd_bool(\%new_unit_info, "Unit", "X-StopOnReconfiguration", 0)) {
-
$units_to_stop{$unit} = 1;
-
}
-
}
-
-
else {
-
my %cur_unit_info = parse_unit($cur_unit_file, $cur_base_unit_file);
-
my %new_unit_info = parse_unit($new_unit_file, $new_base_unit_file);
-
my $diff = compare_units(\%cur_unit_info, \%new_unit_info);
-
if ($diff == 1) {
-
handle_modified_unit($unit, $base_name, $new_unit_file, $new_base_unit_file, \%new_unit_info, $active_cur, \%units_to_stop, \%units_to_start, \%units_to_reload, \%units_to_restart, \%units_to_skip);
-
} elsif ($diff == 2 and not $units_to_restart{$unit}) {
-
$units_to_reload{$unit} = 1;
-
record_unit($reload_list_file, $unit);
-
}
-
}
-
}
-
}
-
-
# Converts a path to the name of a systemd mount unit that would be responsible
-
# for mounting this path.
-
sub path_to_unit_name {
-
my ($path) = @_;
-
# Use current version of systemctl binary before daemon is reexeced.
-
open(my $cmd, "-|", "$cur_systemd/systemd-escape", "--suffix=mount", "-p", $path)
-
or die "Unable to escape $path!\n";
-
my $escaped = do { local $/ = undef; <$cmd> };
-
chomp($escaped);
-
close($cmd) or die("Unable to close systemd-escape pipe");
-
return $escaped;
-
}
-
-
# Compare the previous and new fstab to figure out which filesystems
-
# need a remount or need to be unmounted. New filesystems are mounted
-
# automatically by starting local-fs.target. FIXME: might be nicer if
-
# we generated units for all mounts; then we could unify this with the
-
# unit checking code above.
-
my ($cur_fss, $cur_swaps) = parse_fstab("/etc/fstab");
-
my ($new_fss, $new_swaps) = parse_fstab("$toplevel/etc/fstab");
-
foreach my $mount_point (keys(%{$cur_fss})) {
-
my $cur = $cur_fss->{$mount_point};
-
my $new = $new_fss->{$mount_point};
-
my $unit = path_to_unit_name($mount_point);
-
if (!defined($new)) {
-
# Filesystem entry disappeared, so unmount it.
-
$units_to_stop{$unit} = 1;
-
} elsif ($cur->{fsType} ne $new->{fsType} || $cur->{device} ne $new->{device}) {
-
if ($mount_point eq '/' or $mount_point eq '/nix') {
-
if ($cur->{options} ne $new->{options}) {
-
# Mount options changed, so remount it.
-
$units_to_reload{$unit} = 1;
-
record_unit($reload_list_file, $unit);
-
} else {
-
# Don't unmount / or /nix if the device changed
-
$units_to_skip{$unit} = 1;
-
}
-
} else {
-
# Filesystem type or device changed, so unmount and mount it.
-
$units_to_restart{$unit} = 1;
-
record_unit($restart_list_file, $unit);
-
}
-
} elsif ($cur->{options} ne $new->{options}) {
-
# Mount options changes, so remount it.
-
$units_to_reload{$unit} = 1;
-
record_unit($reload_list_file, $unit);
-
}
-
}
-
-
# Also handles swap devices.
-
foreach my $device (keys(%{$cur_swaps})) {
-
my $cur = $cur_swaps->{$device};
-
my $new = $new_swaps->{$device};
-
if (!defined($new)) {
-
# Swap entry disappeared, so turn it off. Can't use
-
# "systemctl stop" here because systemd has lots of alias
-
# units that prevent a stop from actually calling
-
# "swapoff".
-
if ($action eq "dry-activate") {
-
print STDERR "would stop swap device: $device\n";
-
} else {
-
print STDERR "stopping swap device: $device\n";
-
system("@utillinux@/sbin/swapoff", $device);
-
}
-
}
-
# FIXME: update swap options (i.e. its priority).
-
}
-
-
-
# Should we have systemd re-exec itself?
-
my $cur_pid1_path = abs_path("/proc/1/exe") // "/unknown";
-
my $cur_systemd_system_config = abs_path("/etc/systemd/system.conf") // "/unknown";
-
my $new_pid1_path = abs_path("$new_systemd/lib/systemd/systemd") or die;
-
my $new_systemd_system_config = abs_path("$toplevel/etc/systemd/system.conf") // "/unknown";
-
-
my $restart_systemd = $cur_pid1_path ne $new_pid1_path;
-
if ($cur_systemd_system_config ne $new_systemd_system_config) {
-
$restart_systemd = 1;
-
}
-
-
# Takes an array of unit names and returns an array with the same elements,
-
# except all units that are also in the global variable `unitsToFilter`.
-
sub filter_units {
-
my ($units) = @_;
-
my @res;
-
foreach my $unit (sort(keys(%{$units}))) {
-
if (!defined($units_to_filter{$unit})) {
-
push(@res, $unit);
-
}
-
}
-
return @res;
-
}
-
-
my @units_to_stop_filtered = filter_units(\%units_to_stop);
-
-
-
# Show dry-run actions.
-
if ($action eq "dry-activate") {
-
if (scalar(@units_to_stop_filtered) > 0) {
-
print STDERR "would stop the following units: ", join(", ", @units_to_stop_filtered), "\n";
-
}
-
if (scalar(keys(%units_to_skip)) > 0) {
-
print STDERR "would NOT stop the following changed units: ", join(", ", sort(keys(%units_to_skip))), "\n";
-
}
-
-
print STDERR "would activate the configuration...\n";
-
system("$out/dry-activate", "$out");
-
-
# Handle the activation script requesting the restart or reload of a unit.
-
foreach (split(/\n/msx, read_file($dry_restart_by_activation_file, err_mode => "quiet") // "")) {
-
my $unit = $_;
-
my $new_unit_file = "$toplevel/etc/systemd/system/$unit";
-
my $base_unit = $unit;
-
my $new_base_unit_file = $new_unit_file;
-
-
# Detect template instances.
-
if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
-
$base_unit = "$1\@.$2";
-
$new_base_unit_file = "$toplevel/etc/systemd/system/$base_unit";
-
}
-
-
my $base_name = $base_unit;
-
$base_name =~ s/\.[[:lower:]]*$//msx;
-
-
# Start units if they were not active previously
-
if (not defined($active_cur->{$unit})) {
-
$units_to_start{$unit} = 1;
-
next;
-
}
-
-
handle_modified_unit($unit, $base_name, $new_unit_file, $new_base_unit_file, undef, $active_cur, \%units_to_restart, \%units_to_restart, \%units_to_reload, \%units_to_restart, \%units_to_skip);
-
}
-
unlink($dry_restart_by_activation_file);
-
-
foreach (split(/\n/msx, read_file($dry_reload_by_activation_file, err_mode => "quiet") // "")) {
-
my $unit = $_;
-
-
if (defined($active_cur->{$unit}) and not $units_to_restart{$unit} and not $units_to_stop{$unit}) {
-
$units_to_reload{$unit} = 1;
-
record_unit($reload_list_file, $unit);
-
}
-
}
-
unlink($dry_reload_by_activation_file);
-
-
if ($restart_systemd) {
-
print STDERR "would restart systemd\n";
-
}
-
if (scalar(keys(%units_to_reload)) > 0) {
-
print STDERR "would reload the following units: ", join(", ", sort(keys(%units_to_reload))), "\n";
-
}
-
if (scalar(keys(%units_to_restart)) > 0) {
-
print STDERR "would restart the following units: ", join(", ", sort(keys(%units_to_restart))), "\n";
-
}
-
my @units_to_start_filtered = filter_units(\%units_to_start);
-
if (scalar(@units_to_start_filtered)) {
-
print STDERR "would start the following units: ", join(", ", @units_to_start_filtered), "\n";
-
}
-
exit 0;
-
}
-
-
-
syslog(LOG_NOTICE, "switching to system configuration $toplevel");
-
-
if (scalar(keys(%units_to_stop)) > 0) {
-
if (scalar(@units_to_stop_filtered)) {
-
print STDERR "stopping the following units: ", join(", ", @units_to_stop_filtered), "\n";
-
}
-
# Use current version of systemctl binary before daemon is reexeced.
-
system("$cur_systemd/systemctl", "stop", "--", sort(keys(%units_to_stop)));
-
}
-
-
if (scalar(keys(%units_to_skip)) > 0) {
-
print STDERR "NOT restarting the following changed units: ", join(", ", sort(keys(%units_to_skip))), "\n";
-
}
-
-
# Activate the new configuration (i.e., update /etc, make accounts,
-
# and so on).
-
my $res = 0;
-
print STDERR "activating the configuration...\n";
-
system("$out/activate", "$out") == 0 or $res = 2;
-
-
# Handle the activation script requesting the restart or reload of a unit.
-
foreach (split(/\n/msx, read_file($restart_by_activation_file, err_mode => "quiet") // "")) {
-
my $unit = $_;
-
my $new_unit_file = "$toplevel/etc/systemd/system/$unit";
-
my $base_unit = $unit;
-
my $new_base_unit_file = $new_unit_file;
-
-
# Detect template instances.
-
if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
-
$base_unit = "$1\@.$2";
-
$new_base_unit_file = "$toplevel/etc/systemd/system/$base_unit";
-
}
-
-
my $base_name = $base_unit;
-
$base_name =~ s/\.[[:lower:]]*$//msx;
-
-
# Start units if they were not active previously
-
if (not defined($active_cur->{$unit})) {
-
$units_to_start{$unit} = 1;
-
record_unit($start_list_file, $unit);
-
next;
-
}
-
-
handle_modified_unit($unit, $base_name, $new_unit_file, $new_base_unit_file, undef, $active_cur, \%units_to_restart, \%units_to_restart, \%units_to_reload, \%units_to_restart, \%units_to_skip);
-
}
-
# We can remove the file now because it has been propagated to the other restart/reload files
-
unlink($restart_by_activation_file);
-
-
foreach (split(/\n/msx, read_file($reload_by_activation_file, err_mode => "quiet") // "")) {
-
my $unit = $_;
-
-
if (defined($active_cur->{$unit}) and not $units_to_restart{$unit} and not $units_to_stop{$unit}) {
-
$units_to_reload{$unit} = 1;
-
record_unit($reload_list_file, $unit);
-
}
-
}
-
# We can remove the file now because it has been propagated to the other reload file
-
unlink($reload_by_activation_file);
-
-
# Restart systemd if necessary. Note that this is done using the
-
# current version of systemd, just in case the new one has trouble
-
# communicating with the running pid 1.
-
if ($restart_systemd) {
-
print STDERR "restarting systemd...\n";
-
system("$cur_systemd/systemctl", "daemon-reexec") == 0 or $res = 2;
-
}
-
-
# Forget about previously failed services.
-
system("$new_systemd/bin/systemctl", "reset-failed");
-
-
# Make systemd reload its units.
-
system("$new_systemd/bin/systemctl", "daemon-reload") == 0 or $res = 3;
-
-
# Reload user units
-
open(my $list_active_users, "-|", "$new_systemd/bin/loginctl", "list-users", "--no-legend") || die("Unable to call loginctl");
-
while (my $f = <$list_active_users>) {
-
if ($f !~ /^\s*(?<uid>\d+)\s+(?<user>\S+)/msx) {
-
next;
-
}
-
my ($uid, $name) = ($+{uid}, $+{user});
-
print STDERR "reloading user units for $name...\n";
-
-
system("@su@", "-s", "@shell@", "-l", $name, "-c",
-
"export XDG_RUNTIME_DIR=/run/user/$uid; " .
-
"$cur_systemd/systemctl --user daemon-reexec; " .
-
"$new_systemd/bin/systemctl --user start nixos-activation.service");
-
}
-
-
close($list_active_users) || die("Unable to close the file handle to loginctl");
-
-
# Restart sysinit-reactivation.target.
-
# This target only exists to restart services ordered before sysinit.target. We
-
# cannot use X-StopOnReconfiguration to restart sysinit.target because then ALL
-
# services of the system would be restarted since all normal services have a
-
# default dependency on sysinit.target. sysinit-reactivation.target ensures
-
# that services ordered BEFORE sysinit.target get re-started in the correct
-
# order. Ordering between these services is respected.
-
print STDERR "restarting sysinit-reactivation.target\n";
-
system("$new_systemd/bin/systemctl", "restart", "sysinit-reactivation.target") == 0 or $res = 4;
-
-
# Before reloading we need to ensure that the units are still active. They may have been
-
# deactivated because one of their requirements got stopped. If they are inactive
-
# but should have been reloaded, the user probably expects them to be started.
-
if (scalar(keys(%units_to_reload)) > 0) {
-
for my $unit (keys(%units_to_reload)) {
-
if (!unit_is_active($unit)) {
-
# Figure out if we need to start the unit
-
my %unit_info = parse_unit("$toplevel/etc/systemd/system/$unit", "$toplevel/etc/systemd/system/$unit");
-
if (!(parse_systemd_bool(\%unit_info, "Unit", "RefuseManualStart", 0) || parse_systemd_bool(\%unit_info, "Unit", "X-OnlyManualStart", 0))) {
-
$units_to_start{$unit} = 1;
-
record_unit($start_list_file, $unit);
-
}
-
# Don't reload the unit, reloading would fail
-
delete %units_to_reload{$unit};
-
unrecord_unit($reload_list_file, $unit);
-
}
-
}
-
}
-
# Reload units that need it. This includes remounting changed mount
-
# units.
-
if (scalar(keys(%units_to_reload)) > 0) {
-
print STDERR "reloading the following units: ", join(", ", sort(keys(%units_to_reload))), "\n";
-
system("$new_systemd/bin/systemctl", "reload", "--", sort(keys(%units_to_reload))) == 0 or $res = 4;
-
unlink($reload_list_file);
-
}
-
-
# Restart changed services (those that have to be restarted rather
-
# than stopped and started).
-
if (scalar(keys(%units_to_restart)) > 0) {
-
print STDERR "restarting the following units: ", join(", ", sort(keys(%units_to_restart))), "\n";
-
system("$new_systemd/bin/systemctl", "restart", "--", sort(keys(%units_to_restart))) == 0 or $res = 4;
-
unlink($restart_list_file);
-
}
-
-
# Start all active targets, as well as changed units we stopped above.
-
# The latter is necessary because some may not be dependencies of the
-
# targets (i.e., they were manually started). FIXME: detect units
-
# that are symlinks to other units. We shouldn't start both at the
-
# same time because we'll get a "Failed to add path to set" error from
-
# systemd.
-
my @units_to_start_filtered = filter_units(\%units_to_start);
-
if (scalar(@units_to_start_filtered)) {
-
print STDERR "starting the following units: ", join(", ", @units_to_start_filtered), "\n"
-
}
-
system("$new_systemd/bin/systemctl", "start", "--", sort(keys(%units_to_start))) == 0 or $res = 4;
-
unlink($start_list_file);
-
-
-
# Print failed and new units.
-
my (@failed, @new);
-
my $active_new = get_active_units();
-
while (my ($unit, $state) = each(%{$active_new})) {
-
if ($state->{state} eq "failed") {
-
push(@failed, $unit);
-
next;
-
}
-
-
if ($state->{substate} eq "auto-restart") {
-
# A unit in auto-restart substate is a failure *if* it previously failed to start
-
open(my $main_status_fd, "-|", "$new_systemd/bin/systemctl", "show", "--value", "--property=ExecMainStatus", $unit) || die("Unable to call 'systemctl show'");
-
my $main_status = do { local $/ = undef; <$main_status_fd> };
-
close($main_status_fd) || die("Unable to close 'systemctl show' fd");
-
chomp($main_status);
-
-
if ($main_status ne "0") {
-
push(@failed, $unit);
-
next;
-
}
-
}
-
-
# Ignore scopes since they are not managed by this script but rather
-
# created and managed by third-party services via the systemd dbus API.
-
# This only lists units that are not failed (including ones that are in auto-restart but have not failed previously)
-
if ($state->{state} ne "failed" && !defined($active_cur->{$unit}) && $unit !~ /\.scope$/msx) {
-
push(@new, $unit);
-
}
-
}
-
-
if (scalar(@new) > 0) {
-
print STDERR "the following new units were started: ", join(", ", sort(@new)), "\n"
-
}
-
-
if (scalar(@failed) > 0) {
-
my @failed_sorted = sort(@failed);
-
print STDERR "warning: the following units failed: ", join(", ", @failed_sorted), "\n\n";
-
system("$new_systemd/bin/systemctl status --no-pager --full '" . join("' '", @failed_sorted) . "' >&2");
-
$res = 4;
-
}
-
-
if ($res == 0) {
-
syslog(LOG_NOTICE, "finished switching to system configuration $toplevel");
-
} else {
-
syslog(LOG_ERR, "switching to system configuration $toplevel failed (status $res)");
-
}
-
-
close($stc_lock) or die "Could not close lock - $!";
-
exit($res);
+37 -89
nixos/modules/system/activation/switchable-system.nix
···
...
}:
-
let
-
perlWrapped = pkgs.perl.withPackages (
-
p: with p; [
-
ConfigIniFiles
-
FileSlurp
-
]
-
);
-
in
{
-
options.system.switch = {
-
enable = lib.mkOption {
-
type = lib.types.bool;
-
default = true;
-
description = ''
-
Whether to include the capability to switch configurations.
+
imports = [
+
(lib.mkRemovedOptionModule [ "system" "switch" "enableNg" ] ''
+
This option controlled the usage of the new switch-to-configuration-ng,
+
which is now the only switch-to-configuration implementation. This option
+
can be removed from configuration. If there are outstanding issues
+
preventing you from using the new implementation, please open an issue on
+
GitHub.
+
'')
+
];
-
Disabling this makes the system unable to be reconfigured via `nixos-rebuild`.
+
options.system.switch.enable = lib.mkOption {
+
type = lib.types.bool;
+
default = true;
+
description = ''
+
Whether to include the capability to switch configurations.
-
This is good for image based appliances where updates are handled
-
outside the image. Reducing features makes the image lighter and
-
slightly more secure.
-
'';
-
};
+
Disabling this makes the system unable to be reconfigured via `nixos-rebuild`.
-
enableNg = lib.mkOption {
-
type = lib.types.bool;
-
default = config.system.switch.enable;
-
defaultText = lib.literalExpression "config.system.switch.enable";
-
description = ''
-
Whether to use `switch-to-configuration-ng`, the Rust-based
-
re-implementation of the original Perl `switch-to-configuration`.
-
'';
-
};
+
This is good for image based appliances where updates are handled
+
outside the image. Reducing features makes the image lighter and
+
slightly more secure.
+
'';
};
-
config = lib.mkMerge [
-
(lib.mkIf (config.system.switch.enable && !config.system.switch.enableNg) {
-
warnings = [
-
''
-
The Perl implementation of switch-to-configuration will be deprecated
-
and removed in the 25.05 release of NixOS. Please migrate to the
-
newer implementation by removing `system.switch.enableNg = false`
-
from your configuration. If you are unable to migrate due to any
-
issues with the new implementation, please create an issue and tag
-
the maintainers of `switch-to-configuration-ng`.
-
''
-
];
+
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 = ''
+
(
+
source ${pkgs.buildPackages.makeWrapper}/nix-support/setup-hook
-
system.activatableSystemBuilderCommands = ''
mkdir $out/bin
-
substitute ${./switch-to-configuration.pl} $out/bin/switch-to-configuration \
-
--subst-var out \
-
--subst-var-by toplevel ''${!toplevelVar} \
-
--subst-var-by coreutils "${pkgs.coreutils}" \
-
--subst-var-by distroId ${lib.escapeShellArg config.system.nixos.distroId} \
-
--subst-var-by installBootLoader ${lib.escapeShellArg config.system.build.installBootLoader} \
-
--subst-var-by preSwitchCheck ${lib.escapeShellArg config.system.preSwitchChecksScript} \
-
--subst-var-by localeArchive "${config.i18n.glibcLocales}/lib/locale/locale-archive" \
-
--subst-var-by perl "${perlWrapped}" \
-
--subst-var-by shell "${pkgs.bash}/bin/sh" \
-
--subst-var-by su "${pkgs.shadow.su}/bin/su" \
-
--subst-var-by systemd "${config.systemd.package}" \
-
--subst-var-by utillinux "${pkgs.util-linux}" \
-
;
-
-
chmod +x $out/bin/switch-to-configuration
-
${lib.optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
-
if ! output=$(${perlWrapped}/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
-
echo "switch-to-configuration syntax is not valid:"
-
echo "$output"
-
exit 1
-
fi
-
''}
-
'';
-
})
-
(lib.mkIf config.system.switch.enableNg {
-
# Use a subshell so we can source makeWrapper's setup hook without
-
# affecting the rest of activatableSystemBuilderCommands.
-
system.activatableSystemBuilderCommands = ''
-
(
-
source ${pkgs.buildPackages.makeWrapper}/nix-support/setup-hook
-
-
mkdir $out/bin
-
ln -sf ${lib.getExe pkgs.switch-to-configuration-ng} $out/bin/switch-to-configuration
-
wrapProgram $out/bin/switch-to-configuration \
-
--set OUT $out \
-
--set TOPLEVEL ''${!toplevelVar} \
-
--set DISTRO_ID ${lib.escapeShellArg config.system.nixos.distroId} \
-
--set INSTALL_BOOTLOADER ${lib.escapeShellArg config.system.build.installBootLoader} \
-
--set PRE_SWITCH_CHECK ${lib.escapeShellArg config.system.preSwitchChecksScript} \
-
--set LOCALE_ARCHIVE ${config.i18n.glibcLocales}/lib/locale/locale-archive \
-
--set SYSTEMD ${config.systemd.package}
-
)
-
'';
-
})
-
];
-
+
ln -sf ${lib.getExe pkgs.switch-to-configuration-ng} $out/bin/switch-to-configuration
+
wrapProgram $out/bin/switch-to-configuration \
+
--set OUT $out \
+
--set TOPLEVEL ''${!toplevelVar} \
+
--set DISTRO_ID ${lib.escapeShellArg config.system.nixos.distroId} \
+
--set INSTALL_BOOTLOADER ${lib.escapeShellArg config.system.build.installBootLoader} \
+
--set PRE_SWITCH_CHECK ${lib.escapeShellArg config.system.preSwitchChecksScript} \
+
--set LOCALE_ARCHIVE ${config.i18n.glibcLocales}/lib/locale/locale-archive \
+
--set SYSTEMD ${config.systemd.package}
+
)
+
'';
+
};
}
+1 -8
nixos/tests/all-tests.nix
···
swapspace = handleTestOn [ "aarch64-linux" "x86_64-linux" ] ./swapspace.nix { };
sway = runTest ./sway.nix;
swayfx = runTest ./swayfx.nix;
-
switchTest = runTest {
-
imports = [ ./switch-test.nix ];
-
defaults.system.switch.enableNg = false;
-
};
-
switchTestNg = runTest {
-
imports = [ ./switch-test.nix ];
-
defaults.system.switch.enableNg = true;
-
};
+
switchTest = runTest ./switch-test.nix;
sx = runTest ./sx.nix;
sympa = runTest ./sympa.nix;
syncthing = runTest ./syncthing.nix;
+1 -1
nixos/tests/switch-test.nix
···
'';
# Returns a comma separated representation of the given list in sorted
-
# order, that matches the output format of switch-to-configuration.pl
+
# order, that matches the output format of switch-to-configuration
sortedUnits = xs: lib.concatStringsSep ", " (builtins.sort builtins.lessThan xs);
dbusService =
+3 -1
pkgs/by-name/sw/switch-to-configuration-ng/README.md
···
# switch-to-configuration-ng
-
This program is a reimplementation of [switch-to-configuration](/nixos/modules/system/activation/switch-to-configuration.pl) in Rust. The goal is to be compatible in as many ways as possible to the original implementation, at least as long as the original is still in nixpkgs. Any behavioral modifications to this program should also be implemented in the original, and vice versa.
+
This program implements the switching/updating of NixOS systems. It starts with the exising running configuration at `/run/current-system` and handles the migration to a new configuration, built from a NixOS configuration's `config.system.build.toplevel` derivation.
+
+
For more information on what happens during a switch, see [what-happens-during-a-system-switch](../../../../nixos/doc/manual/development/what-happens-during-a-system-switch.chapter.md).
## Build in a devshell