···169169 optional (attr ? ${name} && !isInt attr.${name})
170170 "Systemd ${group} field `${name}' is not an integer";
171171172172+ assertRemoved = name: see: group: attr:
173173+ optional (attr ? ${name})
174174+ "Systemd ${group} field `${name}' has been removed. See ${see}";
175175+172176 checkUnitConfig = group: checks: attrs: let
173177 # We're applied at the top-level type (attrsOf unitOption), so the actual
174178 # unit options might contain attributes from mkOverride and mkIf that we need to
+56-15
nixos/lib/systemd-types.nix
···45454646 inherit (lib.types)
4747 attrsOf
4848+ coercedTo
4949+ enum
4850 lines
4951 listOf
5052 nullOr
5353+ oneOf
5454+ package
5155 path
5656+ singleLineStr
5257 submodule
5358 ;
5959+6060+ initrdStorePathModule = { config, ... }: {
6161+ options = {
6262+ enable = (mkEnableOption "copying of this file and symlinking it") // { default = true; };
6363+6464+ target = mkOption {
6565+ type = nullOr path;
6666+ description = ''
6767+ Path of the symlink.
6868+ '';
6969+ default = null;
7070+ };
7171+7272+ source = mkOption {
7373+ type = path;
7474+ description = "Path of the source file.";
7575+ };
7676+7777+ dlopen = {
7878+ usePriority = mkOption {
7979+ type = enum [ "required" "recommended" "suggested" ];
8080+ default = "recommended";
8181+ description = ''
8282+ Priority of dlopen ELF notes to include. "required" is
8383+ minimal, "recommended" includes "required", and
8484+ "suggested" includes "recommended".
8585+8686+ See: https://systemd.io/ELF_DLOPEN_METADATA/
8787+ '';
8888+ };
8989+9090+ features = mkOption {
9191+ type = listOf singleLineStr;
9292+ default = [ ];
9393+ description = ''
9494+ Features to enable via dlopen ELF notes. These will be in
9595+ addition to anything included via 'usePriority',
9696+ regardless of their priority.
9797+ '';
9898+ };
9999+ };
100100+ };
101101+ };
102102+54103in
5510456105{
···86135 automounts = listOf (submodule [ stage2AutomountOptions unitConfig automountConfig ]);
87136 initrdAutomounts = attrsOf (submodule [ stage1AutomountOptions unitConfig automountConfig ]);
88137138138+ initrdStorePath = listOf (coercedTo
139139+ (oneOf [ singleLineStr package ])
140140+ (source: { inherit source; })
141141+ (submodule initrdStorePathModule));
142142+89143 initrdContents = attrsOf (submodule ({ config, options, name, ... }: {
144144+ imports = [ initrdStorePathModule ];
90145 options = {
9191- enable = (mkEnableOption "copying of this file and symlinking it") // { default = true; };
9292-9393- target = mkOption {
9494- type = path;
9595- description = ''
9696- Path of the symlink.
9797- '';
9898- default = name;
9999- };
100100-101146 text = mkOption {
102147 default = null;
103148 type = nullOr lines;
104149 description = "Text of the file.";
105150 };
106106-107107- source = mkOption {
108108- type = path;
109109- description = "Path of the source file.";
110110- };
111151 };
112152113153 config = {
154154+ target = mkDefault name;
114155 source = mkIf (config.text != null) (
115156 let name' = "initrd-" + baseNameOf name;
116157 in mkDerivedConfig options.text (pkgs.writeText name')
···11-use std::collections::{HashSet, VecDeque};
11+use std::collections::{BTreeSet, HashSet, VecDeque};
22use std::env;
33use std::ffi::{OsStr, OsString};
44use std::fs;
55use std::hash::Hash;
66-use std::io::{BufRead, BufReader};
76use std::iter::FromIterator;
87use std::os::unix;
98use std::path::{Component, Path, PathBuf};
···11101211use eyre::Context;
1312use goblin::{elf::Elf, Object};
1313+use serde::Deserialize;
1414+1515+#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Debug, Deserialize, Hash)]
1616+#[serde(rename_all = "lowercase")]
1717+enum DLOpenPriority {
1818+ Required,
1919+ Recommended,
2020+ Suggested,
2121+}
2222+2323+#[derive(PartialEq, Eq, Debug, Deserialize, Clone, Hash)]
2424+#[serde(rename_all = "camelCase")]
2525+struct DLOpenConfig {
2626+ use_priority: DLOpenPriority,
2727+ features: BTreeSet<String>,
2828+}
2929+3030+#[derive(Deserialize, Debug)]
3131+struct DLOpenNote {
3232+ soname: Vec<String>,
3333+ feature: Option<String>,
3434+ // description is in the spec, but we don't need it here.
3535+ // description: Option<String>,
3636+ priority: Option<DLOpenPriority>,
3737+}
3838+3939+#[derive(Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
4040+struct StoreInput {
4141+ source: String,
4242+ target: Option<String>,
4343+ dlopen: Option<DLOpenConfig>,
4444+}
4545+4646+#[derive(PartialEq, Eq, Hash, Clone)]
4747+struct StorePath {
4848+ path: Box<Path>,
4949+ dlopen: Option<DLOpenConfig>,
5050+}
14511552struct NonRepeatingQueue<T> {
1653 queue: VecDeque<T>,
···4279 }
4380}
44814545-fn add_dependencies<P: AsRef<Path> + AsRef<OsStr>>(
8282+fn add_dependencies<P: AsRef<Path> + AsRef<OsStr> + std::fmt::Debug>(
4683 source: P,
4784 elf: Elf,
4848- queue: &mut NonRepeatingQueue<Box<Path>>,
4949-) {
8585+ contents: &[u8],
8686+ dlopen: &Option<DLOpenConfig>,
8787+ queue: &mut NonRepeatingQueue<StorePath>,
8888+) -> eyre::Result<()> {
5089 if let Some(interp) = elf.interpreter {
5151- queue.push_back(Box::from(Path::new(interp)));
9090+ queue.push_back(StorePath {
9191+ path: Box::from(Path::new(interp)),
9292+ dlopen: dlopen.clone(),
9393+ });
9494+ }
9595+9696+ let mut dlopen_libraries = vec![];
9797+ if let Some(dlopen) = dlopen {
9898+ for n in elf
9999+ .iter_note_sections(&contents, Some(".note.dlopen"))
100100+ .into_iter()
101101+ .flatten()
102102+ {
103103+ let note = n.wrap_err_with(|| format!("bad note in {:?}", source))?;
104104+ // Payload is padded and zero terminated
105105+ let payload = ¬e.desc[..note
106106+ .desc
107107+ .iter()
108108+ .position(|x| *x == 0)
109109+ .unwrap_or(note.desc.len())];
110110+ let parsed = serde_json::from_slice::<Vec<DLOpenNote>>(payload)?;
111111+ for mut parsed_note in parsed {
112112+ if dlopen.use_priority
113113+ >= parsed_note.priority.unwrap_or(DLOpenPriority::Recommended)
114114+ || parsed_note
115115+ .feature
116116+ .map(|f| dlopen.features.contains(&f))
117117+ .unwrap_or(false)
118118+ {
119119+ dlopen_libraries.append(&mut parsed_note.soname);
120120+ }
121121+ }
122122+ }
52123 }
5312454125 let rpaths = if elf.runpaths.len() > 0 {
···65136 .map(|p| Box::<Path>::from(Path::new(p)))
66137 .collect::<Vec<_>>();
671386868- for line in elf.libraries {
139139+ for line in elf
140140+ .libraries
141141+ .into_iter()
142142+ .map(|s| s.to_string())
143143+ .chain(dlopen_libraries)
144144+ {
69145 let mut found = false;
70146 for path in &rpaths_as_path {
7171- let lib = path.join(line);
147147+ let lib = path.join(&line);
72148 if lib.exists() {
73149 // No need to recurse. The queue will bring it back round.
7474- queue.push_back(Box::from(lib.as_path()));
150150+ queue.push_back(StorePath {
151151+ path: Box::from(lib.as_path()),
152152+ dlopen: dlopen.clone(),
153153+ });
75154 found = true;
76155 break;
77156 }
···86165 );
87166 }
88167 }
168168+169169+ Ok(())
89170}
9017191172fn copy_file<
···94175>(
95176 source: P,
96177 target: S,
9797- queue: &mut NonRepeatingQueue<Box<Path>>,
178178+ dlopen: &Option<DLOpenConfig>,
179179+ queue: &mut NonRepeatingQueue<StorePath>,
98180) -> eyre::Result<()> {
99181 fs::copy(&source, &target)
100182 .wrap_err_with(|| format!("failed to copy {:?} to {:?}", source, target))?;
···103185 fs::read(&source).wrap_err_with(|| format!("failed to read from {:?}", source))?;
104186105187 if let Ok(Object::Elf(e)) = Object::parse(&contents) {
106106- add_dependencies(source, e, queue);
188188+ add_dependencies(source, e, &contents, &dlopen, queue)?;
107189108190 // Make file writable to strip it
109191 let mut permissions = fs::metadata(&target)
···132214133215fn queue_dir<P: AsRef<Path> + std::fmt::Debug>(
134216 source: P,
135135- queue: &mut NonRepeatingQueue<Box<Path>>,
217217+ dlopen: &Option<DLOpenConfig>,
218218+ queue: &mut NonRepeatingQueue<StorePath>,
136219) -> eyre::Result<()> {
137220 for entry in
138221 fs::read_dir(&source).wrap_err_with(|| format!("failed to read dir {:?}", source))?
139222 {
140223 let entry = entry?;
141224 // No need to recurse. The queue will bring us back round here on its own.
142142- queue.push_back(Box::from(entry.path().as_path()));
225225+ queue.push_back(StorePath {
226226+ path: Box::from(entry.path().as_path()),
227227+ dlopen: dlopen.clone(),
228228+ });
143229 }
144230145231 Ok(())
···147233148234fn handle_path(
149235 root: &Path,
150150- p: &Path,
151151- queue: &mut NonRepeatingQueue<Box<Path>>,
236236+ p: StorePath,
237237+ queue: &mut NonRepeatingQueue<StorePath>,
152238) -> eyre::Result<()> {
153239 let mut source = PathBuf::new();
154240 let mut target = Path::new(root).to_path_buf();
155155- let mut iter = p.components().peekable();
241241+ let mut iter = p.path.components().peekable();
156242 while let Some(comp) = iter.next() {
157243 match comp {
158244 Component::Prefix(_) => panic!("This tool is not meant for Windows"),
···176262 .wrap_err_with(|| format!("failed to get symlink metadata for {:?}", source))?
177263 .file_type();
178264 if typ.is_file() && !target.exists() {
179179- copy_file(&source, &target, queue)?;
265265+ copy_file(&source, &target, &p.dlopen, queue)?;
180266181267 if let Some(filename) = source.file_name() {
182268 source.set_file_name(OsString::from_iter([
···187273188274 let wrapped_path = source.as_path();
189275 if wrapped_path.exists() {
190190- queue.push_back(Box::from(wrapped_path));
276276+ queue.push_back(StorePath {
277277+ path: Box::from(wrapped_path),
278278+ dlopen: p.dlopen.clone(),
279279+ });
191280 }
192281 }
193282 } else if typ.is_symlink() {
···207296 }
208297 let link_target_path = source.as_path();
209298 if link_target_path.exists() {
210210- queue.push_back(Box::from(link_target_path));
299299+ queue.push_back(StorePath {
300300+ path: Box::from(link_target_path),
301301+ dlopen: p.dlopen.clone(),
302302+ });
211303 }
212304 break;
213305 } else if typ.is_dir() {
···218310219311 // Only recursively copy if the directory is the target object
220312 if iter.peek().is_none() {
221221- queue_dir(&source, queue)
313313+ queue_dir(&source, &p.dlopen, queue)
222314 .wrap_err_with(|| format!("failed to queue dir {:?}", source))?;
223315 }
224316 }
···231323232324fn main() -> eyre::Result<()> {
233325 let args: Vec<String> = env::args().collect();
234234- let input =
235235- fs::File::open(&args[1]).wrap_err_with(|| format!("failed to open file {:?}", &args[1]))?;
326326+ let contents =
327327+ fs::read(&args[1]).wrap_err_with(|| format!("failed to open file {:?}", &args[1]))?;
328328+ let input = serde_json::from_slice::<Vec<StoreInput>>(&contents)
329329+ .wrap_err_with(|| format!("failed to parse JSON in {:?}", &args[1]))?;
236330 let output = &args[2];
237331 let out_path = Path::new(output);
238332239239- let mut queue = NonRepeatingQueue::<Box<Path>>::new();
240240-241241- let mut lines = BufReader::new(input).lines();
242242- while let Some(obj) = lines.next() {
243243- // Lines should always come in pairs
244244- let obj = obj?;
245245- let sym = lines.next().unwrap()?;
333333+ let mut queue = NonRepeatingQueue::<StorePath>::new();
246334247247- let obj_path = Path::new(&obj);
248248- queue.push_back(Box::from(obj_path));
249249- if !sym.is_empty() {
250250- println!("{} -> {}", &sym, &obj);
335335+ for sp in input {
336336+ let obj_path = Path::new(&sp.source);
337337+ queue.push_back(StorePath {
338338+ path: Box::from(obj_path),
339339+ dlopen: sp.dlopen,
340340+ });
341341+ if let Some(target) = sp.target {
342342+ println!("{} -> {}", &target, &sp.source);
251343 // We don't care about preserving symlink structure here
252344 // nearly as much as for the actual objects.
253253- let link_string = format!("{}/{}", output, sym);
345345+ let link_string = format!("{}/{}", output, target);
254346 let link_path = Path::new(&link_string);
255347 let mut link_parent = link_path.to_path_buf();
256348 link_parent.pop();
···261353 }
262354 }
263355 while let Some(obj) = queue.pop_front() {
264264- handle_path(out_path, &*obj, &mut queue)?;
356356+ handle_path(out_path, obj, &mut queue)?;
265357 }
266358267359 Ok(())
+75-32
pkgs/build-support/setup-hooks/auto-patchelf.py
···55import pprint
66import subprocess
77import sys
88+import json
89from fnmatch import fnmatch
910from collections import defaultdict
1011from contextlib import contextmanager
1112from dataclasses import dataclass
1213from itertools import chain
1314from pathlib import Path, PurePath
1414-from typing import DefaultDict, Iterator, List, Optional, Set, Tuple
1515+from typing import DefaultDict, Generator, Iterator, Optional
15161617from elftools.common.exceptions import ELFError # type: ignore
1718from elftools.elf.dynamic import DynamicSection # type: ignore
1919+from elftools.elf.sections import NoteSection # type: ignore
1820from elftools.elf.elffile import ELFFile # type: ignore
1921from elftools.elf.enums import ENUM_E_TYPE, ENUM_EI_OSABI # type: ignore
2022···3840 return bool(elf.get_section_by_name(".interp"))
394140424141-def get_dependencies(elf: ELFFile) -> List[str]:
4343+def get_dependencies(elf: ELFFile) -> list[list[Path]]:
4244 dependencies = []
4345 # This convoluted code is here on purpose. For some reason, using
4446 # elf.get_section_by_name(".dynamic") does not always return an
···4648 for section in elf.iter_sections():
4749 if isinstance(section, DynamicSection):
4850 for tag in section.iter_tags('DT_NEEDED'):
4949- dependencies.append(tag.needed)
5151+ dependencies.append([Path(tag.needed)])
5052 break # There is only one dynamic section
51535254 return dependencies
535554565555-def get_rpath(elf: ELFFile) -> List[str]:
5757+def get_dlopen_dependencies(elf: ELFFile) -> list[list[Path]]:
5858+ """
5959+ Extracts dependencies from the `.note.dlopen` section.
6060+ This is a FreeDesktop standard to annotate binaries with libraries that it may `dlopen`.
6161+ See https://systemd.io/ELF_DLOPEN_METADATA/
6262+ """
6363+ dependencies = []
6464+ for section in elf.iter_sections():
6565+ if not isinstance(section, NoteSection) or section.name != ".note.dlopen":
6666+ continue
6767+ for note in section.iter_notes():
6868+ if note["n_type"] != 0x407C0C0A or note["n_name"] != "FDO":
6969+ continue
7070+ note_desc = note["n_desc"]
7171+ text = note_desc.decode("utf-8").rstrip("\0")
7272+ j = json.loads(text)
7373+ for d in j:
7474+ dependencies.append([Path(soname) for soname in d["soname"]])
7575+ return dependencies
7676+7777+7878+def get_rpath(elf: ELFFile) -> list[str]:
5679 # This convoluted code is here on purpose. For some reason, using
5780 # elf.get_section_by_name(".dynamic") does not always return an
5881 # instance of DynamicSection, but that is required to call iter_tags
···119142 return [path] if path.match(pattern) else []
120143121144122122-cached_paths: Set[Path] = set()
123123-soname_cache: DefaultDict[Tuple[str, str], List[Tuple[Path, str]]] = defaultdict(list)
145145+cached_paths: set[Path] = set()
146146+soname_cache: DefaultDict[tuple[str, str], list[tuple[Path, str]]] = defaultdict(list)
124147125148126126-def populate_cache(initial: List[Path], recursive: bool =False) -> None:
149149+def populate_cache(initial: list[Path], recursive: bool =False) -> None:
127150 lib_dirs = list(initial)
128151129152 while lib_dirs:
···174197 found: bool = False # Whether it was found somewhere
175198176199177177-def auto_patchelf_file(path: Path, runtime_deps: list[Path], append_rpaths: List[Path] = [], extra_args: List[str] = []) -> list[Dependency]:
200200+def auto_patchelf_file(path: Path, runtime_deps: list[Path], append_rpaths: list[Path] = [], extra_args: list[str] = []) -> list[Dependency]:
178201 try:
179202 with open_elf(path) as elf:
180203···204227205228 file_is_dynamic_executable = is_dynamic_executable(elf)
206229207207- file_dependencies = map(Path, get_dependencies(elf))
230230+ file_dependencies = get_dependencies(elf) + get_dlopen_dependencies(elf)
208231209232 except ELFError:
210233 return []
···223246 # failing at the first one, because it's more useful when working
224247 # on a new package where you don't yet know the dependencies.
225248 for dep in file_dependencies:
226226- if dep.is_absolute() and dep.is_file():
227227- # This is an absolute path. If it exists, just use it.
228228- # Otherwise, we probably want this to produce an error when
229229- # checked (because just updating the rpath won't satisfy
230230- # it).
231231- continue
232232- elif (libc_lib / dep).is_file():
233233- # This library exists in libc, and will be correctly
234234- # resolved by the linker.
235235- continue
249249+ was_found = False
250250+ for candidate in dep:
251251+252252+ # This loop determines which candidate for a given
253253+ # dependency can be found, and how. There may be multiple
254254+ # candidates for a dep because of '.note.dlopen'
255255+ # dependencies.
256256+ #
257257+ # 1. If a candidate is an absolute path, it is already a
258258+ # valid dependency if that path exists, and nothing needs
259259+ # to be done. It should be an error if that path does not exist.
260260+ # 2. If a candidate is found in our library dependencies, that
261261+ # dependency should be added to rpath.
262262+ # 3. If a candidate is found in libc, it will be correctly
263263+ # resolved by the dynamic linker automatically.
264264+ #
265265+ # These conditions are checked in this order, because #2
266266+ # and #3 may both be true. In that case, we still want to
267267+ # add the dependency to rpath, as the original binary
268268+ # presumably had it and this should be preserved.
269269+270270+ if candidate.is_absolute() and candidate.is_file():
271271+ was_found = True
272272+ break
273273+ elif found_dependency := find_dependency(candidate.name, file_arch, file_osabi):
274274+ rpath.append(found_dependency)
275275+ dependencies.append(Dependency(path, candidate, found=True))
276276+ print(f" {candidate} -> found: {found_dependency}")
277277+ was_found = True
278278+ break
279279+ elif (libc_lib / candidate).is_file():
280280+ was_found = True
281281+ break
236282237237- if found_dependency := find_dependency(dep.name, file_arch, file_osabi):
238238- rpath.append(found_dependency)
239239- dependencies.append(Dependency(path, dep, True))
240240- print(f" {dep} -> found: {found_dependency}")
241241- else:
242242- dependencies.append(Dependency(path, dep, False))
243243- print(f" {dep} -> not found!")
283283+ if not was_found:
284284+ dep_name = dep[0] if len(dep) == 1 else f"any({', '.join(map(str, dep))})"
285285+ dependencies.append(Dependency(path, dep_name, found=False))
286286+ print(f" {dep_name} -> not found!")
244287245288 rpath.extend(append_rpaths)
246289···257300258301259302def auto_patchelf(
260260- paths_to_patch: List[Path],
261261- lib_dirs: List[Path],
262262- runtime_deps: List[Path],
303303+ paths_to_patch: list[Path],
304304+ lib_dirs: list[Path],
305305+ runtime_deps: list[Path],
263306 recursive: bool = True,
264264- ignore_missing: List[str] = [],
265265- append_rpaths: List[Path] = [],
266266- extra_args: List[str] = []) -> None:
307307+ ignore_missing: list[str] = [],
308308+ append_rpaths: list[Path] = [],
309309+ extra_args: list[str] = []) -> None:
267310268311 if not paths_to_patch:
269312 sys.exit("No paths to patch, stopping.")
···11+{ lib
22+, fetchFromGitHub
33+, buildGoModule
44+, fetchpatch
55+}:
66+buildGoModule rec {
77+ pname = "ssm-session-manager-plugin";
88+ version = "1.2.650.0";
99+1010+ src = fetchFromGitHub {
1111+ owner = "aws";
1212+ repo = "session-manager-plugin";
1313+ rev = version;
1414+ hash = "sha256-IcDVt/iE/EYV9Blvl7Gj0UFJcdsUCFdaSQkIto0CKRI=";
1515+ };
1616+1717+ patches = [
1818+ # Add support for Go modules.
1919+ #
2020+ # This patch doesn't belong to any upstream PR, it is specially crafted for
2121+ # nixpkgs. Deleting the vendor dir is left out from the patch and done in
2222+ # postPatch instead, as otherwise the patch would be to big and GitHub returns
2323+ # an error.
2424+ #
2525+ # With https://github.com/aws/session-manager-plugin/pull/74 there is an
2626+ # upstream PR with the same goal. It isn't pulled here as patch for the same
2727+ # reason.
2828+ #
2929+ # Notice that the dependencies are pinned with the patch, and upstream dependency
3030+ # updates won't take effect. Patch should be recreated from time to time.
3131+ # - `rm -rf vendor`
3232+ # - `go mod init github.com/aws/session-manager-plugin`
3333+ # - `go mod tidy`
3434+ # - `go get github.com/twinj/uuid@v0.0.0-20151029044442-89173bcdda19`
3535+ # - `go mod tidy`
3636+ # - `git checkout HEAD vendor`
3737+ ./0001-module-support.patch
3838+ ];
3939+4040+ postPatch = ''
4141+ rm -rf vendor
4242+ '';
4343+4444+ vendorHash = "sha256-wK+aWRC5yrPtdihXAj6RlYC9ZTTPuGUg9wLY33skzeE=";
4545+4646+ subPackages = [ "src/sessionmanagerplugin-main" ];
4747+4848+ preBuild = ''
4949+ echo -n ${lib.escapeShellArg version} > VERSION
5050+ go run src/version/versiongenerator/version-gen.go
5151+ '';
5252+5353+ doCheck = true;
5454+ checkFlags = [ "-skip=TestSetSessionHandlers" ];
5555+5656+ preCheck = ''
5757+ if ! [[ $($GOPATH/bin/sessionmanagerplugin-main --version) = ${lib.escapeShellArg version} ]]; then
5858+ echo 'wrong version'
5959+ exit 1
6060+ fi
6161+ '';
6262+6363+ meta = {
6464+ homepage = "https://docs.aws.amazon.com/systems-manager/latest/userguide/session-manager-working-with-install-plugin.html";
6565+ description = "Amazon SSM Session Manager Plugin";
6666+ mainProgram = "session-manager-plugin";
6767+ license = lib.licenses.asl20;
6868+ maintainers = with lib.maintainers; [ amarshall mbaillie ];
6969+ };
7070+}
···1616 1 file changed, 8 insertions(+)
17171818diff --git a/src/core/manager.c b/src/core/manager.c
1919-index 22cc5cc843..5dc7d4504f 100644
1919+index e26c6c5cfd..6cc1642684 100644
2020--- a/src/core/manager.c
2121+++ b/src/core/manager.c
2222-@@ -3914,9 +3914,17 @@ static int build_generator_environment(Manager *m, char ***ret) {
2222+@@ -4035,9 +4035,17 @@ static int build_generator_environment(Manager *m, char ***ret) {
2323 * adjust generated units to that. Let's pass down some bits of information that are easy for us to
2424 * determine (but a bit harder for generator scripts to determine), as environment variables. */
2525
···3030 1 file changed, 3 insertions(+)
31313232diff --git a/src/systemctl/systemctl-edit.c b/src/systemctl/systemctl-edit.c
3333-index 367afa20f7..5777154d01 100644
3333+index 15398f8364..8d440cee59 100644
3434--- a/src/systemctl/systemctl-edit.c
3535+++ b/src/systemctl/systemctl-edit.c
3636@@ -322,6 +322,9 @@ int verb_edit(int argc, char *argv[], void *userdata) {
···4040+ if (!arg_runtime && arg_runtime_scope == RUNTIME_SCOPE_SYSTEM)
4141+ 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.");
4242+
4343- if (!on_tty())
4343+ if (!on_tty() && !arg_stdin)
4444 return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units if not on a tty.");
4545
···11-From 7a27556920fe1feefd17096841c8f3ca1294a1b3 Mon Sep 17 00:00:00 2001
11+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
22From: Yuri Nesterov <yuriy.nesterov@unikie.com>
33Date: Wed, 21 Jun 2023 17:17:38 +0300
44Subject: [PATCH] timesyncd: disable NSCD when DNSSEC validation is disabled
···1313 1 file changed, 11 insertions(+)
14141515diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c
1616-index 1d8ebecc91..2b0ae361ff 100644
1616+index 5c308a04bc..81aa3d3334 100644
1717--- a/src/timesync/timesyncd.c
1818+++ b/src/timesync/timesyncd.c
1919@@ -21,6 +21,11 @@
···3232 if (r < 0)
3333 return log_error_errno(r, "Failed to parse fallback server strings: %m");
34343535-+ r = getenv_bool_secure("SYSTEMD_NSS_RESOLVE_VALIDATE");
3535++ r = secure_getenv_bool("SYSTEMD_NSS_RESOLVE_VALIDATE");
3636+ if (r == 0) {
3737+ log_info("Disabling NSCD because DNSSEC validation is turned off");
3838+ __nss_disable_nscd(register_traced_file);
···4141 log_debug("systemd-timesyncd running as pid " PID_FMT, getpid_cached());
42424343 notify_message = notify_start("READY=1\n"
4444---
4545-2.34.1
4646-
+35-134
pkgs/os-specific/linux/systemd/default.nix
···66, pkgsCross
77, fetchFromGitHub
88, fetchzip
99+, fetchpatch
910, buildPackages
1011, makeBinaryWrapper
1112, ninja
···1617, gperf
1718, getent
1819, glibcLocales
2020+, autoPatchelfHook
19212022 # glib is only used during tests (test-bus-gvariant, test-bus-marshal)
2123, glib
···6769, p11-kit
6870, libpwquality
6971, qrencode
7272+, libarchive
70737174 # the (optional) BPF feature requires bpftool, libbpf, clang and llvm-strip to
7275 # be available during build time.
···153156 # building disk images for non-NixOS systems. To save users from trying to use it
154157 # on their live NixOS system, we disable it by default.
155158, withKernelInstall ? false
159159+, withLibarchive ? true
156160 # tests assume too much system access for them to be feasible for us right now
157161, withTests ? false
158162 # build only libudev and libsystemd
···179183let
180184 wantCurl = withRemote || withImportd;
181185 wantGcrypt = withResolved || withImportd;
182182- version = "255.9";
186186+ version = "256.2";
183187184188 # Use the command below to update `releaseTimestamp` on every (major) version
185189 # change. More details in the commentary at mesonFlags.
186190 # command:
187191 # $ curl -s https://api.github.com/repos/systemd/systemd/releases/latest | \
188192 # jq '.created_at|strptime("%Y-%m-%dT%H:%M:%SZ")|mktime'
189189- releaseTimestamp = "1701895110";
193193+ releaseTimestamp = "1720202583";
190194in
191195stdenv.mkDerivation (finalAttrs: {
192196 inherit pname version;
···195199 # This has proven to be less error-prone than the previous systemd fork.
196200 src = fetchFromGitHub {
197201 owner = "systemd";
198198- repo = "systemd-stable";
199199- rev = "v${finalAttrs.version}";
200200- hash = "sha256-fnMvBYyMRQrP2x//8ntGTSwoHOtFk2TQ4S5fwcsSLDU=";
202202+ repo = "systemd";
203203+ rev = "v${version}";
204204+ hash = "sha256-fyHzL+oe192YYuwyoTrov10IlrB0NSfY/XKVWzJrQEI=";
201205 };
202206203207 # On major changes, or when otherwise required, you *must* :
···226230 ./0015-tpm2_context_init-fix-driver-name-checking.patch
227231 ./0016-systemctl-edit-suggest-systemdctl-edit-runtime-on-sy.patch
228232 ./0017-meson.build-do-not-create-systemdstatedir.patch
233233+234234+ # https://github.com/systemd/systemd/pull/33258
235235+ # Remove after 256.3
236236+ (fetchpatch {
237237+ url = "https://github.com/systemd/systemd/compare/b268a71069786a45460807967e669d505ba3c5a2..f26b2ec46118a4493608618da2253bb9dfc6b517.patch";
238238+ hash = "sha256-OmuPDm3NykrDeNTA3NcYt9iTXEUFwKJ5apPP4KqtABg=";
239239+ })
240240+241241+ # https://github.com/systemd/systemd/pull/33400
242242+ (fetchpatch {
243243+ url = "https://github.com/systemd/systemd/compare/051d462b42fe6c27824046c15cd3c84fa5afe05b..5e2d802c018f0b6d5dd58745f64d6958fa261096.patch";
244244+ hash = "sha256-drGAnx+ECixOjIP0DUSbCG/emUgoVips9WQL5ny3NKQ=";
245245+ })
229246 ] ++ lib.optional (stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isGnu) [
230247 ./0018-timesyncd-disable-NSCD-when-DNSSEC-validation-is-dis.patch
231248 ] ++ lib.optional stdenv.hostPlatform.isMusl (
···274291 --replace \
275292 "/usr/lib/systemd/boot/efi" \
276293 "$out/lib/systemd/boot/efi"
277277- '' + (
278278- let
279279- # The following patches references to dynamic libraries to ensure that all
280280- # the features that are implemented via dlopen(3) are available (or
281281- # explicitly deactivated) by pointing dlopen to the absolute store path
282282- # instead of relying on the linkers runtime lookup code.
283283- #
284284- # All of the shared library references have to be handled. When new ones
285285- # are introduced by upstream (or one of our patches) they must be
286286- # explicitly declared, otherwise the build will fail.
287287- #
288288- # As of systemd version 247 we've seen a few errors like `libpcre2.… not
289289- # found` when using e.g. --grep with journalctl. Those errors should
290290- # become less unexpected now.
291291- #
292292- # There are generally two classes of dlopen(3) calls. Those that we want
293293- # to support and those that should be deactivated / unsupported. This
294294- # change enforces that we handle all dlopen calls explicitly. Meaning:
295295- # There is not a single dlopen call in the source code tree that we did
296296- # not explicitly handle.
297297- #
298298- # In order to do this we introduced a list of attributes that maps from
299299- # shared object name to the package that contains them. The package can be
300300- # null meaning the reference should be nuked and the shared object will
301301- # never be loadable during runtime (because it points at an invalid store
302302- # path location).
303303- #
304304- # To get a list of dynamically loaded libraries issue something like
305305- # `grep -ri '"lib[a-zA-Z0-9-]*\.so[\.0-9a-zA-z]*"'' $src`
306306- # and update the list below.
307307- dlopenLibs =
308308- let
309309- opt = condition: pkg: if condition then pkg else null;
310310- in
311311- [
312312- # bpf compilation support. We use libbpf 1 now.
313313- { name = "libbpf.so.1"; pkg = opt withLibBPF libbpf; }
314314- { name = "libbpf.so.0"; pkg = null; }
315315-316316- # We did never provide support for libxkbcommon
317317- { name = "libxkbcommon.so.0"; pkg = null; }
318318-319319- # qrencode
320320- { name = "libqrencode.so.4"; pkg = opt withQrencode qrencode; }
321321- { name = "libqrencode.so.3"; pkg = null; }
322322-323323- # Password quality
324324- # We currently do not package passwdqc, only libpwquality.
325325- { name = "libpwquality.so.1"; pkg = opt withPasswordQuality libpwquality; }
326326- { name = "libpasswdqc.so.1"; pkg = null; }
327327-328328- # Only include cryptsetup if it is enabled. We might not be able to
329329- # provide it during "bootstrap" in e.g. the minimal systemd build as
330330- # cryptsetup has udev (aka systemd) in it's dependencies.
331331- { name = "libcryptsetup.so.12"; pkg = opt withCryptsetup cryptsetup; }
332332-333333- # We are using libidn2 so we only provide that and ignore the others.
334334- # Systemd does this decision during configure time and uses ifdef's to
335335- # enable specific branches. We can safely ignore (nuke) the libidn "v1"
336336- # libraries.
337337- { name = "libidn2.so.0"; pkg = opt withLibidn2 libidn2; }
338338- { name = "libidn.so.12"; pkg = null; }
339339- { name = "libidn.so.11"; pkg = null; }
340340-341341- # journalctl --grep requires libpcre so let's provide it
342342- { name = "libpcre2-8.so.0"; pkg = pcre2; }
343343-344344- # Support for TPM2 in systemd-cryptsetup, systemd-repart and systemd-cryptenroll
345345- { name = "libtss2-esys.so.0"; pkg = opt withTpm2Tss tpm2-tss; }
346346- { name = "libtss2-rc.so.0"; pkg = opt withTpm2Tss tpm2-tss; }
347347- { name = "libtss2-mu.so.0"; pkg = opt withTpm2Tss tpm2-tss; }
348348- { name = "libtss2-tcti-"; pkg = opt withTpm2Tss tpm2-tss; }
349349- { name = "libfido2.so.1"; pkg = opt withFido2 libfido2; }
350350-351351- # inspect-elf support
352352- { name = "libelf.so.1"; pkg = opt withCoredump elfutils; }
353353- { name = "libdw.so.1"; pkg = opt withCoredump elfutils; }
354354-355355- # Support for PKCS#11 in systemd-cryptsetup, systemd-cryptenroll and systemd-homed
356356- { name = "libp11-kit.so.0"; pkg = opt (withHomed || withCryptsetup) p11-kit; }
357357-358358- { name = "libip4tc.so.2"; pkg = opt withIptables iptables; }
359359- ];
360360-361361- patchDlOpen = dl:
362362- let
363363- library = "${lib.makeLibraryPath [ dl.pkg ]}/${dl.name}";
364364- in
365365- if dl.pkg == null then ''
366366- # remove the dependency on the library by replacing it with an invalid path
367367- for file in $(grep -lr '"${dl.name}"' src); do
368368- echo "patching dlopen(\"${dl.name}\", …) in $file to an invalid store path ("${builtins.storeDir}/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-not-implemented/${dl.name}")…"
369369- substituteInPlace "$file" --replace '"${dl.name}"' '"${builtins.storeDir}/eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-not-implemented/${dl.name}"'
370370- done
371371- '' else ''
372372- # ensure that the library we provide actually exists
373373- if ! [ -e ${library} ]; then
374374- # exceptional case, details:
375375- # https://github.com/systemd/systemd-stable/blob/v249-stable/src/shared/tpm2-util.c#L157
376376- if ! [[ "${library}" =~ .*libtss2-tcti-$ ]]; then
377377- echo 'The shared library `${library}` does not exist but was given as substitute for `${dl.name}`'
378378- exit 1
379379- fi
380380- fi
381381- # make the path to the dependency explicit
382382- for file in $(grep -lr '"${dl.name}"' src); do
383383- echo "patching dlopen(\"${dl.name}\", …) in $file to ${library}…"
384384- substituteInPlace "$file" --replace '"${dl.name}"' '"${library}"'
385385- done
386386-387387- '';
388388- in
389389- # patch all the dlopen calls to contain absolute paths to the libraries
390390- lib.concatMapStringsSep "\n" patchDlOpen dlopenLibs
391391- )
392392- # finally ensure that there are no left-over dlopen calls (or rather strings
393393- # pointing to shared libraries) that we didn't handle
394394- + ''
395395- if grep -qr '"lib[a-zA-Z0-9-]*\.so[\.0-9a-zA-z]*"' src; then
396396- echo "Found unhandled dynamic library calls: "
397397- grep -r '"lib[a-zA-Z0-9-]*\.so[\.0-9a-zA-z]*"' src
398398- exit 1
399399- fi
400294 ''
401295 # Finally, patch shebangs in scripts used at build time. This must not patch
402296 # scripts that will end up in the output, to avoid build platform references
···425319 glibcLocales
426320 getent
427321 m4
322322+ autoPatchelfHook
428323429324 intltool
430325 gettext
···477372 ++ lib.optional withUkify (python3Packages.python.withPackages (ps: with ps; [ pefile ]))
478373 ++ lib.optionals withPasswordQuality [ libpwquality ]
479374 ++ lib.optionals withQrencode [ qrencode ]
375375+ ++ lib.optionals withLibarchive [ libarchive ]
480376 ;
481377482378 mesonBuildType = "release";
···493389 # https://github.com/systemd/systemd/blob/60e930fc3e6eb8a36fbc184773119eb8d2f30364/NEWS#L258-L266
494390 (lib.mesonOption "time-epoch" releaseTimestamp)
495391496496- (lib.mesonOption "version-tag" finalAttrs.version)
392392+ (lib.mesonOption "version-tag" version)
497393 (lib.mesonOption "mode" "release")
498394 (lib.mesonOption "tty-gid" "3") # tty in NixOS has gid 3
499395 (lib.mesonOption "debug-shell" "${bashInteractive}/bin/bash")
500396 (lib.mesonOption "pamconfdir" "${placeholder "out"}/etc/pam.d")
501501- # Use cgroupsv2. This is already the upstream default, but better be explicit.
502502- (lib.mesonOption "default-hierarchy" "unified")
503397 (lib.mesonOption "kmod-path" "${kmod}/bin/kmod")
504398505399 # Attempts to check /usr/sbin and that fails in macOS sandbox because
···525419 (lib.mesonOption "sbat-distro" "nixos")
526420 (lib.mesonOption "sbat-distro-summary" "NixOS")
527421 (lib.mesonOption "sbat-distro-url" "https://nixos.org/")
528528- (lib.mesonOption "sbat-distro-pkgname" finalAttrs.pname)
529529- (lib.mesonOption "sbat-distro-version" finalAttrs.version)
422422+ (lib.mesonOption "sbat-distro-pkgname" pname)
423423+ (lib.mesonOption "sbat-distro-version" version)
530424531425 # Users
532426 (lib.mesonOption "system-uid-max" "999")
···543437 # Mount
544438 (lib.mesonOption "mount-path" "${lib.getOutput "mount" util-linux}/bin/mount")
545439 (lib.mesonOption "umount-path" "${lib.getOutput "mount" util-linux}/bin/umount")
440440+441441+ # SSH
442442+ # Disabled for now until someone makes this work.
443443+ (lib.mesonOption "sshconfdir" "no")
444444+ (lib.mesonOption "sshdconfdir" "no")
546445547446548447 # Features
···607506 (lib.mesonEnable "kmod" withKmod)
608507 (lib.mesonEnable "qrencode" withQrencode)
609508 (lib.mesonEnable "vmspawn" withVmspawn)
509509+ (lib.mesonEnable "libarchive" withLibarchive)
610510 (lib.mesonEnable "xenctrl" false)
611511 (lib.mesonEnable "gnutls" false)
612512 (lib.mesonEnable "xkbcommon" false)
···874774 interfaceVersion = 2;
875775876776 inherit withBootloader withCryptsetup withEfi withHostnamed withImportd withKmod
877877- withLocaled withMachined withPortabled withTimedated withUtmp util-linux kmod kbd;
777777+ withLocaled withMachined withPortabled withTimedated withTpm2Tss withUtmp
778778+ util-linux kmod kbd;
878779879780 tests = {
880781 inherit (nixosTests)