diff options
-rwxr-xr-x | examples/chhostname.py | 60 |
1 files changed, 60 insertions, 0 deletions
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() |