about summary refs log tree commit diff
path: root/nixpkgs/nixos/modules/system
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/system')
-rwxr-xr-xnixpkgs/nixos/modules/system/activation/switch-to-configuration.pl632
-rw-r--r--nixpkgs/nixos/modules/system/boot/kernel.nix2
-rw-r--r--nixpkgs/nixos/modules/system/boot/modprobe.nix20
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-1.nix5
-rw-r--r--nixpkgs/nixos/modules/system/boot/stage-2.nix30
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd.nix667
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/coredump.nix57
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/journald.nix131
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/logind.nix114
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix (renamed from nixpkgs/nixos/modules/system/boot/systemd-nspawn.nix)0
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/tmpfiles.nix106
-rw-r--r--nixpkgs/nixos/modules/system/boot/systemd/user.nix158
12 files changed, 934 insertions, 988 deletions
diff --git a/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl b/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
index 459d09faa53b..9e5b760434a0 100755
--- a/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixpkgs/nixos/modules/system/activation/switch-to-configuration.pl
@@ -8,18 +8,29 @@ use File::Basename;
 use File::Slurp qw(read_file write_file edit_file);
 use Net::DBus;
 use Sys::Syslog qw(:standard :macros);
-use Cwd 'abs_path';
+use Cwd qw(abs_path);
 
+## 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)
 
+# System closure path to switch to
 my $out = "@out@";
-
-my $curSystemd = abs_path("/run/current-system/sw/bin");
+# Path to the directory containing systemd tools of the old system
+my $cur_systemd = abs_path("/run/current-system/sw/bin");
+# Path to the systemd store path of the new system
+my $new_systemd = "@systemd@";
 
 # To be robust against interruption, record what units need to be started etc.
-my $startListFile = "/run/nixos/start-list";
-my $restartListFile = "/run/nixos/restart-list";
-my $reloadListFile = "/run/nixos/reload-list";
+# 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
@@ -31,21 +42,23 @@ my $reloadListFile = "/run/nixos/reload-list";
 # 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 $restartByActivationFile = "/run/nixos/activation-restart-list";
-my $reloadByActivationFile = "/run/nixos/activation-reload-list";
-my $dryRestartByActivationFile = "/run/nixos/dry-activation-restart-list";
-my $dryReloadByActivationFile = "/run/nixos/dry-activation-reload-list";
-
-make_path("/run/nixos", { mode => oct(755) });
+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")) {
-    print STDERR <<EOF;
+    print STDERR <<"EOF";
 Usage: $0 [switch|boot|test]
 
 switch:       make the configuration the boot default and activate now
@@ -56,37 +69,41 @@ EOF
     exit(1);
 }
 
-$ENV{NIXOS_ACTION} = $action;
-
 # This is a NixOS installation if it has /etc/NIXOS or a proper
 # /etc/os-release.
-die("This is not a NixOS installation!\n") unless
-    -f "/etc/NIXOS" || (read_file("/etc/os-release", err_mode => 'quiet') // "") =~ /ID="?nixos"?/s;
+if (!-f "/etc/NIXOS" && (read_file("/etc/os-release", err_mode => "quiet") // "") !~ /^ID="?nixos"?/msx) {
+    die("This is not a NixOS installation!\n");
+}
 
+make_path("/run/nixos", { mode => oct(755) });
 openlog("nixos", "", LOG_USER);
 
 # Install or update the bootloader.
 if ($action eq "switch" || $action eq "boot") {
-    chomp(my $installBootLoader = <<'EOFBOOTLOADER');
+    chomp(my $install_boot_loader = <<'EOFBOOTLOADER');
 @installBootLoader@
 EOFBOOTLOADER
-    system("$installBootLoader $out") == 0 or exit 1;
+    system("$install_boot_loader $out") == 0 or exit 1;
 }
 
 # Just in case the new configuration hangs the system, do a sync now.
-system("@coreutils@/bin/sync", "-f", "/nix/store") unless ($ENV{"NIXOS_NO_SYNC"} // "") eq "1";
+if (($ENV{"NIXOS_NO_SYNC"} // "") ne "1") {
+    system("@coreutils@/bin/sync", "-f", "/nix/store");
+}
 
-exit(0) if $action eq "boot";
+if ($action eq "boot") {
+    exit(0);
+}
 
 # Check if we can activate the new configuration.
-my $oldVersion = read_file("/run/current-system/init-interface-version", err_mode => 'quiet') // "";
-my $newVersion = read_file("$out/init-interface-version");
+my $cur_init_interface_version = read_file("/run/current-system/init-interface-version", err_mode => "quiet") // "";
+my $new_init_interface_version = read_file("$out/init-interface-version");
 
-if ($newVersion ne $oldVersion) {
-    print STDERR <<EOF;
+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.
+won't take effect until you reboot the system.
 EOF
     exit(100);
 }
@@ -95,41 +112,56 @@ EOF
 # virtual console 1 and we restart the "tty1" unit.
 $SIG{PIPE} = "IGNORE";
 
-sub getActiveUnits {
+# 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 $mgr = Net::DBus->system->get_service("org.freedesktop.systemd1")->get_object("/org/freedesktop/systemd1");
     my $units = $mgr->ListUnitsByPatterns([], []);
     my $res = {};
-    for my $item (@$units) {
+    for my $item (@{$units}) {
         my ($id, $description, $load_state, $active_state, $sub_state,
-            $following, $unit_path, $job_id, $job_type, $job_path) = @$item;
-        next unless $following eq '';
-        next if $job_id == 0 and $active_state eq 'inactive';
+            $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;
 }
 
-# Returns whether a systemd unit is active
+# 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 $mgr = Net::DBus->system->get_service('org.freedesktop.systemd1')->get_object('/org/freedesktop/systemd1');
+    my $mgr = Net::DBus->system->get_service("org.freedesktop.systemd1")->get_object("/org/freedesktop/systemd1");
     my $units = $mgr->ListUnitsByNames([$unit_name]);
     if (scalar(@{$units}) == 0) {
         return 0;
     }
-    my $active_state = $units->[0]->[3]; ## no critic (ValuesAndExpressions::ProhibitMagicNumbers)
-    return $active_state eq 'active' || $active_state eq 'activating';
+    my $active_state = $units->[0]->[3];
+    return $active_state eq "active" || $active_state eq "activating";
 }
 
-sub parseFstab {
+# 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')) {
+    foreach my $line (read_file($filename, err_mode => "quiet")) {
         chomp($line);
-        $line =~ s/^\s*#.*//;
-        next if $line =~ /^\s*$/;
-        my @xs = split(/ /, $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 {
@@ -148,35 +180,35 @@ sub parseFstab {
 #
 # 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 parseSystemdIni {
-    my ($unitContents, $path) = @_;
+sub parse_systemd_ini {
+    my ($unit_contents, $path) = @_;
     # Tie the ini file to a hash for easier access
-    tie(my %fileContents, 'Config::IniFiles', (-file => $path, -allowempty => 1, -allowcontinue => 1)); ## no critic(Miscellanea::ProhibitTies)
+    tie(my %file_contents, "Config::IniFiles", (-file => $path, -allowempty => 1, -allowcontinue => 1)); ## no critic(Miscellanea::ProhibitTies)
 
     # Copy over all sections
-    foreach my $sectionName (keys(%fileContents)) {
-        if ($sectionName eq "Install") {
+    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 $iniKey (keys(%{$fileContents{$sectionName}})) {
+        foreach my $ini_key (keys(%{$file_contents{$section_name}})) {
             # Ensure the value is an array so it's easier to work with
-            my $iniValue = $fileContents{$sectionName}{$iniKey};
-            my @iniValues;
-            if (ref($iniValue) eq "ARRAY") {
-                @iniValues = @{$iniValue};
+            my $ini_value = $file_contents{$section_name}{$ini_key};
+            my @ini_values;
+            if (ref($ini_value) eq "ARRAY") {
+                @ini_values = @{$ini_value};
             } else {
-                @iniValues = $iniValue;
+                @ini_values = $ini_value;
             }
             # Go over all values
-            for my $iniValue (@iniValues) {
+            for my $ini_value (@ini_values) {
                 # If a value is empty, it's an override that tells us to clean the value
-                if ($iniValue eq "") {
-                    delete $unitContents->{$sectionName}->{$iniKey};
+                if ($ini_value eq "") {
+                    delete $unit_contents->{$section_name}->{$ini_key};
                     next;
                 }
-                push(@{$unitContents->{$sectionName}->{$iniKey}}, $iniValue);
+                push(@{$unit_contents->{$section_name}->{$ini_key}}, $ini_value);
             }
         }
     }
@@ -185,7 +217,7 @@ sub parseSystemdIni {
 
 # 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 `parseSystemdIni` subroutine. Neither the sections nor the keys inside
+# 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
@@ -199,36 +231,44 @@ sub parse_unit {
     # Valid characters in unit names are ASCII letters, digits, ":", "-", "_", ".", and "\"
     $unit_path =~ s/\\/\\\\/gmsx;
     foreach (glob("${unit_path}{,.d/*.conf}")) {
-        parseSystemdIni(\%unit_data, "$_")
+        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 parseSystemdBool {
-    my ($unitConfig, $sectionName, $boolName, $default) = @_;
+sub parse_systemd_bool {
+    my ($unit_config, $section_name, $bool_name, $default) = @_;
 
-    my @values = @{$unitConfig->{$sectionName}{$boolName} // []};
+    my @values = @{$unit_config->{$section_name}{$bool_name} // []};
     # Return default if value is not set
-    if (scalar(@values) lt 1 || not defined($values[-1])) {
+    if ((scalar(@values) < 1) || (not defined($values[-1]))) {
         return $default;
     }
     # If value is defined multiple times, use the last definition
-    my $last = $values[-1];
+    my $last_value = $values[-1];
     # These are valid values as of systemd.syntax(7)
-    return $last eq "1" || $last eq "yes" || $last eq "true" || $last eq "on";
+    return $last_value eq "1" || $last_value eq "yes" || $last_value eq "true" || $last_value eq "on";
 }
 
-sub recordUnit {
+# 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) = @_;
-    write_file($fn, { append => 1 }, "$unit\n") if $action ne "dry-activate";
+    if ($action ne "dry-activate") {
+        write_file($fn, { append => 1 }, "$unit\n");
+    }
+    return;
 }
 
-# The opposite of recordUnit, removes a unit name from a file
+# The opposite of record_unit, removes a unit name from a file
 sub unrecord_unit {
     my ($fn, $unit) = @_;
-    edit_file(sub { s/^$unit\n//msx }, $fn) if $action ne "dry-activate";
+    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
@@ -240,8 +280,8 @@ sub unrecord_unit {
 # - 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 {
-    my ($old_unit, $new_unit) = @_;
+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(
@@ -262,13 +302,13 @@ sub compare_units {
     # Comparison hash for the sections
     my %section_cmp = map { $_ => 1 } keys(%{$new_unit});
     # Iterate over the sections
-    foreach my $section_name (keys(%{$old_unit})) {
+    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(%{$old_unit->{'Unit'}})) {
+            if ($section_name eq "Unit") {
+                foreach my $ini_key (keys(%{$cur_unit->{"Unit"}})) {
                     if (not defined($unit_section_ignores{$ini_key})) {
                         return 1;
                     }
@@ -277,7 +317,7 @@ sub compare_units {
             } else {
                 return 1;
             }
-            if ($section_name eq 'Unit' and %{$old_unit->{'Unit'}} == 1 and defined(%{$old_unit->{'Unit'}}{'X-Reload-Triggers'})) {
+            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;
@@ -289,23 +329,23 @@ sub compare_units {
         # 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(%{$old_unit->{$section_name}})) {
+        foreach my $ini_key (keys(%{$cur_unit->{$section_name}})) {
             delete $ini_cmp{$ini_key};
-            my @old_value = @{$old_unit->{$section_name}{$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})) {
+                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->(\@old_value, \@new_value)) {
+            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') {
+                if ($section_name eq "Unit") {
+                    if ($ini_key eq "X-Reload-Triggers") {
                         $ret = 2;
                         next;
                     } elsif (defined($unit_section_ignores{$ini_key})) {
@@ -315,11 +355,11 @@ sub compare_units {
                 return 1;
             }
         }
-        # A key was introduced that was missing in the old unit
+        # A key was introduced that was missing in the previous unit
         if (%ini_cmp) {
-            if ($section_name eq 'Unit') {
+            if ($section_name eq "Unit") {
                 foreach my $ini_key (keys(%ini_cmp)) {
-                    if ($ini_key eq 'X-Reload-Triggers') {
+                    if ($ini_key eq "X-Reload-Triggers") {
                         $ret = 2;
                     } elsif (defined($unit_section_ignores{$ini_key})) {
                         next;
@@ -332,13 +372,13 @@ sub compare_units {
             }
         };
     }
-    # A section was introduced that was missing in the old unit
+    # 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 (%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') {
+                } elsif ($ini_key eq "X-Reload-Triggers") {
                     $ret = 2;
                 }
             }
@@ -350,76 +390,78 @@ sub compare_units {
     return $ret;
 }
 
-sub handleModifiedUnit {
-    my ($unit, $baseName, $newUnitFile, $newUnitInfo, $activePrev, $unitsToStop, $unitsToStart, $unitsToReload, $unitsToRestart, $unitsToSkip) = @_;
+# 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_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$/ || $unit =~ /\.slice$/) {
+    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$/) {
+    } elsif ($unit =~ /\.mount$/msx) {
         # Reload the changed mount unit to force a remount.
         # FIXME: only reload when Options= changed, restart otherwise
-        $unitsToReload->{$unit} = 1;
-        recordUnit($reloadListFile, $unit);
-    } elsif ($unit =~ /\.socket$/) {
+        $units_to_reload->{$unit} = 1;
+        record_unit($reload_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 %unitInfo = $newUnitInfo ? %{$newUnitInfo} : parse_unit($newUnitFile);
-        if (parseSystemdBool(\%unitInfo, "Service", "X-ReloadIfChanged", 0) and not $unitsToRestart->{$unit} and not $unitsToStop->{$unit}) {
-            $unitsToReload->{$unit} = 1;
-            recordUnit($reloadListFile, $unit);
+        my %new_unit_info = $new_unit_info ? %{$new_unit_info} : parse_unit($new_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 (!parseSystemdBool(\%unitInfo, "Service", "X-RestartIfChanged", 1) || parseSystemdBool(\%unitInfo, "Unit", "RefuseManualStop", 0) || parseSystemdBool(\%unitInfo, "Unit", "X-OnlyManualStart", 0)) {
-            $unitsToSkip->{$unit} = 1;
+        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 (!parseSystemdBool(\%unitInfo, "Service", "X-StopIfChanged", 1) || $unit !~ /\.service$/) {
+            if (!parse_systemd_bool(\%new_unit_info, "Service", "X-StopIfChanged", 1) || $unit !~ /\.service$/msx) {
                 # This unit should be restarted instead of
                 # stopped and started.
-                $unitsToRestart->{$unit} = 1;
-                recordUnit($restartListFile, $unit);
+                $units_to_restart->{$unit} = 1;
+                record_unit($restart_list_file, $unit);
                 # Remove from units to reload so we don't restart and reload
-                if ($unitsToReload->{$unit}) {
-                    delete $unitsToReload->{$unit};
-                    unrecord_unit($reloadListFile, $unit);
+                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$/) {
-                    my @sockets = split(/ /, join(" ", @{$unitInfo{Service}{Sockets} // []}));
+                if ($unit =~ /\.service$/msx) {
+                    my @sockets = split(/\s+/msx, join(" ", @{$new_unit_info{Service}{Sockets} // []}));
                     if (scalar(@sockets) == 0) {
-                        @sockets = ("$baseName.socket");
+                        @sockets = ("$base_name.socket");
                     }
                     foreach my $socket (@sockets) {
-                        if (defined($activePrev->{$socket})) {
+                        if (defined($active_cur->{$socket})) {
                             # We can now be sure this is a socket-activate unit
 
-                            $unitsToStop->{$socket} = 1;
+                            $units_to_stop->{$socket} = 1;
                             # Only restart sockets that actually
                             # exist in new configuration:
                             if (-e "$out/etc/systemd/system/$socket") {
-                                $unitsToStart->{$socket} = 1;
-                                if ($unitsToStart eq $unitsToRestart) {
-                                    recordUnit($restartListFile, $socket);
+                                $units_to_start->{$socket} = 1;
+                                if ($units_to_start eq $units_to_restart) {
+                                    record_unit($restart_list_file, $socket);
                                 } else {
-                                    recordUnit($startListFile, $socket);
+                                    record_unit($start_list_file, $socket);
                                 }
                                 $socket_activated = 1;
                             }
                             # Remove from units to reload so we don't restart and reload
-                            if ($unitsToReload->{$unit}) {
-                                delete $unitsToReload->{$unit};
-                                unrecord_unit($reloadListFile, $unit);
+                            if ($units_to_reload->{$unit}) {
+                                delete $units_to_reload->{$unit};
+                                unrecord_unit($reload_list_file, $unit);
                             }
                         }
                     }
@@ -430,64 +472,67 @@ sub handleModifiedUnit {
                 # We write this to a file to ensure that the
                 # service gets restarted if we're interrupted.
                 if (!$socket_activated) {
-                    $unitsToStart->{$unit} = 1;
-                    if ($unitsToStart eq $unitsToRestart) {
-                        recordUnit($restartListFile, $unit);
+                    $units_to_start->{$unit} = 1;
+                    if ($units_to_start eq $units_to_restart) {
+                        record_unit($restart_list_file, $unit);
                     } else {
-                        recordUnit($startListFile, $unit);
+                        record_unit($start_list_file, $unit);
                     }
                 }
 
-                $unitsToStop->{$unit} = 1;
+                $units_to_stop->{$unit} = 1;
                 # Remove from units to reload so we don't restart and reload
-                if ($unitsToReload->{$unit}) {
-                    delete $unitsToReload->{$unit};
-                    unrecord_unit($reloadListFile, $unit);
+                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 (%unitsToStop, %unitsToSkip, %unitsToStart, %unitsToRestart, %unitsToReload);
+my (%units_to_stop, %units_to_skip, %units_to_start, %units_to_restart, %units_to_reload);
 
-my %unitsToFilter; # units not shown
+my %units_to_filter; # units not shown
 
-$unitsToStart{$_} = 1 foreach
-    split('\n', read_file($startListFile, err_mode => 'quiet') // "");
+%units_to_start = map { $_ => 1 }
+    split(/\n/msx, read_file($start_list_file, err_mode => "quiet") // "");
 
-$unitsToRestart{$_} = 1 foreach
-    split('\n', read_file($restartListFile, err_mode => 'quiet') // "");
+%units_to_restart = map { $_ => 1 }
+    split(/\n/msx, read_file($restart_list_file, err_mode => "quiet") // "");
 
-$unitsToReload{$_} = 1 foreach
-    split('\n', read_file($reloadListFile, err_mode => 'quiet') // "");
+%units_to_reload = map { $_ => 1 }
+    split(/\n/msx, read_file($reload_list_file, err_mode => "quiet") // "");
 
-my $activePrev = getActiveUnits();
-while (my ($unit, $state) = each(%{$activePrev})) {
-    my $baseUnit = $unit;
+my $active_cur = get_active_units();
+while (my ($unit, $state) = each(%{$active_cur})) {
+    my $base_unit = $unit;
 
-    my $prevUnitFile = "/etc/systemd/system/$baseUnit";
-    my $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+    my $cur_unit_file = "/etc/systemd/system/$base_unit";
+    my $new_unit_file = "$out/etc/systemd/system/$base_unit";
 
     # Detect template instances.
-    if (!-e $prevUnitFile && !-e $newUnitFile && $unit =~ /^(.*)@[^\.]*\.(.*)$/) {
-      $baseUnit = "$1\@.$2";
-      $prevUnitFile = "/etc/systemd/system/$baseUnit";
-      $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+    if (!-e $cur_unit_file && !-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
+      $base_unit = "$1\@.$2";
+      $cur_unit_file = "/etc/systemd/system/$base_unit";
+      $new_unit_file = "$out/etc/systemd/system/$base_unit";
     }
 
-    my $baseName = $baseUnit;
-    $baseName =~ s/\.[a-z]*$//;
+    my $base_name = $base_unit;
+    $base_name =~ s/\.[[:lower:]]*$//msx;
 
-    if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
-        if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
-            my %unitInfo = parse_unit($prevUnitFile);
-            $unitsToStop{$unit} = 1 if parseSystemdBool(\%unitInfo, "Unit", "X-StopOnRemoval", 1);
+    if (-e $cur_unit_file && ($state->{state} eq "active" || $state->{state} eq "activating")) {
+        if (! -e $new_unit_file || abs_path($new_unit_file) eq "/dev/null") {
+            my %cur_unit_info = parse_unit($cur_unit_file);
+            if (parse_systemd_bool(\%cur_unit_info, "Unit", "X-StopOnRemoval", 1)) {
+                $units_to_stop{$unit} = 1;
+            }
         }
 
-        elsif ($unit =~ /\.target$/) {
-            my %unitInfo = parse_unit($newUnitFile);
+        elsif ($unit =~ /\.target$/msx) {
+            my %new_unit_info = parse_unit($new_unit_file);
 
             # Cause all active target units to be restarted below.
             # This should start most changed units we stop here as
@@ -496,11 +541,11 @@ while (my ($unit, $state) = each(%{$activePrev})) {
             # 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") {
-                unless (parseSystemdBool(\%unitInfo, "Unit", "RefuseManualStart", 0) || parseSystemdBool(\%unitInfo, "Unit", "X-OnlyManualStart", 0)) {
-                    $unitsToStart{$unit} = 1;
-                    recordUnit($startListFile, $unit);
+                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.
-                    $unitsToFilter{$unit} = 1;
+                    $units_to_filter{$unit} = 1;
                 }
             }
 
@@ -515,33 +560,35 @@ while (my ($unit, $state) = each(%{$activePrev})) {
             # 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 (parseSystemdBool(\%unitInfo, "Unit", "X-StopOnReconfiguration", 0)) {
-                $unitsToStop{$unit} = 1;
+            if (parse_systemd_bool(\%new_unit_info, "Unit", "X-StopOnReconfiguration", 0)) {
+                $units_to_stop{$unit} = 1;
             }
         }
 
         else {
-            my %old_unit_info = parse_unit($prevUnitFile);
-            my %new_unit_info = parse_unit($newUnitFile);
-            my $diff = compare_units(\%old_unit_info, \%new_unit_info);
+            my %cur_unit_info = parse_unit($cur_unit_file);
+            my %new_unit_info = parse_unit($new_unit_file);
+            my $diff = compare_units(\%cur_unit_info, \%new_unit_info);
             if ($diff == 1) {
-                handleModifiedUnit($unit, $baseName, $newUnitFile, \%new_unit_info, $activePrev, \%unitsToStop, \%unitsToStart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
-            } elsif ($diff == 2 and not $unitsToRestart{$unit}) {
-                $unitsToReload{$unit} = 1;
-                recordUnit($reloadListFile, $unit);
+                handle_modified_unit($unit, $base_name, $new_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);
             }
         }
     }
 }
 
-sub pathToUnitName {
+# 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, "-|", "$curSystemd/systemd-escape", "--suffix=mount", "-p", $path)
+    open(my $cmd, "-|", "$cur_systemd/systemd-escape", "--suffix=mount", "-p", $path)
         or die "Unable to escape $path!\n";
-    my $escaped = join("", <$cmd>);
+    my $escaped = do { local $/ = undef; <$cmd> };
     chomp($escaped);
-    close($cmd) or die('Unable to close systemd-escape pipe');
+    close($cmd) or die("Unable to close systemd-escape pipe");
     return $escaped;
 }
 
@@ -550,31 +597,31 @@ sub pathToUnitName {
 # 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 ($prevFss, $prevSwaps) = parseFstab("/etc/fstab");
-my ($newFss, $newSwaps) = parseFstab("$out/etc/fstab");
-foreach my $mountPoint (keys(%$prevFss)) {
-    my $prev = $prevFss->{$mountPoint};
-    my $new = $newFss->{$mountPoint};
-    my $unit = pathToUnitName($mountPoint);
+my ($cur_fss, $cur_swaps) = parse_fstab("/etc/fstab");
+my ($new_fss, $new_swaps) = parse_fstab("$out/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.
-        $unitsToStop{$unit} = 1;
-    } elsif ($prev->{fsType} ne $new->{fsType} || $prev->{device} ne $new->{device}) {
+        $units_to_stop{$unit} = 1;
+    } elsif ($cur->{fsType} ne $new->{fsType} || $cur->{device} ne $new->{device}) {
         # Filesystem type or device changed, so unmount and mount it.
-        $unitsToStop{$unit} = 1;
-        $unitsToStart{$unit} = 1;
-        recordUnit($startListFile, $unit);
-    } elsif ($prev->{options} ne $new->{options}) {
+        $units_to_stop{$unit} = 1;
+        $units_to_start{$unit} = 1;
+        record_unit($start_list_file, $unit);
+    } elsif ($cur->{options} ne $new->{options}) {
         # Mount options changes, so remount it.
-        $unitsToReload{$unit} = 1;
-        recordUnit($reloadListFile, $unit);
+        $units_to_reload{$unit} = 1;
+        record_unit($reload_list_file, $unit);
     }
 }
 
 # Also handles swap devices.
-foreach my $device (keys(%$prevSwaps)) {
-    my $prev = $prevSwaps->{$device};
-    my $new = $newSwaps->{$device};
+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
@@ -592,97 +639,109 @@ foreach my $device (keys(%$prevSwaps)) {
 
 
 # Should we have systemd re-exec itself?
-my $prevSystemd = abs_path("/proc/1/exe") // "/unknown";
-my $prevSystemdSystemConfig = abs_path("/etc/systemd/system.conf") // "/unknown";
-my $newSystemd = abs_path("@systemd@/lib/systemd/systemd") or die;
-my $newSystemdSystemConfig = abs_path("$out/etc/systemd/system.conf") // "/unknown";
+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("$out/etc/systemd/system.conf") // "/unknown";
 
-my $restartSystemd = $prevSystemd ne $newSystemd;
-if ($prevSystemdSystemConfig ne $newSystemdSystemConfig) {
-    $restartSystemd = 1;
+my $restart_systemd = $cur_pid1_path ne $new_pid1_path;
+if ($cur_systemd_system_config ne $new_systemd_system_config) {
+    $restart_systemd = 1;
 }
 
-
-sub filterUnits {
+# 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}))) {
-        push(@res, $unit) if !defined($unitsToFilter{$unit});
+        if (!defined($units_to_filter{$unit})) {
+            push(@res, $unit);
+        }
     }
     return @res;
 }
 
-my @unitsToStopFiltered = filterUnits(\%unitsToStop);
+my @units_to_stop_filtered = filter_units(\%units_to_stop);
 
 
 # Show dry-run actions.
 if ($action eq "dry-activate") {
-    print STDERR "would stop the following units: ", join(", ", @unitsToStopFiltered), "\n"
-        if scalar(@unitsToStopFiltered) > 0;
-    print STDERR "would NOT stop the following changed units: ", join(", ", sort(keys(%unitsToSkip))), "\n"
-        if scalar(keys(%unitsToSkip)) > 0;
+    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', read_file($dryRestartByActivationFile, err_mode => 'quiet') // "")) {
+    foreach (split(/\n/msx, read_file($dry_restart_by_activation_file, err_mode => "quiet") // "")) {
         my $unit = $_;
-        my $baseUnit = $unit;
-        my $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+        my $base_unit = $unit;
+        my $new_unit_file = "$out/etc/systemd/system/$base_unit";
 
         # Detect template instances.
-        if (!-e $newUnitFile && $unit =~ /^(.*)@[^\.]*\.(.*)$/) {
-          $baseUnit = "$1\@.$2";
-          $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+        if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
+          $base_unit = "$1\@.$2";
+          $new_unit_file = "$out/etc/systemd/system/$base_unit";
         }
 
-        my $baseName = $baseUnit;
-        $baseName =~ s/\.[a-z]*$//;
+        my $base_name = $base_unit;
+        $base_name =~ s/\.[[:lower:]]*$//msx;
 
         # Start units if they were not active previously
-        if (not defined($activePrev->{$unit})) {
-            $unitsToStart{$unit} = 1;
+        if (not defined($active_cur->{$unit})) {
+            $units_to_start{$unit} = 1;
             next;
         }
 
-        handleModifiedUnit($unit, $baseName, $newUnitFile, undef, $activePrev, \%unitsToRestart, \%unitsToRestart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
+        handle_modified_unit($unit, $base_name, $new_unit_file, undef, $active_cur, \%units_to_restart, \%units_to_restart, \%units_to_reload, \%units_to_restart, \%units_to_skip);
     }
-    unlink($dryRestartByActivationFile);
+    unlink($dry_restart_by_activation_file);
 
-    foreach (split('\n', read_file($dryReloadByActivationFile, err_mode => 'quiet') // "")) {
+    foreach (split(/\n/msx, read_file($dry_reload_by_activation_file, err_mode => "quiet") // "")) {
         my $unit = $_;
 
-        if (defined($activePrev->{$unit}) and not $unitsToRestart{$unit} and not $unitsToStop{$unit}) {
-            $unitsToReload{$unit} = 1;
-            recordUnit($reloadListFile, $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($dryReloadByActivationFile);
+    unlink($dry_reload_by_activation_file);
 
-    print STDERR "would restart systemd\n" if $restartSystemd;
-    print STDERR "would reload the following units: ", join(", ", sort(keys(%unitsToReload))), "\n"
-        if scalar(keys(%unitsToReload)) > 0;
-    print STDERR "would restart the following units: ", join(", ", sort(keys(%unitsToRestart))), "\n"
-        if scalar(keys(%unitsToRestart)) > 0;
-    my @unitsToStartFiltered = filterUnits(\%unitsToStart);
-    print STDERR "would start the following units: ", join(", ", @unitsToStartFiltered), "\n"
-        if scalar(@unitsToStartFiltered);
+    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 $out");
 
-if (scalar(keys(%unitsToStop)) > 0) {
-    print STDERR "stopping the following units: ", join(", ", @unitsToStopFiltered), "\n"
-        if scalar(@unitsToStopFiltered);
+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("$curSystemd/systemctl", "stop", "--", sort(keys(%unitsToStop)));
+    system("$cur_systemd/systemctl", "stop", "--", sort(keys(%units_to_stop)));
 }
 
-print STDERR "NOT restarting the following changed units: ", join(", ", sort(keys(%unitsToSkip))), "\n"
-    if scalar(keys(%unitsToSkip)) > 0;
+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).
@@ -691,108 +750,110 @@ 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', read_file($restartByActivationFile, err_mode => 'quiet') // "")) {
+foreach (split(/\n/msx, read_file($restart_by_activation_file, err_mode => "quiet") // "")) {
     my $unit = $_;
-    my $baseUnit = $unit;
-    my $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+    my $base_unit = $unit;
+    my $new_unit_file = "$out/etc/systemd/system/$base_unit";
 
     # Detect template instances.
-    if (!-e $newUnitFile && $unit =~ /^(.*)@[^\.]*\.(.*)$/) {
-      $baseUnit = "$1\@.$2";
-      $newUnitFile = "$out/etc/systemd/system/$baseUnit";
+    if (!-e $new_unit_file && $unit =~ /^(.*)@[^\.]*\.(.*)$/msx) {
+      $base_unit = "$1\@.$2";
+      $new_unit_file = "$out/etc/systemd/system/$base_unit";
     }
 
-    my $baseName = $baseUnit;
-    $baseName =~ s/\.[a-z]*$//;
+    my $base_name = $base_unit;
+    $base_name =~ s/\.[[:lower:]]*$//msx;
 
     # Start units if they were not active previously
-    if (not defined($activePrev->{$unit})) {
-        $unitsToStart{$unit} = 1;
-        recordUnit($startListFile, $unit);
+    if (not defined($active_cur->{$unit})) {
+        $units_to_start{$unit} = 1;
+        record_unit($start_list_file, $unit);
         next;
     }
 
-    handleModifiedUnit($unit, $baseName, $newUnitFile, undef, $activePrev, \%unitsToRestart, \%unitsToRestart, \%unitsToReload, \%unitsToRestart, \%unitsToSkip);
+    handle_modified_unit($unit, $base_name, $new_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($restartByActivationFile);
+unlink($restart_by_activation_file);
 
-foreach (split('\n', read_file($reloadByActivationFile, err_mode => 'quiet') // "")) {
+foreach (split(/\n/msx, read_file($reload_by_activation_file, err_mode => "quiet") // "")) {
     my $unit = $_;
 
-    if (defined($activePrev->{$unit}) and not $unitsToRestart{$unit} and not $unitsToStop{$unit}) {
-        $unitsToReload{$unit} = 1;
-        recordUnit($reloadListFile, $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($reloadByActivationFile);
+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 ($restartSystemd) {
+if ($restart_systemd) {
     print STDERR "restarting systemd...\n";
-    system("$curSystemd/systemctl", "daemon-reexec") == 0 or $res = 2;
+    system("$cur_systemd/systemctl", "daemon-reexec") == 0 or $res = 2;
 }
 
 # Forget about previously failed services.
-system("@systemd@/bin/systemctl", "reset-failed");
+system("$new_systemd/bin/systemctl", "reset-failed");
 
 # Make systemd reload its units.
-system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3;
+system("$new_systemd/bin/systemctl", "daemon-reload") == 0 or $res = 3;
 
 # Reload user units
-open(my $listActiveUsers, '-|', '@systemd@/bin/loginctl', 'list-users', '--no-legend');
-while (my $f = <$listActiveUsers>) {
-    next unless $f =~ /^\s*(?<uid>\d+)\s+(?<user>\S+)/;
+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; " .
-           "$curSystemd/systemctl --user daemon-reexec; " .
-           "@systemd@/bin/systemctl --user start nixos-activation.service");
+           "$cur_systemd/systemctl --user daemon-reexec; " .
+           "$new_systemd/bin/systemctl --user start nixos-activation.service");
 }
 
-close($listActiveUsers);
+close($list_active_users) || die("Unable to close the file handle to loginctl");
 
 # Set the new tmpfiles
 print STDERR "setting up tmpfiles\n";
-system("@systemd@/bin/systemd-tmpfiles", "--create", "--remove", "--exclude-prefix=/dev") == 0 or $res = 3;
+system("$new_systemd/bin/systemd-tmpfiles", "--create", "--remove", "--exclude-prefix=/dev") == 0 or $res = 3;
 
 # 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(%unitsToReload)) > 0) {
-    for my $unit (keys(%unitsToReload)) {
+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("$out/etc/systemd/system/$unit");
-            if (!(parseSystemdBool(\%unit_info, 'Unit', 'RefuseManualStart', 0) || parseSystemdBool(\%unit_info, 'Unit', 'X-OnlyManualStart', 0))) {
-                $unitsToStart{$unit} = 1;
-                recordUnit($startListFile, $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 %unitsToReload{$unit};
-            unrecord_unit($reloadListFile, $unit);
+            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(%unitsToReload)) > 0) {
-    print STDERR "reloading the following units: ", join(", ", sort(keys(%unitsToReload))), "\n";
-    system("@systemd@/bin/systemctl", "reload", "--", sort(keys(%unitsToReload))) == 0 or $res = 4;
-    unlink($reloadListFile);
+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(%unitsToRestart)) > 0) {
-    print STDERR "restarting the following units: ", join(", ", sort(keys(%unitsToRestart))), "\n";
-    system("@systemd@/bin/systemctl", "restart", "--", sort(keys(%unitsToRestart))) == 0 or $res = 4;
-    unlink($restartListFile);
+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.
@@ -801,17 +862,18 @@ if (scalar(keys(%unitsToRestart)) > 0) {
 # 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 @unitsToStartFiltered = filterUnits(\%unitsToStart);
-print STDERR "starting the following units: ", join(", ", @unitsToStartFiltered), "\n"
-    if scalar(@unitsToStartFiltered);
-system("@systemd@/bin/systemctl", "start", "--", sort(keys(%unitsToStart))) == 0 or $res = 4;
-unlink($startListFile);
+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 $activeNew = getActiveUnits();
-while (my ($unit, $state) = each(%{$activeNew})) {
+my $active_new = get_active_units();
+while (my ($unit, $state) = each(%{$active_new})) {
     if ($state->{state} eq "failed") {
         push(@failed, $unit);
         next;
@@ -819,7 +881,9 @@ while (my ($unit, $state) = each(%{$activeNew})) {
 
     if ($state->{substate} eq "auto-restart") {
         # A unit in auto-restart substate is a failure *if* it previously failed to start
-        my $main_status = `@systemd@/bin/systemctl show --value --property=ExecMainStatus '$unit'`;
+        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") {
@@ -831,7 +895,7 @@ while (my ($unit, $state) = each(%{$activeNew})) {
     # 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($activePrev->{$unit}) && $unit !~ /\.scope$/msx) {
+    if ($state->{state} ne "failed" && !defined($active_cur->{$unit}) && $unit !~ /\.scope$/msx) {
         push(@new, $unit);
     }
 }
@@ -843,7 +907,7 @@ if (scalar(@new) > 0) {
 if (scalar(@failed) > 0) {
     my @failed_sorted = sort(@failed);
     print STDERR "warning: the following units failed: ", join(", ", @failed_sorted), "\n\n";
-    system("@systemd@/bin/systemctl status --no-pager --full '" . join("' '", @failed_sorted) . "' >&2");
+    system("$new_systemd/bin/systemctl status --no-pager --full '" . join("' '", @failed_sorted) . "' >&2");
     $res = 4;
 }
 
diff --git a/nixpkgs/nixos/modules/system/boot/kernel.nix b/nixpkgs/nixos/modules/system/boot/kernel.nix
index d147155d796c..db00244ca0af 100644
--- a/nixpkgs/nixos/modules/system/boot/kernel.nix
+++ b/nixpkgs/nixos/modules/system/boot/kernel.nix
@@ -36,7 +36,7 @@ in
 
     boot.kernelPackages = mkOption {
       default = pkgs.linuxPackages;
-      type = types.unspecified // { merge = mergeEqualOption; };
+      type = types.raw;
       apply = kernelPackages: kernelPackages.extend (self: super: {
         kernel = super.kernel.override (originalArgs: {
           inherit randstructSeed;
diff --git a/nixpkgs/nixos/modules/system/boot/modprobe.nix b/nixpkgs/nixos/modules/system/boot/modprobe.nix
index 27f78835adb2..e683d1817297 100644
--- a/nixpkgs/nixos/modules/system/boot/modprobe.nix
+++ b/nixpkgs/nixos/modules/system/boot/modprobe.nix
@@ -34,23 +34,6 @@ with lib;
       type = types.lines;
     };
 
-    boot.initrd.extraModprobeConfig = mkOption {
-      default = "";
-      example =
-        ''
-          options zfs zfs_arc_max=1073741824
-        '';
-      description = ''
-        Does exactly the same thing as
-        <option>boot.extraModprobeConfig</option>, except
-        that the generated <filename>modprobe.conf</filename>
-        file is also included in the initrd.
-        This is useful for setting module options for kernel
-        modules that are loaded during early boot in the initrd.
-      '';
-      type = types.lines;
-    };
-
   };
 
 
@@ -67,9 +50,6 @@ with lib;
         '')}
         ${config.boot.extraModprobeConfig}
       '';
-    environment.etc."modprobe.d/nixos-initrd.conf".text = ''
-        ${config.boot.initrd.extraModprobeConfig}
-      '';
     environment.etc."modprobe.d/debian.conf".source = pkgs.kmod-debian-aliases;
 
     environment.etc."modprobe.d/systemd.conf".source = "${pkgs.systemd}/lib/modprobe.d/systemd.conf";
diff --git a/nixpkgs/nixos/modules/system/boot/stage-1.nix b/nixpkgs/nixos/modules/system/boot/stage-1.nix
index 1575c0257d1c..8b011d91563f 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-1.nix
+++ b/nixpkgs/nixos/modules/system/boot/stage-1.nix
@@ -338,9 +338,6 @@ let
         { object = pkgs.writeText "mdadm.conf" config.boot.initrd.mdadmConf;
           symlink = "/etc/mdadm.conf";
         }
-        { object = config.environment.etc."modprobe.d/nixos-initrd.conf".source;
-          symlink = "/etc/modprobe.d/nixos-initrd.conf";
-        }
         { object = pkgs.runCommand "initrd-kmod-blacklist-ubuntu" {
               src = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
               preferLocalBuild = true;
@@ -581,7 +578,7 @@ in
         else "gzip"
       );
       defaultText = literalDocBook "<literal>zstd</literal> if the kernel supports it (5.9+), <literal>gzip</literal> if not";
-      type = types.unspecified; # We don't have a function type...
+      type = types.either types.str (types.functionTo types.str);
       description = ''
         The compressor to use on the initrd image. May be any of:
 
diff --git a/nixpkgs/nixos/modules/system/boot/stage-2.nix b/nixpkgs/nixos/modules/system/boot/stage-2.nix
index f6b6a8e4b0b4..fa2bf938df4f 100644
--- a/nixpkgs/nixos/modules/system/boot/stage-2.nix
+++ b/nixpkgs/nixos/modules/system/boot/stage-2.nix
@@ -47,36 +47,6 @@ in
         '';
       };
 
-      devSize = mkOption {
-        default = "5%";
-        example = "32m";
-        type = types.str;
-        description = ''
-          Size limit for the /dev tmpfs. Look at mount(8), tmpfs size option,
-          for the accepted syntax.
-        '';
-      };
-
-      devShmSize = mkOption {
-        default = "50%";
-        example = "256m";
-        type = types.str;
-        description = ''
-          Size limit for the /dev/shm tmpfs. Look at mount(8), tmpfs size option,
-          for the accepted syntax.
-        '';
-      };
-
-      runSize = mkOption {
-        default = "25%";
-        example = "256m";
-        type = types.str;
-        description = ''
-          Size limit for the /run tmpfs. Look at mount(8), tmpfs size option,
-          for the accepted syntax.
-        '';
-      };
-
       systemdExecutable = mkOption {
         default = "systemd";
         type = types.str;
diff --git a/nixpkgs/nixos/modules/system/boot/systemd.nix b/nixpkgs/nixos/modules/system/boot/systemd.nix
index 2607e57195ca..057474c607ac 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd.nix
@@ -2,7 +2,6 @@
 
 with utils;
 with systemdUtils.unitOptions;
-with systemdUtils.lib;
 with lib;
 
 let
@@ -11,6 +10,24 @@ let
 
   systemd = cfg.package;
 
+  inherit (systemdUtils.lib)
+    makeUnit
+    generateUnits
+    makeJobScript
+    unitConfig
+    serviceConfig
+    mountConfig
+    automountConfig
+    commonUnitText
+    targetToUnit
+    serviceToUnit
+    socketToUnit
+    timerToUnit
+    pathToUnit
+    mountToUnit
+    automountToUnit
+    sliceToUnit;
+
   upstreamSystemUnits =
     [ # Targets.
       "basic.target"
@@ -63,32 +80,6 @@ let
       "printer.target"
       "smartcard.target"
 
-      # Login stuff.
-      "systemd-logind.service"
-      "autovt@.service"
-      "systemd-user-sessions.service"
-      "dbus-org.freedesktop.import1.service"
-      "dbus-org.freedesktop.machine1.service"
-      "dbus-org.freedesktop.login1.service"
-      "user@.service"
-      "user-runtime-dir@.service"
-
-      # Journal.
-      "systemd-journald.socket"
-      "systemd-journald@.socket"
-      "systemd-journald-varlink@.socket"
-      "systemd-journald.service"
-      "systemd-journald@.service"
-      "systemd-journal-flush.service"
-      "systemd-journal-catalog-update.service"
-      ] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [
-      "systemd-journald-dev-log.socket"
-      "syslog.socket"
-
-      # Coredumps.
-      "systemd-coredump.socket"
-      "systemd-coredump@.service"
-
       # Kernel module loading.
       "systemd-modules-load.service"
       "kmod-static-nodes.service"
@@ -149,19 +140,12 @@ let
 
       # Slices / containers.
       "slices.target"
-      "user.slice"
       "machine.slice"
       "machines.target"
       "systemd-importd.service"
       "systemd-machined.service"
       "systemd-nspawn@.service"
 
-      # Temporary file creation / cleanup.
-      "systemd-tmpfiles-clean.service"
-      "systemd-tmpfiles-clean.timer"
-      "systemd-tmpfiles-setup.service"
-      "systemd-tmpfiles-setup-dev.service"
-
       # Misc.
       "systemd-sysctl.service"
       "dbus-org.freedesktop.timedate1.service"
@@ -172,9 +156,6 @@ let
       "systemd-hostnamed.service"
       "systemd-exit.service"
       "systemd-update-done.service"
-    ] ++ optionals config.services.journald.enableHttpGateway [
-      "systemd-journal-gatewayd.socket"
-      "systemd-journal-gatewayd.service"
     ] ++ cfg.additionalUpstreamSystemUnits;
 
   upstreamSystemWants =
@@ -185,237 +166,6 @@ let
       "timers.target.wants"
     ];
 
-    upstreamUserUnits = [
-      "app.slice"
-      "background.slice"
-      "basic.target"
-      "bluetooth.target"
-      "default.target"
-      "exit.target"
-      "graphical-session-pre.target"
-      "graphical-session.target"
-      "paths.target"
-      "printer.target"
-      "session.slice"
-      "shutdown.target"
-      "smartcard.target"
-      "sockets.target"
-      "sound.target"
-      "systemd-exit.service"
-      "systemd-tmpfiles-clean.service"
-      "systemd-tmpfiles-clean.timer"
-      "systemd-tmpfiles-setup.service"
-      "timers.target"
-      "xdg-desktop-autostart.target"
-    ];
-
-  makeJobScript = name: text:
-    let
-      scriptName = replaceChars [ "\\" "@" ] [ "-" "_" ] (shellEscape name);
-      out = (pkgs.writeShellScriptBin scriptName ''
-        set -e
-        ${text}
-      '').overrideAttrs (_: {
-        # The derivation name is different from the script file name
-        # to keep the script file name short to avoid cluttering logs.
-        name = "unit-script-${scriptName}";
-      });
-    in "${out}/bin/${scriptName}";
-
-  unitConfig = { config, options, ... }: {
-    config = {
-      unitConfig =
-        optionalAttrs (config.requires != [])
-          { Requires = toString config.requires; }
-        // optionalAttrs (config.wants != [])
-          { Wants = toString config.wants; }
-        // optionalAttrs (config.after != [])
-          { After = toString config.after; }
-        // optionalAttrs (config.before != [])
-          { Before = toString config.before; }
-        // optionalAttrs (config.bindsTo != [])
-          { BindsTo = toString config.bindsTo; }
-        // optionalAttrs (config.partOf != [])
-          { PartOf = toString config.partOf; }
-        // optionalAttrs (config.conflicts != [])
-          { Conflicts = toString config.conflicts; }
-        // optionalAttrs (config.requisite != [])
-          { Requisite = toString config.requisite; }
-        // optionalAttrs (config.restartTriggers != [])
-          { X-Restart-Triggers = toString config.restartTriggers; }
-        // optionalAttrs (config.reloadTriggers != [])
-          { X-Reload-Triggers = toString config.reloadTriggers; }
-        // optionalAttrs (config.description != "") {
-          Description = config.description; }
-        // optionalAttrs (config.documentation != []) {
-          Documentation = toString config.documentation; }
-        // optionalAttrs (config.onFailure != []) {
-          OnFailure = toString config.onFailure; }
-        // optionalAttrs (options.startLimitIntervalSec.isDefined) {
-          StartLimitIntervalSec = toString config.startLimitIntervalSec;
-        } // optionalAttrs (options.startLimitBurst.isDefined) {
-          StartLimitBurst = toString config.startLimitBurst;
-        };
-    };
-  };
-
-  serviceConfig = { name, config, ... }: {
-    config = mkMerge
-      [ { # Default path for systemd services.  Should be quite minimal.
-          path = mkAfter
-            [ pkgs.coreutils
-              pkgs.findutils
-              pkgs.gnugrep
-              pkgs.gnused
-              systemd
-            ];
-          environment.PATH = "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
-        }
-        (mkIf (config.preStart != "")
-          { serviceConfig.ExecStartPre =
-              [ (makeJobScript "${name}-pre-start" config.preStart) ];
-          })
-        (mkIf (config.script != "")
-          { serviceConfig.ExecStart =
-              makeJobScript "${name}-start" config.script + " " + config.scriptArgs;
-          })
-        (mkIf (config.postStart != "")
-          { serviceConfig.ExecStartPost =
-              [ (makeJobScript "${name}-post-start" config.postStart) ];
-          })
-        (mkIf (config.reload != "")
-          { serviceConfig.ExecReload =
-              makeJobScript "${name}-reload" config.reload;
-          })
-        (mkIf (config.preStop != "")
-          { serviceConfig.ExecStop =
-              makeJobScript "${name}-pre-stop" config.preStop;
-          })
-        (mkIf (config.postStop != "")
-          { serviceConfig.ExecStopPost =
-              makeJobScript "${name}-post-stop" config.postStop;
-          })
-      ];
-  };
-
-  mountConfig = { config, ... }: {
-    config = {
-      mountConfig =
-        { What = config.what;
-          Where = config.where;
-        } // optionalAttrs (config.type != "") {
-          Type = config.type;
-        } // optionalAttrs (config.options != "") {
-          Options = config.options;
-        };
-    };
-  };
-
-  automountConfig = { config, ... }: {
-    config = {
-      automountConfig =
-        { Where = config.where;
-        };
-    };
-  };
-
-  commonUnitText = def: ''
-      [Unit]
-      ${attrsToSection def.unitConfig}
-    '';
-
-  targetToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable;
-      text =
-        ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-        '';
-    };
-
-  serviceToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable;
-      text = commonUnitText def +
-        ''
-          [Service]
-          ${let env = cfg.globalEnvironment // def.environment;
-            in concatMapStrings (n:
-              let s = optionalString (env.${n} != null)
-                "Environment=${builtins.toJSON "${n}=${env.${n}}"}\n";
-              # systemd max line length is now 1MiB
-              # https://github.com/systemd/systemd/commit/e6dde451a51dc5aaa7f4d98d39b8fe735f73d2af
-              in if stringLength s >= 1048576 then throw "The value of the environment variable ‘${n}’ in systemd service ‘${name}.service’ is too long." else s) (attrNames env)}
-          ${if def.reloadIfChanged then ''
-            X-ReloadIfChanged=true
-          '' else if !def.restartIfChanged then ''
-            X-RestartIfChanged=false
-          '' else ""}
-          ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
-          ${attrsToSection def.serviceConfig}
-        '';
-    };
-
-  socketToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable;
-      text = commonUnitText def +
-        ''
-          [Socket]
-          ${attrsToSection def.socketConfig}
-          ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
-          ${concatStringsSep "\n" (map (s: "ListenDatagram=${s}") def.listenDatagrams)}
-        '';
-    };
-
-  timerToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable;
-      text = commonUnitText def +
-        ''
-          [Timer]
-          ${attrsToSection def.timerConfig}
-        '';
-    };
-
-  pathToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable;
-      text = commonUnitText def +
-        ''
-          [Path]
-          ${attrsToSection def.pathConfig}
-        '';
-    };
-
-  mountToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable;
-      text = commonUnitText def +
-        ''
-          [Mount]
-          ${attrsToSection def.mountConfig}
-        '';
-    };
-
-  automountToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable;
-      text = commonUnitText def +
-        ''
-          [Automount]
-          ${attrsToSection def.automountConfig}
-        '';
-    };
-
-  sliceToUnit = name: def:
-    { inherit (def) aliases wantedBy requiredBy enable;
-      text = commonUnitText def +
-        ''
-          [Slice]
-          ${attrsToSection def.sliceConfig}
-        '';
-    };
-
-  logindHandlerType = types.enum [
-    "ignore" "poweroff" "reboot" "halt" "kexec" "suspend"
-    "hibernate" "hybrid-sleep" "suspend-then-hibernate" "lock"
-  ];
-
   proxy_env = config.networking.proxy.envVars;
 
 in
@@ -568,26 +318,6 @@ in
       '';
     };
 
-    systemd.coredump.enable = mkOption {
-      default = true;
-      type = types.bool;
-      description = ''
-        Whether core dumps should be processed by
-        <command>systemd-coredump</command>. If disabled, core dumps
-        appear in the current directory of the crashing process.
-      '';
-    };
-
-    systemd.coredump.extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      example = "Storage=journal";
-      description = ''
-        Extra config options for systemd-coredump. See coredump.conf(5) man page
-        for available options.
-      '';
-    };
-
     systemd.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -598,142 +328,6 @@ in
       '';
     };
 
-    services.journald.console = mkOption {
-      default = "";
-      type = types.str;
-      description = "If non-empty, write log messages to the specified TTY device.";
-    };
-
-    services.journald.rateLimitInterval = mkOption {
-      default = "30s";
-      type = types.str;
-      description = ''
-        Configures the rate limiting interval that is applied to all
-        messages generated on the system. This rate limiting is applied
-        per-service, so that two services which log do not interfere with
-        each other's limit. The value may be specified in the following
-        units: s, min, h, ms, us. To turn off any kind of rate limiting,
-        set either value to 0.
-
-        See <option>services.journald.rateLimitBurst</option> for important
-        considerations when setting this value.
-      '';
-    };
-
-    services.journald.rateLimitBurst = mkOption {
-      default = 10000;
-      type = types.int;
-      description = ''
-        Configures the rate limiting burst limit (number of messages per
-        interval) that is applied to all messages generated on the system.
-        This rate limiting is applied per-service, so that two services
-        which log do not interfere with each other's limit.
-
-        Note that the effective rate limit is multiplied by a factor derived
-        from the available free disk space for the journal as described on
-        <link xlink:href="https://www.freedesktop.org/software/systemd/man/journald.conf.html">
-        journald.conf(5)</link>.
-
-        Note that the total amount of logs stored is limited by journald settings
-        such as <literal>SystemMaxUse</literal>, which defaults to a 4 GB cap.
-
-        It is thus recommended to compute what period of time that you will be
-        able to store logs for when an application logs at full burst rate.
-        With default settings for log lines that are 100 Bytes long, this can
-        amount to just a few hours.
-      '';
-    };
-
-    services.journald.extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      example = "Storage=volatile";
-      description = ''
-        Extra config options for systemd-journald. See man journald.conf
-        for available options.
-      '';
-    };
-
-    services.journald.enableHttpGateway = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Whether to enable the HTTP gateway to the journal.
-      '';
-    };
-
-    services.journald.forwardToSyslog = mkOption {
-      default = config.services.rsyslogd.enable || config.services.syslog-ng.enable;
-      defaultText = literalExpression "services.rsyslogd.enable || services.syslog-ng.enable";
-      type = types.bool;
-      description = ''
-        Whether to forward log messages to syslog.
-      '';
-    };
-
-    services.logind.extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      example = "IdleAction=lock";
-      description = ''
-        Extra config options for systemd-logind. See
-        <link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html">
-        logind.conf(5)</link> for available options.
-      '';
-    };
-
-    services.logind.killUserProcesses = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Specifies whether the processes of a user should be killed
-        when the user logs out.  If true, the scope unit corresponding
-        to the session and all processes inside that scope will be
-        terminated.  If false, the scope is "abandoned" (see
-        <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.scope.html#">
-        systemd.scope(5)</link>), and processes are not killed.
-        </para>
-
-        <para>
-        See <link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html#KillUserProcesses=">logind.conf(5)</link>
-        for more details.
-      '';
-    };
-
-    services.logind.lidSwitch = mkOption {
-      default = "suspend";
-      example = "ignore";
-      type = logindHandlerType;
-
-      description = ''
-        Specifies what to be done when the laptop lid is closed.
-      '';
-    };
-
-    services.logind.lidSwitchDocked = mkOption {
-      default = "ignore";
-      example = "suspend";
-      type = logindHandlerType;
-
-      description = ''
-        Specifies what to be done when the laptop lid is closed
-        and another screen is added.
-      '';
-    };
-
-    services.logind.lidSwitchExternalPower = mkOption {
-      default = config.services.logind.lidSwitch;
-      defaultText = literalExpression "services.logind.lidSwitch";
-      example = "ignore";
-      type = logindHandlerType;
-
-      description = ''
-        Specifies what to do when the laptop lid is closed and the system is
-        on external power. By default use the same action as specified in
-        services.logind.lidSwitch.
-      '';
-    };
-
     systemd.sleep.extraConfig = mkOption {
       default = "";
       type = types.lines;
@@ -744,95 +338,6 @@ in
       '';
     };
 
-    systemd.user.extraConfig = mkOption {
-      default = "";
-      type = types.lines;
-      example = "DefaultCPUAccounting=yes";
-      description = ''
-        Extra config options for systemd user instances. See man systemd-user.conf for
-        available options.
-      '';
-    };
-
-    systemd.tmpfiles.rules = mkOption {
-      type = types.listOf types.str;
-      default = [];
-      example = [ "d /tmp 1777 root root 10d" ];
-      description = ''
-        Rules for creation, deletion and cleaning of volatile and temporary files
-        automatically. See
-        <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
-        for the exact format.
-      '';
-    };
-
-    systemd.tmpfiles.packages = mkOption {
-      type = types.listOf types.package;
-      default = [];
-      example = literalExpression "[ pkgs.lvm2 ]";
-      apply = map getLib;
-      description = ''
-        List of packages containing <command>systemd-tmpfiles</command> rules.
-
-        All files ending in .conf found in
-        <filename><replaceable>pkg</replaceable>/lib/tmpfiles.d</filename>
-        will be included.
-        If this folder does not exist or does not contain any files an error will be returned instead.
-
-        If a <filename>lib</filename> output is available, rules are searched there and only there.
-        If there is no <filename>lib</filename> output it will fall back to <filename>out</filename>
-        and if that does not exist either, the default output will be used.
-      '';
-    };
-
-    systemd.user.units = mkOption {
-      description = "Definition of systemd per-user units.";
-      default = {};
-      type = with types; attrsOf (submodule (
-        { name, config, ... }:
-        { options = concreteUnitOptions;
-          config = {
-            unit = mkDefault (makeUnit name config);
-          };
-        }));
-    };
-
-    systemd.user.paths = mkOption {
-      default = {};
-      type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
-      description = "Definition of systemd per-user path units.";
-    };
-
-    systemd.user.services = mkOption {
-      default = {};
-      type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
-      description = "Definition of systemd per-user service units.";
-    };
-
-    systemd.user.slices = mkOption {
-      default = {};
-      type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
-      description = "Definition of systemd per-user slice units.";
-    };
-
-    systemd.user.sockets = mkOption {
-      default = {};
-      type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
-      description = "Definition of systemd per-user socket units.";
-    };
-
-    systemd.user.targets = mkOption {
-      default = {};
-      type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
-      description = "Definition of systemd per-user target units.";
-    };
-
-    systemd.user.timers = mkOption {
-      default = {};
-      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
-      description = "Definition of systemd per-user timer units.";
-    };
-
     systemd.additionalUpstreamSystemUnits = mkOption {
       default = [ ];
       type = types.listOf types.str;
@@ -968,8 +473,6 @@ in
     in ({
       "systemd/system".source = generateUnits "system" enabledUnits enabledUpstreamSystemUnits upstreamSystemWants;
 
-      "systemd/user".source = generateUnits "user" cfg.user.units upstreamUserUnits [];
-
       "systemd/system.conf".text = ''
         [Manager]
         ${optionalString config.systemd.enableCgroupAccounting ''
@@ -995,76 +498,17 @@ in
         ${config.systemd.extraConfig}
       '';
 
-      "systemd/user.conf".text = ''
-        [Manager]
-        ${config.systemd.user.extraConfig}
-      '';
-
-      "systemd/journald.conf".text = ''
-        [Journal]
-        Storage=persistent
-        RateLimitInterval=${config.services.journald.rateLimitInterval}
-        RateLimitBurst=${toString config.services.journald.rateLimitBurst}
-        ${optionalString (config.services.journald.console != "") ''
-          ForwardToConsole=yes
-          TTYPath=${config.services.journald.console}
-        ''}
-        ${optionalString (config.services.journald.forwardToSyslog) ''
-          ForwardToSyslog=yes
-        ''}
-        ${config.services.journald.extraConfig}
-      '';
-
-      "systemd/coredump.conf".text =
-        ''
-          [Coredump]
-          ${config.systemd.coredump.extraConfig}
-        '';
-
-      "systemd/logind.conf".text = ''
-        [Login]
-        KillUserProcesses=${if config.services.logind.killUserProcesses then "yes" else "no"}
-        HandleLidSwitch=${config.services.logind.lidSwitch}
-        HandleLidSwitchDocked=${config.services.logind.lidSwitchDocked}
-        HandleLidSwitchExternalPower=${config.services.logind.lidSwitchExternalPower}
-        ${config.services.logind.extraConfig}
-      '';
-
       "systemd/sleep.conf".text = ''
         [Sleep]
         ${config.systemd.sleep.extraConfig}
       '';
 
-      # install provided sysctl snippets
-      "sysctl.d/50-coredump.conf".source = "${systemd}/example/sysctl.d/50-coredump.conf";
-      "sysctl.d/50-default.conf".source = "${systemd}/example/sysctl.d/50-default.conf";
-
-      "tmpfiles.d".source = (pkgs.symlinkJoin {
-        name = "tmpfiles.d";
-        paths = map (p: p + "/lib/tmpfiles.d") cfg.tmpfiles.packages;
-        postBuild = ''
-          for i in $(cat $pathsPath); do
-            (test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
-              echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
-              exit 1
-            )
-          done
-        '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
-          rm -f $out/${removePrefix "tmpfiles.d/" name}
-        '') config.system.build.etc.passthru.targets;
-      }) + "/*";
-
       "systemd/system-generators" = { source = hooks "generators" cfg.generators; };
       "systemd/system-shutdown" = { source = hooks "shutdown" cfg.shutdown; };
     });
 
     services.dbus.enable = true;
 
-    users.users.systemd-coredump = {
-      uid = config.ids.uids.systemd-coredump;
-      group = "systemd-coredump";
-    };
-    users.groups.systemd-coredump = {};
     users.users.systemd-network = {
       uid = config.ids.uids.systemd-network;
       group = "systemd-network";
@@ -1084,38 +528,6 @@ in
         unitConfig.X-StopOnReconfiguration = true;
       };
 
-    systemd.tmpfiles.packages = [
-      # Default tmpfiles rules provided by systemd
-      (pkgs.runCommand "systemd-default-tmpfiles" {} ''
-        mkdir -p $out/lib/tmpfiles.d
-        cd $out/lib/tmpfiles.d
-
-        # home.conf creates /srv (which we don't want), and /home, which
-        # is handled by NixOS anyway.
-        # ln -s "${systemd}/example/tmpfiles.d/home.conf"
-        ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf"
-        ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf"
-        ln -s "${systemd}/example/tmpfiles.d/systemd.conf"
-        ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf"
-        ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf"
-        ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf"
-        ln -s "${systemd}/example/tmpfiles.d/tmp.conf"
-        ln -s "${systemd}/example/tmpfiles.d/var.conf"
-        ln -s "${systemd}/example/tmpfiles.d/x11.conf"
-      '')
-      # User-specified tmpfiles rules
-      (pkgs.writeTextFile {
-        name = "nixos-tmpfiles.d";
-        destination = "/lib/tmpfiles.d/00-nixos.conf";
-        text = ''
-          # This file is created automatically and should not be modified.
-          # Please change the option ‘systemd.tmpfiles.rules’ instead.
-
-          ${concatStringsSep "\n" cfg.tmpfiles.rules}
-        '';
-      })
-    ];
-
     systemd.units =
          mapAttrs' (n: v: nameValuePair "${n}.path"    (pathToUnit    n v)) cfg.paths
       // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
@@ -1130,14 +542,6 @@ in
                    (v: let n = escapeSystemdPath v.where;
                        in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
 
-    systemd.user.units =
-         mapAttrs' (n: v: nameValuePair "${n}.path"    (pathToUnit    n v)) cfg.user.paths
-      // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services
-      // mapAttrs' (n: v: nameValuePair "${n}.slice"   (sliceToUnit   n v)) cfg.user.slices
-      // mapAttrs' (n: v: nameValuePair "${n}.socket"  (socketToUnit  n v)) cfg.user.sockets
-      // mapAttrs' (n: v: nameValuePair "${n}.target"  (targetToUnit  n v)) cfg.user.targets
-      // mapAttrs' (n: v: nameValuePair "${n}.timer"   (timerToUnit   n v)) cfg.user.timers;
-
     system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled
       [ "DEVTMPFS" "CGROUPS" "INOTIFY_USER" "SIGNALFD" "TIMERFD" "EPOLL" "NET"
         "SYSFS" "PROC_FS" "FHANDLE" "CRYPTO_USER_API_HASH" "CRYPTO_HMAC"
@@ -1145,11 +549,6 @@ in
         "TMPFS_XATTR" "SECCOMP"
       ];
 
-    users.groups.systemd-journal.gid = config.ids.gids.systemd-journal;
-    users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
-    users.users.systemd-journal-gateway.group = "systemd-journal-gateway";
-    users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
-
     # Generate timer units for all services that have a ‘startAt’ value.
     systemd.timers =
       mapAttrs (name: service:
@@ -1166,42 +565,14 @@ in
         })
         (filterAttrs (name: service: service.startAt != []) cfg.user.services);
 
-    systemd.sockets.systemd-journal-gatewayd.wantedBy =
-      optional config.services.journald.enableHttpGateway "sockets.target";
-
-    # Provide the systemd-user PAM service, required to run systemd
-    # user instances.
-    security.pam.services.systemd-user =
-      { # Ensure that pam_systemd gets included. This is special-cased
-        # in systemd to provide XDG_RUNTIME_DIR.
-        startSession = true;
-      };
-
     # Some overrides to upstream units.
     systemd.services."systemd-backlight@".restartIfChanged = false;
     systemd.services."systemd-fsck@".restartIfChanged = false;
     systemd.services."systemd-fsck@".path = [ config.system.path ];
-    systemd.services."user@".restartIfChanged = false;
-    systemd.services.systemd-journal-flush.restartIfChanged = false;
     systemd.services.systemd-random-seed.restartIfChanged = false;
     systemd.services.systemd-remount-fs.restartIfChanged = false;
     systemd.services.systemd-update-utmp.restartIfChanged = false;
-    systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
     systemd.services.systemd-udev-settle.restartIfChanged = false; # Causes long delays in nixos-rebuild
-    # Restarting systemd-logind breaks X11
-    # - upstream commit: https://cgit.freedesktop.org/xorg/xserver/commit/?id=dc48bd653c7e101
-    # - systemd announcement: https://github.com/systemd/systemd/blob/22043e4317ecd2bc7834b48a6d364de76bb26d91/NEWS#L103-L112
-    # - this might be addressed in the future by xorg
-    #systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
-    systemd.services.systemd-logind.restartIfChanged = false;
-    systemd.services.systemd-logind.stopIfChanged = false;
-    # The user-runtime-dir@ service is managed by systemd-logind we should not touch it or else we break the users' sessions.
-    systemd.services."user-runtime-dir@".stopIfChanged = false;
-    systemd.services."user-runtime-dir@".restartIfChanged = false;
-    systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
-    systemd.services.systemd-journald.stopIfChanged = false;
-    systemd.services."systemd-journald@".restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
-    systemd.services."systemd-journald@".stopIfChanged = false;
     systemd.targets.local-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.remote-fs.unitConfig.X-StopOnReconfiguration = true;
     systemd.targets.network-online.wantedBy = [ "multi-user.target" ];
@@ -1212,8 +583,6 @@ in
     systemd.services.systemd-remount-fs.unitConfig.ConditionVirtualization = "!container";
     systemd.services.systemd-random-seed.unitConfig.ConditionVirtualization = "!container";
 
-    boot.kernel.sysctl."kernel.core_pattern" = mkIf (!cfg.coredump.enable) "core";
-
     # Increase numeric PID range (set directly instead of copying a one-line file from systemd)
     # https://github.com/systemd/systemd/pull/12226
     boot.kernel.sysctl."kernel.pid_max" = mkIf pkgs.stdenv.is64bit (lib.mkDefault 4194304);
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix
new file mode 100644
index 000000000000..b6ee2cff1f9a
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/boot/systemd/coredump.nix
@@ -0,0 +1,57 @@
+{ config, lib, pkgs, utils, ... }:
+
+with lib;
+
+let
+  cfg = config.systemd.coredump;
+  systemd = config.systemd.package;
+in {
+  options = {
+    systemd.coredump.enable = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether core dumps should be processed by
+        <command>systemd-coredump</command>. If disabled, core dumps
+        appear in the current directory of the crashing process.
+      '';
+    };
+
+    systemd.coredump.extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      example = "Storage=journal";
+      description = ''
+        Extra config options for systemd-coredump. See coredump.conf(5) man page
+        for available options.
+      '';
+    };
+  };
+
+  config = {
+    systemd.additionalUpstreamSystemUnits = [
+      "systemd-coredump.socket"
+      "systemd-coredump@.service"
+    ];
+
+    environment.etc = {
+      "systemd/coredump.conf".text =
+      ''
+        [Coredump]
+        ${cfg.extraConfig}
+      '';
+
+      # install provided sysctl snippets
+      "sysctl.d/50-coredump.conf".source = "${systemd}/example/sysctl.d/50-coredump.conf";
+      "sysctl.d/50-default.conf".source = "${systemd}/example/sysctl.d/50-default.conf";
+    };
+
+    users.users.systemd-coredump = {
+      uid = config.ids.uids.systemd-coredump;
+      group = "systemd-coredump";
+    };
+    users.groups.systemd-coredump = {};
+
+    boot.kernel.sysctl."kernel.core_pattern" = mkIf (!cfg.enable) "core";
+  };
+}
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/journald.nix b/nixpkgs/nixos/modules/system/boot/systemd/journald.nix
new file mode 100644
index 000000000000..7e14c8ae4077
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/boot/systemd/journald.nix
@@ -0,0 +1,131 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.services.journald;
+in {
+  options = {
+    services.journald.console = mkOption {
+      default = "";
+      type = types.str;
+      description = "If non-empty, write log messages to the specified TTY device.";
+    };
+
+    services.journald.rateLimitInterval = mkOption {
+      default = "30s";
+      type = types.str;
+      description = ''
+        Configures the rate limiting interval that is applied to all
+        messages generated on the system. This rate limiting is applied
+        per-service, so that two services which log do not interfere with
+        each other's limit. The value may be specified in the following
+        units: s, min, h, ms, us. To turn off any kind of rate limiting,
+        set either value to 0.
+
+        See <option>services.journald.rateLimitBurst</option> for important
+        considerations when setting this value.
+      '';
+    };
+
+    services.journald.rateLimitBurst = mkOption {
+      default = 10000;
+      type = types.int;
+      description = ''
+        Configures the rate limiting burst limit (number of messages per
+        interval) that is applied to all messages generated on the system.
+        This rate limiting is applied per-service, so that two services
+        which log do not interfere with each other's limit.
+
+        Note that the effective rate limit is multiplied by a factor derived
+        from the available free disk space for the journal as described on
+        <link xlink:href="https://www.freedesktop.org/software/systemd/man/journald.conf.html">
+        journald.conf(5)</link>.
+
+        Note that the total amount of logs stored is limited by journald settings
+        such as <literal>SystemMaxUse</literal>, which defaults to a 4 GB cap.
+
+        It is thus recommended to compute what period of time that you will be
+        able to store logs for when an application logs at full burst rate.
+        With default settings for log lines that are 100 Bytes long, this can
+        amount to just a few hours.
+      '';
+    };
+
+    services.journald.extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      example = "Storage=volatile";
+      description = ''
+        Extra config options for systemd-journald. See man journald.conf
+        for available options.
+      '';
+    };
+
+    services.journald.enableHttpGateway = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Whether to enable the HTTP gateway to the journal.
+      '';
+    };
+
+    services.journald.forwardToSyslog = mkOption {
+      default = config.services.rsyslogd.enable || config.services.syslog-ng.enable;
+      defaultText = literalExpression "services.rsyslogd.enable || services.syslog-ng.enable";
+      type = types.bool;
+      description = ''
+        Whether to forward log messages to syslog.
+      '';
+    };
+  };
+
+  config = {
+    systemd.additionalUpstreamSystemUnits = [
+      "systemd-journald.socket"
+      "systemd-journald@.socket"
+      "systemd-journald-varlink@.socket"
+      "systemd-journald.service"
+      "systemd-journald@.service"
+      "systemd-journal-flush.service"
+      "systemd-journal-catalog-update.service"
+      ] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [
+      "systemd-journald-dev-log.socket"
+      "syslog.socket"
+      ] ++ optionals cfg.enableHttpGateway [
+      "systemd-journal-gatewayd.socket"
+      "systemd-journal-gatewayd.service"
+      ];
+
+    environment.etc = {
+      "systemd/journald.conf".text = ''
+        [Journal]
+        Storage=persistent
+        RateLimitInterval=${cfg.rateLimitInterval}
+        RateLimitBurst=${toString cfg.rateLimitBurst}
+        ${optionalString (cfg.console != "") ''
+          ForwardToConsole=yes
+          TTYPath=${cfg.console}
+        ''}
+        ${optionalString (cfg.forwardToSyslog) ''
+          ForwardToSyslog=yes
+        ''}
+        ${cfg.extraConfig}
+      '';
+    };
+
+    users.groups.systemd-journal.gid = config.ids.gids.systemd-journal;
+    users.users.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
+    users.users.systemd-journal-gateway.group = "systemd-journal-gateway";
+    users.groups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
+
+    systemd.sockets.systemd-journal-gatewayd.wantedBy =
+      optional cfg.enableHttpGateway "sockets.target";
+
+    systemd.services.systemd-journal-flush.restartIfChanged = false;
+    systemd.services.systemd-journald.restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
+    systemd.services.systemd-journald.stopIfChanged = false;
+    systemd.services."systemd-journald@".restartTriggers = [ config.environment.etc."systemd/journald.conf".source ];
+    systemd.services."systemd-journald@".stopIfChanged = false;
+  };
+}
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/logind.nix b/nixpkgs/nixos/modules/system/boot/systemd/logind.nix
new file mode 100644
index 000000000000..c1e6cfe61d04
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/boot/systemd/logind.nix
@@ -0,0 +1,114 @@
+{ config, lib, pkgs, utils, ... }:
+
+with lib;
+
+let
+  cfg = config.services.logind;
+
+  logindHandlerType = types.enum [
+    "ignore" "poweroff" "reboot" "halt" "kexec" "suspend"
+    "hibernate" "hybrid-sleep" "suspend-then-hibernate" "lock"
+  ];
+in
+{
+  options = {
+    services.logind.extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      example = "IdleAction=lock";
+      description = ''
+        Extra config options for systemd-logind. See
+        <link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html">
+        logind.conf(5)</link> for available options.
+      '';
+    };
+
+    services.logind.killUserProcesses = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Specifies whether the processes of a user should be killed
+        when the user logs out.  If true, the scope unit corresponding
+        to the session and all processes inside that scope will be
+        terminated.  If false, the scope is "abandoned" (see
+        <link xlink:href="https://www.freedesktop.org/software/systemd/man/systemd.scope.html#">
+        systemd.scope(5)</link>), and processes are not killed.
+        </para>
+
+        <para>
+        See <link xlink:href="https://www.freedesktop.org/software/systemd/man/logind.conf.html#KillUserProcesses=">logind.conf(5)</link>
+        for more details.
+      '';
+    };
+
+    services.logind.lidSwitch = mkOption {
+      default = "suspend";
+      example = "ignore";
+      type = logindHandlerType;
+
+      description = ''
+        Specifies what to be done when the laptop lid is closed.
+      '';
+    };
+
+    services.logind.lidSwitchDocked = mkOption {
+      default = "ignore";
+      example = "suspend";
+      type = logindHandlerType;
+
+      description = ''
+        Specifies what to be done when the laptop lid is closed
+        and another screen is added.
+      '';
+    };
+
+    services.logind.lidSwitchExternalPower = mkOption {
+      default = cfg.lidSwitch;
+      defaultText = literalExpression "services.logind.lidSwitch";
+      example = "ignore";
+      type = logindHandlerType;
+
+      description = ''
+        Specifies what to do when the laptop lid is closed and the system is
+        on external power. By default use the same action as specified in
+        services.logind.lidSwitch.
+      '';
+    };
+  };
+
+  config = {
+    systemd.additionalUpstreamSystemUnits = [
+      "systemd-logind.service"
+      "autovt@.service"
+      "systemd-user-sessions.service"
+      "dbus-org.freedesktop.import1.service"
+      "dbus-org.freedesktop.machine1.service"
+      "dbus-org.freedesktop.login1.service"
+      "user@.service"
+      "user-runtime-dir@.service"
+    ];
+
+    environment.etc = {
+      "systemd/logind.conf".text = ''
+        [Login]
+        KillUserProcesses=${if cfg.killUserProcesses then "yes" else "no"}
+        HandleLidSwitch=${cfg.lidSwitch}
+        HandleLidSwitchDocked=${cfg.lidSwitchDocked}
+        HandleLidSwitchExternalPower=${cfg.lidSwitchExternalPower}
+        ${cfg.extraConfig}
+      '';
+    };
+
+    # Restarting systemd-logind breaks X11
+    # - upstream commit: https://cgit.freedesktop.org/xorg/xserver/commit/?id=dc48bd653c7e101
+    # - systemd announcement: https://github.com/systemd/systemd/blob/22043e4317ecd2bc7834b48a6d364de76bb26d91/NEWS#L103-L112
+    # - this might be addressed in the future by xorg
+    #systemd.services.systemd-logind.restartTriggers = [ config.environment.etc."systemd/logind.conf".source ];
+    systemd.services.systemd-logind.restartIfChanged = false;
+    systemd.services.systemd-logind.stopIfChanged = false;
+
+    # The user-runtime-dir@ service is managed by systemd-logind we should not touch it or else we break the users' sessions.
+    systemd.services."user-runtime-dir@".stopIfChanged = false;
+    systemd.services."user-runtime-dir@".restartIfChanged = false;
+  };
+}
diff --git a/nixpkgs/nixos/modules/system/boot/systemd-nspawn.nix b/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix
index 0c6822319a5b..0c6822319a5b 100644
--- a/nixpkgs/nixos/modules/system/boot/systemd-nspawn.nix
+++ b/nixpkgs/nixos/modules/system/boot/systemd/nspawn.nix
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/tmpfiles.nix b/nixpkgs/nixos/modules/system/boot/systemd/tmpfiles.nix
new file mode 100644
index 000000000000..f626d66dcffe
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/boot/systemd/tmpfiles.nix
@@ -0,0 +1,106 @@
+{ config, lib, pkgs, utils, ... }:
+
+with lib;
+
+let
+  cfg = config.systemd.tmpfiles;
+  systemd = config.systemd.package;
+in
+{
+  options = {
+    systemd.tmpfiles.rules = mkOption {
+      type = types.listOf types.str;
+      default = [];
+      example = [ "d /tmp 1777 root root 10d" ];
+      description = ''
+        Rules for creation, deletion and cleaning of volatile and temporary files
+        automatically. See
+        <citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+        for the exact format.
+      '';
+    };
+
+    systemd.tmpfiles.packages = mkOption {
+      type = types.listOf types.package;
+      default = [];
+      example = literalExpression "[ pkgs.lvm2 ]";
+      apply = map getLib;
+      description = ''
+        List of packages containing <command>systemd-tmpfiles</command> rules.
+
+        All files ending in .conf found in
+        <filename><replaceable>pkg</replaceable>/lib/tmpfiles.d</filename>
+        will be included.
+        If this folder does not exist or does not contain any files an error will be returned instead.
+
+        If a <filename>lib</filename> output is available, rules are searched there and only there.
+        If there is no <filename>lib</filename> output it will fall back to <filename>out</filename>
+        and if that does not exist either, the default output will be used.
+      '';
+    };
+  };
+
+  config = {
+    systemd.additionalUpstreamSystemUnits = [
+      "systemd-tmpfiles-clean.service"
+      "systemd-tmpfiles-clean.timer"
+      "systemd-tmpfiles-setup.service"
+      "systemd-tmpfiles-setup-dev.service"
+    ];
+
+    systemd.additionalUpstreamUserUnits = [
+      "systemd-tmpfiles-clean.service"
+      "systemd-tmpfiles-clean.timer"
+      "systemd-tmpfiles-setup.service"
+    ];
+
+    environment.etc = {
+      "tmpfiles.d".source = (pkgs.symlinkJoin {
+        name = "tmpfiles.d";
+        paths = map (p: p + "/lib/tmpfiles.d") cfg.packages;
+        postBuild = ''
+          for i in $(cat $pathsPath); do
+            (test -d "$i" && test $(ls "$i"/*.conf | wc -l) -ge 1) || (
+              echo "ERROR: The path '$i' from systemd.tmpfiles.packages contains no *.conf files."
+              exit 1
+            )
+          done
+        '' + concatMapStrings (name: optionalString (hasPrefix "tmpfiles.d/" name) ''
+          rm -f $out/${removePrefix "tmpfiles.d/" name}
+        '') config.system.build.etc.passthru.targets;
+      }) + "/*";
+    };
+
+    systemd.tmpfiles.packages = [
+      # Default tmpfiles rules provided by systemd
+      (pkgs.runCommand "systemd-default-tmpfiles" {} ''
+        mkdir -p $out/lib/tmpfiles.d
+        cd $out/lib/tmpfiles.d
+
+        # home.conf creates /srv (which we don't want), and /home, which
+        # is handled by NixOS anyway.
+        # ln -s "${systemd}/example/tmpfiles.d/home.conf"
+        ln -s "${systemd}/example/tmpfiles.d/journal-nocow.conf"
+        ln -s "${systemd}/example/tmpfiles.d/static-nodes-permissions.conf"
+        ln -s "${systemd}/example/tmpfiles.d/systemd.conf"
+        ln -s "${systemd}/example/tmpfiles.d/systemd-nologin.conf"
+        ln -s "${systemd}/example/tmpfiles.d/systemd-nspawn.conf"
+        ln -s "${systemd}/example/tmpfiles.d/systemd-tmp.conf"
+        ln -s "${systemd}/example/tmpfiles.d/tmp.conf"
+        ln -s "${systemd}/example/tmpfiles.d/var.conf"
+        ln -s "${systemd}/example/tmpfiles.d/x11.conf"
+      '')
+      # User-specified tmpfiles rules
+      (pkgs.writeTextFile {
+        name = "nixos-tmpfiles.d";
+        destination = "/lib/tmpfiles.d/00-nixos.conf";
+        text = ''
+          # This file is created automatically and should not be modified.
+          # Please change the option ‘systemd.tmpfiles.rules’ instead.
+
+          ${concatStringsSep "\n" cfg.rules}
+        '';
+      })
+    ];
+  };
+}
diff --git a/nixpkgs/nixos/modules/system/boot/systemd/user.nix b/nixpkgs/nixos/modules/system/boot/systemd/user.nix
new file mode 100644
index 000000000000..e30f83f3457f
--- /dev/null
+++ b/nixpkgs/nixos/modules/system/boot/systemd/user.nix
@@ -0,0 +1,158 @@
+{ config, lib, pkgs, utils, ... }:
+with utils;
+with systemdUtils.unitOptions;
+with lib;
+
+let
+  cfg = config.systemd.user;
+
+  systemd = config.systemd.package;
+
+  inherit
+    (systemdUtils.lib)
+    makeUnit
+    generateUnits
+    makeJobScript
+    unitConfig
+    serviceConfig
+    commonUnitText
+    targetToUnit
+    serviceToUnit
+    socketToUnit
+    timerToUnit
+    pathToUnit;
+
+  upstreamUserUnits = [
+    "app.slice"
+    "background.slice"
+    "basic.target"
+    "bluetooth.target"
+    "default.target"
+    "exit.target"
+    "graphical-session-pre.target"
+    "graphical-session.target"
+    "paths.target"
+    "printer.target"
+    "session.slice"
+    "shutdown.target"
+    "smartcard.target"
+    "sockets.target"
+    "sound.target"
+    "systemd-exit.service"
+    "timers.target"
+    "xdg-desktop-autostart.target"
+  ] ++ config.systemd.additionalUpstreamUserUnits;
+in {
+  options = {
+    systemd.user.extraConfig = mkOption {
+      default = "";
+      type = types.lines;
+      example = "DefaultCPUAccounting=yes";
+      description = ''
+        Extra config options for systemd user instances. See man systemd-user.conf for
+        available options.
+      '';
+    };
+
+    systemd.user.units = mkOption {
+      description = "Definition of systemd per-user units.";
+      default = {};
+      type = with types; attrsOf (submodule (
+        { name, config, ... }:
+        { options = concreteUnitOptions;
+          config = {
+            unit = mkDefault (makeUnit name config);
+          };
+        }));
+    };
+
+    systemd.user.paths = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
+      description = "Definition of systemd per-user path units.";
+    };
+
+    systemd.user.services = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
+      description = "Definition of systemd per-user service units.";
+    };
+
+    systemd.user.slices = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
+      description = "Definition of systemd per-user slice units.";
+    };
+
+    systemd.user.sockets = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
+      description = "Definition of systemd per-user socket units.";
+    };
+
+    systemd.user.targets = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
+      description = "Definition of systemd per-user target units.";
+    };
+
+    systemd.user.timers = mkOption {
+      default = {};
+      type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
+      description = "Definition of systemd per-user timer units.";
+    };
+
+    systemd.additionalUpstreamUserUnits = mkOption {
+      default = [];
+      type = types.listOf types.str;
+      example = [];
+      description = ''
+        Additional units shipped with systemd that should be enabled for per-user systemd instances.
+      '';
+      internal = true;
+    };
+  };
+
+  config = {
+    systemd.additionalUpstreamSystemUnits = [
+      "user.slice"
+    ];
+
+    environment.etc = {
+      "systemd/user".source = generateUnits "user" cfg.units upstreamUserUnits [];
+
+      "systemd/user.conf".text = ''
+        [Manager]
+        ${cfg.extraConfig}
+      '';
+    };
+
+    systemd.user.units =
+         mapAttrs' (n: v: nameValuePair "${n}.path"    (pathToUnit    n v)) cfg.paths
+      // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
+      // mapAttrs' (n: v: nameValuePair "${n}.slice"   (sliceToUnit   n v)) cfg.slices
+      // mapAttrs' (n: v: nameValuePair "${n}.socket"  (socketToUnit  n v)) cfg.sockets
+      // mapAttrs' (n: v: nameValuePair "${n}.target"  (targetToUnit  n v)) cfg.targets
+      // mapAttrs' (n: v: nameValuePair "${n}.timer"   (timerToUnit   n v)) cfg.timers;
+
+    # Generate timer units for all services that have a ‘startAt’ value.
+    systemd.user.timers =
+      mapAttrs (name: service: {
+        wantedBy = ["timers.target"];
+        timerConfig.OnCalendar = service.startAt;
+      })
+      (filterAttrs (name: service: service.startAt != []) cfg.services);
+
+    # Provide the systemd-user PAM service, required to run systemd
+    # user instances.
+    security.pam.services.systemd-user =
+      { # Ensure that pam_systemd gets included. This is special-cased
+        # in systemd to provide XDG_RUNTIME_DIR.
+        startSession = true;
+      };
+
+    # Some overrides to upstream units.
+    systemd.services."user@".restartIfChanged = false;
+    systemd.services.systemd-user-sessions.restartIfChanged = false; # Restart kills all active sessions.
+  };
+}