From e7552a277a2ebd39c689af8569d37362225fbb79 Mon Sep 17 00:00:00 2001 From: Adrian Malacoda Date: Fri, 27 Mar 2020 00:17:20 -0500 Subject: [PATCH] initial commit --- README.md | 25 +++++++++++ otherworld | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 README.md create mode 100755 otherworld diff --git a/README.md b/README.md new file mode 100644 index 0000000..5ecec0a --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Otherworld +Otherworld creates a persistent Docker container running your choice of GNU/Linux distribution, with a matching user account (no need to run everything as root) and with x11 and PulseAudio mappings. The container is created the first time you run otherworld and sticks around until you dispose of it manually. + +## Usage +### Run command as user +`otherworld ` + +### Run command as root +`otherworld --sudo ` + +### Run command without printing anything else +`otherworld --quiet ` + +### Remove the container +`otherworld --rm` + +## Environment variables +### OW_IMAGE +Image to create the container with (default: `debian:stable`) + +### OW_CONTAINER +Name to assign to container (default: `overworld_$USER`) + +## License +[GNU GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html) or later diff --git a/otherworld b/otherworld new file mode 100755 index 0000000..80f836f --- /dev/null +++ b/otherworld @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +from subprocess import call, check_output +from time import sleep +import os +import sys +import json + +CONTAINER_NAME = "otherworld" +IMAGE_NAME = "debian:stable" + +def create_x11_mapping (): + home = os.environ["HOME"] + xdg_dir = os.environ["XDG_RUNTIME_DIR"] + display = os.environ["DISPLAY"] + inner_user = home + Xauthority = os.environ["XAUTHORITY"] + + return ["-e", f"DISPLAY={display}", + "-v", "/tmp/.X11-unix/:/tmp/.X11-unix", + "-v", "/dev/snd:/dev/snd", + "-v", f"{xdg_dir}/pulse:/run/pulse:ro", + "-v", f"{home}/.gtkrc-2.0:{inner_user}/.gtkrc-2.0", + "-v", f"{home}/.config/gtk-3.0/:{inner_user}/.config/gtk-3.0", + "-v", f"{home}/.guix-profile/share/fonts/truetype:/user/share/fonts/truetype-guix", + "-v", f"{home}/.guix-profile/share/fonts/opentype:/usr/share/fonts/opentype-guix", + "-v", f"{Xauthority}:{inner_user}/.Xauthority", + "-v", f"{home}/otherworld:{inner_user}/otherworld", + "-w", inner_user, + "--shm-size", "2g", + "--privileged"] + +DOCKER_CREATE_OPTIONS = ["-t"] + create_x11_mapping() + +BACKGROUND_COMMAND = None +DEFAULT_COMMAND = ["bash"] + +def docker_inspect (container): + return json.loads(check_output(["docker", "inspect", container])) + +def docker_create (image, container, options, command): + docker_command = ["docker", "create", "--name", container] + options + [image] + if command: + docker_command = docker_command + command + call(docker_command) + +def docker_get_user (container, user): + return check_output(["docker", "exec", container, "groups", user]) + +def docker_create_user (container, user): + call(["docker", "exec", container, "adduser", user, "--gecos", ",,,", "--disabled-password"]) + call(["docker", "exec", container, "chown", user, f"/home/{user}"]) + +def docker_start (container): + call(["docker", "start", container]) + +def docker_exec (container, command, user): + call(["docker", "exec", "--user", user, "-ti", container] + command) + +def docker_rm (container): + call(["docker", "rm", "-f", container]) + +user = os.environ["USER"] +image_name = os.environ.get("OW_IMAGE", IMAGE_NAME) +container_name = os.environ.get("OW_CONTAINER", f"{CONTAINER_NAME}_{user}") + +command = sys.argv[1:] +command_user = user + +quiet = False +while len(command) > 0: + arg = command[0] + if arg == "--quiet": + command = command[1:] + quiet = True + elif arg == "--rm": + if not quiet: + print(f">> Removing container: {container_name}") + docker_rm(container_name) + sys.exit(0) + elif arg == "--sudo": + command_user = "root" + command = command[1:] + elif arg.startswith("--container="): + container_name = arg[len("--container="):] + command = command[1:] + elif arg.startswith("--image="): + image_name = arg[len("--image="):] + command = command[1:] + else: + break + +if len(command) == 0: + command = DEFAULT_COMMAND + +container = None +try: + container = docker_inspect(container_name)[0] +except Exception: + if not quiet: + print(f">> Creating container: {container_name} (using image: {image_name})") + docker_create( + image_name, + container_name, + DOCKER_CREATE_OPTIONS, + BACKGROUND_COMMAND + ) + container = docker_inspect(container_name)[0] + +if not container["State"]["Running"]: + if not quiet: + print(f">> Starting container: {container_name}") + docker_start(container_name) + container = docker_inspect(container_name)[0] + +try: + docker_get_user(container_name, user) +except Exception: + if not quiet: + print(f">> Creating container user: {user}") + docker_create_user(container_name, user) + +if not quiet: + print(f">> Welcome to {container_name} (IP {container['NetworkSettings']['IPAddress']})") +docker_exec(container_name, command, command_user)