mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-07-13 21:50:33 +03:00
nixos/switch-to-configuration: Fix backslashes in unit names
systemd needs this so special characters (like the ones in wireguard units that appear because they are part of base64) can be escaped using the \x syntax. Root of the issue is that `glob()` handles the backslash internally which is obviously not what we want here. Also add a test case and fix some perlcritic issues in the subroutine.
This commit is contained in:
parent
b516e55d9d
commit
3617ecb67f
2 changed files with 51 additions and 10 deletions
|
@ -173,13 +173,18 @@ sub parseSystemdIni {
|
||||||
#
|
#
|
||||||
# If a directory with the same basename ending in .d exists next to the unit file, it will be
|
# 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.
|
# assumed to contain override files which will be parsed as well and handled properly.
|
||||||
sub parseUnit {
|
sub parse_unit {
|
||||||
my ($unitPath) = @_;
|
my ($unit_path) = @_;
|
||||||
|
|
||||||
# Parse the main unit and all overrides
|
# Parse the main unit and all overrides
|
||||||
my %unitData;
|
my %unit_data;
|
||||||
parseSystemdIni(\%unitData, $_) for glob("${unitPath}{,.d/*.conf}");
|
# Replace \ with \\ so glob() still works with units that have a \ in them
|
||||||
return %unitData;
|
# Valid characters in unit names are ASCII letters, digits, ":", "-", "_", ".", and "\"
|
||||||
|
$unit_path =~ s/\\/\\\\/gmsx;
|
||||||
|
foreach (glob "${unit_path}{,.d/*.conf}") {
|
||||||
|
parseSystemdIni(\%unit_data, "$_")
|
||||||
|
}
|
||||||
|
return %unit_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Checks whether a specified boolean in a systemd unit is true
|
# Checks whether a specified boolean in a systemd unit is true
|
||||||
|
@ -310,7 +315,7 @@ sub handleModifiedUnit {
|
||||||
# Revert of the attempt: https://github.com/NixOS/nixpkgs/pull/147609
|
# Revert of the attempt: https://github.com/NixOS/nixpkgs/pull/147609
|
||||||
# More details: https://github.com/NixOS/nixpkgs/issues/74899#issuecomment-981142430
|
# More details: https://github.com/NixOS/nixpkgs/issues/74899#issuecomment-981142430
|
||||||
} else {
|
} else {
|
||||||
my %unitInfo = $newUnitInfo ? %{$newUnitInfo} : parseUnit($newUnitFile);
|
my %unitInfo = $newUnitInfo ? %{$newUnitInfo} : parse_unit($newUnitFile);
|
||||||
if (parseSystemdBool(\%unitInfo, "Service", "X-ReloadIfChanged", 0) and not $unitsToRestart->{$unit} and not $unitsToStop->{$unit}) {
|
if (parseSystemdBool(\%unitInfo, "Service", "X-ReloadIfChanged", 0) and not $unitsToRestart->{$unit} and not $unitsToStop->{$unit}) {
|
||||||
$unitsToReload->{$unit} = 1;
|
$unitsToReload->{$unit} = 1;
|
||||||
recordUnit($reloadListFile, $unit);
|
recordUnit($reloadListFile, $unit);
|
||||||
|
@ -412,12 +417,12 @@ while (my ($unit, $state) = each %{$activePrev}) {
|
||||||
|
|
||||||
if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
|
if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
|
||||||
if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
|
if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
|
||||||
my %unitInfo = parseUnit($prevUnitFile);
|
my %unitInfo = parse_unit($prevUnitFile);
|
||||||
$unitsToStop{$unit} = 1 if parseSystemdBool(\%unitInfo, "Unit", "X-StopOnRemoval", 1);
|
$unitsToStop{$unit} = 1 if parseSystemdBool(\%unitInfo, "Unit", "X-StopOnRemoval", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
elsif ($unit =~ /\.target$/) {
|
elsif ($unit =~ /\.target$/) {
|
||||||
my %unitInfo = parseUnit($newUnitFile);
|
my %unitInfo = parse_unit($newUnitFile);
|
||||||
|
|
||||||
# Cause all active target units to be restarted below.
|
# Cause all active target units to be restarted below.
|
||||||
# This should start most changed units we stop here as
|
# This should start most changed units we stop here as
|
||||||
|
@ -451,8 +456,8 @@ while (my ($unit, $state) = each %{$activePrev}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
my %old_unit_info = parseUnit($prevUnitFile);
|
my %old_unit_info = parse_unit($prevUnitFile);
|
||||||
my %new_unit_info = parseUnit($newUnitFile);
|
my %new_unit_info = parse_unit($newUnitFile);
|
||||||
my $diff = compare_units(\%old_unit_info, \%new_unit_info);
|
my $diff = compare_units(\%old_unit_info, \%new_unit_info);
|
||||||
if ($diff eq 1) {
|
if ($diff eq 1) {
|
||||||
handleModifiedUnit($unit, $baseName, $newUnitFile, \%new_unit_info, $activePrev, \%unitsToStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
|
handleModifiedUnit($unit, $baseName, $newUnitFile, \%new_unit_info, $activePrev, \%unitsToStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
|
||||||
|
|
|
@ -145,6 +145,23 @@ import ./make-test-python.nix ({ pkgs, ...} : {
|
||||||
systemd.services.test.serviceConfig."X-Test" = "test";
|
systemd.services.test.serviceConfig."X-Test" = "test";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unitWithBackslash.configuration = {
|
||||||
|
systemd.services."escaped\\x2ddash" = {
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
ExecStart = "${pkgs.coreutils}/bin/true";
|
||||||
|
ExecReload = "${pkgs.coreutils}/bin/true";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
unitWithBackslashModified.configuration = {
|
||||||
|
imports = [ unitWithBackslash.configuration ];
|
||||||
|
systemd.services."escaped\\x2ddash".serviceConfig.X-Test = "test";
|
||||||
|
};
|
||||||
|
|
||||||
restart-and-reload-by-activation-script.configuration = {
|
restart-and-reload-by-activation-script.configuration = {
|
||||||
systemd.services = rec {
|
systemd.services = rec {
|
||||||
simple-service = {
|
simple-service = {
|
||||||
|
@ -433,6 +450,25 @@ import ./make-test-python.nix ({ pkgs, ...} : {
|
||||||
assert_lacks(out, "as well:")
|
assert_lacks(out, "as well:")
|
||||||
assert_contains(out, "would start the following units: test.service\n")
|
assert_contains(out, "would start the following units: test.service\n")
|
||||||
|
|
||||||
|
# Ensure \ works in unit names
|
||||||
|
out = switch_to_specialisation("${machine}", "unitWithBackslash")
|
||||||
|
assert_contains(out, "stopping the following units: test.service\n")
|
||||||
|
assert_lacks(out, "NOT restarting the following changed units:")
|
||||||
|
assert_lacks(out, "reloading the following units:")
|
||||||
|
assert_lacks(out, "\nrestarting the following units:")
|
||||||
|
assert_lacks(out, "\nstarting the following units:")
|
||||||
|
assert_contains(out, "the following new units were started: escaped\\x2ddash.service\n")
|
||||||
|
assert_lacks(out, "as well:")
|
||||||
|
|
||||||
|
out = switch_to_specialisation("${machine}", "unitWithBackslashModified")
|
||||||
|
assert_contains(out, "stopping the following units: escaped\\x2ddash.service\n")
|
||||||
|
assert_lacks(out, "NOT restarting the following changed units:")
|
||||||
|
assert_lacks(out, "reloading the following units:")
|
||||||
|
assert_lacks(out, "\nrestarting the following units:")
|
||||||
|
assert_contains(out, "\nstarting the following units: escaped\\x2ddash.service\n")
|
||||||
|
assert_lacks(out, "the following new units were started:")
|
||||||
|
assert_lacks(out, "as well:")
|
||||||
|
|
||||||
with subtest("failing units"):
|
with subtest("failing units"):
|
||||||
# Let the simple service fail
|
# Let the simple service fail
|
||||||
switch_to_specialisation("${machine}", "simpleServiceModified")
|
switch_to_specialisation("${machine}", "simpleServiceModified")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue