diff options
-rwxr-xr-x | examples/cgroup.py | 4 | ||||
-rwxr-xr-x | examples/chhostname.py | 60 | ||||
-rwxr-xr-x | examples/withallsubuids.py | 4 | ||||
-rw-r--r-- | linuxnamespaces/__init__.py | 5 |
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 |