forked from mirrors/nixpkgs
Merge pull request #164943 from ElvishJerricco/systemd-initrd-reuse-systemd-module
initrd: Opt-in bare bones systemd-based initrd
This commit is contained in:
commit
7cdc4dd5d1
nixos
pkgs
build-support
docker
fake-nss
kernel
os-specific/linux/systemd
top-level
|
@ -122,10 +122,15 @@ in rec {
|
|||
(if isList value then value else [value]))
|
||||
as));
|
||||
|
||||
generateUnits = generateUnits' true;
|
||||
|
||||
generateUnits' = allowCollisions: type: units: upstreamUnits: upstreamWants:
|
||||
pkgs.runCommand "${type}-units"
|
||||
generateUnits = { allowCollisions ? true, type, units, upstreamUnits, upstreamWants, packages ? cfg.packages, package ? cfg.package }:
|
||||
let
|
||||
typeDir = ({
|
||||
system = "system";
|
||||
initrd = "system";
|
||||
user = "user";
|
||||
nspawn = "nspawn";
|
||||
}).${type};
|
||||
in pkgs.runCommand "${type}-units"
|
||||
{ preferLocalBuild = true;
|
||||
allowSubstitutes = false;
|
||||
} ''
|
||||
|
@ -133,7 +138,7 @@ in rec {
|
|||
|
||||
# Copy the upstream systemd units we're interested in.
|
||||
for i in ${toString upstreamUnits}; do
|
||||
fn=${cfg.package}/example/systemd/${type}/$i
|
||||
fn=${package}/example/systemd/${typeDir}/$i
|
||||
if ! [ -e $fn ]; then echo "missing $fn"; false; fi
|
||||
if [ -L $fn ]; then
|
||||
target="$(readlink "$fn")"
|
||||
|
@ -150,7 +155,7 @@ in rec {
|
|||
# Copy .wants links, but only those that point to units that
|
||||
# we're interested in.
|
||||
for i in ${toString upstreamWants}; do
|
||||
fn=${cfg.package}/example/systemd/${type}/$i
|
||||
fn=${package}/example/systemd/${typeDir}/$i
|
||||
if ! [ -e $fn ]; then echo "missing $fn"; false; fi
|
||||
x=$out/$(basename $fn)
|
||||
mkdir $x
|
||||
|
@ -162,14 +167,14 @@ in rec {
|
|||
done
|
||||
|
||||
# Symlink all units provided listed in systemd.packages.
|
||||
packages="${toString cfg.packages}"
|
||||
packages="${toString packages}"
|
||||
|
||||
# Filter duplicate directories
|
||||
declare -A unique_packages
|
||||
for k in $packages ; do unique_packages[$k]=1 ; done
|
||||
|
||||
for i in ''${!unique_packages[@]}; do
|
||||
for fn in $i/etc/systemd/${type}/* $i/lib/systemd/${type}/*; do
|
||||
for fn in $i/etc/systemd/${typeDir}/* $i/lib/systemd/${typeDir}/*; do
|
||||
if ! [[ "$fn" =~ .wants$ ]]; then
|
||||
if [[ -d "$fn" ]]; then
|
||||
targetDir="$out/$(basename "$fn")"
|
||||
|
@ -270,9 +275,9 @@ in rec {
|
|||
{ Conflicts = toString config.conflicts; }
|
||||
// optionalAttrs (config.requisite != [])
|
||||
{ Requisite = toString config.requisite; }
|
||||
// optionalAttrs (config.restartTriggers != [])
|
||||
// optionalAttrs (config ? restartTriggers && config.restartTriggers != [])
|
||||
{ X-Restart-Triggers = toString config.restartTriggers; }
|
||||
// optionalAttrs (config.reloadTriggers != [])
|
||||
// optionalAttrs (config ? reloadTriggers && config.reloadTriggers != [])
|
||||
{ X-Reload-Triggers = toString config.reloadTriggers; }
|
||||
// optionalAttrs (config.description != "") {
|
||||
Description = config.description; }
|
||||
|
@ -288,45 +293,24 @@ in rec {
|
|||
};
|
||||
};
|
||||
|
||||
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;
|
||||
})
|
||||
];
|
||||
serviceConfig = { config, ... }: {
|
||||
config.environment.PATH = mkIf (config.path != []) "${makeBinPath config.path}:${makeSearchPathOutput "bin" "sbin" config.path}";
|
||||
};
|
||||
|
||||
stage2ServiceConfig = {
|
||||
imports = [ serviceConfig ];
|
||||
# Default path for systemd services. Should be quite minimal.
|
||||
config.path = mkAfter [
|
||||
pkgs.coreutils
|
||||
pkgs.findutils
|
||||
pkgs.gnugrep
|
||||
pkgs.gnused
|
||||
systemd
|
||||
];
|
||||
};
|
||||
|
||||
stage1ServiceConfig = serviceConfig;
|
||||
|
||||
mountConfig = { config, ... }: {
|
||||
config = {
|
||||
mountConfig =
|
||||
|
@ -374,12 +358,12 @@ in rec {
|
|||
# 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 ''
|
||||
${if def ? reloadIfChanged && def.reloadIfChanged then ''
|
||||
X-ReloadIfChanged=true
|
||||
'' else if !def.restartIfChanged then ''
|
||||
'' else if (def ? restartIfChanged && !def.restartIfChanged) then ''
|
||||
X-RestartIfChanged=false
|
||||
'' else ""}
|
||||
${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"}
|
||||
${optionalString (def ? stopIfChanged && !def.stopIfChanged) "X-StopIfChanged=false"}
|
||||
${attrsToSection def.serviceConfig}
|
||||
'';
|
||||
};
|
||||
|
|
37
nixos/lib/systemd-types.nix
Normal file
37
nixos/lib/systemd-types.nix
Normal file
|
@ -0,0 +1,37 @@
|
|||
{ lib, systemdUtils }:
|
||||
|
||||
with systemdUtils.lib;
|
||||
with systemdUtils.unitOptions;
|
||||
with lib;
|
||||
|
||||
rec {
|
||||
units = with types;
|
||||
attrsOf (submodule ({ name, config, ... }: {
|
||||
options = concreteUnitOptions;
|
||||
config = { unit = mkDefault (systemdUtils.lib.makeUnit name config); };
|
||||
}));
|
||||
|
||||
services = with types; attrsOf (submodule [ stage2ServiceOptions unitConfig stage2ServiceConfig ]);
|
||||
initrdServices = with types; attrsOf (submodule [ stage1ServiceOptions unitConfig stage1ServiceConfig ]);
|
||||
|
||||
targets = with types; attrsOf (submodule [ stage2CommonUnitOptions unitConfig ]);
|
||||
initrdTargets = with types; attrsOf (submodule [ stage1CommonUnitOptions unitConfig ]);
|
||||
|
||||
sockets = with types; attrsOf (submodule [ stage2SocketOptions unitConfig ]);
|
||||
initrdSockets = with types; attrsOf (submodule [ stage1SocketOptions unitConfig ]);
|
||||
|
||||
timers = with types; attrsOf (submodule [ stage2TimerOptions unitConfig ]);
|
||||
initrdTimers = with types; attrsOf (submodule [ stage1TimerOptions unitConfig ]);
|
||||
|
||||
paths = with types; attrsOf (submodule [ stage2PathOptions unitConfig ]);
|
||||
initrdPaths = with types; attrsOf (submodule [ stage1PathOptions unitConfig ]);
|
||||
|
||||
slices = with types; attrsOf (submodule [ stage2SliceOptions unitConfig ]);
|
||||
initrdSlices = with types; attrsOf (submodule [ stage1SliceOptions unitConfig ]);
|
||||
|
||||
mounts = with types; listOf (submodule [ stage2MountOptions unitConfig mountConfig ]);
|
||||
initrdMounts = with types; listOf (submodule [ stage1MountOptions unitConfig mountConfig ]);
|
||||
|
||||
automounts = with types; listOf (submodule [ stage2AutomountOptions unitConfig automountConfig ]);
|
||||
initrdAutomounts = with types; attrsOf (submodule [ stage1AutomountOptions unitConfig automountConfig ]);
|
||||
}
|
|
@ -94,7 +94,7 @@ in rec {
|
|||
|
||||
};
|
||||
|
||||
commonUnitOptions = sharedOptions // {
|
||||
commonUnitOptions = { options = (sharedOptions // {
|
||||
|
||||
description = mkOption {
|
||||
default = "";
|
||||
|
@ -191,27 +191,6 @@ 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
|
||||
be restarted.
|
||||
'';
|
||||
};
|
||||
|
||||
reloadTriggers = mkOption {
|
||||
default = [];
|
||||
type = types.listOf unitOption;
|
||||
description = ''
|
||||
An arbitrary list of items such as derivations. If any item
|
||||
in the list changes between reconfigurations, the service will
|
||||
be reloaded. If anything but a reload trigger changes in the
|
||||
unit file, the unit will be restarted instead.
|
||||
'';
|
||||
};
|
||||
|
||||
onFailure = mkOption {
|
||||
default = [];
|
||||
type = types.listOf unitNameType;
|
||||
|
@ -239,10 +218,39 @@ in rec {
|
|||
'';
|
||||
};
|
||||
|
||||
}); };
|
||||
|
||||
stage2CommonUnitOptions = {
|
||||
imports = [
|
||||
commonUnitOptions
|
||||
];
|
||||
|
||||
options = {
|
||||
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
|
||||
be restarted.
|
||||
'';
|
||||
};
|
||||
|
||||
reloadTriggers = mkOption {
|
||||
default = [];
|
||||
type = types.listOf unitOption;
|
||||
description = ''
|
||||
An arbitrary list of items such as derivations. If any item
|
||||
in the list changes between reconfigurations, the service will
|
||||
be reloaded. If anything but a reload trigger changes in the
|
||||
unit file, the unit will be restarted instead.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
stage1CommonUnitOptions = commonUnitOptions;
|
||||
|
||||
|
||||
serviceOptions = commonUnitOptions // {
|
||||
serviceOptions = { options = {
|
||||
|
||||
environment = mkOption {
|
||||
default = {};
|
||||
|
@ -276,121 +284,164 @@ in rec {
|
|||
'';
|
||||
};
|
||||
|
||||
script = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Shell commands executed as the service's main process.";
|
||||
}; };
|
||||
|
||||
stage2ServiceOptions = { name, config, ... }: {
|
||||
imports = [
|
||||
stage2CommonUnitOptions
|
||||
serviceOptions
|
||||
];
|
||||
|
||||
options = {
|
||||
script = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = "Shell commands executed as the service's main process.";
|
||||
};
|
||||
|
||||
scriptArgs = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = "Arguments passed to the main process script.";
|
||||
};
|
||||
|
||||
preStart = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed before the service's main process
|
||||
is started.
|
||||
'';
|
||||
};
|
||||
|
||||
postStart = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed after the service's main process
|
||||
is started.
|
||||
'';
|
||||
};
|
||||
|
||||
reload = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed when the service's main process
|
||||
is reloaded.
|
||||
'';
|
||||
};
|
||||
|
||||
preStop = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed to stop the service.
|
||||
'';
|
||||
};
|
||||
|
||||
postStop = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed after the service's main process
|
||||
has exited.
|
||||
'';
|
||||
};
|
||||
|
||||
restartIfChanged = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether the service should be restarted during a NixOS
|
||||
configuration switch if its definition has changed.
|
||||
'';
|
||||
};
|
||||
|
||||
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.
|
||||
|
||||
This option should not be used anymore in favor of
|
||||
<option>reloadTriggers</option> which allows more granular
|
||||
control of when a service is reloaded and when a service
|
||||
is restarted.
|
||||
'';
|
||||
};
|
||||
|
||||
stopIfChanged = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
If set, a changed unit is restarted by calling
|
||||
<command>systemctl stop</command> in the old configuration,
|
||||
then <command>systemctl start</command> in the new one.
|
||||
Otherwise, it is restarted in a single step using
|
||||
<command>systemctl restart</command> in the new configuration.
|
||||
The latter is less correct because it runs the
|
||||
<literal>ExecStop</literal> commands from the new
|
||||
configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
startAt = mkOption {
|
||||
type = with types; either str (listOf str);
|
||||
default = [];
|
||||
example = "Sun 14:00:00";
|
||||
description = ''
|
||||
Automatically start this unit at the given date/time, which
|
||||
must be in the format described in
|
||||
<citerefentry><refentrytitle>systemd.time</refentrytitle>
|
||||
<manvolnum>7</manvolnum></citerefentry>. This is equivalent
|
||||
to adding a corresponding timer unit with
|
||||
<option>OnCalendar</option> set to the value given here.
|
||||
'';
|
||||
apply = v: if isList v then v else [ v ];
|
||||
};
|
||||
};
|
||||
|
||||
scriptArgs = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = "Arguments passed to the main process script.";
|
||||
};
|
||||
|
||||
preStart = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed before the service's main process
|
||||
is started.
|
||||
'';
|
||||
};
|
||||
|
||||
postStart = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed after the service's main process
|
||||
is started.
|
||||
'';
|
||||
};
|
||||
|
||||
reload = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed when the service's main process
|
||||
is reloaded.
|
||||
'';
|
||||
};
|
||||
|
||||
preStop = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed to stop the service.
|
||||
'';
|
||||
};
|
||||
|
||||
postStop = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Shell commands executed after the service's main process
|
||||
has exited.
|
||||
'';
|
||||
};
|
||||
|
||||
restartIfChanged = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Whether the service should be restarted during a NixOS
|
||||
configuration switch if its definition has changed.
|
||||
'';
|
||||
};
|
||||
|
||||
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.
|
||||
|
||||
This option should not be used anymore in favor of
|
||||
<option>reloadTriggers</option> which allows more granular
|
||||
control of when a service is reloaded and when a service
|
||||
is restarted.
|
||||
'';
|
||||
};
|
||||
|
||||
stopIfChanged = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
If set, a changed unit is restarted by calling
|
||||
<command>systemctl stop</command> in the old configuration,
|
||||
then <command>systemctl start</command> in the new one.
|
||||
Otherwise, it is restarted in a single step using
|
||||
<command>systemctl restart</command> in the new configuration.
|
||||
The latter is less correct because it runs the
|
||||
<literal>ExecStop</literal> commands from the new
|
||||
configuration.
|
||||
'';
|
||||
};
|
||||
|
||||
startAt = mkOption {
|
||||
type = with types; either str (listOf str);
|
||||
default = [];
|
||||
example = "Sun 14:00:00";
|
||||
description = ''
|
||||
Automatically start this unit at the given date/time, which
|
||||
must be in the format described in
|
||||
<citerefentry><refentrytitle>systemd.time</refentrytitle>
|
||||
<manvolnum>7</manvolnum></citerefentry>. This is equivalent
|
||||
to adding a corresponding timer unit with
|
||||
<option>OnCalendar</option> set to the value given here.
|
||||
'';
|
||||
apply = v: if isList v then v else [ v ];
|
||||
};
|
||||
config = mkMerge
|
||||
[ (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;
|
||||
})
|
||||
];
|
||||
};
|
||||
|
||||
stage1ServiceOptions = {
|
||||
imports = [
|
||||
stage1CommonUnitOptions
|
||||
serviceOptions
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
socketOptions = commonUnitOptions // {
|
||||
socketOptions = { options = {
|
||||
|
||||
listenStreams = mkOption {
|
||||
default = [];
|
||||
|
@ -424,10 +475,24 @@ in rec {
|
|||
'';
|
||||
};
|
||||
|
||||
}; };
|
||||
|
||||
stage2SocketOptions = {
|
||||
imports = [
|
||||
stage2CommonUnitOptions
|
||||
socketOptions
|
||||
];
|
||||
};
|
||||
|
||||
stage1SocketOptions = {
|
||||
imports = [
|
||||
stage1CommonUnitOptions
|
||||
socketOptions
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
timerOptions = commonUnitOptions // {
|
||||
timerOptions = { options = {
|
||||
|
||||
timerConfig = mkOption {
|
||||
default = {};
|
||||
|
@ -443,10 +508,24 @@ in rec {
|
|||
'';
|
||||
};
|
||||
|
||||
}; };
|
||||
|
||||
stage2TimerOptions = {
|
||||
imports = [
|
||||
stage2CommonUnitOptions
|
||||
timerOptions
|
||||
];
|
||||
};
|
||||
|
||||
stage1TimerOptions = {
|
||||
imports = [
|
||||
stage1CommonUnitOptions
|
||||
timerOptions
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
pathOptions = commonUnitOptions // {
|
||||
pathOptions = { options = {
|
||||
|
||||
pathConfig = mkOption {
|
||||
default = {};
|
||||
|
@ -460,10 +539,24 @@ in rec {
|
|||
'';
|
||||
};
|
||||
|
||||
}; };
|
||||
|
||||
stage2PathOptions = {
|
||||
imports = [
|
||||
stage2CommonUnitOptions
|
||||
pathOptions
|
||||
];
|
||||
};
|
||||
|
||||
stage1PathOptions = {
|
||||
imports = [
|
||||
stage1CommonUnitOptions
|
||||
pathOptions
|
||||
];
|
||||
};
|
||||
|
||||
|
||||
mountOptions = commonUnitOptions // {
|
||||
mountOptions = { options = {
|
||||
|
||||
what = mkOption {
|
||||
example = "/dev/sda1";
|
||||
|
@ -505,9 +598,23 @@ in rec {
|
|||
<manvolnum>5</manvolnum></citerefentry> for details.
|
||||
'';
|
||||
};
|
||||
}; };
|
||||
|
||||
stage2MountOptions = {
|
||||
imports = [
|
||||
stage2CommonUnitOptions
|
||||
mountOptions
|
||||
];
|
||||
};
|
||||
|
||||
automountOptions = commonUnitOptions // {
|
||||
stage1MountOptions = {
|
||||
imports = [
|
||||
stage1CommonUnitOptions
|
||||
mountOptions
|
||||
];
|
||||
};
|
||||
|
||||
automountOptions = { options = {
|
||||
|
||||
where = mkOption {
|
||||
example = "/mnt";
|
||||
|
@ -529,11 +636,23 @@ in rec {
|
|||
<manvolnum>5</manvolnum></citerefentry> for details.
|
||||
'';
|
||||
};
|
||||
}; };
|
||||
|
||||
stage2AutomountOptions = {
|
||||
imports = [
|
||||
stage2CommonUnitOptions
|
||||
automountOptions
|
||||
];
|
||||
};
|
||||
|
||||
targetOptions = commonUnitOptions;
|
||||
stage1AutomountOptions = {
|
||||
imports = [
|
||||
stage1CommonUnitOptions
|
||||
automountOptions
|
||||
];
|
||||
};
|
||||
|
||||
sliceOptions = commonUnitOptions // {
|
||||
sliceOptions = { options = {
|
||||
|
||||
sliceConfig = mkOption {
|
||||
default = {};
|
||||
|
@ -547,6 +666,20 @@ in rec {
|
|||
'';
|
||||
};
|
||||
|
||||
}; };
|
||||
|
||||
stage2SliceOptions = {
|
||||
imports = [
|
||||
stage2CommonUnitOptions
|
||||
sliceOptions
|
||||
];
|
||||
};
|
||||
|
||||
stage1SliceOptions = {
|
||||
imports = [
|
||||
stage1CommonUnitOptions
|
||||
sliceOptions
|
||||
];
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -197,5 +197,6 @@ rec {
|
|||
systemdUtils = {
|
||||
lib = import ./systemd-lib.nix { inherit lib config pkgs; };
|
||||
unitOptions = import ./systemd-unit-options.nix { inherit lib systemdUtils; };
|
||||
types = import ./systemd-types.nix { inherit lib systemdUtils; };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -15,6 +15,26 @@ let
|
|||
mapAttrsToList (n: v: ''${n}=${escapeIfNeccessary (toString v)}'') attrs
|
||||
);
|
||||
|
||||
osReleaseContents = {
|
||||
NAME = "NixOS";
|
||||
ID = "nixos";
|
||||
VERSION = "${cfg.release} (${cfg.codeName})";
|
||||
VERSION_CODENAME = toLower cfg.codeName;
|
||||
VERSION_ID = cfg.release;
|
||||
BUILD_ID = cfg.version;
|
||||
PRETTY_NAME = "NixOS ${cfg.release} (${cfg.codeName})";
|
||||
LOGO = "nix-snowflake";
|
||||
HOME_URL = "https://nixos.org/";
|
||||
DOCUMENTATION_URL = "https://nixos.org/learn.html";
|
||||
SUPPORT_URL = "https://nixos.org/community.html";
|
||||
BUG_REPORT_URL = "https://github.com/NixOS/nixpkgs/issues";
|
||||
};
|
||||
|
||||
initrdReleaseContents = osReleaseContents // {
|
||||
PRETTY_NAME = "${osReleaseContents.PRETTY_NAME} (Initrd)";
|
||||
};
|
||||
initrdRelease = pkgs.writeText "initrd-release" (attrsToText initrdReleaseContents);
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
|
@ -119,20 +139,12 @@ in
|
|||
DISTRIB_DESCRIPTION = "NixOS ${cfg.release} (${cfg.codeName})";
|
||||
};
|
||||
|
||||
"os-release".text = attrsToText {
|
||||
NAME = "NixOS";
|
||||
ID = "nixos";
|
||||
VERSION = "${cfg.release} (${cfg.codeName})";
|
||||
VERSION_CODENAME = toLower cfg.codeName;
|
||||
VERSION_ID = cfg.release;
|
||||
BUILD_ID = cfg.version;
|
||||
PRETTY_NAME = "NixOS ${cfg.release} (${cfg.codeName})";
|
||||
LOGO = "nix-snowflake";
|
||||
HOME_URL = "https://nixos.org/";
|
||||
DOCUMENTATION_URL = "https://nixos.org/learn.html";
|
||||
SUPPORT_URL = "https://nixos.org/community.html";
|
||||
BUG_REPORT_URL = "https://github.com/NixOS/nixpkgs/issues";
|
||||
};
|
||||
"os-release".text = attrsToText osReleaseContents;
|
||||
};
|
||||
|
||||
boot.initrd.systemd.contents = {
|
||||
"/etc/os-release".source = initrdRelease;
|
||||
"/etc/initrd-release".source = initrdRelease;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1181,6 +1181,7 @@
|
|||
./system/boot/systemd/nspawn.nix
|
||||
./system/boot/systemd/tmpfiles.nix
|
||||
./system/boot/systemd/user.nix
|
||||
./system/boot/systemd/initrd.nix
|
||||
./system/boot/timesyncd.nix
|
||||
./system/boot/tmp.nix
|
||||
./system/etc/etc-activation.nix
|
||||
|
|
|
@ -703,8 +703,12 @@ in
|
|||
}
|
||||
];
|
||||
|
||||
system.build =
|
||||
{ inherit bootStage1 initialRamdisk initialRamdiskSecretAppender extraUtils; };
|
||||
system.build = mkMerge [
|
||||
{ inherit bootStage1 initialRamdiskSecretAppender extraUtils; }
|
||||
|
||||
# generated in nixos/modules/system/boot/systemd/initrd.nix
|
||||
(mkIf (!config.boot.initrd.systemd.enable) { inherit initialRamdisk; })
|
||||
];
|
||||
|
||||
system.requiredKernelConfig = with config.lib.kernelConfig; [
|
||||
(isYes "TMPFS")
|
||||
|
|
|
@ -11,14 +11,7 @@ let
|
|||
systemd = cfg.package;
|
||||
|
||||
inherit (systemdUtils.lib)
|
||||
makeUnit
|
||||
generateUnits
|
||||
makeJobScript
|
||||
unitConfig
|
||||
serviceConfig
|
||||
mountConfig
|
||||
automountConfig
|
||||
commonUnitText
|
||||
targetToUnit
|
||||
serviceToUnit
|
||||
socketToUnit
|
||||
|
@ -185,13 +178,7 @@ in
|
|||
systemd.units = mkOption {
|
||||
description = "Definition of systemd units.";
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule (
|
||||
{ name, config, ... }:
|
||||
{ options = concreteUnitOptions;
|
||||
config = {
|
||||
unit = mkDefault (makeUnit name config);
|
||||
};
|
||||
}));
|
||||
type = systemdUtils.types.units;
|
||||
};
|
||||
|
||||
systemd.packages = mkOption {
|
||||
|
@ -203,37 +190,37 @@ in
|
|||
|
||||
systemd.targets = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
|
||||
type = systemdUtils.types.targets;
|
||||
description = "Definition of systemd target units.";
|
||||
};
|
||||
|
||||
systemd.services = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ]);
|
||||
type = systemdUtils.types.services;
|
||||
description = "Definition of systemd service units.";
|
||||
};
|
||||
|
||||
systemd.sockets = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ]);
|
||||
type = systemdUtils.types.sockets;
|
||||
description = "Definition of systemd socket units.";
|
||||
};
|
||||
|
||||
systemd.timers = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ]);
|
||||
type = systemdUtils.types.timers;
|
||||
description = "Definition of systemd timer units.";
|
||||
};
|
||||
|
||||
systemd.paths = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
|
||||
type = systemdUtils.types.paths;
|
||||
description = "Definition of systemd path units.";
|
||||
};
|
||||
|
||||
systemd.mounts = mkOption {
|
||||
default = [];
|
||||
type = with types; listOf (submodule [ { options = mountOptions; } unitConfig mountConfig ]);
|
||||
type = systemdUtils.types.mounts;
|
||||
description = ''
|
||||
Definition of systemd mount units.
|
||||
This is a list instead of an attrSet, because systemd mandates the names to be derived from
|
||||
|
@ -243,7 +230,7 @@ in
|
|||
|
||||
systemd.automounts = mkOption {
|
||||
default = [];
|
||||
type = with types; listOf (submodule [ { options = automountOptions; } unitConfig automountConfig ]);
|
||||
type = systemdUtils.types.automounts;
|
||||
description = ''
|
||||
Definition of systemd automount units.
|
||||
This is a list instead of an attrSet, because systemd mandates the names to be derived from
|
||||
|
@ -253,7 +240,7 @@ in
|
|||
|
||||
systemd.slices = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig] );
|
||||
type = systemdUtils.types.slices;
|
||||
description = "Definition of slice configurations.";
|
||||
};
|
||||
|
||||
|
@ -362,10 +349,11 @@ in
|
|||
type = types.listOf types.str;
|
||||
example = [ "systemd-backlight@.service" ];
|
||||
description = ''
|
||||
A list of units to suppress when generating system systemd configuration directory. This has
|
||||
A list of units to skip when generating system systemd configuration directory. This has
|
||||
priority over upstream units, <option>systemd.units</option>, and
|
||||
<option>systemd.additionalUpstreamSystemUnits</option>. The main purpose of this is to
|
||||
suppress a upstream systemd unit with any modifications made to it by other NixOS modules.
|
||||
prevent a upstream systemd unit from being added to the initrd with any modifications made to it
|
||||
by other NixOS modules.
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -482,7 +470,12 @@ in
|
|||
enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedSystemUnits) cfg.units;
|
||||
|
||||
in ({
|
||||
"systemd/system".source = generateUnits "system" enabledUnits enabledUpstreamSystemUnits upstreamSystemWants;
|
||||
"systemd/system".source = generateUnits {
|
||||
type = "system";
|
||||
units = enabledUnits;
|
||||
upstreamUnits = enabledUpstreamSystemUnits;
|
||||
upstreamWants = upstreamSystemWants;
|
||||
};
|
||||
|
||||
"systemd/system.conf".text = ''
|
||||
[Manager]
|
||||
|
|
417
nixos/modules/system/boot/systemd/initrd.nix
Normal file
417
nixos/modules/system/boot/systemd/initrd.nix
Normal file
|
@ -0,0 +1,417 @@
|
|||
{ lib, config, utils, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
inherit (utils) systemdUtils escapeSystemdPath;
|
||||
inherit (systemdUtils.lib)
|
||||
generateUnits
|
||||
pathToUnit
|
||||
serviceToUnit
|
||||
sliceToUnit
|
||||
socketToUnit
|
||||
targetToUnit
|
||||
timerToUnit
|
||||
mountToUnit
|
||||
automountToUnit;
|
||||
|
||||
|
||||
cfg = config.boot.initrd.systemd;
|
||||
|
||||
# Copied from fedora
|
||||
upstreamUnits = [
|
||||
"basic.target"
|
||||
"ctrl-alt-del.target"
|
||||
"emergency.service"
|
||||
"emergency.target"
|
||||
"final.target"
|
||||
"halt.target"
|
||||
"initrd-cleanup.service"
|
||||
"initrd-fs.target"
|
||||
"initrd-parse-etc.service"
|
||||
"initrd-root-device.target"
|
||||
"initrd-root-fs.target"
|
||||
"initrd-switch-root.service"
|
||||
"initrd-switch-root.target"
|
||||
"initrd.target"
|
||||
"initrd-udevadm-cleanup-db.service"
|
||||
"kexec.target"
|
||||
"kmod-static-nodes.service"
|
||||
"local-fs-pre.target"
|
||||
"local-fs.target"
|
||||
"multi-user.target"
|
||||
"paths.target"
|
||||
"poweroff.target"
|
||||
"reboot.target"
|
||||
"rescue.service"
|
||||
"rescue.target"
|
||||
"rpcbind.target"
|
||||
"shutdown.target"
|
||||
"sigpwr.target"
|
||||
"slices.target"
|
||||
"sockets.target"
|
||||
"swap.target"
|
||||
"sysinit.target"
|
||||
"sys-kernel-config.mount"
|
||||
"syslog.socket"
|
||||
"systemd-ask-password-console.path"
|
||||
"systemd-ask-password-console.service"
|
||||
"systemd-fsck@.service"
|
||||
"systemd-halt.service"
|
||||
"systemd-hibernate-resume@.service"
|
||||
"systemd-journald-audit.socket"
|
||||
"systemd-journald-dev-log.socket"
|
||||
"systemd-journald.service"
|
||||
"systemd-journald.socket"
|
||||
"systemd-kexec.service"
|
||||
"systemd-modules-load.service"
|
||||
"systemd-poweroff.service"
|
||||
"systemd-random-seed.service"
|
||||
"systemd-reboot.service"
|
||||
"systemd-sysctl.service"
|
||||
"systemd-tmpfiles-setup-dev.service"
|
||||
"systemd-tmpfiles-setup.service"
|
||||
"systemd-udevd-control.socket"
|
||||
"systemd-udevd-kernel.socket"
|
||||
"systemd-udevd.service"
|
||||
"systemd-udev-settle.service"
|
||||
"systemd-udev-trigger.service"
|
||||
"systemd-vconsole-setup.service"
|
||||
"timers.target"
|
||||
"umount.target"
|
||||
|
||||
# TODO: Networking
|
||||
# "network-online.target"
|
||||
# "network-pre.target"
|
||||
# "network.target"
|
||||
# "nss-lookup.target"
|
||||
# "nss-user-lookup.target"
|
||||
# "remote-fs-pre.target"
|
||||
# "remote-fs.target"
|
||||
] ++ cfg.additionalUpstreamUnits;
|
||||
|
||||
upstreamWants = [
|
||||
"sysinit.target.wants"
|
||||
];
|
||||
|
||||
enabledUpstreamUnits = filter (n: ! elem n cfg.suppressedUnits) upstreamUnits;
|
||||
enabledUnits = filterAttrs (n: v: ! elem n cfg.suppressedUnits) cfg.units;
|
||||
|
||||
stage1Units = generateUnits {
|
||||
type = "initrd";
|
||||
units = enabledUnits;
|
||||
upstreamUnits = enabledUpstreamUnits;
|
||||
inherit upstreamWants;
|
||||
inherit (cfg) packages package;
|
||||
};
|
||||
|
||||
fileSystems = filter utils.fsNeededForBoot config.system.build.fileSystems;
|
||||
|
||||
fstab = pkgs.writeText "fstab" (lib.concatMapStringsSep "\n"
|
||||
({ fsType, mountPoint, device, options, autoFormat, autoResize, ... }@fs: let
|
||||
opts = options ++ optional autoFormat "x-systemd.makefs" ++ optional autoResize "x-systemd.growfs";
|
||||
in "${device} /sysroot${mountPoint} ${fsType} ${lib.concatStringsSep "," opts}") fileSystems);
|
||||
|
||||
kernel-name = config.boot.kernelPackages.kernel.name or "kernel";
|
||||
modulesTree = config.system.modulesTree.override { name = kernel-name + "-modules"; };
|
||||
firmware = config.hardware.firmware;
|
||||
# Determine the set of modules that we need to mount the root FS.
|
||||
modulesClosure = pkgs.makeModulesClosure {
|
||||
rootModules = config.boot.initrd.availableKernelModules ++ config.boot.initrd.kernelModules;
|
||||
kernel = modulesTree;
|
||||
firmware = firmware;
|
||||
allowMissing = false;
|
||||
};
|
||||
|
||||
initrdBinEnv = pkgs.buildEnv {
|
||||
name = "initrd-emergency-env";
|
||||
paths = map getBin cfg.initrdBin;
|
||||
pathsToLink = ["/bin" "/sbin"];
|
||||
# Make recovery easier
|
||||
postBuild = ''
|
||||
ln -s ${cfg.package.util-linux}/bin/mount $out/bin/
|
||||
ln -s ${cfg.package.util-linux}/bin/umount $out/bin/
|
||||
'';
|
||||
};
|
||||
|
||||
initialRamdisk = pkgs.makeInitrdNG {
|
||||
contents = map (path: { object = path; symlink = ""; }) (subtractLists cfg.suppressedStorePaths cfg.storePaths)
|
||||
++ mapAttrsToList (_: v: { object = v.source; symlink = v.target; }) (filterAttrs (_: v: v.enable) cfg.contents);
|
||||
};
|
||||
|
||||
in {
|
||||
options.boot.initrd.systemd = {
|
||||
enable = mkEnableOption ''systemd in initrd.
|
||||
|
||||
Note: This is in very early development and is highly
|
||||
experimental. Most of the features NixOS supports in initrd are
|
||||
not yet supported by the intrd generated with this option.
|
||||
'';
|
||||
|
||||
package = (mkPackageOption pkgs "systemd" {
|
||||
default = "systemdMinimal";
|
||||
}) // {
|
||||
visible = false;
|
||||
};
|
||||
|
||||
contents = mkOption {
|
||||
description = "Set of files that have to be linked into the initrd";
|
||||
example = literalExpression ''
|
||||
{
|
||||
"/etc/hostname".text = "mymachine";
|
||||
}
|
||||
'';
|
||||
visible = false;
|
||||
default = {};
|
||||
type = types.attrsOf (types.submodule ({ config, options, name, ... }: {
|
||||
options = {
|
||||
enable = mkEnableOption "copying of this file to initrd and symlinking it" // { default = true; };
|
||||
|
||||
target = mkOption {
|
||||
type = types.path;
|
||||
description = ''
|
||||
Path of the symlink.
|
||||
'';
|
||||
default = name;
|
||||
};
|
||||
|
||||
text = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.lines;
|
||||
description = "Text of the file.";
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
type = types.path;
|
||||
description = "Path of the source file.";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
source = mkIf (config.text != null) (
|
||||
let name' = "initrd-" + baseNameOf name;
|
||||
in mkDerivedConfig options.text (pkgs.writeText name')
|
||||
);
|
||||
};
|
||||
}));
|
||||
};
|
||||
|
||||
storePaths = mkOption {
|
||||
description = ''
|
||||
Store paths to copy into the initrd as well.
|
||||
'';
|
||||
type = types.listOf types.singleLineStr;
|
||||
default = [];
|
||||
};
|
||||
|
||||
suppressedStorePaths = mkOption {
|
||||
description = ''
|
||||
Store paths specified in the storePaths option that
|
||||
should not be copied.
|
||||
'';
|
||||
type = types.listOf types.singleLineStr;
|
||||
default = [];
|
||||
};
|
||||
|
||||
emergencyAccess = mkOption {
|
||||
type = with types; oneOf [ bool singleLineStr ];
|
||||
visible = false;
|
||||
description = ''
|
||||
Set to true for unauthenticated emergency access, and false for
|
||||
no emergency access.
|
||||
|
||||
Can also be set to a hashed super user password to allow
|
||||
authenticated access to the emergency mode.
|
||||
'';
|
||||
default = false;
|
||||
};
|
||||
|
||||
initrdBin = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [];
|
||||
visible = false;
|
||||
description = ''
|
||||
Packages to include in /bin for the stage 1 emergency shell.
|
||||
'';
|
||||
};
|
||||
|
||||
additionalUpstreamUnits = mkOption {
|
||||
default = [ ];
|
||||
type = types.listOf types.str;
|
||||
visible = false;
|
||||
example = [ "debug-shell.service" "systemd-quotacheck.service" ];
|
||||
description = ''
|
||||
Additional units shipped with systemd that shall be enabled.
|
||||
'';
|
||||
};
|
||||
|
||||
suppressedUnits = mkOption {
|
||||
default = [ ];
|
||||
type = types.listOf types.str;
|
||||
example = [ "systemd-backlight@.service" ];
|
||||
visible = false;
|
||||
description = ''
|
||||
A list of units to skip when generating system systemd configuration directory. This has
|
||||
priority over upstream units, <option>boot.initrd.systemd.units</option>, and
|
||||
<option>boot.initrd.systemd.additionalUpstreamUnits</option>. The main purpose of this is to
|
||||
prevent a upstream systemd unit from being added to the initrd with any modifications made to it
|
||||
by other NixOS modules.
|
||||
'';
|
||||
};
|
||||
|
||||
units = mkOption {
|
||||
description = "Definition of systemd units.";
|
||||
default = {};
|
||||
visible = false;
|
||||
type = systemdUtils.types.units;
|
||||
};
|
||||
|
||||
packages = mkOption {
|
||||
default = [];
|
||||
visible = false;
|
||||
type = types.listOf types.package;
|
||||
example = literalExpression "[ pkgs.systemd-cryptsetup-generator ]";
|
||||
description = "Packages providing systemd units and hooks.";
|
||||
};
|
||||
|
||||
targets = mkOption {
|
||||
default = {};
|
||||
visible = false;
|
||||
type = systemdUtils.types.initrdTargets;
|
||||
description = "Definition of systemd target units.";
|
||||
};
|
||||
|
||||
services = mkOption {
|
||||
default = {};
|
||||
type = systemdUtils.types.initrdServices;
|
||||
visible = false;
|
||||
description = "Definition of systemd service units.";
|
||||
};
|
||||
|
||||
sockets = mkOption {
|
||||
default = {};
|
||||
type = systemdUtils.types.initrdSockets;
|
||||
visible = false;
|
||||
description = "Definition of systemd socket units.";
|
||||
};
|
||||
|
||||
timers = mkOption {
|
||||
default = {};
|
||||
type = systemdUtils.types.initrdTimers;
|
||||
visible = false;
|
||||
description = "Definition of systemd timer units.";
|
||||
};
|
||||
|
||||
paths = mkOption {
|
||||
default = {};
|
||||
type = systemdUtils.types.initrdPaths;
|
||||
visible = false;
|
||||
description = "Definition of systemd path units.";
|
||||
};
|
||||
|
||||
mounts = mkOption {
|
||||
default = [];
|
||||
type = systemdUtils.types.initrdMounts;
|
||||
visible = false;
|
||||
description = ''
|
||||
Definition of systemd mount units.
|
||||
This is a list instead of an attrSet, because systemd mandates the names to be derived from
|
||||
the 'where' attribute.
|
||||
'';
|
||||
};
|
||||
|
||||
automounts = mkOption {
|
||||
default = [];
|
||||
type = systemdUtils.types.automounts;
|
||||
visible = false;
|
||||
description = ''
|
||||
Definition of systemd automount units.
|
||||
This is a list instead of an attrSet, because systemd mandates the names to be derived from
|
||||
the 'where' attribute.
|
||||
'';
|
||||
};
|
||||
|
||||
slices = mkOption {
|
||||
default = {};
|
||||
type = systemdUtils.types.slices;
|
||||
visible = false;
|
||||
description = "Definition of slice configurations.";
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (config.boot.initrd.enable && cfg.enable) {
|
||||
system.build = { inherit initialRamdisk; };
|
||||
boot.initrd.systemd = {
|
||||
initrdBin = [pkgs.bash pkgs.coreutils pkgs.kmod cfg.package] ++ config.system.fsPackages;
|
||||
|
||||
contents = {
|
||||
"/init".source = "${cfg.package}/lib/systemd/systemd";
|
||||
"/etc/systemd/system".source = stage1Units;
|
||||
|
||||
"/etc/systemd/system.conf".text = ''
|
||||
[Manager]
|
||||
DefaultEnvironment=PATH=/bin:/sbin
|
||||
'';
|
||||
|
||||
"/etc/fstab".source = fstab;
|
||||
|
||||
"/lib/modules".source = "${modulesClosure}/lib/modules";
|
||||
|
||||
"/etc/modules-load.d/nixos.conf".text = concatStringsSep "\n" config.boot.initrd.kernelModules;
|
||||
|
||||
"/etc/passwd".source = "${pkgs.fakeNss}/etc/passwd";
|
||||
"/etc/shadow".text = "root:${if isBool cfg.emergencyAccess then "!" else cfg.emergencyAccess}:::::::";
|
||||
|
||||
"/bin".source = "${initrdBinEnv}/bin";
|
||||
"/sbin".source = "${initrdBinEnv}/sbin";
|
||||
|
||||
"/etc/sysctl.d/nixos.conf".text = "kernel.modprobe = /sbin/modprobe";
|
||||
};
|
||||
|
||||
storePaths = [
|
||||
# TODO: Limit this to the bare necessities
|
||||
"${cfg.package}/lib"
|
||||
|
||||
"${cfg.package.util-linux}/bin/mount"
|
||||
"${cfg.package.util-linux}/bin/umount"
|
||||
"${cfg.package.util-linux}/bin/sulogin"
|
||||
|
||||
# so NSS can look up usernames
|
||||
"${pkgs.glibc}/lib/libnss_files.so"
|
||||
];
|
||||
|
||||
targets.initrd.aliases = ["default.target"];
|
||||
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
|
||||
// listToAttrs (map
|
||||
(v: let n = escapeSystemdPath v.where;
|
||||
in nameValuePair "${n}.mount" (mountToUnit n v)) cfg.mounts)
|
||||
// listToAttrs (map
|
||||
(v: let n = escapeSystemdPath v.where;
|
||||
in nameValuePair "${n}.automount" (automountToUnit n v)) cfg.automounts);
|
||||
|
||||
services.emergency = mkIf (isBool cfg.emergencyAccess && cfg.emergencyAccess) {
|
||||
environment.SYSTEMD_SULOGIN_FORCE = "1";
|
||||
};
|
||||
# The unit in /run/systemd/generator shadows the unit in
|
||||
# /etc/systemd/system, but will still apply drop-ins from
|
||||
# /etc/systemd/system/foo.service.d/
|
||||
#
|
||||
# We need IgnoreOnIsolate, otherwise the Requires dependency of
|
||||
# a mount unit on its makefs unit causes it to be unmounted when
|
||||
# we isolate for switch-root. Use a dummy package so that
|
||||
# generateUnits will generate drop-ins instead of unit files.
|
||||
packages = [(pkgs.runCommand "dummy" {} ''
|
||||
mkdir -p $out/etc/systemd/system
|
||||
touch $out/etc/systemd/system/systemd-{makefs,growfs}@.service
|
||||
'')];
|
||||
services."systemd-makefs@".unitConfig.IgnoreOnIsolate = true;
|
||||
services."systemd-growfs@".unitConfig.IgnoreOnIsolate = true;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -116,7 +116,13 @@ in {
|
|||
in
|
||||
mkMerge [
|
||||
(mkIf (cfg != {}) {
|
||||
environment.etc."systemd/nspawn".source = mkIf (cfg != {}) (generateUnits' false "nspawn" units [] []);
|
||||
environment.etc."systemd/nspawn".source = mkIf (cfg != {}) (generateUnits {
|
||||
allowCollisions = false;
|
||||
type = "nspawn";
|
||||
inherit units;
|
||||
upstreamUnits = [];
|
||||
upstreamWants = [];
|
||||
});
|
||||
})
|
||||
{
|
||||
systemd.targets.multi-user.wants = [ "machines.target" ];
|
||||
|
|
|
@ -12,10 +12,6 @@ let
|
|||
(systemdUtils.lib)
|
||||
makeUnit
|
||||
generateUnits
|
||||
makeJobScript
|
||||
unitConfig
|
||||
serviceConfig
|
||||
commonUnitText
|
||||
targetToUnit
|
||||
serviceToUnit
|
||||
socketToUnit
|
||||
|
@ -57,48 +53,42 @@ in {
|
|||
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);
|
||||
};
|
||||
}));
|
||||
type = systemdUtils.types.units;
|
||||
};
|
||||
|
||||
systemd.user.paths = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = pathOptions; } unitConfig ]);
|
||||
type = systemdUtils.types.paths;
|
||||
description = "Definition of systemd per-user path units.";
|
||||
};
|
||||
|
||||
systemd.user.services = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = serviceOptions; } unitConfig serviceConfig ] );
|
||||
type = systemdUtils.types.services;
|
||||
description = "Definition of systemd per-user service units.";
|
||||
};
|
||||
|
||||
systemd.user.slices = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = sliceOptions; } unitConfig ] );
|
||||
type = systemdUtils.types.slices;
|
||||
description = "Definition of systemd per-user slice units.";
|
||||
};
|
||||
|
||||
systemd.user.sockets = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = socketOptions; } unitConfig ] );
|
||||
type = systemdUtils.types.sockets;
|
||||
description = "Definition of systemd per-user socket units.";
|
||||
};
|
||||
|
||||
systemd.user.targets = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = targetOptions; } unitConfig] );
|
||||
type = systemdUtils.types.targets;
|
||||
description = "Definition of systemd per-user target units.";
|
||||
};
|
||||
|
||||
systemd.user.timers = mkOption {
|
||||
default = {};
|
||||
type = with types; attrsOf (submodule [ { options = timerOptions; } unitConfig ] );
|
||||
type = systemdUtils.types.timers;
|
||||
description = "Definition of systemd per-user timer units.";
|
||||
};
|
||||
|
||||
|
@ -119,7 +109,12 @@ in {
|
|||
];
|
||||
|
||||
environment.etc = {
|
||||
"systemd/user".source = generateUnits "user" cfg.units upstreamUserUnits [];
|
||||
"systemd/user".source = generateUnits {
|
||||
type = "user";
|
||||
inherit (cfg) units;
|
||||
upstreamUnits = upstreamUserUnits;
|
||||
upstreamWants = [];
|
||||
};
|
||||
|
||||
"systemd/user.conf".text = ''
|
||||
[Manager]
|
||||
|
|
|
@ -352,7 +352,7 @@ in
|
|||
unitConfig.DefaultDependencies = false; # needed to prevent a cycle
|
||||
serviceConfig.Type = "oneshot";
|
||||
};
|
||||
in listToAttrs (map formatDevice (filter (fs: fs.autoFormat) fileSystems)) // {
|
||||
in listToAttrs (map formatDevice (filter (fs: fs.autoFormat && !(utils.fsNeededForBoot fs)) fileSystems)) // {
|
||||
# Mount /sys/fs/pstore for evacuating panic logs and crashdumps from persistent storage onto the disk using systemd-pstore.
|
||||
# This cannot be done with the other special filesystems because the pstore module (which creates the mount point) is not loaded then.
|
||||
"mount-pstore" = {
|
||||
|
|
|
@ -923,6 +923,8 @@ in
|
|||
mkVMOverride (cfg.fileSystems //
|
||||
{
|
||||
"/".device = cfg.bootDevice;
|
||||
"/".fsType = "ext4";
|
||||
"/".autoFormat = true;
|
||||
|
||||
"/tmp" = mkIf config.boot.tmpOnTmpfs
|
||||
{ device = "tmpfs";
|
||||
|
@ -953,6 +955,28 @@ in
|
|||
};
|
||||
} // lib.mapAttrs' mkSharedDir cfg.sharedDirectories);
|
||||
|
||||
boot.initrd.systemd = lib.mkIf (config.boot.initrd.systemd.enable && cfg.writableStore) {
|
||||
mounts = [{
|
||||
where = "/sysroot/nix/store";
|
||||
what = "overlay";
|
||||
type = "overlay";
|
||||
options = "lowerdir=/sysroot/nix/.ro-store,upperdir=/sysroot/nix/.rw-store/store,workdir=/sysroot/nix/.rw-store/work";
|
||||
wantedBy = ["local-fs.target"];
|
||||
before = ["local-fs.target"];
|
||||
requires = ["sysroot-nix-.ro\\x2dstore.mount" "sysroot-nix-.rw\\x2dstore.mount" "rw-store.service"];
|
||||
after = ["sysroot-nix-.ro\\x2dstore.mount" "sysroot-nix-.rw\\x2dstore.mount" "rw-store.service"];
|
||||
unitConfig.IgnoreOnIsolate = true;
|
||||
}];
|
||||
services.rw-store = {
|
||||
after = ["sysroot-nix-.rw\\x2dstore.mount"];
|
||||
unitConfig.DefaultDependencies = false;
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
ExecStart = "/bin/mkdir -p 0755 /sysroot/nix/.rw-store/store /sysroot/nix/.rw-store/work /sysroot/nix/store";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
swapDevices = mkVMOverride [ ];
|
||||
boot.initrd.luks.devices = mkVMOverride {};
|
||||
|
||||
|
|
|
@ -514,6 +514,7 @@ in
|
|||
systemd-confinement = handleTest ./systemd-confinement.nix {};
|
||||
systemd-cryptenroll = handleTest ./systemd-cryptenroll.nix {};
|
||||
systemd-escaping = handleTest ./systemd-escaping.nix {};
|
||||
systemd-initrd-simple = handleTest ./systemd-initrd-simple.nix {};
|
||||
systemd-journal = handleTest ./systemd-journal.nix {};
|
||||
systemd-machinectl = handleTest ./systemd-machinectl.nix {};
|
||||
systemd-networkd = handleTest ./systemd-networkd.nix {};
|
||||
|
|
27
nixos/tests/systemd-initrd-simple.nix
Normal file
27
nixos/tests/systemd-initrd-simple.nix
Normal file
|
@ -0,0 +1,27 @@
|
|||
import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
name = "systemd-initrd-simple";
|
||||
|
||||
machine = { pkgs, ... }: {
|
||||
boot.initrd.systemd = {
|
||||
enable = true;
|
||||
emergencyAccess = true;
|
||||
};
|
||||
fileSystems = lib.mkVMOverride {
|
||||
"/".autoResize = true;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import subprocess
|
||||
|
||||
oldAvail = machine.succeed("df --output=avail / | sed 1d")
|
||||
machine.shutdown()
|
||||
|
||||
subprocess.check_call(["qemu-img", "resize", "vm-state-machine/machine.qcow2", "+1G"])
|
||||
|
||||
machine.start()
|
||||
newAvail = machine.succeed("df --output=avail / | sed 1d")
|
||||
|
||||
assert int(oldAvail) < int(newAvail), "File system did not grow"
|
||||
'';
|
||||
})
|
|
@ -6,6 +6,7 @@
|
|||
, coreutils
|
||||
, e2fsprogs
|
||||
, fakechroot
|
||||
, fakeNss
|
||||
, fakeroot
|
||||
, findutils
|
||||
, go
|
||||
|
@ -747,25 +748,7 @@ rec {
|
|||
# Useful when packaging binaries that insist on using nss to look up
|
||||
# username/groups (like nginx).
|
||||
# /bin/sh is fine to not exist, and provided by another shim.
|
||||
fakeNss = symlinkJoin {
|
||||
name = "fake-nss";
|
||||
paths = [
|
||||
(writeTextDir "etc/passwd" ''
|
||||
root:x:0:0:root user:/var/empty:/bin/sh
|
||||
nobody:x:65534:65534:nobody:/var/empty:/bin/sh
|
||||
'')
|
||||
(writeTextDir "etc/group" ''
|
||||
root:x:0:
|
||||
nobody:x:65534:
|
||||
'')
|
||||
(writeTextDir "etc/nsswitch.conf" ''
|
||||
hosts: files dns
|
||||
'')
|
||||
(runCommand "var-empty" { } ''
|
||||
mkdir -p $out/var/empty
|
||||
'')
|
||||
];
|
||||
};
|
||||
inherit fakeNss; # alias
|
||||
|
||||
# This provides a /usr/bin/env, for shell scripts using the
|
||||
# "#!/usr/bin/env executable" shebang.
|
||||
|
|
24
pkgs/build-support/fake-nss/default.nix
Normal file
24
pkgs/build-support/fake-nss/default.nix
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Provide a /etc/passwd and /etc/group that contain root and nobody.
|
||||
# Useful when packaging binaries that insist on using nss to look up
|
||||
# username/groups (like nginx).
|
||||
# /bin/sh is fine to not exist, and provided by another shim.
|
||||
{ symlinkJoin, writeTextDir, runCommand }:
|
||||
symlinkJoin {
|
||||
name = "fake-nss";
|
||||
paths = [
|
||||
(writeTextDir "etc/passwd" ''
|
||||
root:x:0:0:root user:/var/empty:/bin/sh
|
||||
nobody:x:65534:65534:nobody:/var/empty:/bin/sh
|
||||
'')
|
||||
(writeTextDir "etc/group" ''
|
||||
root:x:0:
|
||||
nobody:x:65534:
|
||||
'')
|
||||
(writeTextDir "etc/nsswitch.conf" ''
|
||||
hosts: files dns
|
||||
'')
|
||||
(runCommand "var-empty" { } ''
|
||||
mkdir -p $out/var/empty
|
||||
'')
|
||||
];
|
||||
}
|
9
pkgs/build-support/kernel/make-initrd-ng-tool.nix
Normal file
9
pkgs/build-support/kernel/make-initrd-ng-tool.nix
Normal file
|
@ -0,0 +1,9 @@
|
|||
{ rustPlatform }:
|
||||
|
||||
rustPlatform.buildRustPackage {
|
||||
pname = "make-initrd-ng";
|
||||
version = "0.1.0";
|
||||
|
||||
src = ./make-initrd-ng;
|
||||
cargoLock.lockFile = ./make-initrd-ng/Cargo.lock;
|
||||
}
|
79
pkgs/build-support/kernel/make-initrd-ng.nix
Normal file
79
pkgs/build-support/kernel/make-initrd-ng.nix
Normal file
|
@ -0,0 +1,79 @@
|
|||
let
|
||||
# Some metadata on various compression programs, relevant to naming
|
||||
# the initramfs file and, if applicable, generating a u-boot image
|
||||
# from it.
|
||||
compressors = import ./initrd-compressor-meta.nix;
|
||||
# Get the basename of the actual compression program from the whole
|
||||
# compression command, for the purpose of guessing the u-boot
|
||||
# compression type and filename extension.
|
||||
compressorName = fullCommand: builtins.elemAt (builtins.match "([^ ]*/)?([^ ]+).*" fullCommand) 1;
|
||||
in
|
||||
{ stdenvNoCC, perl, cpio, ubootTools, lib, pkgsBuildHost, makeInitrdNGTool, patchelf, runCommand, glibc
|
||||
# Name of the derivation (not of the resulting file!)
|
||||
, name ? "initrd"
|
||||
|
||||
# Program used to compress the cpio archive; use "cat" for no compression.
|
||||
# This can also be a function which takes a package set and returns the path to the compressor,
|
||||
# such as `pkgs: "${pkgs.lzop}/bin/lzop"`.
|
||||
, compressor ? "gzip"
|
||||
, _compressorFunction ?
|
||||
if lib.isFunction compressor then compressor
|
||||
else if ! builtins.hasContext compressor && builtins.hasAttr compressor compressors then compressors.${compressor}.executable
|
||||
else _: compressor
|
||||
, _compressorExecutable ? _compressorFunction pkgsBuildHost
|
||||
, _compressorName ? compressorName _compressorExecutable
|
||||
, _compressorMeta ? compressors.${_compressorName} or {}
|
||||
|
||||
# List of arguments to pass to the compressor program, or null to use its defaults
|
||||
, compressorArgs ? null
|
||||
, _compressorArgsReal ? if compressorArgs == null then _compressorMeta.defaultArgs or [] else compressorArgs
|
||||
|
||||
# Filename extension to use for the compressed initramfs. This is
|
||||
# included for clarity, but $out/initrd will always be a symlink to
|
||||
# the final image.
|
||||
# If this isn't guessed, you may want to complete the metadata above and send a PR :)
|
||||
, extension ? _compressorMeta.extension or
|
||||
(throw "Unrecognised compressor ${_compressorName}, please specify filename extension")
|
||||
|
||||
# List of { object = path_or_derivation; symlink = "/path"; }
|
||||
# The paths are copied into the initramfs in their nix store path
|
||||
# form, then linked at the root according to `symlink`.
|
||||
, contents
|
||||
|
||||
# List of uncompressed cpio files to prepend to the initramfs. This
|
||||
# can be used to add files in specified paths without them becoming
|
||||
# symlinks to store paths.
|
||||
, prepend ? []
|
||||
|
||||
# Whether to wrap the initramfs in a u-boot image.
|
||||
, makeUInitrd ? stdenvNoCC.hostPlatform.linux-kernel.target == "uImage"
|
||||
|
||||
# If generating a u-boot image, the architecture to use. The default
|
||||
# guess may not align with u-boot's nomenclature correctly, so it can
|
||||
# be overridden.
|
||||
# See https://gitlab.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L81-106 for a list.
|
||||
, uInitrdArch ? stdenvNoCC.hostPlatform.linuxArch
|
||||
|
||||
# The name of the compression, as recognised by u-boot.
|
||||
# See https://gitlab.denx.de/u-boot/u-boot/-/blob/9bfb567e5f1bfe7de8eb41f8c6d00f49d2b9a426/common/image.c#L195-204 for a list.
|
||||
# If this isn't guessed, you may want to complete the metadata above and send a PR :)
|
||||
, uInitrdCompression ? _compressorMeta.ubootName or
|
||||
(throw "Unrecognised compressor ${_compressorName}, please specify uInitrdCompression")
|
||||
}: runCommand name {
|
||||
compress = "${_compressorExecutable} ${lib.escapeShellArgs _compressorArgsReal}";
|
||||
passthru = {
|
||||
compressorExecutableFunction = _compressorFunction;
|
||||
compressorArgs = _compressorArgsReal;
|
||||
};
|
||||
|
||||
passAsFile = ["contents"];
|
||||
contents = lib.concatMapStringsSep "\n" ({ object, symlink, ... }: "${object}\n${if symlink == null then "" else symlink}") contents + "\n";
|
||||
|
||||
nativeBuildInputs = [makeInitrdNGTool patchelf glibc cpio];
|
||||
} ''
|
||||
mkdir ./root
|
||||
make-initrd-ng "$contentsPath" ./root
|
||||
mkdir "$out"
|
||||
(cd root && find * .[^.*] -exec touch -h -d '@1' '{}' +)
|
||||
(cd root && find * .[^.*] -print0 | sort -z | cpio -o -H newc -R +0:+0 --reproducible --null | eval -- $compress >> "$out/initrd")
|
||||
''
|
5
pkgs/build-support/kernel/make-initrd-ng/Cargo.lock
generated
Normal file
5
pkgs/build-support/kernel/make-initrd-ng/Cargo.lock
generated
Normal file
|
@ -0,0 +1,5 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "make-initrd-ng"
|
||||
version = "0.1.0"
|
9
pkgs/build-support/kernel/make-initrd-ng/Cargo.toml
Normal file
9
pkgs/build-support/kernel/make-initrd-ng/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "make-initrd-ng"
|
||||
version = "0.1.0"
|
||||
authors = ["Will Fancher <elvishjerricco@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
79
pkgs/build-support/kernel/make-initrd-ng/README.md
Normal file
79
pkgs/build-support/kernel/make-initrd-ng/README.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
# What is this for?
|
||||
|
||||
NixOS's traditional initrd is generated by listing the paths that
|
||||
should be included in initrd and copying the full runtime closure of
|
||||
those paths into the archive. For most things, like almost any
|
||||
executable, this involves copying the entirety of huge packages like
|
||||
glibc, when only things like the shared library files are needed. To
|
||||
solve this, NixOS does a variety of patchwork to edit the files being
|
||||
copied in so they only refer to small, patched up paths. For instance,
|
||||
executables and their shared library dependencies are copied into an
|
||||
`extraUtils` derivation, and every ELF file is patched to refer to
|
||||
files in that output.
|
||||
|
||||
The problem with this is that it is often difficult to correctly patch
|
||||
some things. For instance, systemd bakes the path to the `mount`
|
||||
command into the binary, so patchelf is no help. Instead, it's very
|
||||
often easier to simply copy the desired files to their original store
|
||||
locations in initrd and not copy their entire runtime closure. This
|
||||
does mean that it is the burden of the developer to ensure that all
|
||||
necessary dependencies are copied in, as closures won't be
|
||||
consulted. However, it is rare that full closures are actually
|
||||
desirable, so in the traditional initrd, the developer was likely to
|
||||
do manual work on patching the dependencies explicitly anyway.
|
||||
|
||||
# How it works
|
||||
|
||||
This program is similar to its inspiration (`find-libs` from the
|
||||
traditional initrd), except that it also handles symlinks and
|
||||
directories according to certain rules. As input, it receives a
|
||||
sequence of pairs of paths. The first path is an object to copy into
|
||||
initrd. The second path (if not empty) is the path to a symlink that
|
||||
should be placed in the initrd, pointing to that object. How that
|
||||
object is copied depends on its type.
|
||||
|
||||
1. A regular file is copied directly to the same absolute path in the
|
||||
initrd.
|
||||
|
||||
- If it is *also* an ELF file, then all of its direct shared
|
||||
library dependencies are also listed as objects to be copied.
|
||||
|
||||
2. A directory's direct children are listed as objects to be copied,
|
||||
and a directory at the same absolute path in the initrd is created.
|
||||
|
||||
3. A symlink's target is listed as an object to be copied.
|
||||
|
||||
There are a couple of quirks to mention here. First, the term "object"
|
||||
refers to the final file path that the developer intends to have
|
||||
copied into initrd. This means any parent directory is not considered
|
||||
an object just because its child was listed as an object in the
|
||||
program input; instead those intermediate directories are simply
|
||||
created in support of the target object. Second, shared libraries,
|
||||
directory children, and symlink targets aren't immediately recursed,
|
||||
because they simply get listed as objects themselves, and are
|
||||
therefore traversed when they themselves are processed. Finally,
|
||||
symlinks in the intermediate directories leading to an object are
|
||||
preserved, meaning an input object `/a/symlink/b` will just result in
|
||||
initrd containing `/a/symlink -> /target/b` and `/target/b`, even if
|
||||
`/target` has other children. Preserving symlinks in this manner is
|
||||
important for things like systemd.
|
||||
|
||||
These rules automate the most important and obviously necessary
|
||||
copying that needs to be done in most cases, allowing programs and
|
||||
configuration files to go unpatched, while keeping the content of the
|
||||
initrd to a minimum.
|
||||
|
||||
# Why Rust?
|
||||
|
||||
- A prototype of this logic was written in Bash, in an attempt to keep
|
||||
with its `find-libs` ancestor, but that program was difficult to
|
||||
write, and ended up taking several minutes to run. This program runs
|
||||
in less than a second, and the code is substantially easier to work
|
||||
with.
|
||||
|
||||
- This will not require end users to install a rust toolchain to use
|
||||
NixOS, as long as this tool is cached by Hydra. And if you're
|
||||
bootstrapping NixOS from source, rustc is already required anyway.
|
||||
|
||||
- Rust was favored over Python for its type system, and because if you
|
||||
want to go fast, why not go *really fast*?
|
208
pkgs/build-support/kernel/make-initrd-ng/src/main.rs
Normal file
208
pkgs/build-support/kernel/make-initrd-ng/src/main.rs
Normal file
|
@ -0,0 +1,208 @@
|
|||
use std::collections::{HashSet, VecDeque};
|
||||
use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::hash::Hash;
|
||||
use std::io::{BufReader, BufRead, Error, ErrorKind};
|
||||
use std::os::unix;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
struct NonRepeatingQueue<T> {
|
||||
queue: VecDeque<T>,
|
||||
seen: HashSet<T>,
|
||||
}
|
||||
|
||||
impl<T> NonRepeatingQueue<T> {
|
||||
fn new() -> NonRepeatingQueue<T> {
|
||||
NonRepeatingQueue {
|
||||
queue: VecDeque::new(),
|
||||
seen: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Eq + Hash> NonRepeatingQueue<T> {
|
||||
fn push_back(&mut self, value: T) -> bool {
|
||||
if self.seen.contains(&value) {
|
||||
false
|
||||
} else {
|
||||
self.seen.insert(value.clone());
|
||||
self.queue.push_back(value);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_front(&mut self) -> Option<T> {
|
||||
self.queue.pop_front()
|
||||
}
|
||||
}
|
||||
|
||||
fn patch_elf<S: AsRef<OsStr>, P: AsRef<OsStr>>(mode: S, path: P) -> Result<String, Error> {
|
||||
let output = Command::new("patchelf")
|
||||
.arg(&mode)
|
||||
.arg(&path)
|
||||
.stderr(Stdio::inherit())
|
||||
.output()?;
|
||||
if output.status.success() {
|
||||
Ok(String::from_utf8(output.stdout).expect("Failed to parse output"))
|
||||
} else {
|
||||
Err(Error::new(ErrorKind::Other, format!("failed: patchelf {:?} {:?}", OsStr::new(&mode), OsStr::new(&path))))
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_file<P: AsRef<Path> + AsRef<OsStr>, S: AsRef<Path>>(
|
||||
source: P,
|
||||
target: S,
|
||||
queue: &mut NonRepeatingQueue<Box<Path>>,
|
||||
) -> Result<(), Error> {
|
||||
fs::copy(&source, target)?;
|
||||
|
||||
if !Command::new("ldd").arg(&source).output()?.status.success() {
|
||||
//stdout(Stdio::inherit()).stderr(Stdio::inherit()).
|
||||
println!("{:?} is not dynamically linked. Not recursing.", OsStr::new(&source));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let rpath_string = patch_elf("--print-rpath", &source)?;
|
||||
let needed_string = patch_elf("--print-needed", &source)?;
|
||||
// Shared libraries don't have an interpreter
|
||||
if let Ok(interpreter_string) = patch_elf("--print-interpreter", &source) {
|
||||
queue.push_back(Box::from(Path::new(&interpreter_string.trim())));
|
||||
}
|
||||
|
||||
let rpath = rpath_string.trim().split(":").map(|p| Box::<Path>::from(Path::new(p))).collect::<Vec<_>>();
|
||||
|
||||
for line in needed_string.lines() {
|
||||
let mut found = false;
|
||||
for path in &rpath {
|
||||
let lib = path.join(line);
|
||||
if lib.exists() {
|
||||
// No need to recurse. The queue will bring it back round.
|
||||
queue.push_back(Box::from(lib.as_path()));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
// glibc makes it tricky to make this an error because
|
||||
// none of the files have a useful rpath.
|
||||
println!("Warning: Couldn't satisfy dependency {} for {:?}", line, OsStr::new(&source));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue_dir<P: AsRef<Path>>(
|
||||
source: P,
|
||||
queue: &mut NonRepeatingQueue<Box<Path>>,
|
||||
) -> Result<(), Error> {
|
||||
for entry in fs::read_dir(source)? {
|
||||
let entry = entry?;
|
||||
// No need to recurse. The queue will bring us back round here on its own.
|
||||
queue.push_back(Box::from(entry.path().as_path()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_path(
|
||||
root: &Path,
|
||||
p: &Path,
|
||||
queue: &mut NonRepeatingQueue<Box<Path>>,
|
||||
) -> Result<(), Error> {
|
||||
let mut source = PathBuf::new();
|
||||
let mut target = Path::new(root).to_path_buf();
|
||||
let mut iter = p.components().peekable();
|
||||
while let Some(comp) = iter.next() {
|
||||
match comp {
|
||||
Component::Prefix(_) => panic!("This tool is not meant for Windows"),
|
||||
Component::RootDir => {
|
||||
target.clear();
|
||||
target.push(root);
|
||||
source.clear();
|
||||
source.push("/");
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
// Don't over-pop the target if the path has too many ParentDirs
|
||||
if source.pop() {
|
||||
target.pop();
|
||||
}
|
||||
}
|
||||
Component::Normal(name) => {
|
||||
target.push(name);
|
||||
source.push(name);
|
||||
let typ = fs::symlink_metadata(&source)?.file_type();
|
||||
if typ.is_file() && !target.exists() {
|
||||
copy_file(&source, &target, queue)?;
|
||||
} else if typ.is_symlink() {
|
||||
let link_target = fs::read_link(&source)?;
|
||||
|
||||
// Create the link, then push its target to the queue
|
||||
if !target.exists() {
|
||||
unix::fs::symlink(&link_target, &target)?;
|
||||
}
|
||||
source.pop();
|
||||
source.push(link_target);
|
||||
while let Some(c) = iter.next() {
|
||||
source.push(c);
|
||||
}
|
||||
let link_target_path = source.as_path();
|
||||
if link_target_path.exists() {
|
||||
queue.push_back(Box::from(link_target_path));
|
||||
}
|
||||
break;
|
||||
} else if typ.is_dir() {
|
||||
if !target.exists() {
|
||||
fs::create_dir(&target)?;
|
||||
}
|
||||
|
||||
// Only recursively copy if the directory is the target object
|
||||
if iter.peek().is_none() {
|
||||
queue_dir(&source, queue)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let input = fs::File::open(&args[1])?;
|
||||
let output = &args[2];
|
||||
let out_path = Path::new(output);
|
||||
|
||||
let mut queue = NonRepeatingQueue::<Box<Path>>::new();
|
||||
|
||||
let mut lines = BufReader::new(input).lines();
|
||||
while let Some(obj) = lines.next() {
|
||||
// Lines should always come in pairs
|
||||
let obj = obj?;
|
||||
let sym = lines.next().unwrap()?;
|
||||
|
||||
let obj_path = Path::new(&obj);
|
||||
queue.push_back(Box::from(obj_path));
|
||||
if !sym.is_empty() {
|
||||
println!("{} -> {}", &sym, &obj);
|
||||
// We don't care about preserving symlink structure here
|
||||
// nearly as much as for the actual objects.
|
||||
let link_string = format!("{}/{}", output, sym);
|
||||
let link_path = Path::new(&link_string);
|
||||
let mut link_parent = link_path.to_path_buf();
|
||||
link_parent.pop();
|
||||
fs::create_dir_all(link_parent)?;
|
||||
unix::fs::symlink(obj_path, link_path)?;
|
||||
}
|
||||
}
|
||||
while let Some(obj) = queue.pop_front() {
|
||||
println!("{:?}", obj);
|
||||
handle_path(out_path, &*obj, &mut queue)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -603,7 +603,7 @@ stdenv.mkDerivation {
|
|||
# runtime; otherwise we can't and we need to reboot.
|
||||
interfaceVersion = 2;
|
||||
|
||||
inherit withCryptsetup;
|
||||
inherit withCryptsetup util-linux;
|
||||
|
||||
tests = {
|
||||
inherit (nixosTests) switchTest;
|
||||
|
|
|
@ -451,6 +451,9 @@ with pkgs;
|
|||
dockerTools = callPackage ../build-support/docker {
|
||||
writePython3 = buildPackages.writers.writePython3;
|
||||
};
|
||||
|
||||
fakeNss = callPackage ../build-support/fake-nss { };
|
||||
|
||||
tarsum = callPackage ../build-support/docker/tarsum.nix { };
|
||||
|
||||
snapTools = callPackage ../build-support/snap { };
|
||||
|
@ -747,6 +750,9 @@ with pkgs;
|
|||
|
||||
makeInitrd = callPackage ../build-support/kernel/make-initrd.nix; # Args intentionally left out
|
||||
|
||||
makeInitrdNG = callPackage ../build-support/kernel/make-initrd-ng.nix;
|
||||
makeInitrdNGTool = callPackage ../build-support/kernel/make-initrd-ng-tool.nix {};
|
||||
|
||||
makeWrapper = makeSetupHook
|
||||
{ deps = [ dieHook ];
|
||||
substitutions = {
|
||||
|
|
Loading…
Reference in a new issue