From 179acfb664ed06519ac515eada7bbef677cbee87 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 17 Apr 2014 18:52:31 +0200 Subject: [PATCH] Allow upstream systemd units to be extended If you define a unit, and either systemd or a package in systemd.packages already provides that unit, then we now generate a file /etc/systemd/system/.d/overrides.conf. This makes it possible to use upstream units, while allowing them to be customised from the NixOS configuration. For instance, the module nix-daemon.nix now uses the units provided by the Nix package. And all unit definitions that duplicated upstream systemd units are finally gone. This makes the baseUnit option unnecessary, so I've removed it. --- nixos/modules/config/sysctl.nix | 13 +--- nixos/modules/services/hardware/udev.nix | 3 +- nixos/modules/services/misc/nix-daemon.nix | 20 ++---- nixos/modules/services/ttys/agetty.nix | 9 +-- .../activation/switch-to-configuration.pl | 10 ++- nixos/modules/system/boot/kernel.nix | 33 +++------- .../system/boot/systemd-unit-options.nix | 6 -- nixos/modules/system/boot/systemd.nix | 61 ++++++++++++++----- nixos/modules/tasks/kbd.nix | 14 +---- 9 files changed, 72 insertions(+), 97 deletions(-) diff --git a/nixos/modules/config/sysctl.nix b/nixos/modules/config/sysctl.nix index b4cd22caa79..54236021919 100644 --- a/nixos/modules/config/sysctl.nix +++ b/nixos/modules/config/sysctl.nix @@ -45,19 +45,8 @@ in ) config.boot.kernel.sysctl); systemd.services.systemd-sysctl = - { description = "Apply Kernel Variables"; - before = [ "sysinit.target" "shutdown.target" ]; - wantedBy = [ "sysinit.target" "multi-user.target" ]; + { wantedBy = [ "multi-user.target" ]; restartTriggers = [ config.environment.etc."sysctl.d/nixos.conf".source ]; - unitConfig = { - DefaultDependencies = false; # needed to prevent a cycle - ConditionPathIsReadWrite = "/proc/sys/"; # prevent systemd-sysctl in containers - }; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "${config.systemd.package}/lib/systemd/systemd-sysctl"; - }; }; # Enable hardlink and symlink restrictions. See diff --git a/nixos/modules/services/hardware/udev.nix b/nixos/modules/services/hardware/udev.nix index 875de55b6c6..169a2e88414 100644 --- a/nixos/modules/services/hardware/udev.nix +++ b/nixos/modules/services/hardware/udev.nix @@ -244,8 +244,7 @@ in ''; systemd.services.systemd-udevd = - { baseUnit = "${config.systemd.package}/example/systemd/system/systemd-udevd.service"; - environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules"; + { environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules"; }; }; diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix index 1516736dc2e..e6528777c32 100644 --- a/nixos/modules/services/misc/nix-daemon.nix +++ b/nixos/modules/services/misc/nix-daemon.nix @@ -275,28 +275,16 @@ in ) cfg.buildMachines; }; - systemd.sockets."nix-daemon" = - { description = "Nix Daemon Socket"; - wantedBy = [ "sockets.target" ]; - before = [ "multi-user.target" ]; - unitConfig.ConditionPathIsReadWrite = "/nix/var/nix/daemon-socket/"; - socketConfig.ListenStream = "/nix/var/nix/daemon-socket/socket"; - }; + systemd.packages = [ nix ]; - systemd.services."nix-daemon" = - { description = "Nix Daemon"; - - path = [ nix pkgs.openssl pkgs.utillinux pkgs.openssh ] + systemd.services.nix-daemon = + { path = [ nix pkgs.openssl pkgs.utillinux pkgs.openssh ] ++ optionals cfg.distributedBuilds [ pkgs.gzip ]; environment = cfg.envVars // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-bundle.crt"; }; - unitConfig.ConditionPathIsReadWrite = "/nix/var/nix/daemon-socket/"; - serviceConfig = - { ExecStart = "@${nix}/bin/nix-daemon nix-daemon --daemon"; - KillMode = "process"; - Nice = cfg.daemonNiceLevel; + { Nice = cfg.daemonNiceLevel; IOSchedulingPriority = cfg.daemonIONiceLevel; LimitNOFILE = 4096; }; diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix index c4571ef856f..df21ebbd974 100644 --- a/nixos/modules/services/ttys/agetty.nix +++ b/nixos/modules/services/ttys/agetty.nix @@ -49,22 +49,19 @@ with lib; config = { systemd.services."getty@" = - { baseUnit = "${config.systemd.package}/example/systemd/system/getty@.service"; - serviceConfig.ExecStart = "@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login --keep-baud %I 115200,38400,9600 $TERM"; + { serviceConfig.ExecStart = "@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login --keep-baud %I 115200,38400,9600 $TERM"; restartIfChanged = false; }; systemd.services."serial-getty@" = - { baseUnit = "${config.systemd.package}/example/systemd/system/serial-getty@.service"; - serviceConfig.ExecStart = + { serviceConfig.ExecStart = let speeds = concatStringsSep "," (map toString config.services.mingetty.serialSpeed); in "@${pkgs.utillinux}/sbin/agetty agetty --login-program ${pkgs.shadow}/bin/login %I ${speeds} $TERM"; restartIfChanged = false; }; systemd.services."container-getty@" = - { baseUnit = "${config.systemd.package}/example/systemd/system/container-getty@.service"; - unitConfig.ConditionPathExists = "/dev/pts/%I"; # Work around being respawned when "machinectl login" exits. + { unitConfig.ConditionPathExists = "/dev/pts/%I"; # Work around being respawned when "machinectl login" exits. serviceConfig.ExecStart = "@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login --keep-baud pts/%I 115200,38400,9600 $TERM"; restartIfChanged = false; }; diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl index fd2b5b7950d..13bd0b60f82 100644 --- a/nixos/modules/system/activation/switch-to-configuration.pl +++ b/nixos/modules/system/activation/switch-to-configuration.pl @@ -115,6 +115,14 @@ sub boolIsTrue { return $s eq "yes" || $s eq "true"; } +# As a fingerprint for determining whether a unit has changed, we use +# its absolute path. If it has an override file, we append *its* +# absolute path as well. +sub fingerprintUnit { + my ($s) = @_; + return abs_path($s) . (-f "${s}.d/overrides.conf" ? " " . abs_path "${s}.d/overrides.conf" : ""); +} + # Stop all services that no longer exist or have changed in the new # configuration. my (@unitsToStop, @unitsToSkip); @@ -166,7 +174,7 @@ while (my ($unit, $state) = each %{$activePrev}) { } } - elsif (abs_path($prevUnitFile) ne abs_path($newUnitFile)) { + elsif (fingerprintUnit($prevUnitFile) ne fingerprintUnit($newUnitFile)) { if ($unit eq "sysinit.target" || $unit eq "basic.target" || $unit eq "multi-user.target" || $unit eq "graphical.target") { # Do nothing. These cannot be restarted directly. } elsif ($unit =~ /\.mount$/) { diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix index 2e036fafae6..5e5b2abbb9c 100644 --- a/nixos/modules/system/boot/kernel.nix +++ b/nixos/modules/system/boot/kernel.nix @@ -147,12 +147,6 @@ in config = mkIf (!config.boot.isContainer) { - systemd.services.kmod-static-nodes = - { wantedBy = [ "sysinit.target" ]; - baseUnit = "${config.systemd.package}/example/systemd/system/kmod-static-nodes.service"; - environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules"; - }; - system.build = { inherit kernel; }; system.modulesTree = [ kernel ] ++ config.boot.extraModulePackages; @@ -224,37 +218,26 @@ in # Create /etc/modules-load.d/nixos.conf, which is read by # systemd-modules-load.service to load required kernel modules. - # FIXME: ensure that systemd-modules-load.service is restarted if - # this file changes. environment.etc = singleton { target = "modules-load.d/nixos.conf"; source = kernelModulesConf; }; - # Sigh. This overrides systemd's systemd-modules-load.service - # just so we can set a restart trigger. Also make - # multi-user.target pull it in so that it gets started if it - # failed earlier. systemd.services."systemd-modules-load" = - { description = "Load Kernel Modules"; - wantedBy = [ "sysinit.target" "multi-user.target" ]; - before = [ "sysinit.target" "shutdown.target" ]; - conflicts = [ "shutdown.target" ]; - unitConfig = - { DefaultDependencies = false; - ConditionCapability = "CAP_SYS_MODULE"; - }; + { wantedBy = [ "multi-user.target" ]; + restartTriggers = [ kernelModulesConf ]; + environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules"; serviceConfig = - { Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "${config.systemd.package}/lib/systemd/systemd-modules-load"; - # Ignore failed module loads. Typically some of the + { # Ignore failed module loads. Typically some of the # modules in ‘boot.kernelModules’ are "nice to have but # not required" (e.g. acpi-cpufreq), so we don't want to # barf on those. SuccessExitStatus = "0 1"; }; - restartTriggers = [ kernelModulesConf ]; + }; + + systemd.services.kmod-static-nodes = + { wantedBy = [ "sysinit.target" ]; environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules"; }; diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix index 20ea0ba874d..a16263b390d 100644 --- a/nixos/modules/system/boot/systemd-unit-options.nix +++ b/nixos/modules/system/boot/systemd-unit-options.nix @@ -41,12 +41,6 @@ in rec { ''; }; - baseUnit = mkOption { - type = types.nullOr types.path; - default = null; - description = "Path to an upstream unit file on which the NixOS unit configuration will be based."; - }; - description = mkOption { default = ""; type = types.str; diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix index 5d144a3642b..ee809a8ec44 100644 --- a/nixos/modules/system/boot/systemd.nix +++ b/nixos/modules/system/boot/systemd.nix @@ -31,7 +31,6 @@ let "sockets.target" "graphical.target" "multi-user.target" - "getty.target" "network.target" "network-online.target" "nss-lookup.target" @@ -50,10 +49,17 @@ let # Udev. "systemd-udevd-control.socket" "systemd-udevd-kernel.socket" - #"systemd-udevd.service" + "systemd-udevd.service" "systemd-udev-settle.service" "systemd-udev-trigger.service" + # Consoles. + "getty.target" + "getty@.service" + "serial-getty@.service" + "container-getty@.service" + "systemd-vconsole-setup.service" + # Hardware (started by udev when a relevant device is plugged in). "sound.target" "bluetooth.target" @@ -80,7 +86,8 @@ let "systemd-initctl.service" # Kernel module loading. - #"systemd-modules-load.service" + "systemd-modules-load.service" + "kmod-static-nodes.service" # Filesystems. "systemd-fsck@.service" @@ -146,6 +153,9 @@ let "systemd-tmpfiles-clean.timer" "systemd-tmpfiles-setup.service" "systemd-tmpfiles-setup-dev.service" + + # Misc. + "systemd-sysctl.service" ] ++ optionals cfg.enableEmergencyMode [ @@ -198,7 +208,7 @@ let serviceConfig = { name, config, ... }: { config = mkMerge - [ (mkIf (config.baseUnit == null) { # Default path for systemd services. Should be quite minimal. + [ { # Default path for systemd services. Should be quite minimal. path = [ pkgs.coreutils pkgs.findutils @@ -207,7 +217,7 @@ let systemd ]; environment.PATH = config.path; - }) + } (mkIf (config.preStart != "") { serviceConfig.ExecStartPre = makeJobScript "${name}-pre-start" '' #! ${pkgs.stdenv.shell} -e @@ -275,10 +285,7 @@ let (if isList value then value else [value])) as)); - commonUnitText = def: - optionalString (def.baseUnit != null) '' - .include ${def.baseUnit} - '' + '' + commonUnitText = def: '' [Unit] ${attrsToSection def.unitConfig} ''; @@ -358,6 +365,8 @@ let units = pkgs.runCommand "units" { preferLocalBuild = true; } '' mkdir -p $out + + # Copy the upstream systemd units we're interested in. for i in ${toString upstreamUnits}; do fn=${systemd}/example/systemd/system/$i if ! [ -e $fn ]; then echo "missing $fn"; false; fi @@ -368,6 +377,8 @@ let fi done + # Copy .wants links, but only those that point to units that + # we're interested in. for i in ${toString upstreamWants}; do fn=${systemd}/example/systemd/system/$i if ! [ -e $fn ]; then echo "missing $fn"; false; fi @@ -376,18 +387,35 @@ let for i in $fn/*; do y=$x/$(basename $i) cp -pd $i $y - if ! [ -e $y ]; then rm -v $y; fi + if ! [ -e $y ]; then rm $y; fi done done - for i in ${toString (mapAttrsToList (n: v: v.unit) cfg.units)}; do - ln -fs $i/* $out/ - done - + # Symlink all units provided listed in systemd.packages. for i in ${toString cfg.packages}; do - ln -s $i/etc/systemd/system/* $out/ + ln -s $i/etc/systemd/system/* $i/lib/systemd/system/* $out/ done + # Symlink all units defined by systemd.units. If these are also + # provided by systemd or systemd.packages, then add them as + # .d/overrides.conf, which makes them extend the + # upstream unit. + for i in ${toString (mapAttrsToList (n: v: v.unit) cfg.units)}; do + fn=$(basename $i/*) + if [ -e $out/$fn ]; then + if [ "$(readlink -f $i/$fn)" = /dev/null ]; then + ln -sfn /dev/null $out/$fn + else + mkdir $out/$fn.d + ln -s $i/$fn $out/$fn.d/overrides.conf + fi + else + ln -fs $i/$fn $out/ + fi + done + + # Created .wants and .requires symlinks from the wantedBy and + # requiredBy options. ${concatStrings (mapAttrsToList (name: unit: concatMapStrings (name2: '' mkdir -p $out/'${name2}.wants' @@ -400,6 +428,7 @@ let ln -sfn '../${name}' $out/'${name2}.requires'/ '') unit.requiredBy) cfg.units)} + # Stupid misc. symlinks. ln -s ${cfg.defaultUnit} $out/default.target ln -s rescue.target $out/kbrequest.target @@ -411,7 +440,7 @@ let ../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/ ${ optionalString config.services.journald.enableHttpGateway '' - ln -s ../systemd-journal-gatewayd.service $out/multi-user-target.wants/ + ln -s ../systemd-journal-gatewayd.service $out/multi-user-target.wants/ ''} ''; # */ diff --git a/nixos/modules/tasks/kbd.nix b/nixos/modules/tasks/kbd.nix index df4737305da..03c42404e5d 100644 --- a/nixos/modules/tasks/kbd.nix +++ b/nixos/modules/tasks/kbd.nix @@ -52,19 +52,7 @@ in # /dev/tty0 to prevent putting the X server in non-raw mode, and # it has a restart trigger. systemd.services."systemd-vconsole-setup" = - { description = "Setup Virtual Console"; - wantedBy = [ "sysinit.target" "multi-user.target" ]; - before = [ "sysinit.target" "shutdown.target" ]; - conflicts = [ "shutdown.target" ]; - unitConfig = - { DefaultDependencies = "no"; - ConditionPathExists = "/dev/tty1"; - }; - serviceConfig = - { Type = "oneshot"; - RemainAfterExit = true; - ExecStart = "${config.systemd.package}/lib/systemd/systemd-vconsole-setup /dev/tty1"; - }; + { wantedBy = [ "multi-user.target" ]; restartTriggers = [ vconsoleConf ]; };