commit e7552a277a2ebd39c689af8569d37362225fbb79 Author: Adrian Malacoda Date: Fri Mar 27 00:17:20 2020 -0500 initial commit 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)