summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xexamples/cgroup.py4
-rwxr-xr-xexamples/chhostname.py60
-rwxr-xr-xexamples/withallsubuids.py4
-rw-r--r--linuxnamespaces/__init__.py5
4 files changed, 69 insertions, 4 deletions
diff --git a/examples/cgroup.py b/examples/cgroup.py
index 219dc62..34bf01b 100755
--- a/examples/cgroup.py
+++ b/examples/cgroup.py
@@ -56,8 +56,8 @@ def main() -> None:
| linuxnamespaces.CloneFlags.NEWCGROUP
)
linuxnamespaces.unshare_user_idmap(
- [linuxnamespaces.IDMapping(os.getuid(), os.getuid(), 1)],
- [linuxnamespaces.IDMapping(os.getgid(), os.getgid(), 1)],
+ [linuxnamespaces.IDMapping.identity(os.getuid())],
+ [linuxnamespaces.IDMapping.identity(os.getgid())],
namespaces,
)
linuxnamespaces.populate_sys("/", "/", namespaces, mycgroup)
diff --git a/examples/chhostname.py b/examples/chhostname.py
new file mode 100755
index 0000000..bf174e6
--- /dev/null
+++ b/examples/chhostname.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python3
+# Copyright 2024 Helmut Grohne <helmut@subdivi.de>
+# SPDX-License-Identifier: GPL-3
+
+"""Unshare a UTS (and user and mount) namespace and change the hostname."""
+
+import os
+import pathlib
+import socket
+import sys
+import tempfile
+
+if __file__.split("/")[-2:-1] == ["examples"]:
+ sys.path.insert(0, "/".join(__file__.split("/")[:-2]))
+
+import linuxnamespaces
+
+
+def change_file(location: pathlib.Path, content: bytes | str) -> None:
+ if isinstance(content, str):
+ content = content.encode("ascii")
+ try:
+ st = location.stat()
+ except FileNotFoundError as err:
+ raise ValueError(
+ f"cannot change non-existent file: {location!r}"
+ ) from err
+ if st.st_size == len(content) and location.read_bytes() == content:
+ return
+ with tempfile.NamedTemporaryFile() as tfile:
+ tfile.write(content)
+ # In Python >= 3.12, we should set delete_on_close=False rather than
+ # closing the underlying file object behind tempfile's back.
+ tfile.file.close()
+ linuxnamespaces.bind_mount(tfile.name, location)
+
+
+def main() -> None:
+ hostname = sys.argv[1]
+ linuxnamespaces.unshare_user_idmap(
+ [linuxnamespaces.IDMapping.identity(os.getuid())],
+ [linuxnamespaces.IDMapping.identity(os.getgid())],
+ linuxnamespaces.CloneFlags.NEWUSER
+ | linuxnamespaces.CloneFlags.NEWNS
+ | linuxnamespaces.CloneFlags.NEWUTS,
+ )
+ socket.sethostname(hostname)
+ etc = pathlib.Path("/etc")
+ change_file(etc / "hostname", f"{hostname}\n")
+ change_file(
+ etc / "hosts",
+ f"""127.0.0.1 {hostname} localhost
+::1 {hostname} localhost
+""",
+ )
+ os.execlp(os.environ["SHELL"], os.environ["SHELL"])
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/withallsubuids.py b/examples/withallsubuids.py
index 3bed3bc..2a0c9d5 100755
--- a/examples/withallsubuids.py
+++ b/examples/withallsubuids.py
@@ -19,7 +19,7 @@ import linuxnamespaces
def main() -> None:
# Construct an identity mapping of all available user/group ids
uidmap = [
- linuxnamespaces.IDMapping(os.getuid(), os.getuid(), 1),
+ linuxnamespaces.IDMapping.identity(os.getuid()),
*(
linuxnamespaces.IDMapping(start, start, count)
for start, count
@@ -27,7 +27,7 @@ def main() -> None:
),
]
gidmap = [
- linuxnamespaces.IDMapping(os.getgid(), os.getgid(), 1),
+ linuxnamespaces.IDMapping.identity(os.getgid()),
*(
linuxnamespaces.IDMapping(start, start, count)
for start, count
diff --git a/linuxnamespaces/__init__.py b/linuxnamespaces/__init__.py
index 8c1def3..a2c7985 100644
--- a/linuxnamespaces/__init__.py
+++ b/linuxnamespaces/__init__.py
@@ -59,6 +59,11 @@ class IDMapping:
if self.innerstart + self.count >= 1 << 64:
raise ValueError("innerstart + count exceed 64bits")
+ @classmethod
+ def identity(cls, idn: int, count: int = 1) -> typing.Self:
+ """Construct an identity mapping for the given identifier."""
+ return cls(idn, idn, count)
+
class IDAllocation:
"""This represents a subset of IDs (user or group). It can be used to