Merge branch 'master' into dbus-switch-to-configuration

Conflicts:
	nixos/modules/system/activation/switch-to-configuration.pl
This commit is contained in:
Oliver Charles
2014-04-20 19:17:05 +01:00
2724 changed files with 61040 additions and 23079 deletions
@@ -1,7 +1,7 @@
# generate the script used to activate the configuration.
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
+2 -2
View File
@@ -1,6 +1,6 @@
{pkgs, ...}:
{ lib, ... }:
with pkgs.lib;
with lib;
{
boot.loader.grub.device = mkOverride 0 "nodev";
@@ -27,7 +27,10 @@ EOF
exit 1;
}
die "This is not a NixOS installation (/etc/NIXOS is missing)!\n" unless -f "/etc/NIXOS";
# 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;
openlog("nixos", "", LOG_USER);
@@ -96,12 +99,18 @@ sub parseFstab {
sub parseUnit {
my ($filename) = @_;
my $info = {};
foreach my $line (read_file($filename)) {
parseKeyValues($info, read_file($filename));
parseKeyValues($info, read_file("${filename}.d/overrides.conf")) if -f "${filename}.d/overrides.conf";
return $info;
}
sub parseKeyValues {
my $info = shift;
foreach my $line (@_) {
# FIXME: not quite correct.
$line =~ /^([^=]+)=(.*)$/ or next;
$info->{$1} = $2;
}
return $info;
}
sub boolIsTrue {
@@ -109,6 +118,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);
@@ -125,7 +142,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
$baseName =~ s/\.[a-z]*$//;
if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
if (! -e $newUnitFile) {
if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
push @unitsToStop, $unit;
}
@@ -160,7 +177,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$/) {
@@ -170,7 +187,10 @@ while (my ($unit, $state) = each %{$activePrev}) {
# FIXME: do something?
} else {
my $unitInfo = parseUnit($newUnitFile);
if (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) {
if (boolIsTrue($unitInfo->{'X-ReloadIfChanged'} // "no")) {
write_file($reloadListFile, { append => 1 }, "$unit\n");
}
elsif (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) {
push @unitsToSkip, $unit;
} else {
# If this unit is socket-activated, then stop the
@@ -319,7 +339,7 @@ if (scalar @restart > 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 @start = unique("default.target", "timers.target", split('\n', read_file($startListFile, err_mode => 'quiet') // ""));
my @start = unique("default.target", "timers.target", "sockets.target", split('\n', read_file($startListFile, err_mode => 'quiet') // ""));
print STDERR "starting the following units: ", join(", ", sort(@start)), "\n";
$systemdManager->StartUnit($_, "replace") for @start;
unlink($startListFile);
@@ -1,6 +1,6 @@
{ config, pkgs, modules, baseModules, ... }:
{ config, lib, pkgs, modules, baseModules, ... }:
with pkgs.lib;
with lib;
let
@@ -11,7 +11,7 @@ let
# you can provide an easy way to boot the same configuration
# as you use, but with another kernel
# !!! fix this
cloner = inheritParent: list: with pkgs.lib;
cloner = inheritParent: list:
map (childConfig:
(import ../../../lib/eval-config.nix {
inherit baseModules;
@@ -68,6 +68,7 @@ let
echo -n "$configurationName" > $out/configuration-name
echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
echo -n "$nixosVersion" > $out/nixos-version
echo -n "$system" > $out/system
mkdir $out/fine-tune
childCount=0
+14 -22
View File
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
@@ -159,7 +159,7 @@ in
boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
boot.kernelModules = [ "loop" ];
boot.kernelModules = [ "loop" "configs" ];
boot.initrd.availableKernelModules =
[ # Note: most of these (especially the SATA/PATA modules)
@@ -203,6 +203,9 @@ in
# To wait for SCSI devices to appear.
"scsi_wait_scan"
# Needed by the stage 2 init script.
"rtc_cmos"
];
boot.initrd.kernelModules =
@@ -215,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 =
{ environment.MODULE_DIR = "/run/booted-system/kernel-modules/lib/modules";
};
lib.kernelConfig = {
+2 -2
View File
@@ -1,6 +1,6 @@
{ pkgs, ... }:
{ lib, ... }:
with pkgs.lib;
with lib;
{
options.boot.loader.efi = {
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
@@ -133,11 +133,8 @@ in
chainloader (hd0,1)+1
# GRUB 2 example
menuentry "Windows7" {
title Windows7
insmod ntfs
set root='(hd1,1)'
chainloader +1
menuentry "Windows 7" {
chainloader (hd0,4)+1
}
'';
description = ''
@@ -1,33 +1,87 @@
# This module adds Memtest86+ to the GRUB boot menu.
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
memtest86 = pkgs.memtest86plus;
cfg = config.boot.loader.grub.memtest86;
in
{
options = {
boot.loader.grub.memtest86 = mkOption {
default = false;
type = types.bool;
description = ''
Make Memtest86+, a memory testing program, available from the
GRUB boot menu.
'';
boot.loader.grub.memtest86 = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Make Memtest86+, a memory testing program, available from the
GRUB boot menu.
'';
};
params = mkOption {
default = [];
example = [ "console=ttyS0,115200" ];
type = types.listOf types.str;
description = ''
Parameters added to the Memtest86+ command line. As of memtest86+ 5.01
the following list of (apparently undocumented) parameters are
accepted:
<itemizedlist>
<listitem>
<para><literal>console=...</literal>, set up a serial console.
Examples:
<literal>console=ttyS0</literal>,
<literal>console=ttyS0,9600</literal> or
<literal>console=ttyS0,115200n8</literal>.</para>
</listitem>
<listitem>
<para><literal>btrace</literal>, enable boot trace.</para>
</listitem>
<listitem>
<para><literal>maxcpus=N</literal>, limit number of CPUs.</para>
</listitem>
<listitem>
<para><literal>onepass</literal>, run one pass and exit if there
are no errors.</para>
</listitem>
<listitem>
<para><literal>tstlist=...</literal>, list of tests to run.
Example: <literal>0,1,2</literal>.</para>
</listitem>
<listitem>
<para><literal>cpumask=...</literal>, set a CPU mask, to select CPUs
to use for testing.</para>
</listitem>
</itemizedlist>
This list of command line options was obtained by reading the
Memtest86+ source code.
'';
};
};
};
config = mkIf config.boot.loader.grub.memtest86 {
config = mkIf cfg.enable {
boot.loader.grub.extraEntries =
if config.boot.loader.grub.version == 2 then
''
menuentry "Memtest86+" {
linux16 @bootRoot@/memtest.bin
linux16 @bootRoot@/memtest.bin ${toString cfg.params}
}
''
else
@@ -9,7 +9,6 @@ import tempfile
import errno
def copy_if_not_exists(source, dest):
known_paths.append(dest)
if not os.path.exists(dest):
shutil.copyfile(source, dest)
@@ -38,12 +37,13 @@ def write_loader_conf(generation):
print >> f, "default nixos-generation-%d" % (generation)
os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
def copy_from_profile(generation, name):
def copy_from_profile(generation, name, dry_run=False):
store_file_path = os.readlink("%s/%s" % (system_dir(generation), name))
suffix = os.path.basename(store_file_path)
store_dir = os.path.basename(os.path.dirname(store_file_path))
efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
if not dry_run:
copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
return efi_file_path
def add_entry(generation):
@@ -72,6 +72,10 @@ def get_generations(profile):
def remove_old_entries(gens):
slice_start = len("@efiSysMountPoint@/loader/entries/nixos-generation-")
slice_end = -1 * len(".conf")
known_paths = []
for gen in gens:
known_paths.append(copy_from_profile(gen, "kernel", True))
known_paths.append(copy_from_profile(gen, "initrd", True))
for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos-generation-[1-9]*.conf"):
try:
gen = int(path[slice_start:slice_end])
@@ -94,7 +98,6 @@ if os.getenv("NIXOS_INSTALL_GRUB") == "1":
else:
subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "--no-variables", "install"])
known_paths = []
mkdir_p("@efiSysMountPoint@/efi/nixos")
mkdir_p("@efiSysMountPoint@/loader/entries")
try:
@@ -106,9 +109,8 @@ except IOError as e:
machine_id = None
gens = get_generations("system")
remove_old_entries(gens)
for gen in gens:
add_entry(gen)
if os.readlink(system_dir(gen)) == args.default_config:
write_loader_conf(gen)
remove_old_entries(gens)
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
cfg = config.boot.loader.gummiboot;
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
+294 -11
View File
@@ -1,11 +1,11 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
luks = config.boot.initrd.luks;
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, ... }: ''
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, yubikey, ... }: ''
# Wait for luksRoot to appear, e.g. if on a usb drive.
# XXX: copied and adapted from stage-1-init.sh - should be
# available as a function.
@@ -31,9 +31,161 @@ let
fi
''}
open_normally() {
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
}
${optionalString (luks.yubikeySupport && (yubikey != null)) ''
rbtohex() {
( od -An -vtx1 | tr -d ' \n' )
}
hextorb() {
( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf )
}
open_yubikey() {
# Make all of these local to this function
# to prevent their values being leaked
local salt
local iterations
local k_user
local challenge
local response
local k_luks
local opened
local new_salt
local new_iterations
local new_challenge
local new_response
local new_k_luks
mkdir -p ${yubikey.storage.mountPoint}
mount -t ${yubikey.storage.fsType} ${toString yubikey.storage.device} ${yubikey.storage.mountPoint}
salt="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
iterations="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
for try in $(seq 3); do
${optionalString yubikey.twoFactor ''
echo -n "Enter two-factor passphrase: "
read -s k_user
echo
''}
if [ ! -z "$k_user" ]; then
k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
else
k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
fi
echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
if [ $? == "0" ]; then
opened=true
break
else
opened=false
echo "Authentication failed!"
fi
done
if [ "$opened" == false ]; then
umount ${yubikey.storage.mountPoint}
echo "Maximum authentication errors reached"
exit 1
fi
echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
for i in $(seq ${toString yubikey.saltLength}); do
byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
new_salt="$new_salt$byte";
echo -n .
done;
echo "ok"
new_iterations="$iterations"
${optionalString (yubikey.iterationStep > 0) ''
new_iterations="$(($new_iterations + ${toString yubikey.iterationStep}))"
''}
new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
new_response="$(ykchalresp -${toString yubikey.slot} -x $new_challenge 2>/dev/null)"
if [ ! -z "$k_user" ]; then
new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
else
new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
fi
mkdir -p ${yubikey.ramfsMountPoint}
# A ramfs is used here to ensure that the file used to update
# the key slot with cryptsetup will never get swapped out.
# Warning: Do NOT replace with tmpfs!
mount -t ramfs none ${yubikey.ramfsMountPoint}
echo -n "$new_k_luks" | hextorb > ${yubikey.ramfsMountPoint}/new_key
echo -n "$k_luks" | hextorb | cryptsetup luksChangeKey ${device} --key-file=- ${yubikey.ramfsMountPoint}/new_key
if [ $? == "0" ]; then
echo -ne "$new_salt\n$new_iterations" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
else
echo "Warning: Could not update LUKS key, current challenge persists!"
fi
rm -f ${yubikey.ramfsMountPoint}/new_key
umount ${yubikey.ramfsMountPoint}
rm -rf ${yubikey.ramfsMountPoint}
umount ${yubikey.storage.mountPoint}
}
${optionalString (yubikey.gracePeriod > 0) ''
echo -n "Waiting ${toString yubikey.gracePeriod} seconds as grace..."
for i in $(seq ${toString yubikey.gracePeriod}); do
sleep 1
echo -n .
done
echo "ok"
''}
yubikey_missing=true
ykinfo -v 1>/dev/null 2>&1
if [ $? != "0" ]; then
echo -n "waiting 10 seconds for yubikey to appear..."
for try in $(seq 10); do
sleep 1
ykinfo -v 1>/dev/null 2>&1
if [ $? == "0" ]; then
yubikey_missing=false
break
fi
echo -n .
done
echo "ok"
else
yubikey_missing=false
fi
if [ "$yubikey_missing" == true ]; then
echo "no yubikey found, falling back to non-yubikey open procedure"
open_normally
else
open_yubikey
fi
''}
# open luksRoot and scan for logical volumes
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
${optionalString ((!luks.yubikeySupport) || (yubikey == null)) ''
open_normally
''}
'';
isPreLVM = f: f.preLVM;
@@ -139,10 +291,108 @@ in
'';
};
};
yubikey = mkOption {
default = null;
type = types.nullOr types.optionSet;
description = ''
The options to use for this LUKS device in Yubikey-PBA.
If null (the default), Yubikey-PBA will be disabled for this device.
'';
options = {
twoFactor = mkOption {
default = true;
type = types.bool;
description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false)";
};
slot = mkOption {
default = 2;
type = types.int;
description = "Which slot on the Yubikey to challenge";
};
saltLength = mkOption {
default = 16;
type = types.int;
description = "Length of the new salt in byte (64 is the effective maximum)";
};
keyLength = mkOption {
default = 64;
type = types.int;
description = "Length of the LUKS slot key derived with PBKDF2 in byte";
};
iterationStep = mkOption {
default = 0;
type = types.int;
description = "How much the iteration count for PBKDF2 is increased at each successful authentication";
};
gracePeriod = mkOption {
default = 2;
type = types.int;
description = "Time in seconds to wait before attempting to find the Yubikey";
};
ramfsMountPoint = mkOption {
default = "/crypt-ramfs";
type = types.string;
description = "Path where the ramfs used to update the LUKS key will be mounted in stage-1";
};
storage = mkOption {
type = types.optionSet;
description = "Options related to the storing the salt";
options = {
device = mkOption {
default = /dev/sda1;
type = types.path;
description = ''
An unencrypted device that will temporarily be mounted in stage-1.
Must contain the current salt to create the challenge for this LUKS device.
'';
};
fsType = mkOption {
default = "vfat";
type = types.string;
description = "The filesystem of the unencrypted device";
};
mountPoint = mkOption {
default = "/crypt-storage";
type = types.string;
description = "Path where the unencrypted device will be mounted in stage-1";
};
path = mkOption {
default = "/crypt-storage/default";
type = types.string;
description = ''
Absolute path of the salt on the unencrypted device with
that device's root directory as "/".
'';
};
};
};
};
};
};
};
boot.initrd.luks.yubikeySupport = mkOption {
default = false;
type = types.bool;
description = ''
Enables support for authenticating with a Yubikey on LUKS devices.
See the NixOS wiki for information on how to properly setup a LUKS device
and a Yubikey to work with this feature.
'';
};
};
config = mkIf (luks.devices != []) {
@@ -157,15 +407,48 @@ in
# copy the cryptsetup binary and it's dependencies
boot.initrd.extraUtilsCommands = ''
cp -pdv ${pkgs.cryptsetup}/sbin/cryptsetup $out/bin
# XXX: do we have a function that does this?
for lib in $(ldd $out/bin/cryptsetup |grep '=>' |grep /nix/store/ |cut -d' ' -f3); do
cp -pdvn $lib $out/lib
cp -pvn $(readlink -f $lib) $out/lib
done
cp -pdv ${pkgs.libgcrypt}/lib/libgcrypt*.so.* $out/lib
cp -pdv ${pkgs.libgpgerror}/lib/libgpg-error*.so.* $out/lib
cp -pdv ${pkgs.cryptsetup}/lib/libcryptsetup*.so.* $out/lib
cp -pdv ${pkgs.popt}/lib/libpopt*.so.* $out/lib
${optionalString luks.yubikeySupport ''
cp -pdv ${pkgs.ykpers}/bin/ykchalresp $out/bin
cp -pdv ${pkgs.ykpers}/bin/ykinfo $out/bin
cp -pdv ${pkgs.openssl}/bin/openssl $out/bin
cc -O3 -I${pkgs.openssl}/include -L${pkgs.openssl}/lib ${./pbkdf2-sha512.c} -o $out/bin/pbkdf2-sha512 -lcrypto
strip -s $out/bin/pbkdf2-sha512
cp -pdv ${pkgs.libusb1}/lib/libusb*.so.* $out/lib
cp -pdv ${pkgs.ykpers}/lib/libykpers*.so.* $out/lib
cp -pdv ${pkgs.libyubikey}/lib/libyubikey*.so.* $out/lib
cp -pdv ${pkgs.openssl}/lib/libssl*.so.* $out/lib
cp -pdv ${pkgs.openssl}/lib/libcrypto*.so.* $out/lib
mkdir -p $out/etc/ssl
cp -pdv ${pkgs.openssl}/etc/ssl/openssl.cnf $out/etc/ssl
cat > $out/bin/openssl-wrap <<EOF
#!$out/bin/sh
EOF
chmod +x $out/bin/openssl-wrap
''}
'';
boot.initrd.extraUtilsCommandsTest = ''
$out/bin/cryptsetup --version
${optionalString luks.yubikeySupport ''
$out/bin/ykchalresp -V
$out/bin/ykinfo -V
cat > $out/bin/openssl-wrap <<EOF
#!$out/bin/sh
export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
$out/bin/openssl "\$@"
EOF
$out/bin/openssl-wrap version
''}
'';
boot.initrd.preLVMCommands = concatMapStrings openCommand preLVM;
+8 -20
View File
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
{
@@ -68,7 +68,10 @@ with pkgs.lib;
config = mkIf (!config.boot.isContainer) {
environment.etc = singleton
environment.etc = [
{ source = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
target = "modprobe.d/ubuntu.conf";
}
{ source = pkgs.writeText "modprobe.conf"
''
${flip concatMapStrings config.boot.blacklistedKernelModules (name: ''
@@ -77,26 +80,11 @@ with pkgs.lib;
${config.boot.extraModprobeConfig}
'';
target = "modprobe.d/nixos.conf";
};
}
];
environment.systemPackages = [ config.system.sbin.modprobe pkgs.kmod ];
boot.blacklistedKernelModules =
[ # This module is for debugging and generates gigantic amounts
# of log output, so it should never be loaded automatically.
"evbug"
# This module causes ALSA to occassionally select the wrong
# default sound device, and is little more than an annoyance
# on modern machines.
"snd_pcsp"
# The cirrusfb module prevents X11 from starting. FIXME:
# Ubuntu blacklists all framebuffer devices because they're
# "buggy" and cause suspend problems. Maybe we should too?
"cirrusfb"
];
system.activationScripts.modprobe =
''
# Allow the kernel to find our wrapped modprobe (which searches
+38
View File
@@ -0,0 +1,38 @@
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <openssl/evp.h>
void hextorb(uint8_t* hex, uint8_t* rb)
{
while(sscanf(hex, "%2x", rb) == 1)
{
hex += 2;
rb += 1;
}
*rb = '\0';
}
int main(int argc, char** argv)
{
uint8_t k_user[2048];
uint8_t salt[2048];
uint8_t key[4096];
uint32_t key_length = atoi(argv[1]);
uint32_t iteration_count = atoi(argv[2]);
hextorb(argv[3], salt);
uint32_t salt_length = strlen(argv[3]) / 2;
fgets(k_user, 2048, stdin);
uint32_t k_user_length = strlen(k_user);
if(k_user[k_user_length - 1] == '\n') {
k_user[k_user_length - 1] = '\0';
}
PKCS5_PBKDF2_HMAC(k_user, k_user_length, salt, salt_length, iteration_count, EVP_sha512(), key_length, key);
fwrite(key, 1, key_length, stdout);
return 0;
}
+2 -2
View File
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
{
+5 -3
View File
@@ -14,7 +14,7 @@ fail() {
# in an interactive shell.
cat <<EOF
An error occured in stage 1 of the boot process, which must mount the
An error occurred in stage 1 of the boot process, which must mount the
root filesystem on \`$targetRoot' and then start stage 2. Press one
of the following keys:
@@ -139,8 +139,6 @@ mkdir -p /dev/.mdadm
systemd-udevd --daemon
udevadm trigger --action=add
udevadm settle || true
modprobe scsi_wait_scan || true
udevadm settle || true
# Load boot-time keymap before any LVM/LUKS initialization
@@ -320,6 +318,10 @@ while read -u 3 mountPoint; do
echo -n "waiting for device $device to appear..."
for try in $(seq 1 20); do
sleep 1
# also re-try lvm activation now that new block devices might have appeared
lvm vgchange -ay
# and tell udev to create nodes for the new LVs
udevadm trigger --action=add
if test -e $device; then break; fi
echo -n "."
done
+3 -3
View File
@@ -3,9 +3,9 @@
# the modules necessary to mount the root file system, then calls the
# init in the root file system to start the second boot stage.
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
@@ -74,7 +74,7 @@ let
cp -v ${pkgs.lvm2}/sbin/dmsetup $out/bin/dmsetup
cp -v ${pkgs.lvm2}/sbin/lvm $out/bin/lvm
cp -v ${pkgs.lvm2}/lib/libdevmapper.so.*.* $out/lib
cp -v ${pkgs.systemd}/lib/libsystemd-daemon.so.* $out/lib
cp -v ${pkgs.systemd}/lib/libsystemd.so.* $out/lib
# Add RAID mdadm tool.
cp -v ${pkgs.mdadm}/sbin/mdadm $out/bin/mdadm
+30 -15
View File
@@ -82,7 +82,7 @@ done
# More special file systems, initialise required directories.
mkdir -m 0755 /dev/shm
mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /dev/shm
mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" none /dev/shm
mkdir -m 0755 -p /dev/pts
[ -e /proc/bus/usb ] && mount -t usbfs none /proc/bus/usb # UML doesn't have USB by default
mkdir -m 01777 -p /tmp
@@ -96,28 +96,14 @@ mkdir -m 0755 -p /etc/nixos
# Miscellaneous boot time cleanup.
rm -rf /var/run /var/lock
rm -f /etc/resolv.conf
touch /etc/resolv.conf
rm -f /etc/{group,passwd,shadow}.lock
if test -n "@cleanTmpDir@"; then
echo -n "cleaning \`/tmp'..."
find /tmp -maxdepth 1 -mindepth 1 -print0 | xargs -0r rm -rf --one-file-system
echo " done"
else
# Get rid of ICE locks...
rm -rf /tmp/.ICE-unix
fi
# ... and ensure that it's owned by root.
mkdir -m 1777 /tmp/.ICE-unix
# This is a good time to clean up /nix/var/nix/chroots. Doing an `rm
# -rf' on it isn't safe in general because it can contain bind mounts
# to /nix/store and other places. But after rebooting these are all
# gone, of course.
rm -rf /nix/var/nix/chroots # recreated in activate-configuration.sh
# Also get rid of temporary GC roots.
rm -rf /nix/var/nix/gcroots/tmp /nix/var/nix/temproots
@@ -131,6 +117,15 @@ if ! mountpoint -q /run; then
mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
fi
# Create a ramfs on /run/keys to hold secrets that shouldn't be
# written to disk (generally used for NixOps, harmless elsewhere).
if ! mountpoint -q /run/keys; then
rm -rf /run/keys
mkdir -m 0750 /run/keys
chown 0:96 /run/keys
mount -t ramfs none /run/keys
fi
mkdir -m 0755 -p /run/lock
@@ -146,12 +141,32 @@ if test -n "$resumeDevice"; then
fi
# Use /etc/resolv.conf supplied by systemd-nspawn, if applicable.
if [ -n "@useHostResolvConf@" -a -e /etc/resolv.conf ]; then
cat /etc/resolv.conf | resolvconf -m 1000 -a host
else
touch /etc/resolv.conf
fi
# Create /var/setuid-wrappers as a tmpfs.
rm -rf /var/setuid-wrappers
mkdir -m 0755 -p /var/setuid-wrappers
mount -t tmpfs -o "mode=0755" none /var/setuid-wrappers
# Run the script that performs all configuration activation that does
# not have to be done at boot time.
echo "running activation script..."
$systemConfig/activate
# Restore the system time from the hardware clock. We do this after
# running the activation script to be sure that /etc/localtime points
# at the current time zone.
hwclock --hctosys
# Record the boot configuration.
ln -sfn "$systemConfig" /run/booted-system
+5 -2
View File
@@ -1,6 +1,6 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
@@ -19,11 +19,13 @@ let
isExecutable = true;
inherit (config.boot) devShmSize runSize cleanTmpDir;
inherit (config.nix) readOnlyStore;
inherit (config.networking) useHostResolvConf;
ttyGid = config.ids.gids.tty;
path =
[ pkgs.coreutils
pkgs.utillinux
pkgs.sysvtools
pkgs.openresolv
] ++ (optional config.boot.cleanTmpDir pkgs.findutils)
++ optional config.nix.readOnlyStore readonlyMountpoint;
postBootCommands = pkgs.writeText "local-cmds"
@@ -79,6 +81,7 @@ in
'';
};
# FIXME: should replace this with something that uses systemd-tmpfiles.
cleanTmpDir = mkOption {
type = types.bool;
default = false;
@@ -1,6 +1,6 @@
{ config, pkgs }:
{ config, lib }:
with pkgs.lib;
with lib;
let
@@ -28,7 +28,7 @@ let
in rec {
unitOptions = {
sharedOptions = {
enable = mkOption {
default = true;
@@ -41,6 +41,37 @@ in rec {
'';
};
requiredBy = mkOption {
default = [];
type = types.listOf types.string;
description = "Units that require (i.e. depend on and need to go down with) this unit.";
};
wantedBy = mkOption {
default = [];
type = types.listOf types.string;
description = "Units that want (i.e. depend on) this unit.";
};
};
concreteUnitOptions = sharedOptions // {
text = mkOption {
type = types.nullOr types.str;
default = null;
description = "Text of this systemd unit.";
};
unit = mkOption {
internal = true;
description = "The generated unit.";
};
};
commonUnitOptions = sharedOptions // {
description = mkOption {
default = "";
type = types.str;
@@ -109,18 +140,6 @@ in rec {
'';
};
requiredBy = mkOption {
default = [];
type = types.listOf types.str;
description = "Units that require (i.e. depend on and need to go down with) this unit.";
};
wantedBy = mkOption {
default = [];
type = types.listOf types.str;
description = "Units that want (i.e. depend on) this unit.";
};
unitConfig = mkOption {
default = {};
example = { RequiresMountsFor = "/data"; };
@@ -135,6 +154,7 @@ in rec {
restartTriggers = mkOption {
default = [];
type = types.listOf types.unspecified;
description = ''
An arbitrary list of items such as derivations. If any item
in the list changes between reconfigurations, the service will
@@ -145,7 +165,7 @@ in rec {
};
serviceOptions = unitOptions // {
serviceOptions = commonUnitOptions // {
environment = mkOption {
default = {};
@@ -236,6 +256,17 @@ in rec {
'';
};
reloadIfChanged = mkOption {
type = types.bool;
default = false;
description = ''
Whether the service should be reloaded during a NixOS
configuration switch if its definition has changed. If
enabled, the value of <option>restartIfChanged</option> is
ignored.
'';
};
stopIfChanged = mkOption {
type = types.bool;
default = true;
@@ -268,7 +299,7 @@ in rec {
};
socketOptions = unitOptions // {
socketOptions = commonUnitOptions // {
listenStreams = mkOption {
default = [];
@@ -295,7 +326,7 @@ in rec {
};
timerOptions = unitOptions // {
timerOptions = commonUnitOptions // {
timerConfig = mkOption {
default = {};
@@ -314,7 +345,24 @@ in rec {
};
mountOptions = unitOptions // {
pathOptions = commonUnitOptions // {
pathConfig = mkOption {
default = {};
example = { PathChanged = "/some/path"; Unit = "changedpath.service"; };
type = types.attrsOf unitOption;
description = ''
Each attribute in this set specifies an option in the
<literal>[Path]</literal> section of the unit. See
<citerefentry><refentrytitle>systemd.path</refentrytitle>
<manvolnum>5</manvolnum></citerefentry> for details.
'';
};
};
mountOptions = commonUnitOptions // {
what = mkOption {
example = "/dev/sda1";
@@ -358,7 +406,7 @@ in rec {
};
};
automountOptions = unitOptions // {
automountOptions = commonUnitOptions // {
where = mkOption {
example = "/mnt";
@@ -382,4 +430,6 @@ in rec {
};
};
targetOptions = commonUnitOptions;
}
+247 -157
View File
@@ -1,8 +1,8 @@
{ config, pkgs, utils, ... }:
{ config, lib, pkgs, utils, ... }:
with pkgs.lib;
with lib;
with utils;
with import ./systemd-unit-options.nix { inherit config pkgs; };
with import ./systemd-unit-options.nix { inherit config lib; };
let
@@ -11,29 +11,26 @@ let
systemd = cfg.package;
makeUnit = name: unit:
pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
((if !unit.enable then ''
mkdir -p $out
ln -s /dev/null $out/${name}
'' else if unit.linkTarget != null then ''
mkdir -p $out
ln -s ${unit.linkTarget} $out/${name}
'' else if unit.text != null then ''
mkdir -p $out
echo -n "$text" > $out/${name}
'' else "") + optionalString (unit.extraConfig != {}) ''
mkdir -p $out/${name}.d
${concatStringsSep "\n" (mapAttrsToList (n: v: "echo -n \"${v}\" > $out/${name}.d/${n}") unit.extraConfig)}
'');
if unit.enable then
pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
''
mkdir -p $out
echo -n "$text" > $out/${name}
''
else
pkgs.runCommand "unit" { preferLocalBuild = true; }
''
mkdir -p $out
ln -s /dev/null $out/${name}
'';
upstreamUnits =
upstreamSystemUnits =
[ # Targets.
"basic.target"
"sysinit.target"
"sockets.target"
"graphical.target"
"multi-user.target"
"getty.target"
"network.target"
"network-online.target"
"nss-lookup.target"
@@ -43,6 +40,7 @@ let
"sigpwr.target"
"timers.target"
"paths.target"
"rpcbind.target"
# Rescue mode.
"rescue.target"
@@ -55,6 +53,13 @@ let
"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"
@@ -67,12 +72,15 @@ let
#"systemd-vconsole-setup.service"
"systemd-user-sessions.service"
"dbus-org.freedesktop.login1.service"
"dbus-org.freedesktop.machine1.service"
"user@.service"
# Journal.
"systemd-journald.socket"
"systemd-journald.service"
"systemd-journal-flush.service"
"systemd-journal-gatewayd.socket"
"systemd-journal-gatewayd.service"
"syslog.socket"
# SysV init compatibility.
@@ -80,7 +88,8 @@ let
"systemd-initctl.service"
# Kernel module loading.
#"systemd-modules-load.service"
"systemd-modules-load.service"
"kmod-static-nodes.service"
# Filesystems.
"systemd-fsck@.service"
@@ -93,10 +102,16 @@ let
"swap.target"
"dev-hugepages.mount"
"dev-mqueue.mount"
"proc-sys-fs-binfmt_misc.mount"
"sys-fs-fuse-connections.mount"
"sys-kernel-config.mount"
"sys-kernel-debug.mount"
# Maintaining state across reboots.
"systemd-random-seed.service"
"systemd-backlight@.service"
"systemd-rfkill@.service"
# Hibernate / suspend.
"hibernate.target"
"suspend.target"
@@ -121,12 +136,30 @@ let
"final.target"
"kexec.target"
"systemd-kexec.service"
"systemd-update-utmp.service"
# Password entry.
"systemd-ask-password-console.path"
"systemd-ask-password-console.service"
"systemd-ask-password-wall.path"
"systemd-ask-password-wall.service"
# Slices / containers.
"slices.target"
"-.slice"
"system.slice"
"user.slice"
"machine.slice"
"systemd-machined.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"
]
++ optionals cfg.enableEmergencyMode [
@@ -134,16 +167,26 @@ let
"emergency.service"
];
upstreamWants =
upstreamSystemWants =
[ #"basic.target.wants"
"sysinit.target.wants"
"sockets.target.wants"
"local-fs.target.wants"
"multi-user.target.wants"
"shutdown.target.wants"
"timers.target.wants"
];
upstreamUserUnits =
[ "basic.target"
"default.target"
"exit.target"
"paths.target"
"shutdown.target"
"sockets.target"
"systemd-exit.service"
"timers.target"
];
makeJobScript = name: text:
let x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${name}"; inherit text; };
in "${x}/bin/${name}";
@@ -151,15 +194,23 @@ let
unitConfig = { name, config, ... }: {
config = {
unitConfig =
{ Requires = concatStringsSep " " config.requires;
Wants = concatStringsSep " " config.wants;
After = concatStringsSep " " config.after;
Before = concatStringsSep " " config.before;
BindsTo = concatStringsSep " " config.bindsTo;
PartOf = concatStringsSep " " config.partOf;
Conflicts = concatStringsSep " " config.conflicts;
"X-Restart-Triggers" = toString config.restartTriggers;
} // optionalAttrs (config.description != "") {
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.restartTriggers != [])
{ X-Restart-Triggers = toString config.restartTriggers; }
// optionalAttrs (config.description != "") {
Description = config.description;
};
};
@@ -244,6 +295,11 @@ let
(if isList value then value else [value]))
as));
commonUnitText = def: ''
[Unit]
${attrsToSection def.unitConfig}
'';
targetToUnit = name: def:
{ inherit (def) wantedBy requiredBy enable;
text =
@@ -255,15 +311,16 @@ let
serviceToUnit = name: def:
{ inherit (def) wantedBy requiredBy enable;
text =
text = commonUnitText def +
''
[Unit]
${attrsToSection def.unitConfig}
[Service]
${let env = cfg.globalEnvironment // def.environment;
in concatMapStrings (n: "Environment=\"${n}=${getAttr n env}\"\n") (attrNames env)}
${optionalString (!def.restartIfChanged) "X-RestartIfChanged=false"}
${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}
'';
@@ -271,11 +328,8 @@ let
socketToUnit = name: def:
{ inherit (def) wantedBy requiredBy enable;
text =
text = commonUnitText def +
''
[Unit]
${attrsToSection def.unitConfig}
[Socket]
${attrsToSection def.socketConfig}
${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
@@ -284,23 +338,26 @@ let
timerToUnit = name: def:
{ inherit (def) wantedBy requiredBy enable;
text =
text = commonUnitText def +
''
[Unit]
${attrsToSection def.unitConfig}
[Timer]
${attrsToSection def.timerConfig}
'';
};
pathToUnit = name: def:
{ inherit (def) wantedBy requiredBy enable;
text = commonUnitText def +
''
[Path]
${attrsToSection def.pathConfig}
'';
};
mountToUnit = name: def:
{ inherit (def) wantedBy requiredBy enable;
text =
text = commonUnitText def +
''
[Unit]
${attrsToSection def.unitConfig}
[Mount]
${attrsToSection def.mountConfig}
'';
@@ -308,70 +365,99 @@ let
automountToUnit = name: def:
{ inherit (def) wantedBy requiredBy enable;
text =
text = commonUnitText def +
''
[Unit]
${attrsToSection def.unitConfig}
[Automount]
${attrsToSection def.automountConfig}
'';
};
units = pkgs.runCommand "units" { preferLocalBuild = true; }
''
generateUnits = type: units: upstreamUnits: upstreamWants:
pkgs.runCommand "${type}-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
fn=${systemd}/example/systemd/${type}/$i
if ! [ -e $fn ]; then echo "missing $fn"; false; fi
if [ -L $fn ]; then
cp -pd $fn $out/
target="$(readlink "$fn")"
if [ ''${target:0:3} = ../ ]; then
ln -s "$(readlink -f "$fn")" $out/
else
cp -pd $fn $out/
fi
else
ln -s $fn $out/
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
fn=${systemd}/example/systemd/${type}/$i
if ! [ -e $fn ]; then echo "missing $fn"; false; fi
x=$out/$(basename $fn)
mkdir $x
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/
files=$(echo $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*)
if [ -n "$files" ]; then
ln -s $files $out/
fi
done
# Symlink all units defined by systemd.units. If these are also
# provided by systemd or systemd.packages, then add them as
# <unit-name>.d/overrides.conf, which makes them extend the
# upstream unit.
for i in ${toString (mapAttrsToList (n: v: v.unit) 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'
ln -sfn '../${name}' $out/'${name2}.wants'/
'') unit.wantedBy) cfg.units)}
'') unit.wantedBy) units)}
${concatStrings (mapAttrsToList (name: unit:
concatMapStrings (name2: ''
mkdir -p $out/'${name2}.requires'
ln -sfn '../${name}' $out/'${name2}.requires'/
'') unit.requiredBy) cfg.units)}
'') unit.requiredBy) units)}
ln -s ${cfg.defaultUnit} $out/default.target
${optionalString (type == "system") ''
# Stupid misc. symlinks.
ln -s ${cfg.defaultUnit} $out/default.target
ln -s rescue.target $out/kbrequest.target
ln -s rescue.target $out/kbrequest.target
mkdir -p $out/getty.target.wants/
ln -s ../autovt@tty1.service $out/getty.target.wants/
mkdir -p $out/getty.target.wants/
ln -s ../autovt@tty1.service $out/getty.target.wants/
ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \
../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/
ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \
../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/
''}
''; # */
in
@@ -393,55 +479,9 @@ in
default = {};
type = types.attrsOf types.optionSet;
options = { name, config, ... }:
{ options = {
text = mkOption {
type = types.nullOr types.str;
default = null;
description = "Text of this systemd unit.";
};
enable = mkOption {
default = true;
type = types.bool;
description = ''
If set to false, this unit will be a symlink to
/dev/null. This is primarily useful to prevent specific
template instances (e.g. <literal>serial-getty@ttyS0</literal>)
from being started.
'';
};
requiredBy = mkOption {
default = [];
type = types.listOf types.string;
description = "Units that require (i.e. depend on and need to go down with) this unit.";
};
wantedBy = mkOption {
default = [];
type = types.listOf types.string;
description = "Units that want (i.e. depend on) this unit.";
};
unit = mkOption {
internal = true;
description = "The generated unit.";
};
linkTarget = mkOption {
default = null;
description = "The file to symlink this target to.";
type = types.nullOr types.path;
};
extraConfig = mkOption {
default = {};
example = { "foo@1.conf" = "X-RestartIfChanged=false"; };
type = types.attrsOf types.lines;
description = ''
Extra files to be appended to the configuration for the unit.
This can be used to override configuration for a unit provided
by systemd or another package, or to override only a single instance
of a template unit.
'';
};
};
{ options = concreteUnitOptions;
config = {
unit = makeUnit name config;
unit = mkDefault (makeUnit name config);
};
};
};
@@ -455,7 +495,7 @@ in
systemd.targets = mkOption {
default = {};
type = types.attrsOf types.optionSet;
options = [ unitOptions unitConfig ];
options = [ targetOptions unitConfig ];
description = "Definition of systemd target units.";
};
@@ -480,6 +520,13 @@ in
description = "Definition of systemd timer units.";
};
systemd.paths = mkOption {
default = {};
type = types.attrsOf types.optionSet;
options = [ pathOptions unitConfig ];
description = "Definition of systemd path units.";
};
systemd.mounts = mkOption {
default = [];
type = types.listOf types.optionSet;
@@ -567,6 +614,14 @@ in
'';
};
services.journald.enableHttpGateway = mkOption {
default = false;
type = types.bool;
description = ''
Whether to enable the HTTP gateway to the journal.
'';
};
services.logind.extraConfig = mkOption {
default = "";
type = types.lines;
@@ -590,6 +645,41 @@ in
'';
};
systemd.tmpfiles.rules = mkOption {
type = types.listOf types.str;
default = [];
example = [ "d /tmp 1777 root root 10d" ];
description = ''
Rules for creating and cleaning up temporary files
automatically. See
<citerefentry><refentrytitle>tmpfiles.d</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for the exact format. You should not use this option to create
files required by systemd services, since there is no
guarantee that <command>systemd-tmpfiles</command> runs when
the system is reconfigured using
<command>nixos-rebuild</command>.
'';
};
systemd.user.units = mkOption {
description = "Definition of systemd per-user units.";
default = {};
type = types.attrsOf types.optionSet;
options = { name, config, ... }:
{ options = concreteUnitOptions;
config = {
unit = mkDefault (makeUnit name config);
};
};
};
systemd.user.services = mkOption {
default = {};
type = types.attrsOf types.optionSet;
options = [ serviceOptions unitConfig serviceConfig ];
description = "Definition of systemd per-user service units.";
};
};
@@ -597,11 +687,20 @@ in
config = {
system.build.units = units;
assertions = mapAttrsToList (name: service: {
assertion = service.serviceConfig.Type or "" == "oneshot" -> service.serviceConfig.Restart or "no" == "no";
message = "${name}: Type=oneshot services must have Restart=no";
}) cfg.services;
system.build.units = cfg.units;
environment.systemPackages = [ systemd ];
environment.etc."systemd/system".source = units;
environment.etc."systemd/system".source =
generateUnits "system" cfg.units upstreamSystemUnits upstreamSystemWants;
environment.etc."systemd/user".source =
generateUnits "user" cfg.user.units upstreamUserUnits [];
environment.etc."systemd/system.conf".text =
''
@@ -645,8 +744,11 @@ in
'';
# Target for charon send-keys to hook into.
users.extraGroups.keys.gid = config.ids.gids.keys;
systemd.targets.keys =
{ description = "Security Keys";
unitConfig.X-StopOnReconfiguration = true;
};
systemd.units =
@@ -654,6 +756,7 @@ in
// mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services
// mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets
// mapAttrs' (n: v: nameValuePair "${n}.timer" (timerToUnit n v)) cfg.timers
// mapAttrs' (n: v: nameValuePair "${n}.path" (pathToUnit n v)) cfg.paths
// listToAttrs (map
(v: let n = escapeSystemdPath v.where;
in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
@@ -661,6 +764,9 @@ in
(v: let n = escapeSystemdPath v.where;
in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
systemd.user.units =
mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.user.services;
system.requiredKernelConfig = map config.lib.kernelConfig.isEnabled [
"CGROUPS" "AUTOFS4_FS" "DEVTMPFS"
];
@@ -673,6 +779,8 @@ in
};
users.extraGroups.systemd-journal.gid = config.ids.gids.systemd-journal;
users.extraUsers.systemd-journal-gateway.uid = config.ids.uids.systemd-journal-gateway;
users.extraGroups.systemd-journal-gateway.gid = config.ids.gids.systemd-journal-gateway;
# Generate timer units for all services that have a startAt value.
systemd.timers =
@@ -682,43 +790,25 @@ in
})
(filterAttrs (name: service: service.startAt != "") cfg.services);
# FIXME: These are borrowed from upstream systemd.
systemd.services."systemd-update-utmp" =
{ description = "Update UTMP about System Reboot/Shutdown";
wantedBy = [ "sysinit.target" ];
after = [ "systemd-remount-fs.service" ];
before = [ "sysinit.target" "shutdown.target" ];
conflicts = [ "shutdown.target" ];
unitConfig = {
DefaultDependencies = false;
RequiresMountsFor = "/var/log";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${systemd}/lib/systemd/systemd-update-utmp reboot";
ExecStop = "${systemd}/lib/systemd/systemd-update-utmp shutdown";
};
restartIfChanged = false;
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;
};
systemd.services."systemd-random-seed" =
{ description = "Load/Save Random Seed";
wantedBy = [ "sysinit.target" "multi-user.target" ];
after = [ "systemd-remount-fs.service" ];
before = [ "sysinit.target" "shutdown.target" ];
conflicts = [ "shutdown.target" ];
unitConfig = {
DefaultDependencies = false;
RequiresMountsFor = "/var/lib";
};
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${systemd}/lib/systemd/systemd-random-seed load";
ExecStop = "${systemd}/lib/systemd/systemd-random-seed save";
};
};
environment.etc."tmpfiles.d/x11.conf".source = "${systemd}/example/tmpfiles.d/x11.conf";
environment.etc."tmpfiles.d/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}
'';
};
}
+22 -2
View File
@@ -1,8 +1,8 @@
# Management of static files in /etc.
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with lib;
let
@@ -19,6 +19,8 @@ let
sources = map (x: x.source) etc';
targets = map (x: x.target) etc';
modes = map (x: x.mode) etc';
uids = map (x: x.uid) etc';
gids = map (x: x.gid) etc';
};
in
@@ -87,6 +89,24 @@ in
'';
};
uid = mkOption {
default = 0;
type = types.int;
description = ''
UID of created file. Only takes affect when the file is
copied (that is, the mode is not 'symlink').
'';
};
gid = mkOption {
default = 0;
type = types.int;
description = ''
GID of created file. Only takes affect when the file is
copied (that is, the mode is not 'symlink').
'';
};
};
config = {
+4
View File
@@ -6,6 +6,8 @@ set -f
sources_=($sources)
targets_=($targets)
modes_=($modes)
uids_=($uids)
gids_=($gids)
set +f
for ((i = 0; i < ${#targets_[@]}; i++)); do
@@ -35,6 +37,8 @@ for ((i = 0; i < ${#targets_[@]}; i++)); do
if test "${modes_[$i]}" != symlink; then
echo "${modes_[$i]}" > $out/etc/$target.mode
echo "${uids_[$i]}" > $out/etc/$target.uid
echo "${gids_[$i]}" > $out/etc/$target.gid
fi
fi
+8
View File
@@ -60,7 +60,15 @@ sub link {
if ($mode eq "direct-symlink") {
atomicSymlink readlink("$static/$fn"), $target or warn;
} else {
open UID, "<$_.uid";
my $uid = <UID>; chomp $uid;
close UID;
open GID, "<$_.gid";
my $gid = <GID>; chomp $gid;
close GID;
copy "$static/$fn", "$target.tmp" or warn;
chown int($uid), int($gid), "$target.tmp" or warn;
chmod oct($mode), "$target.tmp" or warn;
rename "$target.tmp", $target or warn;
}
+4 -4
View File
@@ -1,7 +1,7 @@
{ config, pkgs, ... }:
{ config, lib, pkgs, ... }:
with pkgs.lib;
with import ../boot/systemd-unit-options.nix { inherit config pkgs; };
with lib;
with import ../boot/systemd-unit-options.nix { inherit config lib; };
let
@@ -93,7 +93,7 @@ let
if job.daemonType == "fork" || job.daemonType == "daemon" then { Type = "forking"; GuessMainPID = true; } else
if job.daemonType == "none" then { } else
throw "invalid daemon type `${job.daemonType}'")
// optionalAttrs (!job.task && job.respawn)
// optionalAttrs (!job.task && !(job.script == "" && job.exec == "") && job.respawn)
{ Restart = "always"; }
// optionalAttrs job.task
{ Type = "oneshot"; RemainAfterExit = false; };