summaryrefslogtreecommitdiff
path: root/examples/chhostname.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/chhostname.py')
-rwxr-xr-xexamples/chhostname.py60
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()