mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-26 08:53:21 +00:00
Merge pull request #307068 from nikstur/systemd-256
systemd: 255.6 -> 256.2
This commit is contained in:
commit
91c8eb8975
@ -169,6 +169,10 @@ in rec {
|
||||
optional (attr ? ${name} && !isInt attr.${name})
|
||||
"Systemd ${group} field `${name}' is not an integer";
|
||||
|
||||
assertRemoved = name: see: group: attr:
|
||||
optional (attr ? ${name})
|
||||
"Systemd ${group} field `${name}' has been removed. See ${see}";
|
||||
|
||||
checkUnitConfig = group: checks: attrs: let
|
||||
# We're applied at the top-level type (attrsOf unitOption), so the actual
|
||||
# unit options might contain attributes from mkOverride and mkIf that we need to
|
||||
|
@ -45,12 +45,61 @@ let
|
||||
|
||||
inherit (lib.types)
|
||||
attrsOf
|
||||
coercedTo
|
||||
enum
|
||||
lines
|
||||
listOf
|
||||
nullOr
|
||||
oneOf
|
||||
package
|
||||
path
|
||||
singleLineStr
|
||||
submodule
|
||||
;
|
||||
|
||||
initrdStorePathModule = { config, ... }: {
|
||||
options = {
|
||||
enable = (mkEnableOption "copying of this file and symlinking it") // { default = true; };
|
||||
|
||||
target = mkOption {
|
||||
type = nullOr path;
|
||||
description = ''
|
||||
Path of the symlink.
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
type = path;
|
||||
description = "Path of the source file.";
|
||||
};
|
||||
|
||||
dlopen = {
|
||||
usePriority = mkOption {
|
||||
type = enum [ "required" "recommended" "suggested" ];
|
||||
default = "recommended";
|
||||
description = ''
|
||||
Priority of dlopen ELF notes to include. "required" is
|
||||
minimal, "recommended" includes "required", and
|
||||
"suggested" includes "recommended".
|
||||
|
||||
See: https://systemd.io/ELF_DLOPEN_METADATA/
|
||||
'';
|
||||
};
|
||||
|
||||
features = mkOption {
|
||||
type = listOf singleLineStr;
|
||||
default = [ ];
|
||||
description = ''
|
||||
Features to enable via dlopen ELF notes. These will be in
|
||||
addition to anything included via 'usePriority',
|
||||
regardless of their priority.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
@ -86,31 +135,23 @@ in
|
||||
automounts = listOf (submodule [ stage2AutomountOptions unitConfig automountConfig ]);
|
||||
initrdAutomounts = attrsOf (submodule [ stage1AutomountOptions unitConfig automountConfig ]);
|
||||
|
||||
initrdStorePath = listOf (coercedTo
|
||||
(oneOf [ singleLineStr package ])
|
||||
(source: { inherit source; })
|
||||
(submodule initrdStorePathModule));
|
||||
|
||||
initrdContents = attrsOf (submodule ({ config, options, name, ... }: {
|
||||
imports = [ initrdStorePathModule ];
|
||||
options = {
|
||||
enable = (mkEnableOption "copying of this file and symlinking it") // { default = true; };
|
||||
|
||||
target = mkOption {
|
||||
type = path;
|
||||
description = ''
|
||||
Path of the symlink.
|
||||
'';
|
||||
default = name;
|
||||
};
|
||||
|
||||
text = mkOption {
|
||||
default = null;
|
||||
type = nullOr lines;
|
||||
description = "Text of the file.";
|
||||
};
|
||||
|
||||
source = mkOption {
|
||||
type = path;
|
||||
description = "Path of the source file.";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
target = mkDefault name;
|
||||
source = mkIf (config.text != null) (
|
||||
let name' = "initrd-" + baseNameOf name;
|
||||
in mkDerivedConfig options.text (pkgs.writeText name')
|
||||
|
@ -18,12 +18,16 @@ let
|
||||
"ManageForeignRoutes"
|
||||
"RouteTable"
|
||||
"IPv6PrivacyExtensions"
|
||||
"IPv4Forwarding"
|
||||
"IPv6Forwarding"
|
||||
])
|
||||
(assertValueOneOf "SpeedMeter" boolValues)
|
||||
(assertInt "SpeedMeterIntervalSec")
|
||||
(assertValueOneOf "ManageForeignRoutingPolicyRules" boolValues)
|
||||
(assertValueOneOf "ManageForeignRoutes" boolValues)
|
||||
(assertValueOneOf "IPv6PrivacyExtensions" (boolValues ++ ["prefer-public" "kernel"]))
|
||||
(assertValueOneOf "IPv4Forwarding" boolValues)
|
||||
(assertValueOneOf "IPv6Forwarding" boolValues)
|
||||
];
|
||||
|
||||
sectionDHCPv4 = checkUnitConfig "DHCPv4" [
|
||||
@ -652,6 +656,8 @@ let
|
||||
"DNSDefaultRoute"
|
||||
"NTP"
|
||||
"IPForward"
|
||||
"IPv4Forwarding"
|
||||
"IPv6Forwarding"
|
||||
"IPMasquerade"
|
||||
"IPv6PrivacyExtensions"
|
||||
"IPv6AcceptRA"
|
||||
@ -700,7 +706,9 @@ let
|
||||
(assertValueOneOf "LLDP" (boolValues ++ ["routers-only"]))
|
||||
(assertValueOneOf "EmitLLDP" (boolValues ++ ["nearest-bridge" "non-tpmr-bridge" "customer-bridge"]))
|
||||
(assertValueOneOf "DNSDefaultRoute" boolValues)
|
||||
(assertValueOneOf "IPForward" (boolValues ++ ["ipv4" "ipv6"]))
|
||||
(assertRemoved "IPForward" "IPv4Forwarding and IPv6Forwarding in systemd.network(5) and networkd.conf(5)")
|
||||
(assertValueOneOf "IPv4Forwarding" boolValues)
|
||||
(assertValueOneOf "IPv6Forwarding" boolValues)
|
||||
(assertValueOneOf "IPMasquerade" (boolValues ++ ["ipv4" "ipv6" "both"]))
|
||||
(assertValueOneOf "IPv6PrivacyExtensions" (boolValues ++ ["prefer-public" "kernel"]))
|
||||
(assertValueOneOf "IPv6AcceptRA" boolValues)
|
||||
@ -2835,6 +2843,7 @@ let
|
||||
"systemd-networkd-wait-online.service"
|
||||
"systemd-networkd.service"
|
||||
"systemd-networkd.socket"
|
||||
"systemd-networkd-persistent-storage.service"
|
||||
];
|
||||
|
||||
environment.etc."systemd/networkd.conf" = renderConfig cfg.config;
|
||||
|
@ -131,6 +131,7 @@ let
|
||||
|
||||
# Copy udev.
|
||||
copy_bin_and_libs ${udev}/bin/udevadm
|
||||
cp ${lib.getLib udev.kmod}/lib/libkmod.so* $out/lib
|
||||
copy_bin_and_libs ${udev}/lib/systemd/systemd-sysctl
|
||||
for BIN in ${udev}/lib/udev/*_id; do
|
||||
copy_bin_and_libs $BIN
|
||||
|
@ -37,6 +37,8 @@ let
|
||||
"cryptsetup.target"
|
||||
"cryptsetup-pre.target"
|
||||
"remote-cryptsetup.target"
|
||||
] ++ optionals cfg.package.withTpm2Tss [
|
||||
"tpm2.target"
|
||||
] ++ [
|
||||
"sigpwr.target"
|
||||
"timers.target"
|
||||
@ -112,6 +114,7 @@ let
|
||||
"sleep.target"
|
||||
"hybrid-sleep.target"
|
||||
"systemd-hibernate.service"
|
||||
"systemd-hibernate-clear.service"
|
||||
"systemd-hybrid-sleep.service"
|
||||
"systemd-suspend.service"
|
||||
"systemd-suspend-then-hibernate.service"
|
||||
@ -136,6 +139,16 @@ let
|
||||
"systemd-ask-password-wall.path"
|
||||
"systemd-ask-password-wall.service"
|
||||
|
||||
# Varlink APIs
|
||||
"systemd-bootctl@.service"
|
||||
"systemd-bootctl.socket"
|
||||
"systemd-creds@.service"
|
||||
"systemd-creds.socket"
|
||||
] ++ lib.optional cfg.package.withTpm2Tss [
|
||||
"systemd-pcrlock@.service"
|
||||
"systemd-pcrlock.socket"
|
||||
] ++ [
|
||||
|
||||
# Slices / containers.
|
||||
"slices.target"
|
||||
] ++ optionals cfg.package.withImportd [
|
||||
@ -158,6 +171,7 @@ let
|
||||
] ++ optionals cfg.package.withHostnamed [
|
||||
"dbus-org.freedesktop.hostname1.service"
|
||||
"systemd-hostnamed.service"
|
||||
"systemd-hostnamed.socket"
|
||||
] ++ optionals cfg.package.withPortabled [
|
||||
"dbus-org.freedesktop.portable1.service"
|
||||
"systemd-portabled.service"
|
||||
|
@ -70,6 +70,7 @@ let
|
||||
"systemd-tmpfiles-setup-dev.service"
|
||||
"systemd-tmpfiles-setup.service"
|
||||
"timers.target"
|
||||
"tpm2.target"
|
||||
"umount.target"
|
||||
"systemd-bsod.service"
|
||||
] ++ cfg.additionalUpstreamUnits;
|
||||
@ -111,8 +112,7 @@ let
|
||||
inherit (config.boot.initrd) compressor compressorArgs prepend;
|
||||
inherit (cfg) strip;
|
||||
|
||||
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);
|
||||
contents = lib.filter ({ source, ... }: !lib.elem source cfg.suppressedStorePaths) cfg.storePaths;
|
||||
};
|
||||
|
||||
in {
|
||||
@ -171,7 +171,7 @@ in {
|
||||
description = ''
|
||||
Store paths to copy into the initrd as well.
|
||||
'';
|
||||
type = with types; listOf (oneOf [ singleLineStr package ]);
|
||||
type = utils.systemdUtils.types.initrdStorePath;
|
||||
default = [];
|
||||
};
|
||||
|
||||
@ -344,7 +344,8 @@ in {
|
||||
};
|
||||
|
||||
enableTpm2 = mkOption {
|
||||
default = true;
|
||||
default = cfg.package.withTpm2Tss;
|
||||
defaultText = "boot.initrd.systemd.package.withTpm2Tss";
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to enable TPM2 support in the initrd.
|
||||
@ -460,6 +461,7 @@ in {
|
||||
"${cfg.package}/lib/systemd/systemd-sulogin-shell"
|
||||
"${cfg.package}/lib/systemd/systemd-sysctl"
|
||||
"${cfg.package}/lib/systemd/systemd-bsod"
|
||||
"${cfg.package}/lib/systemd/systemd-sysroot-fstab-check"
|
||||
|
||||
# generators
|
||||
"${cfg.package}/lib/systemd/system-generators/systemd-debug-generator"
|
||||
@ -486,7 +488,8 @@ in {
|
||||
# fido2 support
|
||||
"${cfg.package}/lib/cryptsetup/libcryptsetup-token-systemd-fido2.so"
|
||||
"${pkgs.libfido2}/lib/libfido2.so.1"
|
||||
] ++ jobScripts;
|
||||
] ++ jobScripts
|
||||
++ map (c: builtins.removeAttrs c ["text"]) (builtins.attrValues cfg.contents);
|
||||
|
||||
targets.initrd.aliases = ["default.target"];
|
||||
units =
|
||||
|
@ -96,6 +96,7 @@ in {
|
||||
"systemd-journald@.service"
|
||||
"systemd-journal-flush.service"
|
||||
"systemd-journal-catalog-update.service"
|
||||
"systemd-journald-sync@.service"
|
||||
] ++ (optional (!config.boot.isContainer) "systemd-journald-audit.socket") ++ [
|
||||
"systemd-journald-dev-log.socket"
|
||||
"syslog.socket"
|
||||
|
@ -2,10 +2,7 @@
|
||||
|
||||
cfg = config.systemd.shutdownRamfs;
|
||||
|
||||
ramfsContents = let
|
||||
storePaths = map (p: "${p}\n") cfg.storePaths;
|
||||
contents = lib.mapAttrsToList (_: v: "${v.source}\n${v.target}") (lib.filterAttrs (_: v: v.enable) cfg.contents);
|
||||
in pkgs.writeText "shutdown-ramfs-contents" (lib.concatStringsSep "\n" (storePaths ++ contents));
|
||||
ramfsContents = pkgs.writeText "shutdown-ramfs-contents.json" (builtins.toJSON cfg.storePaths);
|
||||
|
||||
in {
|
||||
options.systemd.shutdownRamfs = {
|
||||
@ -24,7 +21,7 @@ in {
|
||||
description = ''
|
||||
Store paths to copy into the shutdown ramfs as well.
|
||||
'';
|
||||
type = lib.types.listOf lib.types.singleLineStr;
|
||||
type = utils.systemdUtils.types.initrdStorePath;
|
||||
default = [];
|
||||
};
|
||||
};
|
||||
@ -35,7 +32,8 @@ in {
|
||||
"/etc/initrd-release".source = config.environment.etc.os-release.source;
|
||||
"/etc/os-release".source = config.environment.etc.os-release.source;
|
||||
};
|
||||
systemd.shutdownRamfs.storePaths = [pkgs.runtimeShell "${pkgs.coreutils}/bin"];
|
||||
systemd.shutdownRamfs.storePaths = [pkgs.runtimeShell "${pkgs.coreutils}/bin"]
|
||||
++ map (c: builtins.removeAttrs c ["text"]) (builtins.attrValues cfg.contents);
|
||||
|
||||
systemd.mounts = [{
|
||||
what = "tmpfs";
|
||||
|
@ -44,7 +44,8 @@ in
|
||||
enable = true;
|
||||
networks."rosenpass" = {
|
||||
matchConfig.Name = deviceName;
|
||||
networkConfig.IPForward = true;
|
||||
networkConfig.IPv4Forwarding = true;
|
||||
networkConfig.IPv6Forwarding = true;
|
||||
address = [ "${peer.ip}/64" ];
|
||||
};
|
||||
|
||||
|
@ -232,8 +232,10 @@ in
|
||||
"""
|
||||
)
|
||||
|
||||
output = machine.succeed("/run/current-system/bin/switch-to-configuration boot")
|
||||
output = machine.succeed("/run/current-system/bin/switch-to-configuration boot 2>&1")
|
||||
assert "updating systemd-boot from 000.0-1-notnixos to " in output, "Couldn't find systemd-boot update message"
|
||||
assert 'to "/boot/EFI/systemd/systemd-bootx64.efi"' in output, "systemd-boot not copied to to /boot/EFI/systemd/systemd-bootx64.efi"
|
||||
assert 'to "/boot/EFI/BOOT/BOOTX64.EFI"' in output, "systemd-boot not copied to to /boot/EFI/BOOT/BOOTX64.EFI"
|
||||
'';
|
||||
};
|
||||
|
||||
|
@ -24,8 +24,6 @@ import ./make-test-python.nix ({ lib, ... }: {
|
||||
"01-eth1" = {
|
||||
name = "eth1";
|
||||
networkConfig = {
|
||||
# IPForward prevents dynamic address configuration
|
||||
IPForward = true;
|
||||
DHCPServer = true;
|
||||
Address = "10.0.0.1/24";
|
||||
};
|
||||
|
@ -40,7 +40,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: {
|
||||
address = [
|
||||
"2001:DB8::1/64"
|
||||
];
|
||||
networkConfig.IPForward = true;
|
||||
networkConfig.IPv4Forwarding = true;
|
||||
networkConfig.IPv6Forwarding = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -16,7 +16,8 @@ import ./make-test-python.nix ({ pkgs, lib, ... }: let
|
||||
linkConfig.RequiredForOnline = "no";
|
||||
networkConfig = {
|
||||
Address = "192.168.${toString vlan}.${toString id}/24";
|
||||
IPForward = "yes";
|
||||
IPv4Forwarding = "yes";
|
||||
IPv6Forwarding = "yes";
|
||||
};
|
||||
};
|
||||
};
|
||||
@ -57,14 +58,16 @@ in {
|
||||
|
||||
networks."10-vrf1" = {
|
||||
matchConfig.Name = "vrf1";
|
||||
networkConfig.IPForward = "yes";
|
||||
networkConfig.IPv4Forwarding = "yes";
|
||||
networkConfig.IPv6Forwarding = "yes";
|
||||
routes = [
|
||||
{ Destination = "192.168.1.2"; Metric = 100; }
|
||||
];
|
||||
};
|
||||
networks."10-vrf2" = {
|
||||
matchConfig.Name = "vrf2";
|
||||
networkConfig.IPForward = "yes";
|
||||
networkConfig.IPv4Forwarding = "yes";
|
||||
networkConfig.IPv6Forwarding = "yes";
|
||||
routes = [
|
||||
{ Destination = "192.168.2.3"; Metric = 100; }
|
||||
];
|
||||
@ -76,7 +79,8 @@ in {
|
||||
networkConfig = {
|
||||
VRF = "vrf1";
|
||||
Address = "192.168.1.1/24";
|
||||
IPForward = "yes";
|
||||
IPv4Forwarding = "yes";
|
||||
IPv6Forwarding = "yes";
|
||||
};
|
||||
};
|
||||
networks."10-eth2" = {
|
||||
@ -85,7 +89,8 @@ in {
|
||||
networkConfig = {
|
||||
VRF = "vrf2";
|
||||
Address = "192.168.2.1/24";
|
||||
IPForward = "yes";
|
||||
IPv4Forwarding = "yes";
|
||||
IPv6Forwarding = "yes";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -204,8 +204,7 @@ import ./make-test-python.nix ({ pkgs, ... }: {
|
||||
assert "0B read, 0B written" not in output
|
||||
|
||||
with subtest("systemd per-unit accounting works"):
|
||||
assert "IP traffic received: 84B" in output_ping
|
||||
assert "IP traffic sent: 84B" in output_ping
|
||||
assert "IP traffic received: 84B sent: 84B" in output_ping
|
||||
|
||||
with subtest("systemd environment is properly set"):
|
||||
machine.systemctl("daemon-reexec") # Rewrites /proc/1/environ
|
||||
|
@ -72,7 +72,7 @@ in
|
||||
${if makeUInitrd then "uInitrdCompression" else null} = uInitrdCompression;
|
||||
|
||||
passAsFile = ["contents"];
|
||||
contents = lib.concatMapStringsSep "\n" ({ object, symlink, ... }: "${object}\n${lib.optionalString (symlink != null) symlink}") contents + "\n";
|
||||
contents = builtins.toJSON contents;
|
||||
|
||||
nativeBuildInputs = [makeInitrdNGTool libarchive] ++ lib.optional makeUInitrd ubootTools ++ lib.optional strip binutils;
|
||||
|
||||
|
65
pkgs/build-support/kernel/make-initrd-ng/Cargo.lock
generated
65
pkgs/build-support/kernel/make-initrd-ng/Cargo.lock
generated
@ -30,10 +30,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.20"
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "make-initrd-ng"
|
||||
@ -41,6 +47,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"eyre",
|
||||
"goblin",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -57,22 +65,28 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "scroll"
|
||||
version = "0.11.0"
|
||||
@ -94,10 +108,41 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
name = "serde"
|
||||
version = "1.0.203"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.203"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.118"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d947f6b3163d8857ea16c4fa0dd4840d52f3041039a85decd46867eb1abef2e4"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.68"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -9,3 +9,5 @@ edition = "2018"
|
||||
[dependencies]
|
||||
eyre = "0.6.8"
|
||||
goblin = "0.5.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
|
@ -1,9 +1,8 @@
|
||||
use std::collections::{HashSet, VecDeque};
|
||||
use std::collections::{BTreeSet, HashSet, VecDeque};
|
||||
use std::env;
|
||||
use std::ffi::{OsStr, OsString};
|
||||
use std::fs;
|
||||
use std::hash::Hash;
|
||||
use std::io::{BufRead, BufReader};
|
||||
use std::iter::FromIterator;
|
||||
use std::os::unix;
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
@ -11,6 +10,44 @@ use std::process::Command;
|
||||
|
||||
use eyre::Context;
|
||||
use goblin::{elf::Elf, Object};
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Deserialize, Hash)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
enum DLOpenPriority {
|
||||
Required,
|
||||
Recommended,
|
||||
Suggested,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Deserialize, Clone, Hash)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct DLOpenConfig {
|
||||
use_priority: DLOpenPriority,
|
||||
features: BTreeSet<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct DLOpenNote {
|
||||
soname: Vec<String>,
|
||||
feature: Option<String>,
|
||||
// description is in the spec, but we don't need it here.
|
||||
// description: Option<String>,
|
||||
priority: Option<DLOpenPriority>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
|
||||
struct StoreInput {
|
||||
source: String,
|
||||
target: Option<String>,
|
||||
dlopen: Option<DLOpenConfig>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
struct StorePath {
|
||||
path: Box<Path>,
|
||||
dlopen: Option<DLOpenConfig>,
|
||||
}
|
||||
|
||||
struct NonRepeatingQueue<T> {
|
||||
queue: VecDeque<T>,
|
||||
@ -42,13 +79,47 @@ impl<T: Clone + Eq + Hash> NonRepeatingQueue<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>(
|
||||
fn add_dependencies<P: AsRef<Path> + AsRef<OsStr> + std::fmt::Debug>(
|
||||
source: P,
|
||||
elf: Elf,
|
||||
queue: &mut NonRepeatingQueue<Box<Path>>,
|
||||
) {
|
||||
contents: &[u8],
|
||||
dlopen: &Option<DLOpenConfig>,
|
||||
queue: &mut NonRepeatingQueue<StorePath>,
|
||||
) -> eyre::Result<()> {
|
||||
if let Some(interp) = elf.interpreter {
|
||||
queue.push_back(Box::from(Path::new(interp)));
|
||||
queue.push_back(StorePath {
|
||||
path: Box::from(Path::new(interp)),
|
||||
dlopen: dlopen.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut dlopen_libraries = vec![];
|
||||
if let Some(dlopen) = dlopen {
|
||||
for n in elf
|
||||
.iter_note_sections(&contents, Some(".note.dlopen"))
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
let note = n.wrap_err_with(|| format!("bad note in {:?}", source))?;
|
||||
// Payload is padded and zero terminated
|
||||
let payload = ¬e.desc[..note
|
||||
.desc
|
||||
.iter()
|
||||
.position(|x| *x == 0)
|
||||
.unwrap_or(note.desc.len())];
|
||||
let parsed = serde_json::from_slice::<Vec<DLOpenNote>>(payload)?;
|
||||
for mut parsed_note in parsed {
|
||||
if dlopen.use_priority
|
||||
>= parsed_note.priority.unwrap_or(DLOpenPriority::Recommended)
|
||||
|| parsed_note
|
||||
.feature
|
||||
.map(|f| dlopen.features.contains(&f))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
dlopen_libraries.append(&mut parsed_note.soname);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rpaths = if elf.runpaths.len() > 0 {
|
||||
@ -65,13 +136,21 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>(
|
||||
.map(|p| Box::<Path>::from(Path::new(p)))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for line in elf.libraries {
|
||||
for line in elf
|
||||
.libraries
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
.chain(dlopen_libraries)
|
||||
{
|
||||
let mut found = false;
|
||||
for path in &rpaths_as_path {
|
||||
let lib = path.join(line);
|
||||
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()));
|
||||
queue.push_back(StorePath {
|
||||
path: Box::from(lib.as_path()),
|
||||
dlopen: dlopen.clone(),
|
||||
});
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -86,6 +165,8 @@ fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_file<
|
||||
@ -94,7 +175,8 @@ fn copy_file<
|
||||
>(
|
||||
source: P,
|
||||
target: S,
|
||||
queue: &mut NonRepeatingQueue<Box<Path>>,
|
||||
dlopen: &Option<DLOpenConfig>,
|
||||
queue: &mut NonRepeatingQueue<StorePath>,
|
||||
) -> eyre::Result<()> {
|
||||
fs::copy(&source, &target)
|
||||
.wrap_err_with(|| format!("failed to copy {:?} to {:?}", source, target))?;
|
||||
@ -103,7 +185,7 @@ fn copy_file<
|
||||
fs::read(&source).wrap_err_with(|| format!("failed to read from {:?}", source))?;
|
||||
|
||||
if let Ok(Object::Elf(e)) = Object::parse(&contents) {
|
||||
add_dependencies(source, e, queue);
|
||||
add_dependencies(source, e, &contents, &dlopen, queue)?;
|
||||
|
||||
// Make file writable to strip it
|
||||
let mut permissions = fs::metadata(&target)
|
||||
@ -132,14 +214,18 @@ fn copy_file<
|
||||
|
||||
fn queue_dir<P: AsRef<Path> + std::fmt::Debug>(
|
||||
source: P,
|
||||
queue: &mut NonRepeatingQueue<Box<Path>>,
|
||||
dlopen: &Option<DLOpenConfig>,
|
||||
queue: &mut NonRepeatingQueue<StorePath>,
|
||||
) -> eyre::Result<()> {
|
||||
for entry in
|
||||
fs::read_dir(&source).wrap_err_with(|| format!("failed to 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()));
|
||||
queue.push_back(StorePath {
|
||||
path: Box::from(entry.path().as_path()),
|
||||
dlopen: dlopen.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -147,12 +233,12 @@ fn queue_dir<P: AsRef<Path> + std::fmt::Debug>(
|
||||
|
||||
fn handle_path(
|
||||
root: &Path,
|
||||
p: &Path,
|
||||
queue: &mut NonRepeatingQueue<Box<Path>>,
|
||||
p: StorePath,
|
||||
queue: &mut NonRepeatingQueue<StorePath>,
|
||||
) -> eyre::Result<()> {
|
||||
let mut source = PathBuf::new();
|
||||
let mut target = Path::new(root).to_path_buf();
|
||||
let mut iter = p.components().peekable();
|
||||
let mut iter = p.path.components().peekable();
|
||||
while let Some(comp) = iter.next() {
|
||||
match comp {
|
||||
Component::Prefix(_) => panic!("This tool is not meant for Windows"),
|
||||
@ -176,7 +262,7 @@ fn handle_path(
|
||||
.wrap_err_with(|| format!("failed to get symlink metadata for {:?}", source))?
|
||||
.file_type();
|
||||
if typ.is_file() && !target.exists() {
|
||||
copy_file(&source, &target, queue)?;
|
||||
copy_file(&source, &target, &p.dlopen, queue)?;
|
||||
|
||||
if let Some(filename) = source.file_name() {
|
||||
source.set_file_name(OsString::from_iter([
|
||||
@ -187,7 +273,10 @@ fn handle_path(
|
||||
|
||||
let wrapped_path = source.as_path();
|
||||
if wrapped_path.exists() {
|
||||
queue.push_back(Box::from(wrapped_path));
|
||||
queue.push_back(StorePath {
|
||||
path: Box::from(wrapped_path),
|
||||
dlopen: p.dlopen.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
} else if typ.is_symlink() {
|
||||
@ -207,7 +296,10 @@ fn handle_path(
|
||||
}
|
||||
let link_target_path = source.as_path();
|
||||
if link_target_path.exists() {
|
||||
queue.push_back(Box::from(link_target_path));
|
||||
queue.push_back(StorePath {
|
||||
path: Box::from(link_target_path),
|
||||
dlopen: p.dlopen.clone(),
|
||||
});
|
||||
}
|
||||
break;
|
||||
} else if typ.is_dir() {
|
||||
@ -218,7 +310,7 @@ fn handle_path(
|
||||
|
||||
// Only recursively copy if the directory is the target object
|
||||
if iter.peek().is_none() {
|
||||
queue_dir(&source, queue)
|
||||
queue_dir(&source, &p.dlopen, queue)
|
||||
.wrap_err_with(|| format!("failed to queue dir {:?}", source))?;
|
||||
}
|
||||
}
|
||||
@ -231,26 +323,26 @@ fn handle_path(
|
||||
|
||||
fn main() -> eyre::Result<()> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let input =
|
||||
fs::File::open(&args[1]).wrap_err_with(|| format!("failed to open file {:?}", &args[1]))?;
|
||||
let contents =
|
||||
fs::read(&args[1]).wrap_err_with(|| format!("failed to open file {:?}", &args[1]))?;
|
||||
let input = serde_json::from_slice::<Vec<StoreInput>>(&contents)
|
||||
.wrap_err_with(|| format!("failed to parse JSON in {:?}", &args[1]))?;
|
||||
let output = &args[2];
|
||||
let out_path = Path::new(output);
|
||||
|
||||
let mut queue = NonRepeatingQueue::<Box<Path>>::new();
|
||||
let mut queue = NonRepeatingQueue::<StorePath>::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);
|
||||
for sp in input {
|
||||
let obj_path = Path::new(&sp.source);
|
||||
queue.push_back(StorePath {
|
||||
path: Box::from(obj_path),
|
||||
dlopen: sp.dlopen,
|
||||
});
|
||||
if let Some(target) = sp.target {
|
||||
println!("{} -> {}", &target, &sp.source);
|
||||
// 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_string = format!("{}/{}", output, target);
|
||||
let link_path = Path::new(&link_string);
|
||||
let mut link_parent = link_path.to_path_buf();
|
||||
link_parent.pop();
|
||||
@ -261,7 +353,7 @@ fn main() -> eyre::Result<()> {
|
||||
}
|
||||
}
|
||||
while let Some(obj) = queue.pop_front() {
|
||||
handle_path(out_path, &*obj, &mut queue)?;
|
||||
handle_path(out_path, obj, &mut queue)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -5,16 +5,18 @@ import os
|
||||
import pprint
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
from fnmatch import fnmatch
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import dataclass
|
||||
from itertools import chain
|
||||
from pathlib import Path, PurePath
|
||||
from typing import DefaultDict, Iterator, List, Optional, Set, Tuple
|
||||
from typing import DefaultDict, Generator, Iterator, Optional
|
||||
|
||||
from elftools.common.exceptions import ELFError # type: ignore
|
||||
from elftools.elf.dynamic import DynamicSection # type: ignore
|
||||
from elftools.elf.sections import NoteSection # type: ignore
|
||||
from elftools.elf.elffile import ELFFile # type: ignore
|
||||
from elftools.elf.enums import ENUM_E_TYPE, ENUM_EI_OSABI # type: ignore
|
||||
|
||||
@ -38,7 +40,7 @@ def is_dynamic_executable(elf: ELFFile) -> bool:
|
||||
return bool(elf.get_section_by_name(".interp"))
|
||||
|
||||
|
||||
def get_dependencies(elf: ELFFile) -> List[str]:
|
||||
def get_dependencies(elf: ELFFile) -> list[list[Path]]:
|
||||
dependencies = []
|
||||
# This convoluted code is here on purpose. For some reason, using
|
||||
# elf.get_section_by_name(".dynamic") does not always return an
|
||||
@ -46,13 +48,34 @@ def get_dependencies(elf: ELFFile) -> List[str]:
|
||||
for section in elf.iter_sections():
|
||||
if isinstance(section, DynamicSection):
|
||||
for tag in section.iter_tags('DT_NEEDED'):
|
||||
dependencies.append(tag.needed)
|
||||
dependencies.append([Path(tag.needed)])
|
||||
break # There is only one dynamic section
|
||||
|
||||
return dependencies
|
||||
|
||||
|
||||
def get_rpath(elf: ELFFile) -> List[str]:
|
||||
def get_dlopen_dependencies(elf: ELFFile) -> list[list[Path]]:
|
||||
"""
|
||||
Extracts dependencies from the `.note.dlopen` section.
|
||||
This is a FreeDesktop standard to annotate binaries with libraries that it may `dlopen`.
|
||||
See https://systemd.io/ELF_DLOPEN_METADATA/
|
||||
"""
|
||||
dependencies = []
|
||||
for section in elf.iter_sections():
|
||||
if not isinstance(section, NoteSection) or section.name != ".note.dlopen":
|
||||
continue
|
||||
for note in section.iter_notes():
|
||||
if note["n_type"] != 0x407C0C0A or note["n_name"] != "FDO":
|
||||
continue
|
||||
note_desc = note["n_desc"]
|
||||
text = note_desc.decode("utf-8").rstrip("\0")
|
||||
j = json.loads(text)
|
||||
for d in j:
|
||||
dependencies.append([Path(soname) for soname in d["soname"]])
|
||||
return dependencies
|
||||
|
||||
|
||||
def get_rpath(elf: ELFFile) -> list[str]:
|
||||
# This convoluted code is here on purpose. For some reason, using
|
||||
# elf.get_section_by_name(".dynamic") does not always return an
|
||||
# instance of DynamicSection, but that is required to call iter_tags
|
||||
@ -119,11 +142,11 @@ def glob(path: Path, pattern: str, recursive: bool) -> Iterator[Path]:
|
||||
return [path] if path.match(pattern) else []
|
||||
|
||||
|
||||
cached_paths: Set[Path] = set()
|
||||
soname_cache: DefaultDict[Tuple[str, str], List[Tuple[Path, str]]] = defaultdict(list)
|
||||
cached_paths: set[Path] = set()
|
||||
soname_cache: DefaultDict[tuple[str, str], list[tuple[Path, str]]] = defaultdict(list)
|
||||
|
||||
|
||||
def populate_cache(initial: List[Path], recursive: bool =False) -> None:
|
||||
def populate_cache(initial: list[Path], recursive: bool =False) -> None:
|
||||
lib_dirs = list(initial)
|
||||
|
||||
while lib_dirs:
|
||||
@ -174,7 +197,7 @@ class Dependency:
|
||||
found: bool = False # Whether it was found somewhere
|
||||
|
||||
|
||||
def auto_patchelf_file(path: Path, runtime_deps: list[Path], append_rpaths: List[Path] = [], extra_args: List[str] = []) -> list[Dependency]:
|
||||
def auto_patchelf_file(path: Path, runtime_deps: list[Path], append_rpaths: list[Path] = [], extra_args: list[str] = []) -> list[Dependency]:
|
||||
try:
|
||||
with open_elf(path) as elf:
|
||||
|
||||
@ -204,7 +227,7 @@ def auto_patchelf_file(path: Path, runtime_deps: list[Path], append_rpaths: List
|
||||
|
||||
file_is_dynamic_executable = is_dynamic_executable(elf)
|
||||
|
||||
file_dependencies = map(Path, get_dependencies(elf))
|
||||
file_dependencies = get_dependencies(elf) + get_dlopen_dependencies(elf)
|
||||
|
||||
except ELFError:
|
||||
return []
|
||||
@ -223,24 +246,44 @@ def auto_patchelf_file(path: Path, runtime_deps: list[Path], append_rpaths: List
|
||||
# failing at the first one, because it's more useful when working
|
||||
# on a new package where you don't yet know the dependencies.
|
||||
for dep in file_dependencies:
|
||||
if dep.is_absolute() and dep.is_file():
|
||||
# This is an absolute path. If it exists, just use it.
|
||||
# Otherwise, we probably want this to produce an error when
|
||||
# checked (because just updating the rpath won't satisfy
|
||||
# it).
|
||||
continue
|
||||
elif (libc_lib / dep).is_file():
|
||||
# This library exists in libc, and will be correctly
|
||||
# resolved by the linker.
|
||||
continue
|
||||
was_found = False
|
||||
for candidate in dep:
|
||||
|
||||
if found_dependency := find_dependency(dep.name, file_arch, file_osabi):
|
||||
rpath.append(found_dependency)
|
||||
dependencies.append(Dependency(path, dep, True))
|
||||
print(f" {dep} -> found: {found_dependency}")
|
||||
else:
|
||||
dependencies.append(Dependency(path, dep, False))
|
||||
print(f" {dep} -> not found!")
|
||||
# This loop determines which candidate for a given
|
||||
# dependency can be found, and how. There may be multiple
|
||||
# candidates for a dep because of '.note.dlopen'
|
||||
# dependencies.
|
||||
#
|
||||
# 1. If a candidate is an absolute path, it is already a
|
||||
# valid dependency if that path exists, and nothing needs
|
||||
# to be done. It should be an error if that path does not exist.
|
||||
# 2. If a candidate is found in our library dependencies, that
|
||||
# dependency should be added to rpath.
|
||||
# 3. If a candidate is found in libc, it will be correctly
|
||||
# resolved by the dynamic linker automatically.
|
||||
#
|
||||
# These conditions are checked in this order, because #2
|
||||
# and #3 may both be true. In that case, we still want to
|
||||
# add the dependency to rpath, as the original binary
|
||||
# presumably had it and this should be preserved.
|
||||
|
||||
if candidate.is_absolute() and candidate.is_file():
|
||||
was_found = True
|
||||
break
|
||||
elif found_dependency := find_dependency(candidate.name, file_arch, file_osabi):
|
||||
rpath.append(found_dependency)
|
||||
dependencies.append(Dependency(path, candidate, found=True))
|
||||
print(f" {candidate} -> found: {found_dependency}")
|
||||
was_found = True
|
||||
break
|
||||
elif (libc_lib / candidate).is_file():
|
||||
was_found = True
|
||||
break
|
||||
|
||||
if not was_found:
|
||||
dep_name = dep[0] if len(dep) == 1 else f"any({', '.join(map(str, dep))})"
|
||||
dependencies.append(Dependency(path, dep_name, found=False))
|
||||
print(f" {dep_name} -> not found!")
|
||||
|
||||
rpath.extend(append_rpaths)
|
||||
|
||||
@ -257,13 +300,13 @@ def auto_patchelf_file(path: Path, runtime_deps: list[Path], append_rpaths: List
|
||||
|
||||
|
||||
def auto_patchelf(
|
||||
paths_to_patch: List[Path],
|
||||
lib_dirs: List[Path],
|
||||
runtime_deps: List[Path],
|
||||
paths_to_patch: list[Path],
|
||||
lib_dirs: list[Path],
|
||||
runtime_deps: list[Path],
|
||||
recursive: bool = True,
|
||||
ignore_missing: List[str] = [],
|
||||
append_rpaths: List[Path] = [],
|
||||
extra_args: List[str] = []) -> None:
|
||||
ignore_missing: list[str] = [],
|
||||
append_rpaths: list[Path] = [],
|
||||
extra_args: list[str] = []) -> None:
|
||||
|
||||
if not paths_to_patch:
|
||||
sys.exit("No paths to patch, stopping.")
|
||||
|
@ -13,17 +13,17 @@ unit. (However, this ignores the fsck unit, so it's not perfect...)
|
||||
1 file changed, 4 deletions(-)
|
||||
|
||||
diff --git a/rules.d/99-systemd.rules.in b/rules.d/99-systemd.rules.in
|
||||
index 0d68f31d36..6b52f7ed4b 100644
|
||||
index ad0c7e2fb5..79f3086e78 100644
|
||||
--- a/rules.d/99-systemd.rules.in
|
||||
+++ b/rules.d/99-systemd.rules.in
|
||||
@@ -22,10 +22,6 @@ SUBSYSTEM=="block", TAG+="systemd"
|
||||
SUBSYSTEM=="block", ENV{DM_SUSPENDED}=="1", IMPORT{db}="SYSTEMD_READY", GOTO="systemd_end"
|
||||
SUBSYSTEM=="block", ACTION=="add", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}="0"
|
||||
@@ -26,10 +26,6 @@ SUBSYSTEM=="block", ACTION=="add", KERNEL=="dm-*", ENV{DM_NAME}!="?*", ENV{SYSTE
|
||||
# Import previous SYSTEMD_READY state.
|
||||
SUBSYSTEM=="block", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="1", ENV{SYSTEMD_READY}=="", IMPORT{db}="SYSTEMD_READY"
|
||||
|
||||
-# Ignore encrypted devices with no identified superblock on it, since
|
||||
-# we are probably still calling mke2fs or mkswap on it.
|
||||
-SUBSYSTEM=="block", ENV{DM_UUID}=="CRYPT-*", ENV{ID_PART_TABLE_TYPE}=="", ENV{ID_FS_USAGE}=="", ENV{SYSTEMD_READY}="0"
|
||||
-
|
||||
# Explicitly set SYSTEMD_READY=1 for DM devices that don't have it set yet, so that we always have something to import above
|
||||
SUBSYSTEM=="block", ENV{DM_UUID}=="?*", ENV{SYSTEMD_READY}=="", ENV{SYSTEMD_READY}="1"
|
||||
|
||||
# add symlink to GPT root disk
|
||||
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}!="crypto_LUKS", SYMLINK+="gpt-auto-root"
|
||||
SUBSYSTEM=="block", ENV{ID_PART_GPT_AUTO_ROOT}=="1", ENV{ID_FS_TYPE}=="crypto_LUKS", SYMLINK+="gpt-auto-root-luks"
|
||||
|
@ -14,7 +14,7 @@ Original-Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
|
||||
2 files changed, 6 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c
|
||||
index 55e76b6e16..015a608035 100644
|
||||
index eac5bb8d3b..d8187bfa99 100644
|
||||
--- a/src/shared/fstab-util.c
|
||||
+++ b/src/shared/fstab-util.c
|
||||
@@ -66,6 +66,8 @@ bool fstab_is_extrinsic(const char *mount, const char *opts) {
|
||||
@ -27,7 +27,7 @@ index 55e76b6e16..015a608035 100644
|
||||
"/etc"))
|
||||
return true;
|
||||
diff --git a/src/shutdown/umount.c b/src/shutdown/umount.c
|
||||
index 1a9b99d761..04ef9af1ea 100644
|
||||
index ca6d36e054..0a9227c9a8 100644
|
||||
--- a/src/shutdown/umount.c
|
||||
+++ b/src/shutdown/umount.c
|
||||
@@ -170,8 +170,10 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) {
|
||||
|
@ -10,10 +10,10 @@ container, so checking early whether it exists will fail.
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
|
||||
index 38196ef3d6..57d1750b00 100644
|
||||
index 4fee8a693c..756ce11b1f 100644
|
||||
--- a/src/nspawn/nspawn.c
|
||||
+++ b/src/nspawn/nspawn.c
|
||||
@@ -5602,6 +5602,7 @@ static int run(int argc, char *argv[]) {
|
||||
@@ -6028,6 +6028,7 @@ static int run(int argc, char *argv[]) {
|
||||
goto finish;
|
||||
}
|
||||
} else {
|
||||
@ -21,7 +21,7 @@ index 38196ef3d6..57d1750b00 100644
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
if (arg_pivot_root_new)
|
||||
@@ -5618,6 +5619,7 @@ static int run(int argc, char *argv[]) {
|
||||
@@ -6044,6 +6045,7 @@ static int run(int argc, char *argv[]) {
|
||||
"Directory %s doesn't look like it has an OS tree (/usr/ directory is missing). Refusing.", arg_directory);
|
||||
goto finish;
|
||||
}
|
||||
|
@ -16,10 +16,10 @@ Original-Author: Eelco Dolstra <eelco.dolstra@logicblox.com>
|
||||
2 files changed, 6 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/src/basic/path-lookup.c b/src/basic/path-lookup.c
|
||||
index 4e3d59fc56..0d18b9a2d0 100644
|
||||
index 540256b73b..a115ec09a3 100644
|
||||
--- a/src/basic/path-lookup.c
|
||||
+++ b/src/basic/path-lookup.c
|
||||
@@ -92,11 +92,7 @@ int xdg_user_data_dir(char **ret, const char *suffix) {
|
||||
@@ -123,11 +123,7 @@ int runtime_directory(char **ret, RuntimeScope scope, const char *suffix) {
|
||||
}
|
||||
|
||||
static const char* const user_data_unit_paths[] = {
|
||||
@ -31,7 +31,7 @@ index 4e3d59fc56..0d18b9a2d0 100644
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -613,16 +609,13 @@ int lookup_paths_init(
|
||||
@@ -634,16 +630,13 @@ int lookup_paths_init(
|
||||
persistent_config,
|
||||
SYSTEM_CONFIG_UNIT_DIR,
|
||||
"/etc/systemd/system",
|
||||
@ -49,7 +49,7 @@ index 4e3d59fc56..0d18b9a2d0 100644
|
||||
STRV_IFNOTNULL(generator_late));
|
||||
break;
|
||||
|
||||
@@ -638,14 +631,11 @@ int lookup_paths_init(
|
||||
@@ -659,14 +652,11 @@ int lookup_paths_init(
|
||||
persistent_config,
|
||||
USER_CONFIG_UNIT_DIR,
|
||||
"/etc/systemd/user",
|
||||
@ -65,7 +65,7 @@ index 4e3d59fc56..0d18b9a2d0 100644
|
||||
STRV_IFNOTNULL(generator_late));
|
||||
break;
|
||||
|
||||
@@ -805,7 +795,6 @@ char **generator_binary_paths(RuntimeScope scope) {
|
||||
@@ -825,7 +815,6 @@ char **generator_binary_paths(RuntimeScope scope) {
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
add = strv_new("/run/systemd/system-generators",
|
||||
"/etc/systemd/system-generators",
|
||||
@ -73,7 +73,7 @@ index 4e3d59fc56..0d18b9a2d0 100644
|
||||
SYSTEM_GENERATOR_DIR);
|
||||
break;
|
||||
|
||||
@@ -813,7 +802,6 @@ char **generator_binary_paths(RuntimeScope scope) {
|
||||
@@ -833,7 +822,6 @@ char **generator_binary_paths(RuntimeScope scope) {
|
||||
case RUNTIME_SCOPE_USER:
|
||||
add = strv_new("/run/systemd/user-generators",
|
||||
"/etc/systemd/user-generators",
|
||||
@ -81,7 +81,7 @@ index 4e3d59fc56..0d18b9a2d0 100644
|
||||
USER_GENERATOR_DIR);
|
||||
break;
|
||||
|
||||
@@ -852,14 +840,12 @@ char **env_generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
@@ -872,14 +860,12 @@ char **env_generator_binary_paths(RuntimeScope runtime_scope) {
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
add = strv_new("/run/systemd/system-environment-generators",
|
||||
"/etc/systemd/system-environment-generators",
|
||||
|
@ -13,10 +13,10 @@ in containers.
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/core/manager.c b/src/core/manager.c
|
||||
index e61ebee253..22cc5cc843 100644
|
||||
index 04103e0fe9..e26c6c5cfd 100644
|
||||
--- a/src/core/manager.c
|
||||
+++ b/src/core/manager.c
|
||||
@@ -1562,7 +1562,8 @@ static unsigned manager_dispatch_stop_when_bound_queue(Manager *m) {
|
||||
@@ -1611,7 +1611,8 @@ static unsigned manager_dispatch_stop_when_bound_queue(Manager *m) {
|
||||
if (!unit_is_bound_by_inactive(u, &culprit))
|
||||
continue;
|
||||
|
||||
|
@ -11,10 +11,10 @@ Subject: [PATCH] hostnamed, localed, timedated: disable methods that change
|
||||
3 files changed, 25 insertions(+)
|
||||
|
||||
diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c
|
||||
index e1d53f2395..a224e6dadc 100644
|
||||
index 82d08803fa..8e40b77eba 100644
|
||||
--- a/src/hostname/hostnamed.c
|
||||
+++ b/src/hostname/hostnamed.c
|
||||
@@ -1053,6 +1053,9 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
|
||||
@@ -1116,6 +1116,9 @@ static int method_set_static_hostname(sd_bus_message *m, void *userdata, sd_bus_
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -24,7 +24,7 @@ index e1d53f2395..a224e6dadc 100644
|
||||
name = empty_to_null(name);
|
||||
|
||||
context_read_etc_hostname(c);
|
||||
@@ -1116,6 +1119,9 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
|
||||
@@ -1178,6 +1181,9 @@ static int set_machine_info(Context *c, sd_bus_message *m, int prop, sd_bus_mess
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -35,10 +35,10 @@ index e1d53f2395..a224e6dadc 100644
|
||||
|
||||
context_read_machine_info(c);
|
||||
diff --git a/src/locale/localed.c b/src/locale/localed.c
|
||||
index 5d96237fae..9af35cd29c 100644
|
||||
index c0d104578d..51a714ee23 100644
|
||||
--- a/src/locale/localed.c
|
||||
+++ b/src/locale/localed.c
|
||||
@@ -229,6 +229,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
|
||||
@@ -226,6 +226,9 @@ static int method_set_locale(sd_bus_message *m, void *userdata, sd_bus_error *er
|
||||
|
||||
use_localegen = locale_gen_check_available();
|
||||
|
||||
@ -48,7 +48,7 @@ index 5d96237fae..9af35cd29c 100644
|
||||
/* If single locale without variable name is provided, then we assume it is LANG=. */
|
||||
if (strv_length(l) == 1 && !strchr(l[0], '=')) {
|
||||
if (!locale_is_valid(l[0]))
|
||||
@@ -347,6 +350,9 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
|
||||
@@ -343,6 +346,9 @@ static int method_set_vc_keyboard(sd_bus_message *m, void *userdata, sd_bus_erro
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
@ -58,7 +58,7 @@ index 5d96237fae..9af35cd29c 100644
|
||||
vc_context_empty_to_null(&in);
|
||||
|
||||
r = vc_context_verify_and_warn(&in, LOG_ERR, error);
|
||||
@@ -465,6 +471,9 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
|
||||
@@ -460,6 +466,9 @@ static int method_set_x11_keyboard(sd_bus_message *m, void *userdata, sd_bus_err
|
||||
if (r < 0)
|
||||
return bus_log_parse_error(r);
|
||||
|
||||
@ -69,10 +69,10 @@ index 5d96237fae..9af35cd29c 100644
|
||||
|
||||
r = x11_context_verify_and_warn(&in, LOG_ERR, error);
|
||||
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
|
||||
index c7be30f563..50f8aa8675 100644
|
||||
index e3b4367ec0..448aa7e94d 100644
|
||||
--- a/src/timedate/timedated.c
|
||||
+++ b/src/timedate/timedated.c
|
||||
@@ -659,6 +659,10 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
|
||||
@@ -673,6 +673,10 @@ static int method_set_timezone(sd_bus_message *m, void *userdata, sd_bus_error *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -83,7 +83,7 @@ index c7be30f563..50f8aa8675 100644
|
||||
if (!timezone_is_valid(z, LOG_DEBUG))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid or not installed time zone '%s'", z);
|
||||
|
||||
@@ -737,6 +741,9 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
|
||||
@@ -750,6 +754,9 @@ static int method_set_local_rtc(sd_bus_message *m, void *userdata, sd_bus_error
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -93,7 +93,7 @@ index c7be30f563..50f8aa8675 100644
|
||||
if (lrtc == c->local_rtc && !fix_system)
|
||||
return sd_bus_reply_method_return(m, NULL);
|
||||
|
||||
@@ -917,6 +924,9 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
|
||||
@@ -928,6 +935,9 @@ static int method_set_ntp(sd_bus_message *m, void *userdata, sd_bus_error *error
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
|
@ -13,7 +13,7 @@ NixOS uses this path.
|
||||
5 files changed, 13 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/man/localtime.xml b/man/localtime.xml
|
||||
index e486474c44..5f373d0723 100644
|
||||
index 3a13e04a27..4fd58068a1 100644
|
||||
--- a/man/localtime.xml
|
||||
+++ b/man/localtime.xml
|
||||
@@ -20,7 +20,7 @@
|
||||
@ -35,7 +35,7 @@ index e486474c44..5f373d0723 100644
|
||||
<literal>Etc/UTC</literal>. The resulting link should lead to the
|
||||
corresponding binary
|
||||
diff --git a/src/basic/time-util.c b/src/basic/time-util.c
|
||||
index f9014dc560..3ee0363369 100644
|
||||
index b94f37c31c..48f5a2526b 100644
|
||||
--- a/src/basic/time-util.c
|
||||
+++ b/src/basic/time-util.c
|
||||
@@ -1412,7 +1412,7 @@ static int get_timezones_from_zone1970_tab(char ***ret) {
|
||||
@ -65,20 +65,20 @@ index f9014dc560..3ee0363369 100644
|
||||
|
||||
fd = open(t, O_RDONLY|O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
@@ -1625,7 +1625,7 @@ int get_timezone(char **ret) {
|
||||
@@ -1617,7 +1617,7 @@ int get_timezone(char **ret) {
|
||||
if (r < 0)
|
||||
return r; /* returns EINVAL if not a symlink */
|
||||
return r; /* Return EINVAL if not a symlink */
|
||||
|
||||
- e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
|
||||
+ e = PATH_STARTSWITH_SET(t, "/etc/zoneinfo/", "../etc/zoneinfo/");
|
||||
- const char *e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
|
||||
+ const char *e = PATH_STARTSWITH_SET(t, "/etc/zoneinfo/", "../etc/zoneinfo/");
|
||||
if (!e)
|
||||
return -EINVAL;
|
||||
|
||||
if (!timezone_is_valid(e, LOG_DEBUG))
|
||||
diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c
|
||||
index f77a5f6266..63bac85b29 100644
|
||||
index 6afabef430..c1e3af77e1 100644
|
||||
--- a/src/firstboot/firstboot.c
|
||||
+++ b/src/firstboot/firstboot.c
|
||||
@@ -632,7 +632,7 @@ static int process_timezone(int rfd) {
|
||||
@@ -648,7 +648,7 @@ static int process_timezone(int rfd) {
|
||||
if (isempty(arg_timezone))
|
||||
return 0;
|
||||
|
||||
@ -88,10 +88,10 @@ index f77a5f6266..63bac85b29 100644
|
||||
r = symlinkat_atomic_full(e, pfd, f, /* make_relative= */ false);
|
||||
if (r < 0)
|
||||
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
|
||||
index e48ebe8342..41796f3358 100644
|
||||
index 756ce11b1f..436804b3bd 100644
|
||||
--- a/src/nspawn/nspawn.c
|
||||
+++ b/src/nspawn/nspawn.c
|
||||
@@ -1845,8 +1845,8 @@ int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid
|
||||
@@ -1862,8 +1862,8 @@ int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid
|
||||
static const char *timezone_from_path(const char *path) {
|
||||
return PATH_STARTSWITH_SET(
|
||||
path,
|
||||
@ -103,10 +103,10 @@ index e48ebe8342..41796f3358 100644
|
||||
|
||||
static bool etc_writable(void) {
|
||||
diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c
|
||||
index 50f8aa8675..aff156ab42 100644
|
||||
index 448aa7e94d..2161e09579 100644
|
||||
--- a/src/timedate/timedated.c
|
||||
+++ b/src/timedate/timedated.c
|
||||
@@ -276,7 +276,7 @@ static int context_read_data(Context *c) {
|
||||
@@ -280,7 +280,7 @@ static int context_read_data(Context *c) {
|
||||
|
||||
r = get_timezone(&t);
|
||||
if (r == -EINVAL)
|
||||
@ -115,7 +115,7 @@ index 50f8aa8675..aff156ab42 100644
|
||||
else if (r < 0)
|
||||
log_warning_errno(r, "Failed to get target of /etc/localtime: %m");
|
||||
|
||||
@@ -300,7 +300,7 @@ static int context_write_data_timezone(Context *c) {
|
||||
@@ -304,7 +304,7 @@ static int context_write_data_timezone(Context *c) {
|
||||
|
||||
if (isempty(c->zone) || streq(c->zone, "UTC")) {
|
||||
|
||||
@ -124,7 +124,7 @@ index 50f8aa8675..aff156ab42 100644
|
||||
|
||||
if (unlink("/etc/localtime") < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
@@ -308,9 +308,9 @@ static int context_write_data_timezone(Context *c) {
|
||||
@@ -312,9 +312,9 @@ static int context_write_data_timezone(Context *c) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -12,10 +12,10 @@ files that I might have missed.
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/basic/constants.h b/src/basic/constants.h
|
||||
index 6bb5f3c281..678d13737d 100644
|
||||
index e70817c51f..859e673a67 100644
|
||||
--- a/src/basic/constants.h
|
||||
+++ b/src/basic/constants.h
|
||||
@@ -65,13 +65,15 @@
|
||||
@@ -62,13 +62,15 @@
|
||||
"/etc/" n "\0" \
|
||||
"/run/" n "\0" \
|
||||
"/usr/local/lib/" n "\0" \
|
||||
@ -23,7 +23,7 @@ index 6bb5f3c281..678d13737d 100644
|
||||
+ "/usr/lib/" n "\0" \
|
||||
+ PREFIX "/lib/" n "\0"
|
||||
|
||||
#define CONF_PATHS_USR(n) \
|
||||
#define CONF_PATHS(n) \
|
||||
"/etc/" n, \
|
||||
"/run/" n, \
|
||||
"/usr/local/lib/" n, \
|
||||
@ -31,5 +31,5 @@ index 6bb5f3c281..678d13737d 100644
|
||||
+ "/usr/lib/" n, \
|
||||
+ PREFIX "/lib/" n
|
||||
|
||||
#define CONF_PATHS(n) \
|
||||
CONF_PATHS_USR(n)
|
||||
#define CONF_PATHS_STRV(n) \
|
||||
STRV_MAKE(CONF_PATHS(n))
|
||||
|
@ -10,10 +10,10 @@ This is needed for NixOS to use such scripts as systemd directory is immutable.
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/src/shutdown/shutdown.c b/src/shutdown/shutdown.c
|
||||
index b976b7d8cf..b1c02df6fd 100644
|
||||
index 67f44e16e9..dda6614561 100644
|
||||
--- a/src/shutdown/shutdown.c
|
||||
+++ b/src/shutdown/shutdown.c
|
||||
@@ -336,6 +336,7 @@ static void init_watchdog(void) {
|
||||
@@ -358,6 +358,7 @@ static void notify_supervisor(void) {
|
||||
int main(int argc, char *argv[]) {
|
||||
static const char* const dirs[] = {
|
||||
SYSTEM_SHUTDOWN_PATH,
|
||||
|
@ -9,10 +9,10 @@ This is needed for NixOS to use such scripts as systemd directory is immutable.
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c
|
||||
index 21af3e9e52..6d096e3c78 100644
|
||||
index c96207428d..1e95eec7f1 100644
|
||||
--- a/src/sleep/sleep.c
|
||||
+++ b/src/sleep/sleep.c
|
||||
@@ -215,6 +215,7 @@ static int execute(
|
||||
@@ -217,6 +217,7 @@ static int execute(
|
||||
};
|
||||
static const char* const dirs[] = {
|
||||
SYSTEM_SLEEP_PATH,
|
||||
|
@ -10,19 +10,20 @@ systemd itself uses extensively.
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/basic/path-util.h b/src/basic/path-util.h
|
||||
index 6d943e967f..d4380aa7e3 100644
|
||||
index fcb3aa9399..b9e69cbf91 100644
|
||||
--- a/src/basic/path-util.h
|
||||
+++ b/src/basic/path-util.h
|
||||
@@ -25,9 +25,9 @@
|
||||
# define PATH_SBIN_BIN_NULSTR(x) PATH_NORMAL_SBIN_BIN_NULSTR(x)
|
||||
#endif
|
||||
@@ -17,10 +17,10 @@
|
||||
#define PATH_MERGED_BIN(x) x "bin"
|
||||
#define PATH_MERGED_BIN_NULSTR(x) x "bin\0"
|
||||
|
||||
-#define DEFAULT_PATH PATH_SBIN_BIN("/usr/local/") ":" PATH_SBIN_BIN("/usr/")
|
||||
-#define DEFAULT_PATH_NULSTR PATH_SBIN_BIN_NULSTR("/usr/local/") PATH_SBIN_BIN_NULSTR("/usr/")
|
||||
-#define DEFAULT_PATH_COMPAT PATH_SPLIT_SBIN_BIN("/usr/local/") ":" PATH_SPLIT_SBIN_BIN("/usr/") ":" PATH_SPLIT_SBIN_BIN("/")
|
||||
+#define DEFAULT_PATH "@defaultPathNormal@"
|
||||
+#define DEFAULT_PATH_NULSTR "@defaultPathNormal@\0"
|
||||
+#define DEFAULT_PATH_COMPAT DEFAULT_PATH
|
||||
-#define DEFAULT_PATH_WITH_SBIN PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/")
|
||||
-#define DEFAULT_PATH_WITHOUT_SBIN PATH_MERGED_BIN("/usr/local/") ":" PATH_MERGED_BIN("/usr/")
|
||||
+#define DEFAULT_PATH_WITH_SBIN "@defaultPathNormal@"
|
||||
+#define DEFAULT_PATH_WITHOUT_SBIN DEFAULT_PATH_WITH_SBIN
|
||||
|
||||
-#define DEFAULT_PATH_COMPAT PATH_SPLIT_BIN("/usr/local/") ":" PATH_SPLIT_BIN("/usr/") ":" PATH_SPLIT_BIN("/")
|
||||
+#define DEFAULT_PATH_COMPAT DEFAULT_PATH_WITH_SBIN
|
||||
|
||||
const char* default_PATH(void);
|
||||
|
||||
#ifndef DEFAULT_USER_PATH
|
||||
# define DEFAULT_USER_PATH DEFAULT_PATH
|
||||
|
@ -16,10 +16,10 @@ executables that are being called from managers.
|
||||
1 file changed, 8 insertions(+)
|
||||
|
||||
diff --git a/src/core/manager.c b/src/core/manager.c
|
||||
index 22cc5cc843..5dc7d4504f 100644
|
||||
index e26c6c5cfd..6cc1642684 100644
|
||||
--- a/src/core/manager.c
|
||||
+++ b/src/core/manager.c
|
||||
@@ -3914,9 +3914,17 @@ static int build_generator_environment(Manager *m, char ***ret) {
|
||||
@@ -4035,9 +4035,17 @@ static int build_generator_environment(Manager *m, char ***ret) {
|
||||
* adjust generated units to that. Let's pass down some bits of information that are easy for us to
|
||||
* determine (but a bit harder for generator scripts to determine), as environment variables. */
|
||||
|
||||
|
@ -13,20 +13,25 @@ so showing the taint isn't really helpful.
|
||||
|
||||
See also: https://github.com/systemd/systemd/issues/24191
|
||||
---
|
||||
src/core/manager.c | 3 ---
|
||||
1 file changed, 3 deletions(-)
|
||||
src/core/taint.c | 8 --------
|
||||
1 file changed, 8 deletions(-)
|
||||
|
||||
diff --git a/src/core/manager.c b/src/core/manager.c
|
||||
index 5dc7d4504f..6208c9aa31 100644
|
||||
--- a/src/core/manager.c
|
||||
+++ b/src/core/manager.c
|
||||
@@ -4800,9 +4800,6 @@ char* manager_taint_string(const Manager *m) {
|
||||
const char* stage[12] = {};
|
||||
size_t n = 0;
|
||||
diff --git a/src/core/taint.c b/src/core/taint.c
|
||||
index 969b37f209..de64e8f1f9 100644
|
||||
--- a/src/core/taint.c
|
||||
+++ b/src/core/taint.c
|
||||
@@ -41,14 +41,6 @@ char* taint_string(void) {
|
||||
|
||||
- _cleanup_free_ char *usrbin = NULL;
|
||||
- if (readlink_malloc("/bin", &usrbin) < 0 || !PATH_IN_SET(usrbin, "usr/bin", "/usr/bin"))
|
||||
_cleanup_free_ char *bin = NULL, *usr_sbin = NULL, *var_run = NULL;
|
||||
|
||||
- if (readlink_malloc("/bin", &bin) < 0 || !PATH_IN_SET(bin, "usr/bin", "/usr/bin"))
|
||||
- stage[n++] = "unmerged-usr";
|
||||
-
|
||||
- /* Note that the check is different from default_PATH(), as we want to taint on uncanonical symlinks
|
||||
- * too. */
|
||||
- if (readlink_malloc("/usr/sbin", &usr_sbin) < 0 || !PATH_IN_SET(usr_sbin, "bin", "/usr/bin"))
|
||||
- stage[n++] = "unmerged-bin";
|
||||
-
|
||||
if (readlink_malloc("/var/run", &var_run) < 0 || !PATH_IN_SET(var_run, "../run", "/run"))
|
||||
stage[n++] = "var-run-bad";
|
||||
|
||||
if (access("/proc/cgroups", F_OK) < 0)
|
||||
stage[n++] = "cgroups-missing";
|
||||
|
@ -27,10 +27,10 @@ filename_is_valid with path_is_valid.
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
|
||||
index 5e07b88a89..8dd7315009 100644
|
||||
index 10a78adfaf..6493d5d270 100644
|
||||
--- a/src/shared/tpm2-util.c
|
||||
+++ b/src/shared/tpm2-util.c
|
||||
@@ -654,7 +654,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
||||
@@ -670,7 +670,7 @@ int tpm2_context_new(const char *device, Tpm2Context **ret_context) {
|
||||
fn = strjoina("libtss2-tcti-", driver, ".so.0");
|
||||
|
||||
/* Better safe than sorry, let's refuse strings that cannot possibly be valid driver early, before going to disk. */
|
||||
|
@ -30,7 +30,7 @@ are written into `$XDG_CONFIG_HOME/systemd/user`.
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c
|
||||
index 367afa20f7..5777154d01 100644
|
||||
index 15398f8364..8d440cee59 100644
|
||||
--- a/src/systemctl/systemctl-edit.c
|
||||
+++ b/src/systemctl/systemctl-edit.c
|
||||
@@ -322,6 +322,9 @@ int verb_edit(int argc, char *argv[], void *userdata) {
|
||||
@ -40,6 +40,6 @@ index 367afa20f7..5777154d01 100644
|
||||
+ if (!arg_runtime && arg_runtime_scope == RUNTIME_SCOPE_SYSTEM)
|
||||
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "The unit-directory '/etc/systemd/system' is read-only on NixOS, so it's not possible to edit system-units directly. Use 'systemctl edit --runtime' instead.");
|
||||
+
|
||||
if (!on_tty())
|
||||
if (!on_tty() && !arg_stdin)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units if not on a tty.");
|
||||
|
||||
|
@ -8,14 +8,14 @@ Subject: [PATCH] meson.build: do not create systemdstatedir
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
diff --git a/meson.build b/meson.build
|
||||
index 7419e2b0b0..c82a527976 100644
|
||||
index cecdbc3aa7..bd7f8ec580 100644
|
||||
--- a/meson.build
|
||||
+++ b/meson.build
|
||||
@@ -2497,7 +2497,6 @@ install_data('LICENSE.GPL2',
|
||||
@@ -2652,7 +2652,6 @@ install_data('LICENSE.GPL2',
|
||||
install_subdir('LICENSES',
|
||||
install_dir : docdir)
|
||||
|
||||
-install_emptydir(systemdstatedir)
|
||||
|
||||
############################################################
|
||||
#####################################################################
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
From 7a27556920fe1feefd17096841c8f3ca1294a1b3 Mon Sep 17 00:00:00 2001
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Yuri Nesterov <yuriy.nesterov@unikie.com>
|
||||
Date: Wed, 21 Jun 2023 17:17:38 +0300
|
||||
Subject: [PATCH] timesyncd: disable NSCD when DNSSEC validation is disabled
|
||||
@ -13,7 +13,7 @@ directly.
|
||||
1 file changed, 11 insertions(+)
|
||||
|
||||
diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
|
||||
index 1d8ebecc91..2b0ae361ff 100644
|
||||
index 5c308a04bc..81aa3d3334 100644
|
||||
--- a/src/timesync/timesyncd.c
|
||||
+++ b/src/timesync/timesyncd.c
|
||||
@@ -21,6 +21,11 @@
|
||||
@ -32,7 +32,7 @@ index 1d8ebecc91..2b0ae361ff 100644
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse fallback server strings: %m");
|
||||
|
||||
+ r = getenv_bool_secure("SYSTEMD_NSS_RESOLVE_VALIDATE");
|
||||
+ r = secure_getenv_bool("SYSTEMD_NSS_RESOLVE_VALIDATE");
|
||||
+ if (r == 0) {
|
||||
+ log_info("Disabling NSCD because DNSSEC validation is turned off");
|
||||
+ __nss_disable_nscd(register_traced_file);
|
||||
@ -41,6 +41,3 @@ index 1d8ebecc91..2b0ae361ff 100644
|
||||
log_debug("systemd-timesyncd running as pid " PID_FMT, getpid_cached());
|
||||
|
||||
notify_message = notify_start("READY=1\n"
|
||||
--
|
||||
2.34.1
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
, pkgsCross
|
||||
, fetchFromGitHub
|
||||
, fetchzip
|
||||
, fetchpatch
|
||||
, buildPackages
|
||||
, makeBinaryWrapper
|
||||
, ninja
|
||||
@ -16,6 +17,7 @@
|
||||
, gperf
|
||||
, getent
|
||||
, glibcLocales
|
||||
, autoPatchelfHook
|
||||
|
||||
# glib is only used during tests (test-bus-gvariant, test-bus-marshal)
|
||||
, glib
|
||||
@ -67,6 +69,7 @@
|
||||
, p11-kit
|
||||
, libpwquality
|
||||
, qrencode
|
||||
, libarchive
|
||||
|
||||
# the (optional) BPF feature requires bpftool, libbpf, clang and llvm-strip to
|
||||
# be available during build time.
|
||||
@ -153,6 +156,7 @@
|
||||
# building disk images for non-NixOS systems. To save users from trying to use it
|
||||
# on their live NixOS system, we disable it by default.
|
||||
, withKernelInstall ? false
|
||||
, withLibarchive ? true
|
||||
# tests assume too much system access for them to be feasible for us right now
|
||||
, withTests ? false
|
||||
# build only libudev and libsystemd
|
||||
@ -179,14 +183,14 @@ assert withBootloader -> withEfi;
|
||||
let
|
||||
wantCurl = withRemote || withImportd;
|
||||
wantGcrypt = withResolved || withImportd;
|
||||
version = "255.9";
|
||||
version = "256.2";
|
||||
|
||||
# Use the command below to update `releaseTimestamp` on every (major) version
|
||||
# change. More details in the commentary at mesonFlags.
|
||||
# command:
|
||||
# $ curl -s https://api.github.com/repos/systemd/systemd/releases/latest | \
|
||||
# jq '.created_at|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime'
|
||||
releaseTimestamp = "1701895110";
|
||||
releaseTimestamp = "1720202583";
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
inherit pname version;
|
||||
@ -195,9 +199,9 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
# This has proven to be less error-prone than the previous systemd fork.
|
||||
src = fetchFromGitHub {
|
||||
owner = "systemd";
|
||||
repo = "systemd-stable";
|
||||
rev = "v${finalAttrs.version}";
|
||||
hash = "sha256-fnMvBYyMRQrP2x//8ntGTSwoHOtFk2TQ4S5fwcsSLDU=";
|
||||
repo = "systemd";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-fyHzL+oe192YYuwyoTrov10IlrB0NSfY/XKVWzJrQEI=";
|
||||
};
|
||||
|
||||
# On major changes, or when otherwise required, you *must* :
|
||||
@ -226,6 +230,19 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
./0015-tpm2_context_init-fix-driver-name-checking.patch
|
||||
./0016-systemctl-edit-suggest-systemdctl-edit-runtime-on-sy.patch
|
||||
./0017-meson.build-do-not-create-systemdstatedir.patch
|
||||
|
||||
# https://github.com/systemd/systemd/pull/33258
|
||||
# Remove after 256.3
|
||||
(fetchpatch {
|
||||
url = "https://github.com/systemd/systemd/compare/b268a71069786a45460807967e669d505ba3c5a2..f26b2ec46118a4493608618da2253bb9dfc6b517.patch";
|
||||
hash = "sha256-OmuPDm3NykrDeNTA3NcYt9iTXEUFwKJ5apPP4KqtABg=";
|
||||
})
|
||||
|
||||
# https://github.com/systemd/systemd/pull/33400
|
||||
(fetchpatch {
|
||||
url = "https://github.com/systemd/systemd/compare/051d462b42fe6c27824046c15cd3c84fa5afe05b..5e2d802c018f0b6d5dd58745f64d6958fa261096.patch";
|
||||
hash = "sha256-drGAnx+ECixOjIP0DUSbCG/emUgoVips9WQL5ny3NKQ=";
|
||||
})
|
||||
] ++ lib.optional (stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isGnu) [
|
||||
./0018-timesyncd-disable-NSCD-when-DNSSEC-validation-is-dis.patch
|
||||
] ++ lib.optional stdenv.hostPlatform.isMusl (
|
||||
@ -274,129 +291,6 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
--replace \
|
||||
"/usr/lib/systemd/boot/efi" \
|
||||
"$out/lib/systemd/boot/efi"
|
||||
'' + (
|
||||
let
|
||||
# The following patches references to dynamic libraries to ensure that all
|
||||
# the features that are implemented via dlopen(3) are available (or
|
||||
# explicitly deactivated) by pointing dlopen to the absolute store path
|
||||
# instead of relying on the linkers runtime lookup code.
|
||||
#
|
||||
# All of the shared library references have to be handled. When new ones
|
||||
# are introduced by upstream (or one of our patches) they must be
|
||||
# explicitly declared, otherwise the build will fail.
|
||||
#
|
||||
# As of systemd version 247 we've seen a few errors like `libpcre2.… not
|
||||
# found` when using e.g. --grep with journalctl. Those errors should
|
||||
# become less unexpected now.
|
||||
#
|
||||
# There are generally two classes of dlopen(3) calls. Those that we want
|
||||
# to support and those that should be deactivated / unsupported. This
|
||||
# change enforces that we handle all dlopen calls explicitly. Meaning:
|
||||
# There is not a single dlopen call in the source code tree that we did
|
||||
# not explicitly handle.
|
||||
#
|
||||
# In order to do this we introduced a list of attributes that maps from
|
||||
# shared object name to the package that contains them. The package can be
|
||||
# null meaning the reference should be nuked and the shared object will
|
||||
# never be loadable during runtime (because it points at an invalid store
|
||||
# path location).
|
||||
#
|
||||
# To get a list of dynamically loaded libraries issue something like
|
||||
# `grep -ri '"lib[a-zA-Z0-9-]*\.so[\.0-9a-zA-z]*"'' $src`
|
||||
# and update the list below.
|
||||
dlopenLibs =
|
||||
let
|
||||
opt = condition: pkg: if condition then pkg else null;
|
||||
in
|
||||
[
|
||||
# bpf compilation support. We use libbpf 1 now.
|
||||
{ name = "libbpf.so.1"; pkg = opt withLibBPF libbpf; }
|
||||
{ name = "libbpf.so.0"; pkg = null; }
|
||||
|
||||
# We did never provide support for libxkbcommon
|
||||
{ name = "libxkbcommon.so.0"; pkg = null; }
|
||||
|
||||
# qrencode
|
||||
{ name = "libqrencode.so.4"; pkg = opt withQrencode qrencode; }
|
||||
{ name = "libqrencode.so.3"; pkg = null; }
|
||||
|
||||
# Password quality
|
||||
# We currently do not package passwdqc, only libpwquality.
|
||||
{ name = "libpwquality.so.1"; pkg = opt withPasswordQuality libpwquality; }
|
||||
{ name = "libpasswdqc.so.1"; pkg = null; }
|
||||
|
||||
# Only include cryptsetup if it is enabled. We might not be able to
|
||||
# provide it during "bootstrap" in e.g. the minimal systemd build as
|
||||
# cryptsetup has udev (aka systemd) in it's dependencies.
|
||||
{ name = "libcryptsetup.so.12"; pkg = opt withCryptsetup cryptsetup; }
|
||||
|
||||
# We are using libidn2 so we only provide that and ignore the others.
|
||||
# Systemd does this decision during configure time and uses ifdef's to
|
||||
# enable specific branches. We can safely ignore (nuke) the libidn "v1"
|
||||
# libraries.
|
||||
{ name = "libidn2.so.0"; pkg = opt withLibidn2 libidn2; }
|
||||
{ name = "libidn.so.12"; pkg = null; }
|
||||
{ name = "libidn.so.11"; pkg = null; }
|
||||
|
||||
# journalctl --grep requires libpcre so let's provide it
|
||||
{ name = "libpcre2-8.so.0"; pkg = pcre2; }
|
||||
|
||||
# Support for TPM2 in systemd-cryptsetup, systemd-repart and systemd-cryptenroll
|
||||
{ name = "libtss2-esys.so.0"; pkg = opt withTpm2Tss tpm2-tss; }
|
||||
{ name = "libtss2-rc.so.0"; pkg = opt withTpm2Tss tpm2-tss; }
|
||||
{ name = "libtss2-mu.so.0"; pkg = opt withTpm2Tss tpm2-tss; }
|
||||
{ name = "libtss2-tcti-"; pkg = opt withTpm2Tss tpm2-tss; }
|
||||
{ name = "libfido2.so.1"; pkg = opt withFido2 libfido2; }
|
||||
|
||||
# inspect-elf support
|
||||
{ name = "libelf.so.1"; pkg = opt withCoredump elfutils; }
|
||||
{ name = "libdw.so.1"; pkg = opt withCoredump elfutils; }
|
||||
|
||||
# Support for PKCS#11 in systemd-cryptsetup, systemd-cryptenroll and systemd-homed
|
||||
{ name = "libp11-kit.so.0"; pkg = opt (withHomed || withCryptsetup) p11-kit; }
|
||||
|
||||
{ name = "libip4tc.so.2"; pkg = opt withIptables iptables; }
|
||||
];
|
||||
|
||||
patchDlOpen = dl:
|
||||
let
|
||||
library = "${lib.makeLibraryPath [ dl.pkg ]}/${dl.name}";
|
||||
in
|
||||
if dl.pkg == null then ''
|
||||
# remove the dependency on the library by replacing it with an invalid path
|
||||
for file in $(grep -lr '"${dl.name}"' src); do
|
||||
echo "patching dlopen(\"${dl.name}\", …) in $file to an invalid store path ("${builtins.storeDir}/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-not-implemented/${dl.name}")…"
|
||||
substituteInPlace "$file" --replace '"${dl.name}"' '"${builtins.storeDir}/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-not-implemented/${dl.name}"'
|
||||
done
|
||||
'' else ''
|
||||
# ensure that the library we provide actually exists
|
||||
if ! [ -e ${library} ]; then
|
||||
# exceptional case, details:
|
||||
# https://github.com/systemd/systemd-stable/blob/v249-stable/src/shared/tpm2-util.c#L157
|
||||
if ! [[ "${library}" =~ .*libtss2-tcti-$ ]]; then
|
||||
echo 'The shared library `${library}` does not exist but was given as substitute for `${dl.name}`'
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
# make the path to the dependency explicit
|
||||
for file in $(grep -lr '"${dl.name}"' src); do
|
||||
echo "patching dlopen(\"${dl.name}\", …) in $file to ${library}…"
|
||||
substituteInPlace "$file" --replace '"${dl.name}"' '"${library}"'
|
||||
done
|
||||
|
||||
'';
|
||||
in
|
||||
# patch all the dlopen calls to contain absolute paths to the libraries
|
||||
lib.concatMapStringsSep "\n" patchDlOpen dlopenLibs
|
||||
)
|
||||
# finally ensure that there are no left-over dlopen calls (or rather strings
|
||||
# pointing to shared libraries) that we didn't handle
|
||||
+ ''
|
||||
if grep -qr '"lib[a-zA-Z0-9-]*\.so[\.0-9a-zA-z]*"' src; then
|
||||
echo "Found unhandled dynamic library calls: "
|
||||
grep -r '"lib[a-zA-Z0-9-]*\.so[\.0-9a-zA-z]*"' src
|
||||
exit 1
|
||||
fi
|
||||
''
|
||||
# Finally, patch shebangs in scripts used at build time. This must not patch
|
||||
# scripts that will end up in the output, to avoid build platform references
|
||||
@ -424,6 +318,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
glibcLocales
|
||||
getent
|
||||
m4
|
||||
autoPatchelfHook
|
||||
|
||||
intltool
|
||||
gettext
|
||||
@ -476,6 +371,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
++ lib.optional withUkify (python3Packages.python.withPackages (ps: with ps; [ pefile ]))
|
||||
++ lib.optionals withPasswordQuality [ libpwquality ]
|
||||
++ lib.optionals withQrencode [ qrencode ]
|
||||
++ lib.optionals withLibarchive [ libarchive ]
|
||||
;
|
||||
|
||||
mesonBuildType = "release";
|
||||
@ -492,13 +388,11 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
# https://github.com/systemd/systemd/blob/60e930fc3e6eb8a36fbc184773119eb8d2f30364/NEWS#L258-L266
|
||||
(lib.mesonOption "time-epoch" releaseTimestamp)
|
||||
|
||||
(lib.mesonOption "version-tag" finalAttrs.version)
|
||||
(lib.mesonOption "version-tag" version)
|
||||
(lib.mesonOption "mode" "release")
|
||||
(lib.mesonOption "tty-gid" "3") # tty in NixOS has gid 3
|
||||
(lib.mesonOption "debug-shell" "${bashInteractive}/bin/bash")
|
||||
(lib.mesonOption "pamconfdir" "${placeholder "out"}/etc/pam.d")
|
||||
# Use cgroupsv2. This is already the upstream default, but better be explicit.
|
||||
(lib.mesonOption "default-hierarchy" "unified")
|
||||
(lib.mesonOption "kmod-path" "${kmod}/bin/kmod")
|
||||
|
||||
# Attempts to check /usr/sbin and that fails in macOS sandbox because
|
||||
@ -524,8 +418,8 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
(lib.mesonOption "sbat-distro" "nixos")
|
||||
(lib.mesonOption "sbat-distro-summary" "NixOS")
|
||||
(lib.mesonOption "sbat-distro-url" "https://nixos.org/")
|
||||
(lib.mesonOption "sbat-distro-pkgname" finalAttrs.pname)
|
||||
(lib.mesonOption "sbat-distro-version" finalAttrs.version)
|
||||
(lib.mesonOption "sbat-distro-pkgname" pname)
|
||||
(lib.mesonOption "sbat-distro-version" version)
|
||||
|
||||
# Users
|
||||
(lib.mesonOption "system-uid-max" "999")
|
||||
@ -543,6 +437,11 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
(lib.mesonOption "mount-path" "${lib.getOutput "mount" util-linux}/bin/mount")
|
||||
(lib.mesonOption "umount-path" "${lib.getOutput "mount" util-linux}/bin/umount")
|
||||
|
||||
# SSH
|
||||
# Disabled for now until someone makes this work.
|
||||
(lib.mesonOption "sshconfdir" "no")
|
||||
(lib.mesonOption "sshdconfdir" "no")
|
||||
|
||||
|
||||
# Features
|
||||
|
||||
@ -606,6 +505,7 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
(lib.mesonEnable "kmod" withKmod)
|
||||
(lib.mesonEnable "qrencode" withQrencode)
|
||||
(lib.mesonEnable "vmspawn" withVmspawn)
|
||||
(lib.mesonEnable "libarchive" withLibarchive)
|
||||
(lib.mesonEnable "xenctrl" false)
|
||||
(lib.mesonEnable "gnutls" false)
|
||||
(lib.mesonEnable "xkbcommon" false)
|
||||
@ -873,7 +773,8 @@ stdenv.mkDerivation (finalAttrs: {
|
||||
interfaceVersion = 2;
|
||||
|
||||
inherit withBootloader withCryptsetup withEfi withHostnamed withImportd withKmod
|
||||
withLocaled withMachined withPortabled withTimedated withUtmp util-linux kmod kbd;
|
||||
withLocaled withMachined withPortabled withTimedated withTpm2Tss withUtmp
|
||||
util-linux kmod kbd;
|
||||
|
||||
tests = {
|
||||
inherit (nixosTests)
|
||||
|
@ -27573,6 +27573,7 @@ with pkgs;
|
||||
withPasswordQuality = false;
|
||||
withVmspawn = false;
|
||||
withQrencode = false;
|
||||
withLibarchive = false;
|
||||
};
|
||||
systemdLibs = systemdMinimal.override {
|
||||
pname = "systemd-minimal-libs";
|
||||
|
Loading…
Reference in New Issue
Block a user