├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── PKGBUILD ├── README.md ├── binsrc ├── Makefile ├── genie-wrapper │ ├── Makefile │ ├── genie.c │ └── test-as-root ├── genie │ ├── __main__.py │ ├── apparmor.py │ ├── binfmts.py │ ├── configuration.py │ ├── helpers.py │ ├── host.py │ ├── requirements.txt │ └── resolved.py └── runinwsl │ └── __main__.py ├── container-package-arch.sh ├── container-package-fedora.sh ├── debian ├── changelog ├── control ├── copyright ├── docs ├── rules ├── source │ └── format ├── systemd-genie.links ├── systemd-genie.lintian-overrides └── systemd-genie.manpages ├── genie.spec ├── othersrc ├── docs │ ├── genie.8 │ ├── license.md │ ├── readme.md │ └── wslgenie.png ├── etc │ └── genie.ini ├── lib-systemd-system │ └── user-runtime-dir@.service.d │ │ └── override.conf ├── scripts │ ├── 80-genie-envar.sh │ ├── map-user-runtime-dir.sh │ └── unmap-user-runtime-dir.sh └── usr-lib │ └── tmpfiles.d │ └── wslg.conf └── readme-1.44.md /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ['cerebrate'] 2 | ko_fi: arkanesystems 3 | 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: cerebrate 7 | 8 | --- 9 | 10 | **Windows version (build number):** 11 | Find this by running `winver` 12 | 13 | **Linux distribution:** 14 | i.e. Debian bullseye, Ubuntu focal, etc. 15 | If your distro was not installed from the Microsoft Store, please indicate source and method of installation. 16 | 17 | **Kernel version:** 18 | The output of `cat /proc/sys/kernel/osrelease` 19 | 20 | **Genie version:** 21 | The output of `genie --version` 22 | 23 | **Describe the bug** 24 | A clear and concise description of what the bug is. 25 | 26 | **Confirm that you are running inside the bottle:** 27 | The output of `genie -b`. 28 | 29 | **To Reproduce** 30 | Steps to reproduce the behavior: 31 | 1. Go to '...' 32 | 2. Click on '....' 33 | 3. Scroll down to '....' 34 | 4. See error 35 | 36 | **Expected behavior** 37 | A clear and concise description of what you expected to happen. 38 | 39 | **Screenshots** 40 | If applicable, add screenshots to help explain your problem. 41 | 42 | **Additional context** 43 | Add any other context about the problem here. 44 | 45 | **I confirm that I have read the ENTIRE supplied readme file and checked for relevant information on the repository wiki before raising this issue, and that if the solution to this issue is found in either location, it will be closed without further comment:** 46 | 47 | - [ ] Yes. 48 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: cerebrate 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build-debian: 11 | runs-on: ubuntu-20.04 12 | 13 | steps: 14 | - name: Update apt db 15 | run: sudo apt update 16 | 17 | - name: Install Packagers & Cross-Compiler 18 | run: sudo apt install -y devscripts debhelper gcc-aarch64-linux-gnu 19 | 20 | - name: Checkout 21 | uses: actions/checkout@v2 22 | 23 | - name: Build & Make Debian Package 24 | run: make package-debian 25 | 26 | - name: Upload Results 27 | uses: actions/upload-artifact@v3 28 | with: 29 | name: debpkg 30 | path: out/debian/* 31 | 32 | build-tarball: 33 | runs-on: ubuntu-20.04 34 | 35 | steps: 36 | - name: Update apt db 37 | run: sudo apt update 38 | 39 | - name: Checkout 40 | uses: actions/checkout@v3 41 | 42 | - name: Build & Make Tarball 43 | run: make package-tar 44 | 45 | - name: Upload Results 46 | uses: actions/upload-artifact@v3 47 | with: 48 | name: tar 49 | path: out/tar/* 50 | 51 | build-arch: 52 | runs-on: ubuntu-latest 53 | container: 54 | image: cerebrate/fuckarch:right-in-the-ear 55 | 56 | steps: 57 | - name: Checkout 58 | uses: actions/checkout@v2 59 | 60 | - name: Permissions fixup 61 | run: sudo chown -R build . 62 | 63 | - name: Build Package 64 | run: sudo -u build make package-arch 65 | 66 | - name: Upload Results 67 | uses: actions/upload-artifact@v3 68 | with: 69 | name: zst 70 | path: out/arch/* 71 | 72 | build-fedora: 73 | strategy: 74 | matrix: 75 | fedora_version: [36] # [35, 36, 37] 76 | runs-on: ubuntu-latest 77 | container: 78 | image: fedora:${{ matrix.fedora_version }} 79 | steps: 80 | - name: dnf update 81 | run: sudo dnf update -y 82 | 83 | - name: Install Packagers & Cross-Compiler 84 | run: | 85 | sudo dnf install -y \ 86 | @development-tools \ 87 | @rpm-development-tools \ 88 | python-pip \ 89 | gcc-aarch64-linux-gnu 90 | 91 | - name: Checkout 92 | uses: actions/checkout@v2 93 | 94 | - name: Build RPMs 95 | run: | 96 | make package-fedora RPMBUILD_TARGET=x86_64 97 | make package-fedora RPMBUILD_TARGET=aarch64 98 | 99 | - name: Upload Results 100 | uses: actions/upload-artifact@v3 101 | with: 102 | name: rpm 103 | path: out/fedora/* 104 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | dist/ 3 | out/ 4 | 5 | # Genie wrapper 6 | binsrc/genie-wrapper/genie 7 | binsrc/genie-wrapper/genie-arm64 8 | 9 | # Genie python modules 10 | binsrc/genie/*.dist-info 11 | binsrc/genie/bin/ 12 | binsrc/genie/nsenter/ 13 | binsrc/genie/psutil 14 | binsrc/genie/python_hosts/ 15 | 16 | # Built binaries 17 | binsrc/out 18 | 19 | # Debian build intermediates 20 | 21 | debian/systemd-genie 22 | debian/.debhelper/ 23 | debian/debhelper-build-stamp 24 | debian/files 25 | debian/systemd-genie.debhelper.log 26 | debian/systemd-genie.postinst.debhelper 27 | debian/systemd-genie.substvars 28 | debian/genie-* 29 | 30 | # Tar build intermediates 31 | tarball 32 | 33 | # Arch build intermediates 34 | /genie/ 35 | 36 | # Visual Studio code 37 | .vscode 38 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | debian/changelog -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | First and foremost, thanks for taking the time to contribute! 👍 4 | 5 | ## How can I contribute? 6 | 7 | The _genie_ project welcomes pull requests, whether to address existing issues or for general improvements. 8 | 9 | ## Pull requests 10 | 11 | Please endeavour to maintain the overall style and layout of the project when making a pull request. Also, pull requests should be made against the current _dev_ branch (there will normally be only one branch whose name begins with _dev-_ at a time; if there are more than one, use the highest-numbered branch) rather than the _master_ branch. 12 | 13 | If your update significantly changes the behavior of _genie_ , adds new configuration options, etc., please update the README file and the man page as well. 14 | 15 | If you have or would like to create the pull request (for comment, for example) but it still requires more work before merging, please tag it _work-in-progess_ . 16 | 17 | Thank you! 18 | 19 | 20 | ## Building 21 | 22 | Builds are carried out automatically by GitHub Actions on pull-request or push to master. The only build I use 23 | locally is the Debian one, as that's my build platform. If you change any of the others and they stop working on 24 | GitHub Actions, the pull request will not be accepted even if they do work for you locally. 25 | 26 | The Arch build makes use of the special container image `cerebrate/fuckarch:right-in-the-ear`, which exists to deal 27 | with the pain-in-the-ass that is having to compile a second package manager to deal with getting packages from a 28 | different repository, all for the sake of one lousy package. 29 | 30 | If you need to know the gory details, the Dockerfile for the image is here: 31 | 32 | https://gist.github.com/cerebrate/45daae1bf6ad82ecd041d347bd2b1173 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | othersrc/docs/license.md -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This Makefile builds and packages genie by invoking relevant sub-makefiles. 3 | # 4 | 5 | # Genie version 6 | GENIEVERSION = 2.5 7 | 8 | # Determine this makefile's path. 9 | # Be sure to place this BEFORE `include` directives, if any. 10 | THIS_FILE := $(lastword $(MAKEFILE_LIST)) 11 | 12 | # The values of these variables depend upon DESTDIR, set in the recursive call to 13 | # the internal-package target. 14 | INSTALLDIR = $(DESTDIR)/usr/lib/genie 15 | BINDIR = $(DESTDIR)/usr/bin 16 | ETCDIR = $(DESTDIR)/etc 17 | SVCDIR = $(DESTDIR)/usr/lib/systemd/system 18 | USRLIBDIR = $(DESTDIR)/usr/lib 19 | 20 | # used only by TAR installer 21 | ENVGENDIR = $(DESTDIR)/usr/lib/systemd/system-environment-generators 22 | USRENVGENDIR = $(DESTDIR)/usr/lib/systemd/user-environment-generators 23 | MAN8DIR = $(DESTDIR)/usr/share/man/man8 24 | 25 | # 26 | # Default target: list options 27 | # 28 | 29 | default: 30 | # Build options include: 31 | # 32 | # Build binaries only. 33 | # 34 | # make build-binaries 35 | # 36 | # Package 37 | # 38 | # make package 39 | # make package-debian 40 | # make package-debian-amd64 41 | # make package-debian-arm64 42 | # make package-tar 43 | # make package-arch (requires Arch packaging environment) 44 | # make package-fedora (requires Fedora packaging environment) 45 | # 46 | # Clean up 47 | # 48 | # make clean 49 | # make clean-debian 50 | # make clean-tar 51 | # make clean-arch 52 | # make clean-fedora (requires Fedora packaging environment) 53 | 54 | # 55 | # Targets: individual end-product build. 56 | # 57 | 58 | clean: clean-debian clean-tar clean-arch 59 | make -C binsrc clean 60 | rm -rf out 61 | 62 | package: package-debian 63 | 64 | # 65 | # Debian packaging 66 | # 67 | 68 | package-debian: package-debian-amd64 package-debian-arm64 69 | 70 | package-debian-amd64: make-output-directory 71 | mkdir -p out/debian 72 | debuild --no-sign 73 | mv ../systemd-genie_* out/debian 74 | 75 | package-debian-arm64: make-output-directory 76 | mkdir -p out/debian 77 | debuild -aarm64 -b --no-sign 78 | mv ../systemd-genie_* out/debian 79 | 80 | clean-debian: 81 | debuild -- clean 82 | 83 | package-tar: make-output-directory build-binaries 84 | mkdir -p out/tar 85 | mkdir -p tarball 86 | 87 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball internal-package 88 | 89 | # Do the things that TAR needs that debuild would otherwise do 90 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball internal-supplement 91 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball internal-tar 92 | 93 | mv genie-systemd-*-amd64.tar.gz out/tar 94 | 95 | clean-tar: 96 | rm -rf tarball 97 | 98 | package-arch: 99 | mkdir -p out/arch 100 | updpkgsums 101 | BUILDDIR=/tmp PKDEST=$(PWD)/out/arch makepkg 102 | rm -rf $(PWD)/genie 103 | mv *.zst out/arch 104 | 105 | clean-arch: 106 | rm -rf $(PWD)/genie 107 | rm -rf out/arch 108 | 109 | package-fedora: genie_version := $(shell rpmspec -q --qf %{Version} --srpm genie.spec) 110 | 111 | RPMBUILD_TARGET = $(shell uname --processor) 112 | package-fedora: 113 | rpmdev-setuptree 114 | tar zcvf $(shell rpm --eval '%{_sourcedir}')/genie-${genie_version}.tar.gz * --dereference --transform='s/^/genie-${genie_version}\//' 115 | fakeroot rpmbuild --target $(RPMBUILD_TARGET) -ba -v genie.spec 116 | mkdir -p out/fedora 117 | mv $(shell rpm --eval '%{_rpmdir}')/*/genie* out/fedora 118 | 119 | clean-fedora: 120 | rpmdev-wipetree 121 | rm -rf out/fedora 122 | 123 | # Internal packaging functions 124 | 125 | internal-debian-package: 126 | mkdir -p debian/systemd-genie 127 | @$(MAKE) -f $(THIS_FILE) DESTDIR=debian/systemd-genie internal-package 128 | 129 | # We can assume DESTDIR is set, due to how the following are called. 130 | 131 | internal-package: 132 | 133 | # Binaries. 134 | mkdir -p "$(BINDIR)" 135 | install -Dm 6755 -o root "binsrc/genie-wrapper/genie" -t "$(BINDIR)" 136 | install -Dm 0755 -o root "binsrc/out/genie" -t "$(INSTALLDIR)" 137 | install -Dm 0755 -o root "binsrc/out/runinwsl" -t "$(INSTALLDIR)" 138 | 139 | # Environment generator. 140 | install -Dm 0755 -o root "othersrc/scripts/80-genie-envar.sh" -t "$(INSTALLDIR)" 141 | 142 | # Runtime dir mapping 143 | install -Dm 0755 -o root "othersrc/scripts/map-user-runtime-dir.sh" -t "$(INSTALLDIR)" 144 | install -Dm 0755 -o root "othersrc/scripts/unmap-user-runtime-dir.sh" -t "$(INSTALLDIR)" 145 | 146 | # Configuration file. 147 | install -Dm 0644 -o root "othersrc/etc/genie.ini" -t "$(ETCDIR)" 148 | 149 | # Unit files. 150 | install -Dm 0644 -o root "othersrc/lib-systemd-system/user-runtime-dir@.service.d/override.conf" -t "$(SVCDIR)/user-runtime-dir@.service.d" 151 | 152 | # tmpfiles.d 153 | install -Dm 0644 -o root "othersrc/usr-lib/tmpfiles.d/wslg.conf" -t "$(USRLIBDIR)/tmpfiles.d" 154 | 155 | internal-clean: 156 | make -C binsrc clean 157 | 158 | internal-supplement: 159 | # Fixup symbolic links 160 | mkdir -p $(ENVGENDIR) 161 | mkdir -p $(USRENVGENDIR) 162 | ln -sr $(INSTALLDIR)/80-genie-envar.sh $(ENVGENDIR)/80-genie-envar.sh 163 | ln -sr $(INSTALLDIR)/80-genie-envar.sh $(USRENVGENDIR)/80-genie-envar.sh 164 | 165 | # Man page. 166 | # Make sure directory exists. 167 | mkdir -p "$(MAN8DIR)" 168 | 169 | # this bit would ordinarily be handed by debuild, etc. 170 | 171 | 172 | cp "othersrc/docs/genie.8" /tmp/genie.8 173 | gzip -f9 "/tmp/genie.8" 174 | install -Dm 0644 -o root "/tmp/genie.8.gz" -t "$(MAN8DIR)" 175 | 176 | internal-tar: 177 | # tar it up 178 | tar zcvf genie-systemd-$(GENIEVERSION)-amd64.tar.gz tarball/* --transform='s/^tarball//' 179 | 180 | # 181 | # Helpers: intermediate build stages. 182 | # 183 | 184 | make-output-directory: 185 | mkdir -p out 186 | 187 | build-binaries: 188 | make -C binsrc 189 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Ong Yong Xin 2 | # Contributor: Xuanrui Qi 3 | # Contributor: Rayfalling 4 | # Contributor: facekapow, rayfalling, Ducksoft 5 | _pkgname=genie 6 | pkgname=${_pkgname}-systemd 7 | pkgver=2.5 8 | pkgrel=1 9 | pkgdesc="A quick way into a systemd \"bottle\" for WSL" 10 | arch=('x86_64') 11 | url="https://github.com/arkane-systems/genie" 12 | license=('Unlicense') 13 | depends=('daemonize' 'python>=3.7' 'python-psutil' 'systemd>=232.25' 'inetutils') 14 | conflicts=('bottle-imp') 15 | makedepends=('git' 'python-pip') 16 | options=(!strip) 17 | source=("git+https://github.com/arkane-systems/genie.git") 18 | sha256sums=('SKIP') 19 | backup=('etc/genie.ini') 20 | 21 | # pkgver() { 22 | # git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//g' 23 | # } 24 | 25 | build() { 26 | cd genie 27 | make build-binaries 28 | } 29 | 30 | package() { 31 | cd genie 32 | make DESTDIR=${pkgdir} internal-package 33 | make DESTDIR=${pkgdir} internal-supplement 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | othersrc/docs/readme.md -------------------------------------------------------------------------------- /binsrc/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This Makefile produces all the binary images. 3 | # 4 | 5 | # 6 | # default target: build both for installation 7 | # 8 | build: build-wrapper build-runinwsl build-genie 9 | 10 | build-wrapper: 11 | mkdir -p out 12 | make -C genie-wrapper 13 | 14 | build-runinwsl: 15 | mkdir -p out 16 | python3 -m zipapp -o out/runinwsl -p "/usr/bin/env python3" -c runinwsl 17 | 18 | build-genie: 19 | mkdir -p out 20 | python3 -m pip install -r genie/requirements.txt --target genie --upgrade 21 | python3 -m zipapp -o out/genie -p "/usr/bin/env python3" -c genie 22 | 23 | # 24 | # clean: clean up after a build/package 25 | # 26 | clean: clean-wrapper clean-runinwsl clean-genie 27 | 28 | clean-wrapper: 29 | make -C genie-wrapper clean 30 | 31 | clean-runinwsl: 32 | rm -f out/runinwsl 33 | 34 | clean-genie: 35 | rm -f out/genie 36 | -------------------------------------------------------------------------------- /binsrc/genie-wrapper/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This Makefile produces and packages the genie-systemd program. 3 | # 4 | 5 | # default to amd64 build if not specified 6 | DEB_TARGET_ARCH ?= amd64 7 | 8 | # 9 | # default target: build the executable 10 | # 11 | 12 | all: 13 | ifeq ($(DEB_TARGET_ARCH),amd64) 14 | gcc genie.c -o genie 15 | endif 16 | 17 | ifeq ($(DEB_TARGET_ARCH),arm64) 18 | aarch64-linux-gnu-gcc genie.c -o genie 19 | endif 20 | 21 | # 22 | # clean: delete the package interim files 23 | # 24 | 25 | clean: 26 | rm -f genie 27 | -------------------------------------------------------------------------------- /binsrc/genie-wrapper/genie.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A simple wrapper to execute genie as setuid. 3 | */ 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | int main(int argc, char ** argv) 13 | { 14 | /* Set GENIE_LOGNAME environment variable. */ 15 | struct passwd * logname = getpwuid(getuid()); 16 | int result = setenv ("GENIE_LOGNAME", logname->pw_name, 1); 17 | 18 | if (result != 0) 19 | { 20 | perror ("genie-wrapper-logname"); 21 | return 1; 22 | } 23 | 24 | /* Reset uid/gid */ 25 | setregid(getegid(), getegid()); 26 | setreuid(geteuid(), geteuid()); 27 | 28 | /* Attempt to execute script */ 29 | execv("/usr/lib/genie/genie", argv); 30 | // execv("/bin/sh", argv); 31 | 32 | /* Reach here if execv failed */ 33 | perror("genie-wrapper"); 34 | return 1; 35 | } 36 | -------------------------------------------------------------------------------- /binsrc/genie-wrapper/test-as-root: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | sudo chown root:root genie 3 | sudo chmod 6755 genie 4 | 5 | -------------------------------------------------------------------------------- /binsrc/genie/__main__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import argparse 4 | import fcntl 5 | import os 6 | import subprocess 7 | import sys 8 | import time 9 | 10 | import nsenter 11 | 12 | import apparmor 13 | import binfmts 14 | import configuration 15 | import helpers 16 | import host 17 | import resolved 18 | 19 | # Global variables 20 | version = "2.5" 21 | 22 | verbose = False 23 | login = None 24 | 25 | lockfile_fp = None 26 | 27 | 28 | # Init lock functions 29 | def bottle_init_lock(): 30 | """Lock the bottle init process to one instance only.""" 31 | global lockfile_fp 32 | 33 | lockfile_fp = open('/run/genie.init.lock', 'a') 34 | 35 | try: 36 | fcntl.lockf(lockfile_fp, fcntl.LOCK_EX | fcntl.LOCK_NB) 37 | lockfile_fp.seek(0) 38 | lockfile_fp.truncate() 39 | lockfile_fp.write(str(os.getpid())) 40 | lockfile_fp.flush() 41 | running = False 42 | except IOError: 43 | with open('/run/genie.init.lock', 'r') as fp: 44 | running = fp.read() 45 | 46 | return running 47 | 48 | 49 | def bottle_init_unlock(): 50 | """Unlock the bottle init process.""" 51 | fcntl.lockf(lockfile_fp, fcntl.LOCK_UN) 52 | lockfile_fp.close() 53 | 54 | os.remove("/run/genie.init.lock") 55 | 56 | 57 | # Command line parser 58 | def parse_command_line(): 59 | """Create the command-line option parser and parse arguments.""" 60 | parser = argparse.ArgumentParser( 61 | description="Handles transitions to the \"bottle\" namespace for systemd under WSL.", 62 | epilog="For more information, see https://github.com/arkane-systems/genie/") 63 | 64 | # Version command 65 | parser.add_argument('-V', '--version', action='version', 66 | version='%(prog)s ' + version) 67 | 68 | # Verbose option 69 | parser.add_argument('-v', '--verbose', action='store_true', 70 | help="display verbose progress messages") 71 | 72 | # Specify username option 73 | parser.add_argument('-a', '--as-user', action='store', 74 | help="specify user to run shell or command as (use with -s or -c)", dest='user') 75 | 76 | # Commands 77 | group2 = parser.add_argument_group('commands') 78 | group = group2.add_mutually_exclusive_group(required=True) 79 | 80 | group.add_argument('-i', '--initialize', action='store_true', 81 | help='initialize the bottle (if necessary) only') 82 | group.add_argument('-s', '--shell', action='store_true', 83 | help='initialize the bottle (if necessary), and run a shell in it') 84 | group.add_argument('-l', '--login', action='store_true', 85 | help='initialize the bottle (if necessary), and open a logon prompt in it') 86 | group.add_argument( 87 | '-c', '--command', help='initialize the bottle (if necessary), and run the specified command in it\n(preserves working directory)', nargs=argparse.REMAINDER) 88 | group.add_argument('-u', '--shutdown', action='store_true', 89 | help='shut down systemd and exit the bottle') 90 | group.add_argument('-r', '--is-running', action='store_true', 91 | help='check whether systemd is running in genie, or not') 92 | group.add_argument('-b', '--is-in-bottle', action='store_true', 93 | help='check whether currently executing within the bottle, or not') 94 | 95 | group.add_argument('-%', '--parser-test', 96 | action='store_true', help=argparse.SUPPRESS) 97 | 98 | return parser.parse_args() 99 | 100 | 101 | # Subordinate functions. 102 | def pre_systemd_action_checks(sdp): 103 | """Things to check before performing a systemd-requiring action.""" 104 | 105 | if sdp == 0: 106 | # no bottle exists; this means we should recurse and start one 107 | initcommand = ["genie", "-i"] 108 | 109 | if verbose: 110 | initcommand.append("-v") 111 | 112 | init = subprocess.run(initcommand) 113 | 114 | # continue when subprocess done 115 | if init.returncode != 0: 116 | sys.exit( 117 | f"could not initialise bottle, exit code = {init.returncode}") 118 | 119 | # Refresh systemd pid 120 | sdp = helpers.find_systemd() 121 | 122 | state = helpers.get_systemd_state(sdp) 123 | 124 | if 'stopping' in state: 125 | sys.exit("genie: systemd is shutting down, cannot proceed") 126 | 127 | if 'initializing' in state or 'starting' in state: 128 | # wait for it 129 | print("genie: systemd is starting up, please wait...", end="", flush=True) 130 | 131 | timeout = configuration.system_timeout() 132 | 133 | while ('running' not in state) and timeout > 0: 134 | time.sleep(1) 135 | state = helpers.get_systemd_state(sdp) 136 | 137 | print(".", end="", flush=True) 138 | 139 | timeout -= 1 140 | 141 | print("") 142 | 143 | if timeout <= 0: 144 | print("genie: WARNING: timeout waiting for bottle to start") 145 | 146 | if 'degraded' in state: 147 | print('genie: WARNING: systemd is in degraded state, issues may occur!') 148 | 149 | if not ('running' in state or 'degraded' in state): 150 | sys.exit("genie: systemd in unsupported state '" 151 | + state + "'; cannot proceed") 152 | 153 | 154 | def set_secure_path(): 155 | """Set up secure path, saving original if specified.""" 156 | # Default original path. 157 | # TODO: Should reference system drive by letter 158 | originalPath = '/mnt/c/Windows/System32' 159 | 160 | if configuration.clone_path(): 161 | originalPath = os.environ['PATH'] 162 | 163 | # Set the local path 164 | os.environ['PATH'] = configuration.secure_path() 165 | 166 | # Create the path file 167 | with open('/run/genie.path', 'w') as pathfile: 168 | print(originalPath, file=pathfile) 169 | pathfile.close() 170 | 171 | 172 | def stash_environment(): 173 | """Save a copy of the original environment (specified variables only).""" 174 | # Get variables to stash 175 | names = configuration.clonable_envars() 176 | 177 | # Do the stashing. 178 | with open('/run/genie.env', 'w') as envfile: 179 | 180 | # Start with the flag variable that's always added. 181 | print("INSIDE_GENIE=yes", file=envfile) 182 | 183 | for n in names: 184 | if n in os.environ: 185 | print(f"{n}={os.environ[n]}", file=envfile) 186 | 187 | envfile.close() 188 | 189 | 190 | # Commands 191 | # Parser test 192 | def do_parser_test(arguments): 193 | """Parser test option.""" 194 | print("genie: congratulations! you found the hidden parser test option!") 195 | print(arguments) 196 | 197 | 198 | # Initialize bottle 199 | def inner_do_initialize(): 200 | """Initialize the genie bottle (inner function).""" 201 | sdp = helpers.find_systemd() 202 | 203 | if sdp != 0: 204 | sys.exit("genie: bottle is already established (systemd running)") 205 | 206 | # FIRST: As a first step in initing systemd, delete any old runtime pid file 207 | # if such exists. 208 | if os.path.exists('/run/genie.systemd.pid'): 209 | os.remove('/run/genie.systemd.pid') 210 | 211 | # Set secure path, and stash original environment. 212 | set_secure_path() 213 | stash_environment() 214 | 215 | # Check and warn if not multi-user.target. 216 | if configuration.target_warning(): 217 | target = helpers.get_systemd_target() 218 | 219 | if target != 'multi-user.target': 220 | print( 221 | f"genie: WARNING: systemd default target is {target}; targets other than multi-user.target may not work") 222 | print("genie: WARNING: if you wish to use a different target, this warning can be disabled in the config file") 223 | print("genie: WARNING: if you experience problems, please change the target to multi-user.target") 224 | 225 | # Now that the WSL hostname can be set via .wslconfig, we're going to make changing 226 | # it automatically in genie an option, enable/disable in genie.ini. Defaults to on 227 | # for backwards compatibility and because not doing so when using bridged networking is 228 | # a Bad Idea. 229 | if configuration.update_hostname(): 230 | host.update(verbose) 231 | 232 | # If configured to, create the resolv.conf symlink for systemd-resolved. 233 | if configuration.resolved_stub(): 234 | resolved.configure(verbose) 235 | 236 | # Update binfmts config file. 237 | flags = binfmts.check_flags(verbose) 238 | binfmts.write_interop_file(verbose, flags) 239 | 240 | # Unmount the binfmts fs before starting systemd, so systemd can mount it 241 | # again with all the trimmings. 242 | binfmts.umount(verbose) 243 | 244 | # Define systemd startup chain. 245 | startupChain = ["daemonize", helpers.get_unshare_path(), "-fp", "--propagation", "shared", "--mount-proc", "--"] 246 | 247 | # Check whether AppArmor is available in the kernel. 248 | if apparmor.exists(): 249 | 250 | # If so, configure AppArmor. 251 | nsName = apparmor.configure(verbose) 252 | 253 | # Add AppArmor to the startup chain. 254 | if nsName is not None: 255 | startupChain = startupChain + \ 256 | ["aa-exec", "-n", nsName, "-p", "unconfined", "--"] 257 | 258 | else: 259 | if verbose: 260 | print( 261 | "genie: AppArmor not available in kernel; attempting to continue without AppArmor namespace") 262 | 263 | # Update startup chain with systemd command. 264 | startupChain.append("systemd") 265 | 266 | # Run systemd in a container 267 | if verbose: 268 | print("genie: starting systemd with command line: ") 269 | print(' '.join(startupChain)) 270 | 271 | # This requires real UID/GID root as well as effective UID/GID root 272 | suid = os.getuid() 273 | sgid = os.getgid() 274 | 275 | os.setuid(0) 276 | os.setgid(0) 277 | 278 | subprocess.run(startupChain) 279 | 280 | os.setuid(suid) 281 | os.setgid(sgid) 282 | 283 | # Wait for systemd to be up (polling, sigh.) 284 | sdp = 0 285 | print("Waiting for systemd...", end="", flush=True) 286 | 287 | while sdp == 0: 288 | time.sleep(0.5) 289 | sdp = helpers.find_systemd() 290 | 291 | print(".", end="", flush=True) 292 | 293 | # Wait for systemd to be in running state. 294 | state = 'initializing' 295 | timeout = configuration.system_timeout() 296 | 297 | while ('running' not in state and 'degraded' not in state) and timeout > 0: 298 | time.sleep(1) 299 | state = helpers.get_systemd_state(sdp) 300 | 301 | print("!", end="", flush=True) 302 | 303 | timeout -= 1 304 | 305 | print("") 306 | 307 | if 'running' not in state: 308 | print( 309 | f"genie: systemd did not enter running state ({state}) after {configuration.system_timeout()} seconds") 310 | print("genie: this may be due to a problem with your systemd configuration") 311 | print("genie: information on problematic units is available at https://github.com/arkane-systems/genie/wiki/Systemd-units-known-to-be-problematic-under-WSL") 312 | print("genie: a list of failed units follows:\n") 313 | 314 | with nsenter.Namespace(sdp, 'pid'): 315 | subprocess.run(["systemctl", "--failed"]) 316 | 317 | # LAST: Now that systemd exists, write out its (external) pid. 318 | # We do not need to store the inside-bottle pid anywhere for obvious reasons. 319 | with open('/run/genie.systemd.pid', 'w') as pidfile: 320 | print(sdp, file=pidfile) 321 | pidfile.close() 322 | 323 | 324 | def do_initialize(): 325 | """Initialize the genie bottle.""" 326 | if verbose: 327 | print("genie: starting bottle") 328 | 329 | # Secure the bottle init lock 330 | running = bottle_init_lock() 331 | if running: 332 | # Wait for other process to have started the bottle 333 | # The last step is the pid file being created, so we wait 334 | # for that, then return. 335 | if verbose: 336 | print( 337 | f"genie: already initializing, pid={running}, waiting...", end="", flush=True) 338 | 339 | # Allow 10% startup margin 340 | timeout = configuration.system_timeout() * 1.1 341 | 342 | while not os.path.exists('/run/genie.systemd.pid') and timeout > 0: 343 | time.sleep(1) 344 | print(".", end="", flush=True) 345 | timeout -= 1 346 | 347 | print("") 348 | 349 | if timeout <= 0: 350 | print("genie: WARNING: timeout waiting for bottle to start") 351 | 352 | return 353 | 354 | # Do the actual functionality of the thing. 355 | inner_do_initialize() 356 | 357 | # Unlock the init lock 358 | bottle_init_unlock() 359 | 360 | 361 | # Run inside bottle. 362 | def do_shell(): 363 | """Start a shell inside the bottle, initializing it if necessary.""" 364 | 365 | if verbose: 366 | print("genie: starting shell") 367 | 368 | pre_systemd_action_checks(helpers.find_systemd()) 369 | 370 | sdp = helpers.find_systemd() 371 | 372 | if sdp == 1: 373 | # we're already inside the bottle 374 | sys.exit("genie: already inside the bottle; cannot proceed") 375 | 376 | # At this point, we should be outside a bottle, one way or another. 377 | # Get the bottle namespace 378 | with nsenter.Namespace(sdp, 'pid'): 379 | subprocess.run("machinectl shell -q " + login + "@.host", shell=True) 380 | 381 | 382 | def do_login(): 383 | """Start a login prompt inside the bottle, initializing it if necessary.""" 384 | 385 | if verbose: 386 | print("genie: starting login prompt") 387 | 388 | pre_systemd_action_checks(helpers.find_systemd()) 389 | 390 | sdp = helpers.find_systemd() 391 | 392 | if sdp == 1: 393 | # we're already inside the bottle 394 | sys.exit("genie: already inside the bottle; cannot proceed") 395 | 396 | # At this point, we should be outside a bottle, one way or another. 397 | # Get the bottle namespace 398 | with nsenter.Namespace(sdp, 'pid'): 399 | subprocess.run("machinectl login .host", shell=True) 400 | 401 | 402 | def do_command(commandline): 403 | """Run a command in a user session inside the bottle, initializing it if necessary.""" 404 | 405 | if verbose: 406 | print("genie: running command " + ' '.join(commandline)) 407 | 408 | if len(commandline) == 0: 409 | sys.exit("genie: no command specified") 410 | 411 | sdp = helpers.find_systemd() 412 | 413 | if sdp == 1: 414 | # we're already inside the bottle 415 | ic = subprocess.run(commandline) 416 | return ic.returncode 417 | 418 | pre_systemd_action_checks(sdp) 419 | 420 | sdp = helpers.find_systemd() 421 | 422 | command = ["machinectl", "shell", "-q", login + "@.host", 423 | "/usr/lib/genie/runinwsl", os.getcwd()] + commandline 424 | 425 | with nsenter.Namespace(sdp, 'pid'): 426 | sp = subprocess.run(command) 427 | return sp 428 | 429 | 430 | # Shut down bottle. 431 | def do_shutdown(): 432 | """Shutdown the genie bottle and clean up.""" 433 | sdp = helpers.find_systemd() 434 | 435 | if sdp == 0: 436 | sys.exit("genie: no bottle exists") 437 | 438 | if sdp == 1: 439 | sys.exit("genie: cannot shut down bottle from inside bottle") 440 | 441 | state = helpers.get_systemd_state(sdp) 442 | 443 | if 'starting' in state or 'stopping' in state: 444 | sys.exit( 445 | f"genie: bottle is currently {state}; please wait until it is in a stable state") 446 | 447 | if verbose: 448 | print("genie: running systemctl poweroff within bottle") 449 | 450 | with nsenter.Namespace(sdp, 'pid'): 451 | subprocess.run(["systemctl", "poweroff"]) 452 | 453 | # Wait for systemd to exit. 454 | print("Waiting for systemd to exit...", end="", flush=True) 455 | 456 | timeout = configuration.system_timeout() 457 | 458 | while helpers.find_systemd() != 0 and timeout > 0: 459 | time.sleep(1) 460 | print(".", end="", flush=True) 461 | 462 | timeout -= 1 463 | 464 | print("") 465 | 466 | if (timeout <= 0): 467 | print( 468 | f"genie: systemd did not exit after {configuration.system_timeout()} seconds") 469 | print("genie: this may be due to a problem with your systemd configuration") 470 | print("genie: attempting to continue") 471 | 472 | # Reverse the processes we performed to prepare the bottle as the post-shutdown 473 | # cleanup, only in reverse. 474 | if apparmor.exists(): 475 | apparmor.unconfigure(verbose) 476 | 477 | binfmts.mount(verbose) 478 | 479 | # If configured to, remove the resolv.conf symlink for systemd-resolved. 480 | if configuration.resolved_stub(): 481 | resolved.unconfigure(verbose) 482 | 483 | if configuration.update_hostname(): 484 | host.restore(verbose) 485 | 486 | 487 | # Status checks. 488 | def do_is_running(): 489 | """Display whether the bottle is running or not.""" 490 | sdp = helpers.find_systemd() 491 | 492 | if sdp == 0: 493 | print("stopped") 494 | sys.exit(1) 495 | 496 | state = helpers.get_systemd_state(sdp) 497 | 498 | if 'running' in state: 499 | print("running") 500 | sys.exit(0) 501 | 502 | if 'initializing' in state or 'starting' in state: 503 | print("starting") 504 | sys.exit(2) 505 | 506 | if 'stopping' in state: 507 | print("stopping") 508 | sys.exit(3) 509 | 510 | if 'degraded' in state: 511 | print("running (systemd errors)") 512 | sys.exit(4) 513 | 514 | print(f"unknown {state}") 515 | sys.exit(5) 516 | 517 | 518 | def do_is_in_bottle(): 519 | """Display whether we are currently executing within or without the genie bottle.""" 520 | sdp = helpers.find_systemd() 521 | 522 | if sdp == 1: 523 | print("inside") 524 | sys.exit(0) 525 | 526 | if sdp == 0: 527 | print("no-bottle") 528 | sys.exit(2) 529 | 530 | print("outside") 531 | sys.exit(1) 532 | 533 | 534 | # Entrypoint 535 | def entrypoint(): 536 | """Entrypoint of the application.""" 537 | global verbose 538 | global login 539 | 540 | helpers.prelaunch_checks() 541 | configuration.load() 542 | arguments = parse_command_line() 543 | 544 | # Set globals 545 | verbose = arguments.verbose 546 | login = helpers.get_login_session_user() 547 | 548 | # Check user 549 | if arguments.user is not None: 550 | 551 | # Abort if user specified and not -c or -s 552 | if not (arguments.shell or (arguments.command is not None)): 553 | sys.exit( 554 | "genie: error: argument -a/--as-user can only be used with -c/--command or -s/--shell") 555 | 556 | # Check if arguments.user is a real user 557 | helpers.validate_is_real_user(arguments.user) 558 | 559 | login = arguments.user 560 | 561 | if verbose: 562 | print(f"genie: executing as user {login}") 563 | 564 | # Decide what to do. 565 | if arguments.parser_test: 566 | do_parser_test(arguments) 567 | elif arguments.initialize: 568 | do_initialize() 569 | elif arguments.shell: 570 | do_shell() 571 | elif arguments.login: 572 | do_login() 573 | elif arguments.command is not None: 574 | do_command(arguments.command) 575 | elif arguments.shutdown: 576 | do_shutdown() 577 | elif arguments.is_running: 578 | do_is_running() 579 | elif arguments.is_in_bottle: 580 | do_is_in_bottle() 581 | else: 582 | sys.exit("genie: impossible argument - how did we get here?") 583 | 584 | 585 | entrypoint() 586 | 587 | # End of file 588 | -------------------------------------------------------------------------------- /binsrc/genie/apparmor.py: -------------------------------------------------------------------------------- 1 | # AppArmor control module 2 | 3 | import os 4 | import subprocess 5 | 6 | import helpers 7 | 8 | 9 | def exists(): 10 | """Determine whether AppArmor support exists in the kernel.""" 11 | if os.path.exists('/sys/module/apparmor'): 12 | return True 13 | else: 14 | return False 15 | 16 | 17 | def configure(verbose): 18 | """Configure an AppArmor namespace for the genie bottle.""" 19 | 20 | # If the AppArmor filesystem is not mounted, mount it. 21 | if not os.path.exists('/sys/kernel/security/apparmor'): 22 | if verbose: 23 | print("genie: mounting AppArmor filesystem") 24 | 25 | sp = subprocess.run(['mount', '-t', 'securityfs', 26 | 'securityfs', '/sys/kernel/security']) 27 | if sp.returncode != 0: 28 | print( 29 | "genie: failed to mount AppArmor filesystem; attempting to continue without AppArmor") 30 | return None 31 | 32 | # Create AppArmor namespace for genie bottle. 33 | nsName = 'genie-' + helpers.get_wsl_distro_name() 34 | 35 | if verbose: 36 | print(f"genie: creating AppArmor namespace '{nsName}'") 37 | 38 | if not os.path.exists('/sys/kernel/security/apparmor/policy/namespaces'): 39 | print("genie: could not find AppArmor filesystem; attempting to continue without AppArmor") 40 | return None 41 | 42 | os.mkdir('/sys/kernel/security/apparmor/policy/namespaces/' + nsName) 43 | 44 | return nsName 45 | 46 | 47 | def unconfigure(verbose): 48 | """Clean up the AppArmor namespace when the bottle is stopped.""" 49 | 50 | nsName = 'genie-' + helpers.get_wsl_distro_name() 51 | 52 | if verbose: 53 | print(f"genie: deleting AppArmor namespace '{nsName}'") 54 | 55 | if os.path.exists('/sys/kernel/security/apparmor/policy/namespaces/' + nsName): 56 | try: 57 | os.rmdir('/sys/kernel/security/apparmor/policy/namespaces/' + nsName) 58 | except OSError as e: 59 | print( 60 | f"genie: failed to delete AppArmor namespace; attempting to continue; {e.strerror}" + e.strerror) 61 | else: 62 | if verbose: 63 | print("genie: no AppArmor namespace to delete") 64 | -------------------------------------------------------------------------------- /binsrc/genie/binfmts.py: -------------------------------------------------------------------------------- 1 | # Binary formats function module 2 | 3 | import os 4 | import subprocess 5 | 6 | 7 | def mount(verbose): 8 | """Mount the binfmt_misc filesystem, if it is not mounted.""" 9 | 10 | # Having unmounted the binfmts fs before starting systemd, we remount it as 11 | # a courtesy. But remember, genie is not guaranteed to be idempotent, so don't 12 | # rely on this, for the love of Thompson and Ritchie! 13 | 14 | if not os.path.exists('/proc/sys/fs/binfmt_misc'): 15 | 16 | if verbose: 17 | print("genie: remounting binfmt_misc filesystem as a courtesy") 18 | 19 | sp = subprocess.run(['mount', '-t', 'binfmt_misc', 20 | 'binfmt_misc', '/proc/sys/fs/binfmt_misc']) 21 | 22 | if sp.returncode != 0: 23 | print( 24 | "genie: failed to remount binfmt_misc filesystem; attempting to continue") 25 | 26 | 27 | def umount(verbose): 28 | """Unmount the binfmts filesystem, if it is mounted.""" 29 | if os.path.exists('/proc/sys/fs/binfmt_misc'): 30 | 31 | if verbose: 32 | print("genie: unmounting binfmt_misc filesystem before proceeding") 33 | 34 | sp = subprocess.run(['umount', '/proc/sys/fs/binfmt_misc']) 35 | 36 | if sp.returncode != 0: 37 | print( 38 | "genie: failed to unmount binfmt_misc filesystem; attempting to continue") 39 | 40 | else: 41 | if verbose: 42 | print("no binfmt_misc filesystem present") 43 | 44 | 45 | def check_flags(verbose): 46 | """Check the flags for the current binfmt filesystem.""" 47 | if os.path.exists('/proc/sys/fs/binfmt_misc/WSLInterop'): 48 | with open('/proc/sys/fs/binfmt_misc/WSLInterop', 'rt') as wif: 49 | for wl in wif: 50 | if wl.startswith('flags: '): 51 | flags = wl.rstrip()[7:] 52 | 53 | if verbose: 54 | print(f'genie: WSL interop flags detected: {flags}') 55 | 56 | return flags 57 | 58 | if verbose: 59 | print("genie: could not find WSLInterop flags") 60 | 61 | return None 62 | 63 | else: 64 | if verbose: 65 | print("genie: no WSLInterop configuration present") 66 | 67 | return None 68 | 69 | 70 | def write_interop_file(verbose, flags): 71 | """Write out a new WSL interop file with the specified flags.""" 72 | if os.path.exists('/usr/lib/binfmt.d'): 73 | 74 | if flags is None: 75 | if verbose: 76 | print ('genie: no WSLInterop configuration available; assuming PF') 77 | flags = 'PF' 78 | 79 | with open('/usr/lib/binfmt.d/WSLInterop.conf', 'w') as f: 80 | f.write(f':WSLInterop:M::MZ::/init:{flags}') 81 | 82 | if verbose: 83 | print ('genie: written new WSLInterop config') 84 | 85 | else: 86 | print ('genie: systemd binfmt.d is not available') 87 | -------------------------------------------------------------------------------- /binsrc/genie/configuration.py: -------------------------------------------------------------------------------- 1 | # Configuration data module 2 | 3 | import configparser 4 | 5 | # Global variables 6 | 7 | _config = None 8 | 9 | 10 | # functions 11 | def clonable_envars(): 12 | """Get the list of environment variables to clone.""" 13 | return (_config.get('genie', 'clone-env', 14 | fallback='WSL_DISTRO_NAME,WSL_INTEROP,WSLENV,DISPLAY,WAYLAND_DISPLAY,PULSE_SERVER')).split(',') 15 | 16 | 17 | def clone_path(): 18 | """Do we clone the outside-bottle path, or not?""" 19 | return _config.getboolean('genie', 'clone-path', fallback=False) 20 | 21 | 22 | def resolved_stub(): 23 | """Do we make the systemd-resolved stub, or not?""" 24 | return _config.getboolean('genie', 'resolved-stub', fallback=False) 25 | 26 | 27 | def secure_path(): 28 | """Get the configured secure path.""" 29 | return _config.get('genie', 'secure-path', fallback='/lib/systemd:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin') 30 | 31 | 32 | def system_timeout(): 33 | """Return the configured timeout for systemd startup.""" 34 | return _config.getint('genie', 'systemd-timeout', fallback=240) 35 | 36 | 37 | def target_warning(): 38 | """Warn when the systemd target is not set to 'multi-user.target'.""" 39 | return _config.getboolean('genie', 'target-warning', fallback=True) 40 | 41 | 42 | def update_hostname(): 43 | """Update the hostname in the config file?""" 44 | return _config.getboolean('genie', 'update-hostname', fallback=True) 45 | 46 | 47 | def update_hostname_suffix(): 48 | """Update the hostname suffix in the config file with...""" 49 | return _config.get('genie', 'update-hostname-suffix', fallback='-wsl') 50 | 51 | 52 | # Initialization 53 | 54 | def load(): 55 | """Load the configuration from the config file ('/etc/genie.ini').""" 56 | global _config 57 | 58 | _config = configparser.ConfigParser() 59 | _config.read('/etc/genie.ini') 60 | -------------------------------------------------------------------------------- /binsrc/genie/helpers.py: -------------------------------------------------------------------------------- 1 | # Helper functions module 2 | 3 | import os 4 | import pwd 5 | import shutil 6 | import subprocess 7 | import sys 8 | 9 | import nsenter 10 | import psutil 11 | 12 | 13 | def find_systemd(): 14 | """Find the running systemd process and return its pid.""" 15 | for proc in psutil.process_iter(): 16 | if "systemd" in proc.name(): 17 | return proc.pid 18 | 19 | return 0 20 | 21 | 22 | def get_login_session_user(): 23 | """Get the user logged into the current session, pre-setuid.""" 24 | # This environment variable is set by the setuid wrapper. 25 | return os.environ["GENIE_LOGNAME"] 26 | 27 | 28 | def get_systemd_state(sdp): 29 | """Get the systemd state, whether we are within or without the bottle.""" 30 | 31 | if sdp == 0: 32 | return "offline" 33 | 34 | with nsenter.Namespace(sdp, 'pid'): 35 | sc = subprocess.run(["systemctl", "is-system-running"], 36 | capture_output=True, text=True) 37 | return sc.stdout.rstrip() 38 | 39 | 40 | def get_systemd_target(): 41 | """Get the default systemd target.""" 42 | return os.path.basename(os.path.realpath('/etc/systemd/system/default.target')) 43 | 44 | 45 | def get_unshare_path(): 46 | """Find the path to the unshare utility.""" 47 | return shutil.which('unshare') 48 | 49 | 50 | def get_wsl_distro_name(): 51 | """Get the WSL distribution name.""" 52 | return os.environ['WSL_DISTRO_NAME'] 53 | 54 | 55 | def prelaunch_checks(): 56 | """Check that we are on the correct platform, and as the correct user.""" 57 | 58 | # Is this Linux? 59 | if not sys.platform.startswith('linux'): 60 | sys.exit("genie: not executing on the Linux platform - how did we get here?") 61 | 62 | # Is this WSL 1? 63 | root_type = list(filter(lambda x: x.mountpoint == '/', 64 | psutil.disk_partitions(all=True)))[0].fstype 65 | if root_type == 'lxfs' or root_type == 'wslfs': 66 | sys.exit("genie: systemd is not supported under WSL 1.") 67 | 68 | # Is this WSL 2? 69 | if not os.path.exists('/run/WSL'): 70 | if 'microsoft' not in os.uname().release: 71 | sys.exit("genie: not executing under WSL 2 - how did we get here?") 72 | 73 | # Are we effectively root? 74 | if os.geteuid() != 0: 75 | sys.exit("genie: must execute as root - has the setuid bit gone astray?") 76 | 77 | 78 | def validate_is_real_user(username): 79 | """Check that the supplied username is a real user; otherwise exit.""" 80 | try: 81 | pwd.getpwnam(username) 82 | except KeyError: 83 | sys.exit("genie: specified user does not exist") 84 | -------------------------------------------------------------------------------- /binsrc/genie/host.py: -------------------------------------------------------------------------------- 1 | # Hostname and hosts file functions module 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | 7 | from python_hosts import Hosts, HostsEntry 8 | 9 | import configuration 10 | 11 | 12 | def update(verbose): 13 | """Update the hostname and mount over previous hostname.""" 14 | if verbose: 15 | print("genie: generating new hostname") 16 | 17 | external_hostname = os.uname().nodename 18 | 19 | if verbose: 20 | print(f"genie: external hostname is {external_hostname}") 21 | 22 | internal_hostname = external_hostname + configuration.update_hostname_suffix() 23 | 24 | # Create the hostname file 25 | try: 26 | with open('/run/genie.hostname', 'w') as hostfile: 27 | print(internal_hostname, file=hostfile) 28 | hostfile.close() 29 | except UnicodeEncodeError: 30 | print("genie: it appears that your hostname contains characters not permitted by Linux") 31 | print(" (per RFC 952/RFC 1123); this is probably because Windows permits Unicode") 32 | print(" hostnames and WSL inherits them. Please see here for details and workaround:") 33 | print(" https://github.com/arkane-systems/genie/issues/268\n") 34 | sys.exit("genie: cannot continue with illegal hostname") 35 | 36 | os.chmod('/run/genie.hostname', 0o644) 37 | 38 | # Mount the hostname file 39 | if verbose: 40 | print(f"genie: setting new hostname to {internal_hostname}") 41 | 42 | sp = subprocess.run( 43 | ['mount', '--bind', '/run/genie.hostname', '/etc/hostname']) 44 | 45 | if sp.returncode != 0: 46 | print("genie: failed to bind hostname file; attempting to continue") 47 | return 48 | 49 | if verbose: 50 | print("genie: updating hosts file") 51 | 52 | # Update hosts file (remove old hostname, add new hostname) 53 | _modify_hosts_file_entries(external_hostname, internal_hostname) 54 | 55 | 56 | def restore(verbose): 57 | """Restore the hostname.""" 58 | internal_hostname = os.uname().nodename 59 | 60 | if verbose: 61 | print("genie: dropping in-bottle hostname") 62 | 63 | # Drop the in-bottle hostname mount 64 | sp = subprocess.run(['umount', '/etc/hostname']) 65 | 66 | if sp.returncode != 0: 67 | print("genie: failed to unmount hostname file; attempting to continue") 68 | return 69 | 70 | # Remove the hostname file 71 | os.remove('/run/genie.hostname') 72 | 73 | # Reset hostname 74 | subprocess.run(['hostname', '-F', '/etc/hostname']) 75 | 76 | external_hostname = os.uname().nodename 77 | 78 | # Update hosts file (remove new hostname, add old hostname) 79 | _modify_hosts_file_entries(internal_hostname, external_hostname) 80 | 81 | 82 | # Internal functions 83 | def _modify_hosts_file_entries(old_name, new_name): 84 | """Modify the hosts file to replace old_name with new_name.""" 85 | try: 86 | hosts = Hosts() 87 | 88 | entries = hosts.find_all_matching(name=old_name) 89 | 90 | # Iterate through all relevant entries 91 | for e in entries: 92 | # Iterate through all names 93 | new_names = [] 94 | for n in e.names: 95 | # Modify name 96 | new_names.append(n.replace(old_name, new_name)) 97 | new_entry = HostsEntry( 98 | entry_type=e.entry_type, address=e.address, names=new_names, comment=e.comment) 99 | 100 | # Replace old entry 101 | hosts.add([new_entry], force=True) 102 | 103 | hosts.write() 104 | 105 | except: # noqa 106 | print(f"genie: error occurred modifying hosts file ({sys.exc_info()[0]}); check format") 107 | print("genie: attempting to continue anyway...") 108 | -------------------------------------------------------------------------------- /binsrc/genie/requirements.txt: -------------------------------------------------------------------------------- 1 | git+https://github.com/zalando/python-nsenter@b7fd78fef24c456d88130c75fe734417728e97e8 2 | python_hosts>=1.0.1 3 | -------------------------------------------------------------------------------- /binsrc/genie/resolved.py: -------------------------------------------------------------------------------- 1 | # Resolver symlink function module 2 | 3 | import os 4 | 5 | 6 | def configure(verbose): 7 | """Replace resolv.conf with the required stub symlink for systemd-resolved.""" 8 | # We cannot check if the target (/run/systemd/resolve/stub-resolv.conf) exists, 9 | # since it will not be created until after systemd-resolved starts up. So we're 10 | # going to have to live with that uncertainty. 11 | 12 | # Check if source file (/etc/resolv.conf) exists 13 | if os.path.lexists('/etc/resolv.conf'): 14 | # If so, move it to the backup file (/etc/resolv.conf.wsl) 15 | if verbose: 16 | print("genie: backing up /etc/resolv.conf to /etc/resolv.conf.wsl") 17 | 18 | if os.path.exists('/etc/resolv.conf.wsl'): 19 | os.remove('/etc/resolv.conf.wsl') 20 | 21 | os.rename('/etc/resolv.conf', '/etc/resolv.conf.wsl') 22 | 23 | # Create symlink from /etc/resolv.conf to /run/systemd/resolve/stub-resolv.conf 24 | if verbose: 25 | print("genie: creating symlink /etc/resolv.conf -> /run/systemd/resolve/stub-resolv.conf") 26 | 27 | os.symlink('/run/systemd/resolve/stub-resolv.conf', '/etc/resolv.conf') 28 | 29 | 30 | def unconfigure(verbose): 31 | """Restore original resolv.conf.""" 32 | # Check if /etc/resolv.conf exists, and if so, if it is a symlink 33 | if os.path.exists('/etc/resolv.conf'): 34 | if os.path.islink('/etc/resolv.conf'): 35 | # If so, remove it 36 | if verbose: 37 | print("genie: removing symlink /etc/resolv.conf") 38 | 39 | os.remove('/etc/resolv.conf') 40 | 41 | # Check if /etc/resolv.conf.wsl exists 42 | if os.path.exists('/etc/resolv.conf.wsl'): 43 | # If so, move it to /etc/resolv.conf 44 | if verbose: 45 | print("genie: restoring /etc/resolv.conf from /etc/resolv.conf.wsl") 46 | 47 | os.rename('/etc/resolv.conf.wsl', '/etc/resolv.conf') 48 | else: 49 | # If not, warn the user 50 | print( 51 | "genie: WARNING: /etc/resolv.conf.wsl does not exist; please restore /etc/resolv.conf manually") 52 | 53 | else: 54 | print("genie: WARNING: /etc/resolv.conf is not a symlink") 55 | 56 | else: 57 | print("genie: WARNING: /etc/resolv.conf does not exist") 58 | -------------------------------------------------------------------------------- /binsrc/runinwsl/__main__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | 7 | def print_usage(): 8 | """Print usage""" 9 | print ("runinwsl: error in usage; should only be called by genie") 10 | print ("runinwsl ") 11 | 12 | def entrypoint(): 13 | """Entrypoint""" 14 | args = sys.argv[1:] 15 | 16 | if len(args) < 2: 17 | print_usage() 18 | return 127 19 | 20 | ewd = args[0] 21 | cmd = args[1:] 22 | 23 | # Change to correct working directory 24 | os.chdir (ewd) 25 | 26 | try: 27 | sp = subprocess.run (cmd) 28 | exit (sp.returncode) 29 | except Exception as e: 30 | print (f"runinwsl: error running command '{cmd}': {e.strerror}") 31 | exit (127) 32 | 33 | entrypoint() 34 | -------------------------------------------------------------------------------- /container-package-arch.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # This works on my machine with my local packaging container. It will not work for you. 3 | lxc exec package-arch --user 0 -- resolvectl dns eth0 172.16.0.128 4 | lxc exec package-arch --user 1000 --cwd /pkg --env HOME=/home/avatar -- make package-arch 5 | -------------------------------------------------------------------------------- /container-package-fedora.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # This works on my machine with my local packaging container. It will not work for you. 3 | lxc exec package-arch --user 0 -- resolvectl dns eth0 172.16.0.128 4 | lxc exec package-fedora --user 1000 --cwd /pkg --env HOME=/home/avatar -- make package-fedora 5 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | systemd-genie (2.5) bullseye bookworm sid focal jammy; urgency=medium 2 | 3 | * Fixed Debian multiarch packaging. 4 | * Dynamically build WSLInterop.conf based on existing (fixes #287, #295). 5 | * Fix for schrödinbug (fixes #298). 6 | * Allow for faster timeout when system in degraded state. 7 | * Added note and referrer about native systemd support. 8 | * Added bottle-imp conflict to packages. 9 | 10 | -- Alistair Young Fri, 23 Sep 2022 15:00:00 -0500 11 | 12 | systemd-genie (2.4) bullseye bookworm sid focal jammy; urgency=medium 13 | 14 | * Deprecated support for Debian buster and Ubuntu bionic. 15 | * (The above should still function but will no longer be tested.) 16 | * Python refactoring. 17 | * Fixed no-command-specified error for genie -c. 18 | * Added cwd preservation note to help for genie -c. 19 | * Added proper return values for status checks (fixes #269). 20 | * Properly configure WSLInterop binary format (fixes #267, #264). 21 | * Carries through real UID from wrapper (fixes #258). 22 | * Use systemd-tmpfiles for WSLg support (fixes #214, #175). 23 | * Warn user if unsupported Unicode hostname (warns on #268, no fix). 24 | * arm64 package for Fedora. 25 | * Miscellaneous packaging fixes. 26 | 27 | -- Alistair Young Sat, 25 Jun 2022 12:50:00 -0500 28 | 29 | systemd-genie (2.3) buster bullseye bookworm sid bionic focal jammy; urgency=medium 30 | 31 | * Paths-containing-spaces fix (#240). 32 | * Makefile updates for CI build. 33 | * Fixed WSL 1 detection. 34 | * Added -a/--as-user option to allow shell/command as any user. 35 | * Added support for Ubuntu 22.04 LTS (Jammy Jellyfish). 36 | * Greater robustness against misconfigured hosts files (fixes #247). 37 | * arm64 support. 38 | * Miscellaneous fixes. 39 | 40 | -- Alistair Young Tue, 22 Mar 2022 08:15:00 -0500 41 | 42 | systemd-genie (2.2) buster bullseye bookworm sid focal bionic; urgency=medium 43 | 44 | * Single-file package python scripts. 45 | * Man page fixes. 46 | * Fixed building on Python 3.10. 47 | * Dropped the "local" install option (little used; use tarball instead). 48 | 49 | -- Alistair Young Sun, 06 Mar 2022 13:30:00 -0500 50 | 51 | systemd-genie (2.1) buster bullseye bookworm sid focal bionic; urgency=medium 52 | 53 | * Documentation updates. 54 | * Update /etc/hosts after hostname update. 55 | * Minor fixes. 56 | 57 | -- Alistair Young Mon, 28 Feb 2022 16:03:00 -0500 58 | 59 | systemd-genie (2.0b) buster bullseye bookworm sid focal bionic; urgency=medium 60 | 61 | * Major rewrite in Python, eliminating .NET dependency. 62 | * Moved executables from /usr/libexec/genie to /usr/lib/genie. 63 | * Allow configuration of hostname suffix. 64 | * Support for AppArmor namespaces. 65 | * Work to better handle simultaneity. 66 | * Extra warnings for problematic states. 67 | * Miscellaneous fixes. 68 | 69 | -- Alistair Young Tue, 22 Feb 2022 16:40:00 -0500 70 | 71 | systemd-genie (1.44) buster bullseye bookworm sid focal bionic; urgency=medium 72 | 73 | * Standardized use of /usr/lib rather than /lib. 74 | * Updated ArkaneSystems.WSL to 0.2.13. 75 | * Made genie resolv.conf stub option-controlled. 76 | * Misc fixes. 77 | 78 | -- Alistair Young Sat, 07 Aug 2021 16:40:00 -0500 79 | 80 | systemd-genie (1.43) buster bullseye bookworm sid focal bionic; urgency=medium 81 | 82 | * Based on collated systemd-analyze results, re-upped systemd startup 83 | timeout to 240. 84 | * Added automated creation of resolv.conf symlink (per #130). 85 | * Added fix for binfmt mount (per #142). 86 | 87 | -- Alistair Young Thu, 29 Jul 2021 05:40:00 -0500 88 | 89 | systemd-genie (1.42) buster bullseye bookworm sid focal bionic; urgency=medium 90 | 91 | * Regression fixes. 92 | 93 | -- Alistair Young Thu, 13 May 2021 05:40:00 -0500 94 | 95 | systemd-genie (1.41) buster bullseye bookworm sid focal bionic; urgency=medium 96 | 97 | * Moved user-runtime-dir@.service override to ExecStartPost. 98 | * Fix virtualization detection for non-custom kernels. 99 | * Detect slow-start as different from failed-start. 100 | 101 | -- Alistair Young Thu, 06 May 2021 23:40:00 -0500 102 | 103 | systemd-genie (1.40) buster bullseye bookworm sid focal bionic; urgency=medium 104 | 105 | * Moved generic Linux/WSL functionality into shared assembly. 106 | * Upnumbered genie-envar to fix missing path cloning on some systems. 107 | * Fixed typo in wslg-xwayland.socket. 108 | * Map XWayland socket only where WSLg is present and active. 109 | * Mount user runtime directory only where +WSLg and user matches. 110 | 111 | -- Alistair Young Tue, 27 Apr 2021 15:00:00 -0500 112 | 113 | systemd-genie (1.39) buster bullseye bookworm sid focal bionic; urgency=medium 114 | 115 | * Better WSLg support, based on the code of Daniel Llewellyn 116 | (@diddledan), here: 117 | https://github.com/diddledan/one-script-wsl2-systemd. 118 | 119 | -- Alistair Young Thu, 22 Apr 2021 18:00:00 -0500 120 | 121 | systemd-genie (1.38) buster bullseye bookworm sid focal bionic; urgency=medium 122 | 123 | * Restored original default systemd startup timeout. 124 | * Changes to support WSLg. 125 | 126 | -- Alistair Young Thu, 22 Apr 2021 14:32:00 -0500 127 | 128 | systemd-genie (1.37) buster bullseye bookworm sid focal bionic; urgency=medium 129 | 130 | * Reduced default systemd startup timeout to 60s. 131 | * Added display of failed systemd units on timeout. 132 | 133 | -- Alistair Young Fri, 16 Apr 2021 12:32:00 -0500 134 | 135 | systemd-genie (1.36) buster bullseye bookworm sid focal bionic; urgency=medium 136 | 137 | * Added dependency on hostname. 138 | * Added --is-running and --is-in-bottle informational options. 139 | * Added storage of systemd external PID in pidfile. 140 | * Removed dependencies on mount(1) and hostname(1). 141 | * Modified genie -u to wait for systemd exit. 142 | 143 | -- Alistair Young Sun, 14 Mar 2021 13:48:00 -0500 144 | 145 | systemd-genie (1.35) buster bullseye bookworm sid focal bionic; urgency=medium 146 | 147 | * Added dependency on systemd 232. 148 | * Revised build procedure. 149 | 150 | -- Alistair Young Sun, 21 Feb 2021 13:40:00 -0500 151 | 152 | systemd-genie (1.34) unstable; urgency=medium 153 | 154 | * Fixed regression: restored gawk dependency. 155 | 156 | -- Alistair Young Tue, 26 Jan 2021 08:48:00 -0500 157 | 158 | systemd-genie (1.33) unstable; urgency=medium 159 | 160 | * Removed faulty root check. 161 | 162 | -- Alistair Young Sat, 23 Jan 2021 11:38:00 -0500 163 | 164 | systemd-genie (1.32) unstable; urgency=medium 165 | 166 | * Added error-checking to configuration variables. 167 | * [genie -c] Preserved args containing spaces. 168 | * Refactored code into multiple classes. 169 | * Replaced runinwsl with executable to enable space-preservation. 170 | 171 | -- Alistair Young Mon, 18 Jan 2021 11:15:00 -0500 172 | 173 | systemd-genie (1.31) unstable; urgency=medium 174 | 175 | * Fixed bug in path cloning. 176 | * Added wait on initialize for systemd to be fully running. 177 | * Updated documentation accordingly. 178 | 179 | -- Alistair Young Wed, 13 Jan 2021 11:26:00 -0500 180 | 181 | systemd-genie (1.30) unstable; urgency=medium 182 | 183 | * Changed to precompile .NET code. 184 | * Added appending of outside-bottle path to inside-bottle path 185 | (with dedupe) as option. 186 | * Moved saving of environment variables into binary, with 187 | configuration option in genie.ini. 188 | 189 | -- Alistair Young Sat, 26 Dec 2020 13:47:00 -0500 190 | 191 | systemd-genie (1.29) unstable; urgency=medium 192 | 193 | * Updated to use .NET 5.0 framework. 194 | * Changed to single-file executable (framework-dependent). 195 | * Modified the bottle entry to use systemd-machined. 196 | * Misc. cleanup. 197 | 198 | -- Alistair Young Fri, 19 Dec 2020 07:40:00 -0500 199 | 200 | systemd-genie (1.28) unstable; urgency=medium 201 | 202 | * Fixed bug preventing installation on pre-buster Debian releases. 203 | * Added "update-hostname" configuration setting to disable this behavior. 204 | * Improved WSL version detection. 205 | 206 | -- Alistair Young Fri, 04 Sep 2020 18:48:00 -0500 207 | 208 | systemd-genie (1.27) unstable; urgency=medium 209 | 210 | * Initial release (closes: Bug#968331). 211 | 212 | -- Alistair Young Wed, 19 Aug 2020 11:35:51 -0500 213 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: systemd-genie 2 | Section: contrib/misc 3 | Priority: optional 4 | Maintainer: Alistair Young 5 | Build-Depends: debhelper-compat (= 12) 6 | Standards-Version: 4.5.0 7 | Homepage: https://github.com/arkane-systems/genie 8 | Vcs-Git: https://github.com/arkane-systems/genie.git 9 | Vcs-Browser: https://github.com/arkane-systems/genie 10 | 11 | Package: systemd-genie 12 | Architecture: amd64 arm64 13 | Depends: ${misc:Depends}, ${shlibs:Depends}, dbus, policykit-1, daemonize, systemd (>= 232-25), python3 (>= 3.7), python3-pip, python3-psutil, systemd-container (>= 232-25), gawk 14 | Conflicts: bottle-imp 15 | Description: quick way into a systemd "bottle" under Windows Subsystem for Linux 16 | Automates the process of starting up, entering into, and shutting down 17 | a container within which systemd is pid 1, effectively enabling the use 18 | of systemd within a WSL distro. 19 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Source: https://github.com/arkane-systems/genie 3 | Upstream-Contact: Alistair Young 4 | Upstream-Name: genie 5 | Comment: This package is not part of the Debian distribution because it is developed using .NET Core, thus depending on a package which is itself not part of the Debian distribution. 6 | 7 | Files: * 8 | Copyright: 2018-2022 Alistair Young 9 | License: Unlicense 10 | 11 | License: Unlicense 12 | This is free and unencumbered software released into the public domain. 13 | . 14 | Anyone is free to copy, modify, publish, use, compile, sell, or 15 | distribute this software, either in source code form or as a compiled 16 | binary, for any purpose, commercial or non-commercial, and by any 17 | means. 18 | . 19 | In jurisdictions that recognize copyright laws, the author or authors 20 | of this software dedicate any and all copyright interest in the 21 | software to the public domain. We make this dedication for the benefit 22 | of the public at large and to the detriment of our heirs and 23 | successors. We intend this dedication to be an overt act of 24 | relinquishment in perpetuity of all present and future rights to this 25 | software under copyright law. 26 | . 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 28 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 30 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 31 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 32 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 33 | OTHER DEALINGS IN THE SOFTWARE. 34 | . 35 | For more information, please refer to 36 | 37 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | othersrc/docs/readme.md 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # See debhelper(7) (uncomment to enable) 3 | # output every command that modifies files on the build system. 4 | DH_VERBOSE = 1 5 | export DH_OPTIONS=-v 6 | 7 | # see FEATURE AREAS in dpkg-buildflags(1) 8 | #export DEB_BUILD_MAINT_OPTIONS = hardening=+all 9 | 10 | %: 11 | dh $@ 12 | 13 | override_dh_auto_build: 14 | make build-binaries 15 | 16 | override_dh_auto_install: 17 | make internal-debian-package 18 | 19 | override_dh_auto_clean: 20 | make internal-clean 21 | 22 | # Allow our setuid executable to pass unfixed. 23 | override_dh_fixperms: 24 | dh_fixperms -Xgenie 25 | 26 | override_dh_strip: 27 | dh_strip --no-automatic-dbgsym 28 | 29 | override_dh_shlibdeps: 30 | ifeq ($(DEB_TARGET_ARCH),amd64) 31 | dh_shlibdeps 32 | endif 33 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | -------------------------------------------------------------------------------- /debian/systemd-genie.links: -------------------------------------------------------------------------------- 1 | /usr/lib/genie/80-genie-envar.sh /usr/lib/systemd/system-environment-generators/80-genie-envar.sh 2 | /usr/lib/genie/80-genie-envar.sh /usr/lib/systemd/user-environment-generators/80-genie-envar.sh 3 | -------------------------------------------------------------------------------- /debian/systemd-genie.lintian-overrides: -------------------------------------------------------------------------------- 1 | # Must be setuid 2 | elevated-privileges 6755 root/root [usr/bin/genie] 3 | -------------------------------------------------------------------------------- /debian/systemd-genie.manpages: -------------------------------------------------------------------------------- 1 | othersrc/docs/genie.8 2 | -------------------------------------------------------------------------------- /genie.spec: -------------------------------------------------------------------------------- 1 | %global project https://github.com/arkane-systems/genie/ 2 | %global version 2.5 3 | 4 | %global debug_package %{nil} 5 | %global _enable_debug_package 0 6 | %global __os_install_post /usr/lib/rpm/brp-compress %{nil} 7 | 8 | Name: genie 9 | Version: %{version} 10 | Release: 1%{?dist} 11 | Summary: A quick way into systemd "bottle" for WSL 12 | 13 | License: Unlicense 14 | URL: %{project} 15 | Source0: %{project}archive/genie-%{version}.tar.gz 16 | 17 | Requires: daemonize 18 | Requires: dbus 19 | Requires: gawk 20 | Requires: polkit 21 | Requires: python3 >= 3.7 22 | Requires: python3-pip 23 | Requires: python3-psutil 24 | Requires: systemd >= 232.25 25 | Requires: systemd-container >= 232.25 26 | Conflicts: bottle-imp 27 | # BuildRequires: git 28 | BuildRequires: make 29 | 30 | %description 31 | A quick way into systemd "bottle" for WSL 32 | 33 | %prep 34 | %setup -q -n %{name}-%{version} 35 | 36 | %build 37 | make build-binaries 38 | 39 | %install 40 | echo %{buildroot}%{_mandir} 41 | 42 | install -d -p %{buildroot}%{_sysconfdir} 43 | install -d -p %{buildroot}%{_exec_prefix}/lib/%{name} 44 | install -d -p %{buildroot}%{_exec_prefix}/lib/systemd/system-environment-generators 45 | install -d -p %{buildroot}%{_exec_prefix}/lib/systemd/user-environment-generators 46 | install -d -p %{buildroot}%{_exec_prefix}/lib/tmpfiles.d 47 | install -d -p %{buildroot}%{_bindir} 48 | install -d -p %{buildroot}%{_unitdir} 49 | install -d -p %{buildroot}%{_unitdir}/user-runtime-dir@.service.d 50 | install -d -p %{buildroot}%{_mandir}/man8 51 | 52 | make DESTDIR=%{buildroot} internal-package 53 | make DESTDIR=%{buildroot} internal-supplement 54 | 55 | %postun 56 | if [ $1 -eq 0 ]; then 57 | rm -f %{_bindir}/%{name} 58 | rm -rf %{_exec_prefix}/lib/%{name}/* 59 | rm -f %{_unitdir}/user-runtime-dir@.service.d/override.conf 60 | rm -f %{_exec_prefix}/lib/tmpfiles.d/wslg.conf 61 | rm -f %{_exec_prefix}/lib/systemd/system-environment-generators/80-genie-envar.sh 62 | rm -f %{_exec_prefix}/lib/systemd/user-environment-generators/80-genie-envar.sh 63 | rm -f ${_mandir}/man8/genie.8.gz 64 | fi 65 | 66 | %clean 67 | rm -rf %{buildroot} 68 | 69 | %files 70 | 71 | %{_bindir}/%{name} 72 | %{_exec_prefix}/lib/%{name}/* 73 | %config %{_sysconfdir}/genie.ini 74 | %{_unitdir}/user-runtime-dir@.service.d/override.conf 75 | %{_exec_prefix}/lib/tmpfiles.d/wslg.conf 76 | %{_exec_prefix}/lib/systemd/system-environment-generators/80-genie-envar.sh 77 | %{_exec_prefix}/lib/systemd/user-environment-generators/80-genie-envar.sh 78 | %doc %{_mandir}/man8/genie.8.gz 79 | 80 | %changelog 81 | * Fri Sep 23 2022 Alistair Young 2.5-1 82 | - Fixed Debian multiarch packaging. 83 | - Dynamically build WSLInterop.conf based on existing (fixes #287, #295). 84 | - Fix for schrödinbug (fixes #298). 85 | - Allow for faster timeout when system in degraded state. 86 | - Added note and referrer about native systemd support. 87 | - Added bottle-imp conflict to packages. 88 | 89 | * Sat Jun 25 2022 Alistair Young 2.4-1 90 | - Fixed missing dependency versions. 91 | - Python refactoring. 92 | - Fixed no-command-specified error for genie -c. 93 | - Added cwd preservation note to help for genie -c. 94 | - Added proper return values for status checks (fixes #269). 95 | - Properly configure WSLInterop binary format (fixes #267, #264). 96 | - Carries through real UID from wrapper (fixes #258). 97 | - Use systemd-tmpfiles for WSLg support (fixes #214, #175). 98 | - Warn user if unsupported Unicode hostname (warns on #268, no fix). 99 | - arm64 package for Fedora. 100 | - Miscellaneous fixes. 101 | 102 | * Tue Mar 22 2022 Alistair Young 2.3-1 103 | - Paths-containing-spaces fix (#240). 104 | - Makefile updates for CI build. 105 | - Fix WSL 1 detection. 106 | - Added -a/--as-user option to allow shell/command as any user. 107 | - Added support for Ubuntu 22.04 LTS (Jammy Jellyfish). 108 | - Greater robustness against misconfigured hosts files (fixes #247). 109 | - Miscellaneous fixes. 110 | 111 | * Sun Mar 06 2022 Alistair Young 2.2-1 112 | - Single-file package python scripts. 113 | - Man page fixes. 114 | - Fixed building on Python 3.10. 115 | - Dropped the "local" install option (little used; use tarball instead). 116 | 117 | * Mon Feb 28 2022 Alistair Young 2.1-1 118 | - Documentation updates. 119 | - Update /etc/hosts after hostname update. 120 | - Minor fixes. 121 | 122 | * Tue Feb 22 2022 Alistair Young 2.0b-1 123 | 124 | - Major rewrite in Python, eliminating .NET dependency. 125 | - Moved executables from /usr/libexec/genie to /usr/lib/genie. 126 | - Allow configuration of hostname suffix. 127 | - Support for AppArmor namespaces. 128 | - Work to better handle simultaneity. 129 | - Extra warnings for problematic states. 130 | - Miscellaneous fixes. 131 | 132 | * Sat Aug 07 2021 Alistair Young 1.44-1 133 | - Standardized use of /usr/lib rather than /lib. 134 | - Updated to ArkaneSystems.WSL 0.2.13. 135 | - Made stub resolv.conf file option-controlled. 136 | - Misc fixes. 137 | 138 | * Thu Jul 29 2021 Alistair Young 1.43-1 139 | - Based on collated systemd-analyze results, re-upped systemd startup timeout to 240. 140 | - Added automated creation of resolv.conf symlink (per #130). 141 | - Added fix for binfmt mount (per #142). 142 | 143 | * Thu May 06 2021 Alistair Young 1.42-1 144 | - Regression fixes. 145 | 146 | * Thu May 06 2021 Alistair Young 1.41-1 147 | - Moved user-runtime-dir@.service override to ExecStartPost. 148 | - Fix virtualization detection for non-custom kernels. 149 | - Detect slow-start as distinct from failed-start. 150 | 151 | * Tue Apr 27 2021 Alistair Young 1.40-1 152 | - Improved Fedora packaging to eliminate manual unit enable. 153 | - Moved generic Linux/WSL functionality into shared assembly. 154 | - Fixed missing user-environment-generator in Fedora package. 155 | - Upnumbered genie-envar to fix missing path cloning on some systems. 156 | - Fixed typo in wslg-xwayland.socket. 157 | - Map XWayland socket only where WSLg is present and active. 158 | - Mount user runtime directory only where WSLg is present and user matches. 159 | 160 | * Thu Apr 22 2021 Alistair Young 1.39-1 161 | - Better WSLg support, based on the code of Dani Llewellyn (@diddledani), here: https://github.com/diddledani/one-script-wsl2-systemd. 162 | 163 | * Thu Apr 22 2021 Alistair Young 1.38-1 164 | - Restored original default systemd startup timeout. 165 | - Changes to support WSLg. 166 | 167 | * Fri Apr 16 2021 Alistair Young 1.37-1 168 | - Merged fixes to Fedora packaging (PR #138). 169 | - Reduced default systemd startup timeout to 60s. 170 | - Added display of failed systemd units on timeout. 171 | 172 | * Sun Mar 14 2021 Alistair Young 1.36-1 173 | - Added dependency on hostname(1). 174 | - Added --is-running and --is-in-bottle informational options. 175 | - Added storage of systemd external PID in pidfile. 176 | - Removed dependencies on mount(1) and hostname(1). 177 | - Modified genie -u to wait for systemd exit. 178 | 179 | * Mon Feb 22 2021 Alistair Young 1.35-1 180 | - Packager modified for new build system. 181 | 182 | * Wed Feb 17 2021 Gerard Braad 1.34-1 183 | - Initial version for Fedora 184 | -------------------------------------------------------------------------------- /othersrc/docs/genie.8: -------------------------------------------------------------------------------- 1 | .Dd 02/23/22 2 | .Dt genie 8 3 | .Os Linux 4 | .Sh NAME 5 | .Nm genie 6 | .Nd start up, enter into, or shut down a systemd "bottle" under Windows 7 | Subsystem for Linux. 8 | .Sh SYNOPSIS 9 | .Nm 10 | .Op -h 11 | .Op -V 12 | .Op -v 13 | .Op -a 14 | .Ar user 15 | .Op -i 16 | .Op -b 17 | .Op -r 18 | .Op -s 19 | .Op -l 20 | .Op -c 21 | .Ar command... 22 | .Sh DESCRIPTION 23 | .Nm 24 | provides a means of running 25 | .Xr systemd 1 26 | as pid 1, with all the trimmings, within a WSL 2 distribution. It does this by 27 | creating a pid and mount namespace, the eponymous poor-man's container 28 | "bottle", starting up 29 | .Xr systemd 1 30 | within the bottle, and providing helpful shortcuts to start, enter, run 31 | commands within, and shut down the bottle. 32 | .Pp 33 | .Bl -tag -width "-c ..., --command ..." 34 | .It Fl h, -help 35 | Prints a short help text and exits. 36 | .It Fl V, -version 37 | Prints the installed genie version and exits. 38 | .It Fl v, -verbose 39 | Causes any other command to print the details of the operations it is 40 | performing as it goes along. Useful mostly for debugging. 41 | .It Fl a, -as-user 42 | Permits a user to be specified (by name) to execute as when using the -c/--command 43 | or -s/--shell commands. 44 | .It Fl i, -initialize 45 | Sets up the bottle and 46 | .Xr systemd 1 47 | if they are not already initialized, and then exists. This is intended, for 48 | use, for example, if you wish to run services without needing a shell, or to 49 | preinitialize the bottle to avoid startup delays later. This latter can be used 50 | with Task Scheduler, to be run on Windows logon. 51 | .It Fl s, -shell 52 | Sets up the bottle and 53 | .Xr systemd 1 54 | if they are not already initialized, and then runs your login shell within the 55 | bottle. It is intended as the standard way to start a shell within a 56 | distribution with 57 | .Nm 58 | installed. 59 | .Pp 60 | This follows login semantics, and as such does not preserve the current working 61 | directory. 62 | .It Fl l, -login 63 | Sets up the bottle and 64 | .Xr systemd 1 65 | if they are not already initialized, and then opens a login session within the 66 | bottle. This permits you to log in to the WSL distribution as any user. The 67 | login prompt will return when you log out; to terminate the session, press ^] 68 | three times within one second. 69 | .Pp 70 | This follows login semantics, and as such does not preserve the current working 71 | directory. 72 | .It Fl c, -command 73 | Sets up the bottle and 74 | .Xr systemd 1 75 | if they are not already initialized, and then runs the specified command within the 76 | bottle. It is intended as the standard way to run arbitrary commands within a 77 | distribution with 78 | .Nm 79 | installed. 80 | .Pp 81 | Unlike the other options, this preserves the current working directory. 82 | .It Fl u, -shutdown 83 | Shuts down 84 | .Xr systemd 1 85 | cleanly and exits the bottle. This uses the 86 | .Ar systemctl poweroff 87 | command to simulate a normal Linux system shutting down. It is suggested that 88 | this be used before shutting down the Windows machine or restarting the Linux 89 | distribution to ensure a clean shutdown of systemd services. 90 | .Pp 91 | While not compulsory, it is recommended that you shut down and restart the WSL 92 | distribution before using 93 | .Nm 94 | again after you have used 95 | .Ar genie -u. 96 | See BUGS, below, for more details. 97 | .It Fl r, -is-running 98 | Checks whether 99 | .Nm 100 | and an associated 101 | .Xr systemd 1 102 | are currently running. Returns 103 | .Ar running 104 | and exit code 0 if so. Other possible return values include 105 | .Ar stopped 106 | and exit code 1 if they are not; 107 | .Ar starting 108 | and exit code 2 and 109 | .Ar stopping 110 | and exit code 3 if they are currently transitioning between states; 111 | .Ar running (systemd errors) 112 | and exit code 4 if there is a problem with systemd services; and 113 | .Ar unknown 114 | and exit code 5 otherwise. 115 | .It Fl b -is-in-bottle 116 | Checks whether the current command is executing inside the bottle. Returns 117 | .Ar inside 118 | and exit code 0 if so; returns 119 | .Ar outside 120 | and exit code 1 if not. If no bottle exists, returns 121 | .Ar no-bottle 122 | and exit code 2. 123 | .El 124 | .Sh ENVIRONMENT 125 | .Bl -tag -width "INSIDE_GENIE" 126 | .It Ev INSIDE_GENIE 127 | INSIDE_GENIE should not be set by the user. 128 | .Nm 129 | sets INSIDE_GENIE to the string "yes", such that user scripts can detect its 130 | presence to determine whether or not they are running inside the bottle. 131 | .El 132 | .Sh EXIT STATUS 133 | Other than the special exit codes listed under the 134 | .Ar -b 135 | and 136 | .Ar -r 137 | options above, 138 | .Nm 139 | maintains a policy of returning zero on success, and non-zero when an error 140 | occurs. 141 | .Sh FILES 142 | .Bl -tag -width "/run/genie.systemd.pid" -compact 143 | .It Pa /etc/genie.ini 144 | Configuration file for 145 | .Nm 146 | This contains the secure path which 147 | .Nm 148 | uses when searching for executables used internally. In a typical Linux 149 | installation, there should be no need to modify this. Other settings affecting 150 | .Nm 151 | behavior include 152 | .Ar update-hostname 153 | and 154 | .Ar update-hostname-suffix 155 | which control whether 156 | .Nm updates the hostname within the bottle, or not (defaults on); 157 | .Ar clone-path, 158 | which controls whether 159 | .Nm 160 | clones the outside-bottle PATH to inside the bottle (defaults off); 161 | .Ar clone-env, 162 | which lists the environment variables to be copied from outside to inside the 163 | bottle; 164 | .Ar systemd-timeout, 165 | which sets the length of time in seconds 166 | .Nm 167 | will wait for 168 | .Xr systemd 1 169 | to reach the running state; 170 | .Ar resolved-stub, 171 | which determines whether or not 172 | .Nm 173 | will create the symlink needed to run 174 | .Xr systemd-resolved 8 175 | in stub mode; and 176 | .Ar target-warning 177 | which can be used to disable the warning if the default target is set to 178 | something other than 179 | .Ar multi-user.target. 180 | .It Pa /run/genie.env 181 | Contains certain environment variables required for proper functioning copied 182 | from outside the bottle, used internally by 183 | .Nm 184 | to restore them within the bottle. 185 | .It Pa /run/genie.hostname 186 | Contains the modified hostname used by the WSL distribution (see NOTES). This 187 | file is bind mounted over 188 | .Ar /etc/hostname 189 | when the bottle is started up, and unbound at shutdown. 190 | .It Pa /run/genie.path 191 | Contains the system PATH copied from outside the bottle, used internally by 192 | .Nm 193 | to restore the directories therein within the bottle. 194 | .It Pa /run/genie.systemd.pid 195 | Contains the external PID of the systemd(1) instance created by 196 | .Nm 197 | Not used by 198 | .Nm 199 | itself; this PID is recorded as a convenience for the user. No analogous file 200 | exists containing the internal PID, for obvious reasons. 201 | .El 202 | .Sh NOTES 203 | .Nm 204 | can only be used within a WSL 2 distribution, since 205 | .Xr systemd 1 206 | can only be run within a WSL 2 distribution. WSL 1 does not implement the 207 | system calls required to support it. 208 | .Pp 209 | .Nm 210 | serves no purpose on Linux running outside of the WSL environment, or within 211 | other containers. Its behavior if run in such environments is undefined. 212 | .Pp 213 | When setting up the bottle: 214 | .Pp 215 | If configured, the hostname of the WSL session is changed from the default (the 216 | hostname of the Windows machine) by suffixing -wsl, to distinguish it from the 217 | Windows host. 218 | .Pp 219 | The bottle uses pid and mount namespaces. Other namespaces remain shared with 220 | the parent (outside bottle). The mount propagation flag is set to shared. 221 | .Sh SEE ALSO 222 | .Xr systemctl 1 , 223 | .Xr systemd 1 , 224 | .Xr bootup 7 , 225 | .Xr namespaces 7 , 226 | .Xr systemd-machined 8 , 227 | .Xr systemd-resolved 8 228 | .Sh BUGS 229 | .Nm 230 | is not idempotent; i.e., it is possible that changes made by 231 | .Nm 232 | or by 233 | .Xr systemd 1 234 | inside the bottle will not be perfectly reverted when the bottle is shut down 235 | with 236 | .Ar genie -u. 237 | As such, it is recommended that you terminate the entire wsl session with 238 | .Ar wsl -t 239 | or 240 | .Ar wsl --shutdown 241 | in between stopping and restarting the bottle, or errors may occur. 242 | .Pp 243 | If you feel you have found a bug in \fBgenie\fR, please submit a bug report at 244 | .Ar http://github.com/arkane-systems/genie/issues 245 | -------------------------------------------------------------------------------- /othersrc/docs/license.md: -------------------------------------------------------------------------------- 1 | The Unlicense 2 | ============= 3 | 4 | This is free and unencumbered software released into the public domain. 5 | 6 | Anyone is free to copy, modify, publish, use, compile, sell, or 7 | distribute this software, either in source code form or as a compiled 8 | binary, for any purpose, commercial or non-commercial, and by any 9 | means. 10 | 11 | In jurisdictions that recognize copyright laws, the author or authors 12 | of this software dedicate any and all copyright interest in the 13 | software to the public domain. We make this dedication for the benefit 14 | of the public at large and to the detriment of our heirs and 15 | successors. We intend this dedication to be an overt act of 16 | relinquishment in perpetuity of all present and future rights to this 17 | software under copyright law. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 24 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 | OTHER DEALINGS IN THE SOFTWARE. 26 | 27 | For more information, please refer to 28 | -------------------------------------------------------------------------------- /othersrc/docs/readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # genie 4 | 5 | [ ![ci](https://github.com/arkane-systems/genie/workflows/ci/badge.svg?branch=master) ](https://github.com/arkane-systems/genie/actions?query=workflow%3Aci+branch%3Amaster) 6 | 7 | ## A quick way into a systemd "bottle" for WSL 8 | 9 | What does that even mean? 10 | 11 | Well, this gives you a way to run systemd as pid 1, with all the trimmings, inside WSL 2. It does this by creating a pid namespace, the eponymous poor-man's-container "bottle", starting up systemd in there, and entering it, and providing some helpful shortcuts to do so. 12 | 13 | ## WSL NOW HAS NATIVE SYSTEMD SUPPORT 14 | 15 | [As explained here](https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/), if you are running Windows 11 and a version of WSL 0.67.6 or above. If this is available to you, you should use it instead of _genie_, because it's a much more elegant way of getting systemd to run. 16 | 17 | It's not quite perfect, however, so you should also check out _[bottle-imp](https://github.com/arkane-systems/bottle-imp)_, my friendly helper to make working with WSL's native systemd support a little easier. 18 | 19 | Using _genie_ on systems that have native _systemd_ support is not supported. 20 | 21 | ## REQUIREMENTS 22 | 23 | **NOTE:** Before you install _genie_ for the first time, read **ALL** of this page. This will save you a great deal of trouble later on. Especially, please note that on many distributions you **will** encounter the problem described under "Warning: Timing Out" below when you first run genie, and will need to resolve it before your system will operate correctly. 24 | 25 | It is a good idea to set your systemd default target to _multi-user.target_ before installing genie. This is the target that genie is designed to work with, since the default _graphical.target_ used by many distributions includes services for the graphical desktop that would take, at minimum, considerable reconfiguration before operating properly under the WSL/WSLg environment. 26 | 27 | If you are using a custom kernel for WSL, it should comply with the suggested means of detecting WSL given in https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 - i.e., the string "microsoft" should be present in the kernel version string, which can be found in `/proc/sys/kernel/osrelease`. You can check this by running `systemd-detect-virt`; it should return "wsl". 28 | 29 | Also read the [WSLg FAQ](https://github.com/arkane-systems/genie/wiki/WSLg-FAQ) and the [known-problematic systemd units list](https://github.com/arkane-systems/genie/wiki/Systemd-units-known-to-be-problematic-under-WSL) for known problems and known solutions to them. 30 | 31 | More information, tips & tricks, etc. are available on [the genie wiki](https://github.com/arkane-systems/genie/wiki). Please consult it before opening an issue. 32 | 33 | ### NOTE: WSL 2 ONLY 34 | 35 | Note: it is only possible to run _systemd_ (and thus _genie_ ) under WSL 2; WSL 1 does not support the system calls required to do so. If you are running inside a distro configured as WSL 1, even if your system supports WSL 2, genie will fail to operate properly. 36 | 37 | ## INSTALLATION 38 | 39 | If there is a package available for your distribution, this is the recommended method of installing genie. 40 | 41 | ### Debian 42 | 43 | Dependent packages on Debian are _daemonize_, _dbus_, _gawk_, _libc6_ (>= 2.2.5), _policykit-1_, _python3_ (>= 3.7), _python3-pip_, _python3-psutil_, _systemd_ (>= 232-25), and _systemd-container_ (>= 232-25). These should all be in the distro and able to be installed automatically. 44 | 45 | To install, add the wsl-translinux repository here by following the instructions here: 46 | 47 | https://arkane-systems.github.io/wsl-transdebian/ 48 | 49 | then install genie using the commands: 50 | 51 | ```bash 52 | sudo apt update 53 | sudo apt install -y systemd-genie 54 | ``` 55 | 56 | #### Ubuntu & Other Debian Derivatives 57 | 58 | Use the above Debian package. For current Ubuntu releases and the timing-out problem, see the problematic units listed on [the genie wiki](https://github.com/arkane-systems/genie/wiki). 59 | 60 | ### Arch 61 | 62 | An Arch package (.zst) can be downloaded from the releases, to right. Install it manually, using `pacman -U `. 63 | 64 | ### Fedora 65 | 66 | A Fedora package (.rpm) can be downloaded from the releases, to right. Install it manually, using `dnf install `. 67 | 68 | ### Other Distros 69 | 70 | If your distribution supports any of the package formats available, you may wish to try downloading the relevant format and giving it a try. This will almost certainly need some tweaking to work properly. 71 | 72 | Debian is the "native" distribution for _genie_ , for which read, "what the author uses". Specifically, Debian buster+, with _usrmerge_ installed. If you're using anything else, you may need to tweak the configuration file (see below) accordingly. 73 | 74 | #### TAR 75 | 76 | There is a .tar.gz of a complete genie install available from the releases, to right. As a last resort, you can try untarring this (it contains every needed file, with the correct permissions, in the correct path from /) onto your system while root. Don't do this unless you're confident you know what you're doing, you're willing to go looking for any resulting issues yourself, and you aren't afraid of accidentally breaking things. You will need to install the dependencies listed above beforehand. 77 | 78 | #### Maintainers Wanted! 79 | 80 | We're actively looking for maintainers for everything that doesn't have a specific package. If you have the time, please contribute. 81 | 82 | _I am unable to support distributions which there are not prebuilt packages for. I am actively seeking maintainers for these packages._ 83 | 84 | ## CONFIGURATION FILE 85 | 86 | That would be the file _/etc/genie.ini_. This defines the secure path (i.e., those directories in which genie will look for the utilities it depends on; make sure _unshare_, in particular, is available here), and seven settings controlling genie behavior. Normally, it looks like this: 87 | 88 | ``` 89 | [genie] 90 | secure-path=/lib/systemd:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 91 | update-hostname=true 92 | update-hostname-suffix=-wsl 93 | clone-path=false 94 | clone-env=WSL_DISTRO_NAME,WSL_INTEROP,WSLENV,DISPLAY,WAYLAND_DISPLAY,PULSE_SERVER 95 | systemd-timeout=240 96 | resolved-stub=false 97 | target-warning=true 98 | ``` 99 | 100 | The _secure-path_ setting should be generic enough to cover all but the weirdest Linux filesystem layouts, but on the off-chance that yours stores binaries somewhere particularly idiosyncratic, you can change it here. 101 | 102 | The _update-hostname_ setting controls whether or not genie updates the WSL hostname when creating the bottle. By default, genie updates a hostname _foo_ to _foo-wsl_, to prevent hostname clashes between the host Windows machine and the WSL distribution, especially when communicating back and forth between the two. 103 | 104 | However, as recent WSL builds allow the hostname of the WSL distributions to be set in _.wslconfig_, this option has been provided to disable genie's intervention and keep the original hostname. Additionally, the _update-hostname-suffix_ setting allows you to change the suffix added to the original hostname to create the WSL hostname. 105 | 106 | **HOWEVER** if you are using [bridged networking](https://randombytes.substack.com/p/bridged-networking-under-wsl), which uses separate IP addresses for WSL, often acquired via DHCP, we _strongly_ recommend not disabling this feature. On many networks, acquiring an address for WSL via DHCP with the same hostname as your Windows machine will remove your Windows machine's IP address from DNS, with irritatingly vague consequences. 107 | 108 | The _clone-path_ setting controls whether the PATH outside the bottle should be cloned inside the bottle. This can be useful since the outside-bottle path may include system-specific directories not mentioned in secure-path, and since the outside-bottle path includes a transformed version of the host machine's Windows path. 109 | 110 | If this is set to true, the inside-bottle path will be set to the secure-path combined with the outside-bottle path, with duplicate entries removed. It is set to false by default, for backwards compatibility. 111 | 112 | The _clone-env_ setting lists the environment variables which are copied from outside the bottle to inside the bottle. It defaults to only WSL_DISTRO_NAME, WSL_INTEROP, and WSLENV, needed for correct WSL operation, plus DISPLAY, WAYLAND_DISPLAY, and PULSE_SERVER, needed for WSLg but any other environment variables which should be cloned can be added to this list. 113 | 114 | The _systemd-timeout_ setting controls how long (the number of seconds) genie will wait when initializing the bottle for _systemd_ to reach its "running" - i.e. fully operational, with all units required by the default target active - state. This defaults to 240 seconds. 115 | 116 | _genie_ (1.44+) provides the _resolved-stub_ option to automatically back up the existing _/etc/resolv.conf_ and replace it with the symlink necessary to run _systemd-resolved_ in stub mode when initializing the bottle, and revert to the backup when the bottle terminates. (**NOTE:** This last is a courtesy and should NOT be interpreted as meaning idempotency is supported in any way; see _BUGS_ .) 1.43 performed this action by default; upgraders from 1.43 who wish to retain this behavior must set _resolved-stub=true_ in the configuration file. 117 | 118 | By default, _genie_ (2.0+) warns you upon bottle initialization if the default systemd target is not _multi-user.target_, since this is the default with which _genie_ is designed to work. If you have configured a different systemd target to run correctly with _genie_, this warning can be disabled by setting _target-warning_ to false in the config file. 119 | 120 | _genie_ (1.39+) also installs a pair of systemd units (_wslg-xwayland.service_ and _wslg-xwayland.socket_ and an override for _user-runtime-dir@.service_) to ensure that WSLg operates correctly from inside the bottle. If desired, these can be disabled and enabled independently of _genie_ itself. 121 | 122 | ## USAGE 123 | 124 | ``` 125 | usage: genie [-h] [-V] [-v] [-a USER] (-i | -s | -l | -c ... | -u | -r | -b) 126 | 127 | Handles transitions to the "bottle" namespace for systemd under WSL. 128 | 129 | optional arguments: 130 | -h, --help show this help message and exit 131 | -V, --version show program's version number and exit 132 | -v, --verbose display verbose progress messages 133 | -a USER, --as-user USER 134 | specify user to run shell or command as (use with -s or -c) 135 | 136 | commands: 137 | -i, --initialize initialize the bottle (if necessary) only 138 | -s, --shell initialize the bottle (if necessary), and run a shell in it 139 | -l, --login initialize the bottle (if necessary), and open a logon prompt in it 140 | -c ..., --command ... 141 | initialize the bottle (if necessary), and run the specified command in it 142 | -u, --shutdown shut down systemd and exit the bottle 143 | -r, --is-running check whether systemd is running in genie, or not 144 | -b, --is-in-bottle check whether currently executing within the bottle, or not 145 | 146 | For more information, see https://github.com/arkane-systems/genie/ 147 | ``` 148 | 149 | So, it has four modes, all of which will set up the bottle and run systemd in it if it isn't already running for simplicity of use. 150 | 151 | _genie -i_ will set up the bottle, run systemd, and then exit. This is intended for use if you want services running all the time in the background, or to preinitialize things so you needn't worry about startup time later on, and for this purpose is ideally run from Task Scheduler on logon. 152 | 153 | **NOTE:** It is never necessary to run _genie -i_ explicitly; the -s, -l, and -c commands will all set up the bottle if it has not already been initialized. 154 | 155 | **NOTE 2:** genie -i DOES NOT enter the bottle for you. It is important to remember that the genie bottle functions like a container, with its own cgroups and separate pid and mount namespaces. While some systemd or systemd-service powered things may work when invoked from outside the bottle, this is **ENTIRELY BY CHANCE**, and is **NOT A SUPPORTED SCENARIO**. You must enter the bottle using `genie -s`, `genie -l` or `genie -c` first. Ways to do this automatically when you start a WSL session can be found on the repo wiki. 156 | 157 | _genie -s_ runs your login shell inside the bottle; basically, Windows-side, _wsl genie -s_ is your substitute for just _wsl_ to get started, or for the shortcut you get to start a shell in the distro. It follows login semantics, and as such does not preserve the current working directory. 158 | 159 | _genie -c [command]_ runs _command_ inside the bottle, then exits. The return code is the return code of the command. It follows sudo semantics, and so does preserve the cwd. 160 | 161 | With either of the above, the _genie -a [user]_ option may be used to specify a particular user to start a shell for, or to run a command as, rather than using the currently logged-in user. For example, _genie -a bongo -s_ would start a shell as the user _bongo_ . 162 | 163 | _genie -l_ opens a login session within the bottle. This permits you to log in to the WSL distribution as any user. The login prompt will return when you log out; to terminate the session, press ^] three times within one second. It follows login semantics, and as such does not preserve the current working directory. 164 | 165 | Meanwhile, _genie -u_ , run from outside the bottle, will shut down systemd cleanly and exit the bottle. This uses the _systemctl poweroff_ command to simulate a normal Linux system shutting down. It is suggested that this be used before shutting down Windows or restarting the Linux distribution to ensure a clean shutdown of systemd services. 166 | 167 | **NOTE 3:** genie is not and cannot be idempotent. As such, it is strongly recommended that you do not restart genie or continue to use the WSL distro session after using _genie -u_. See **BUGS**, below. 168 | 169 | _genie -r_ and _genie -b_ are informational commands for use in checking the state of the system and/or scripting genie. 170 | 171 | The former checks whether genie (and an associated systemd(1) instance) are currently running. Possible output and return codes are: 172 | 173 | * _running_ (exit code 0) - the bottle (and systemd) are running normally 174 | * _stopped_ (exit code 1) - no bottle is present 175 | * _starting_ (exit code 2); and 176 | * _stopping_ (exit code 3) - the bottle is transitioning between states, please wait 177 | * _running (systemd errors)_ (exit code 4) - the bottle is up but some systemd services are reporting errors 178 | * _unknown_ (exit code 5) - unable to determine bottle state 179 | 180 | The latter, meanwhile, checks whether the current command is executing inside the bottle. Possible output and return codes are: 181 | 182 | * _inside_ (exit code 0) - inside the bottle 183 | * _outside_ (exit code 1) - outside the bottle (bottle exists) 184 | * _no-bottle_ (exit code 2) - no bottle is present 185 | 186 | While running, genie stores the external PID of the systemd instance in the file _/run/genie.systemd.pid_ for use in user scripting. It does not provide a similar file for the internal PID for obvious reasons. 187 | 188 | While not compulsory, it is recommended that you shut down and restart the WSL distro before using genie again after you have used _genie -u_. See BUGS, below, for more details. 189 | 190 | ### WARNING: TIMING OUT 191 | 192 | If _genie_ (1.31+) seems to be blocked at the 193 | 194 | `"Waiting for systemd...!!!!!"` 195 | 196 | stage, this is because of the new feature in 1.31 that waits for all _systemd_ services/units to have started up before continuing, to ensure that they have started before you try and do anything that might require them. (I.e., it waits for the point at which a normal Linux system would have given you a login prompt.) It does this by waiting for _systemd_ to reach the "running" state. 197 | 198 | If it appears to have blocked, wait until the timeout (by default, 240 seconds), at which point a list of units which have not started property will be displayed. Fixing or disabling those units such that _systemd_ can start properly will also allow _genie_ to start properly. Known-problematic units are listed on [the genie wiki](https://github.com/arkane-systems/genie/wiki). 199 | 200 | ### RECOMMENDATIONS 201 | 202 | Once you have this up and running, I suggest disabling via systemctl the _getty@tty1_ service (since logging on and using WSL is done via ptsen, not ttys). 203 | 204 | Further tips on usage from other genie users can be found on the wiki for this repo. 205 | 206 | ## BUGS 207 | 208 | 1. It is considerably clunkier than I'd like it to be, inasmuch as you have to invoke genie every time to get inside the bottle, either manually (replacing, for example, _wsl [command]_ with _wsl genie -c [command]_), or by using your own shortcut in place of the one WSL gives you for the distro, using which will put you _outside_ the bottle. Pull requests, etc. But see also [RunInGenie](https://github.com/arkane-systems/RunInGenie)! 209 | 210 | 2. genie is not idempotent; i.e., it is possible that changes made by genie or by systemd inside the bottle will not be perfectly reverted when the genie bottle is shut down with _genie -u_ . (Linux pid/mount namespaces aren't perfect containers, and systemd units and other actions inside the bottle can and will change things that affect the outside of the bottle, possibly even across distros. And note that _genie -u_ calls _systemctl poweroff_ which believes that it is shutting down the entire machine; the in-bottle systemd is a full systemd installation, not a cut-down container install.) As such, it is **strongly recommended** that you terminate the entire wsl session with _wsl -t _ or _wsl --shutdown_ in between stopping and restarting the bottle, or errors may occur; we cannot support such scenarios. 211 | 212 | 3. As of 1.38, while WSLg operates correctly with _genie_ and GUI apps can be run from inside the bottle, Linux GUI apps started from the Windows Start Menu items created by WSLg will run outside the bottle. This is being worked on. 213 | 214 | ## OLDER VERSIONS 215 | 216 | For information on versions of genie prior to 2.0, see the saved older README-1.44.md file in this repository. 217 | -------------------------------------------------------------------------------- /othersrc/docs/wslgenie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkane-systems/genie/46894d1a1abcb60b78ab837a09cb85afbce6c44d/othersrc/docs/wslgenie.png -------------------------------------------------------------------------------- /othersrc/etc/genie.ini: -------------------------------------------------------------------------------- 1 | [genie] 2 | systemd-timeout=240 3 | clone-env=WSL_DISTRO_NAME,WSL_INTEROP,WSLENV,DISPLAY,WAYLAND_DISPLAY,PULSE_SERVER 4 | secure-path=/lib/systemd:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 5 | clone-path=false 6 | target-warning=true 7 | update-hostname=true 8 | update-hostname-suffix=-wsl 9 | resolved-stub=false 10 | -------------------------------------------------------------------------------- /othersrc/lib-systemd-system/user-runtime-dir@.service.d/override.conf: -------------------------------------------------------------------------------- 1 | [Service] 2 | ExecStart= 3 | ExecStart=/lib/systemd/systemd-user-runtime-dir start %i 4 | ExecStart=/usr/lib/genie/map-user-runtime-dir.sh %i 5 | ExecStop= 6 | ExecStop=/usr/lib/genie/unmap-user-runtime-dir.sh %i 7 | ExecStop=/lib/systemd/systemd-user-runtime-dir stop %i 8 | -------------------------------------------------------------------------------- /othersrc/scripts/80-genie-envar.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -e /run/genie.env ] 3 | then 4 | cat /run/genie.env 5 | fi 6 | 7 | if [ -e /run/genie.path ] 8 | then 9 | PATH=$PATH:$(cat /run/genie.path) 10 | echo PATH="$(echo $PATH | awk -v RS=: '!($0 in a) {a[$0]; printf("%s%s", length(a) > 1 ? ":" : "", $0)}')" 11 | fi 12 | -------------------------------------------------------------------------------- /othersrc/scripts/map-user-runtime-dir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ ! -d /mnt/wslg/runtime-dir ] 3 | then 4 | # WSLg is not present, so do nothing over-and-above previous 5 | exit 6 | fi 7 | 8 | WUID=$(stat -c "%u" /mnt/wslg/runtime-dir) 9 | 10 | if [ $1 -eq $WUID ] 11 | then 12 | # We are the WSLg user, so map the runtime-dir 13 | mount --bind /mnt/wslg/runtime-dir /run/user/$1 14 | exit 15 | fi 16 | -------------------------------------------------------------------------------- /othersrc/scripts/unmap-user-runtime-dir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ ! -d /mnt/wslg/runtime-dir ] 3 | then 4 | # WSLg is not present, so do nothing over-and-above previous 5 | exit 6 | fi 7 | 8 | WUID=$(stat -c "%u" /mnt/wslg/runtime-dir) 9 | 10 | if [ $1 -eq $WUID ] 11 | then 12 | # We are the WSLg user, so unmap the runtime-dir 13 | umount /run/user/$1 14 | exit 15 | fi 16 | -------------------------------------------------------------------------------- /othersrc/usr-lib/tmpfiles.d/wslg.conf: -------------------------------------------------------------------------------- 1 | # This file is part of genie. 2 | # 3 | 4 | # See tmpfiles.d(5) for details 5 | 6 | # Create the needed symlink to make WSLg work under genie. 7 | L+ /tmp/.X11-unix/X0 - - - - /mnt/wslg/.X11-unix/X0 8 | -------------------------------------------------------------------------------- /readme-1.44.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # genie 4 | 5 | [ ![ci](https://github.com/arkane-systems/genie/workflows/ci/badge.svg?branch=master) ](https://github.com/arkane-systems/genie/actions?query=workflow%3Aci+branch%3Amaster) 6 | 7 | ## A quick way into a systemd "bottle" for WSL 8 | 9 | What does that even mean? 10 | 11 | Well, this gives you a way to run systemd as pid 1, with all the trimmings, inside WSL 2. It does this by creating a pid namespace, the eponymous poor-man's-container "bottle", starting up systemd in there, and entering it, and providing some helpful shortcuts to do so. 12 | 13 | If you want to try it, please read this entire document first, _especially_ the BUGS section. Also read the [WSLg FAQ](https://github.com/arkane-systems/genie/wiki/WSLg-FAQ) and the [known-problematic systemd units list](https://github.com/arkane-systems/genie/wiki/Systemd-units-known-to-be-problematic-under-WSL) for known problems and known solutions to them before reporting any issues. 14 | 15 | ## NOTE: WSL 2 ONLY 16 | 17 | Note: it is only possible to run _systemd_ (and thus _genie_ ) under WSL 2; WSL 1 does not support the system calls required to do so. If you are running inside a distro configured as WSL 1, even if your system supports WSL 2, genie will fail to operate properly. 18 | 19 | ## INSTALLATION 20 | 21 | **NOTE:** Before you install _genie_ for the first time, read **ALL** of this page. This will save you a great deal of trouble later on. Especially, please note that on many distributions you **will** encounter the problem described under "Warning: Timing Out" below when you first run genie, and will need to resolve it before your system will operate correctly. 22 | 23 | **NOTE 2:** More information, tips & tricks, etc. are available on [the genie wiki](https://github.com/arkane-systems/genie/wiki). Please consult it before opening an issue. 24 | 25 | If there is a package available for your distribution, this is the recommended method of installing genie. 26 | 27 | ### Debian 28 | 29 | Dependent packages on Debian are _daemonize_, _dbus_, _dotnet-runtime-5.0_, _gawk_, _libc6_, _libstdc++6_, _policykit-1_, _systemd_, and _systemd-container_. For the most part, these are either already installed or in the distro and able to be installed automatically. You need _debhelper_ and _dotnet-sdk-5.0_ (and optionally _pbuilder_) to build the Debian package, but not to simply build _genie_ or install locally. 30 | 31 | The chief exception is _dotnet-runtime-5.0_ , for which you will need to follow the installation instructions here: 32 | 33 | https://docs.microsoft.com/en-us/dotnet/core/install/linux 34 | 35 | To install, add the wsl-translinux repository here by following the instructions here: 36 | 37 | https://arkane-systems.github.io/wsl-transdebian/ 38 | 39 | then install genie using the commands: 40 | 41 | ```bash 42 | sudo apt update 43 | sudo apt install -y systemd-genie 44 | ``` 45 | 46 | (The packagecloud.io repository is now deprecated. If you are still using it, please update your system to use the repository above.) 47 | 48 | #### Ubuntu & Other Debian Derivatives 49 | 50 | Use the above Debian package. For current Ubuntu releases and the timing-out problem, see the problematic units listed on [the genie wiki](https://github.com/arkane-systems/genie/wiki). 51 | 52 | ### Arch 53 | 54 | An Arch package (.zst) can be downloaded from the releases, to right. Install it manually, using `pacman -U `. 55 | 56 | ### Fedora 57 | 58 | A Fedora package (.rpm) can be downloaded from the releases, to right. Install it manually, using `dnf install `. 59 | 60 | ### Other Distros 61 | 62 | If your distribution supports any of the package formats available, you may wish to try downloading the relevant format and giving it a try. This will almost certainly need some tweaking to work properly. 63 | 64 | Debian is the "native" distribution for _genie_ , for which read, "what the author uses". Specifically, Debian stretch+, with _usrmerge_ installed. If you're using anything else, you may need to tweak the configuration file (see below) accordingly. 65 | 66 | #### TAR 67 | 68 | There is a .tar.gz of a complete genie install available from the releases, to right. As a last resort, you can try untarring this (it contains every needed file, with the correct permissions, in the correct path from /) onto your system while root. Don't do this unless you're confident you know what you're doing, you're willing to go looking for any resulting issues yourself, and you aren't afraid of accidentally breaking things. You will need to install the dependencies listed above beforehand. 69 | 70 | If you install from the tarball, you will need to enable the _wslg-xwayland.service_ and _wslg-xwayland.socket_ systemd units manually. 71 | 72 | #### Maintainers Wanted! 73 | 74 | We're actively looking for maintainers for everything that doesn't have a specific package. If you have the time, please contribute. 75 | 76 | _I am unable to support distributions which there are not prebuilt packages for. I am actively seeking maintainers for these packages._ 77 | 78 | ### ...OR BUILD IT YOURSELF 79 | 80 | It is possible to build your own version of genie and install it locally. To do so, you will require _build-essential_ and _dotnet-sdk-5.0_ in addition to the other dependencies, all of which must be installed manually. 81 | 82 | After cloning the repository, run 83 | 84 | ``` 85 | sudo make install-local 86 | ``` 87 | 88 | This will build genie and install it under _/usr/local_ . 89 | 90 | After installing locally and starting genie and systemd for the first time, you will need to enable the _wslg-xwayland.socket_ systemd units manually: 91 | 92 | ``` 93 | systemctl enable wslg-xwayland.socket 94 | ``` 95 | 96 | ## CONFIGURATION FILE 97 | 98 | That would be the file _/etc/genie.ini_. This defines the secure path (i.e., those directories in which genie will look for the utilities it depends on), and the explicit path to _unshare(1)_, required by _daemonize(1)_, and five settings controlling genie behavior. Normally, it looks like this: 99 | 100 | ``` 101 | [genie] 102 | secure-path=/lib/systemd:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 103 | unshare=/usr/bin/unshare 104 | update-hostname=true 105 | clone-path=false 106 | clone-env=WSL_DISTRO_NAME,WSL_INTEROP,WSLENV,DISPLAY,WAYLAND_DISPLAY,PULSE_SERVER 107 | systemd-timeout=240 108 | resolved-stub=false 109 | ``` 110 | 111 | The _secure-path_ setting should be generic enough to cover all but the weirdest Linux filesystem layouts, but on the off-chance that yours stores binaries somewhere particularly idiosyncratic, you can change it here. Meanwhile, the _unshare_ setting is much more likely to be system-dependent; if you are getting errors running genie, please replace this with the output of `which unshare` before trying anything else. 112 | 113 | The _update-hostname_ setting controls whether or not genie updates the WSL hostname when creating the bottle. By default, genie updates a hostname _foo_ to _foo-wsl_, to prevent hostname clashes between the host Windows machine and the WSL distribution, especially when communicating back and forth between the two. However, as recent Insider builds allow the hostname of the WSL distributions to be set in _.wslconfig_, this option has been provided to disable genie's intervention and keep the original hostname. 114 | 115 | The _clone-path_ setting controls whether the PATH outside the bottle should be cloned inside the bottle. This can be useful since the outside-bottle path may include system-specific directories not mentioned in secure-path, and since the outside-bottle path includes a transformed version of the host machine's Windows path. 116 | 117 | If this is set to true, the inside-bottle path will be set to the secure-path combined with the outside-bottle path, with duplicate entries removed. It is set to false by default, for backwards compatibility. 118 | 119 | The _clone-env_ setting lists the environment variables which are copied from outside the bottle to inside the bottle. It defaults to only WSL_DISTRO_NAME, WSL_INTEROP, and WSLENV, needed for correct WSL operation, plus DISPLAY, WAYLAND_DISPLAY, and PULSE_SERVER, needed for WSLg but any other environment variables which should be cloned can be added to this list. This replaces the former ability to copy additional environment variables by editing _/usr/libexec/dumpwslenv.sh_. 120 | 121 | The _systemd-timeout_ setting controls how long (the number of seconds) genie will wait when initializing the bottle for _systemd_ to reach its "running" - i.e. fully operational, with all units required by the default target active - state. This defaults to 240 seconds. 122 | 123 | _genie_ (1.44+) provides the _resolved-stub_ option to automatically back up the existing _/etc/resolv.conf_ and replace it with the symlink necessary to run _systemd-resolved_ in stub mode when initializing the bottle, and revert to the backup when the bottle terminates. (**NOTE:** This last is a courtesy and should NOT be interpreted as meaning idempotency is supported in any way; see _BUGS_ .) 1.43 performed this action by default; upgraders from 1.43 who wish to retain this behavior must set _resolved-stub=true_ in the configuration file. 124 | 125 | _genie_ (1.39+) also installs a pair of systemd units (_wslg-xwayland.service_ and _wslg-xwayland.socket_ and an override for _user-runtime-dir@.service_) to ensure that WSLg operates correctly from inside the bottle. If desired, these can be disabled and enabled independently of _genie_ itself. 126 | 127 | ## USAGE 128 | 129 | ``` 130 | genie: 131 | Handles transitions to the "bottle" namespace for systemd under WSL. 132 | 133 | Usage: 134 | genie [options] [command] 135 | 136 | Options: 137 | -v, --verbose Display verbose progress messages 138 | --version Display version information 139 | 140 | Commands: 141 | -i, --initialize Initialize the bottle (if necessary) only. 142 | -s, --shell Initialize the bottle (if necessary), and run a shell in it. 143 | -l, --login Initialize the bottle (if necessary), and open a logon prompt in it. 144 | -c, --command Initialize the bottle (if necessary), and run the specified command in it. 145 | -u, --shutdown Shut down systemd and exit the bottle. 146 | -r, --is-running Check whether systemd is running in genie, or not. 147 | -b, --is-in-bottle Check whether currently executing within the genie bottle, or not. 148 | ``` 149 | 150 | So, it has four modes, all of which will set up the bottle and run systemd in it if it isn't already running for simplicity of use. 151 | 152 | _genie -i_ will set up the bottle - including changing the WSL hostname by suffixing -wsl, to distinguish it from the Windows host - run systemd, and then exit. This is intended for use if you want services running all the time in the background, or to preinitialize things so you needn't worry about startup time later on, and for this purpose is ideally run from Task Scheduler on logon. 153 | 154 | **NOTE:** It is never necessary to run _genie -i_ explicitly; the -s, -l, and -c commands will all set up the bottle if it has not already been initialized. 155 | 156 | **NOTE 2:** genie -i DOES NOT enter the bottle for you. It is important to remember that the genie bottle functions like a container, with its own cgroups and separate pid and mount namespaces. While some systemd or systemd-service powered things may work when invoked from outside the bottle, this is ENTIRELY BY CHANCE, and is NOT A SUPPORTED SCENARIO. You must enter the bottle using `genie -s`, `genie -l` or `genie -c` first. Ways to do this automatically when you start a WSL session can be found on the repo wiki. 157 | 158 | _genie -s_ runs your login shell inside the bottle; basically, Windows-side, _wsl genie -s_ is your substitute for just _wsl_ to get started, or for the shortcut you get to start a shell in the distro. It follows login semantics, and as such does not preserve the current working directory. 159 | 160 | _genie -l_ opens a login session within the bottle. This permits you to log in to the WSL distribution as any user. The login prompt will return when you log out; to terminate the session, press ^] three times within one second. It follows login semantics, and as such does not preserve the current working directory. 161 | 162 | _genie -c [command]_ runs _command_ inside the bottle, then exits. The return code is the return code of the command. It follows sudo semantics, and so does preserve the cwd. 163 | 164 | Meanwhile, _genie -u_ , run from outside the bottle, will shut down systemd cleanly and exit the bottle. This uses the _systemctl poweroff_ command to simulate a normal Linux system shutting down. It is suggested that this be used before shutting down Windows or restarting the Linux distribution to ensure a clean shutdown of systemd services. 165 | 166 | **NOTE 3:** genie is not and cannot be idempotent. As such, it is strongly recommended that you do not restart genie or continue to use the WSL distro session after using _genie -u_. See BUGS, below. 167 | 168 | _genie -r_ and _genie -b_ are informational commands for use in checking the state of the system and/or scripting genie. The former checks whether genie (and an associated systemd(1) instance) is currently running. It returns the string "running" and exit code 0 if one is found; it returns the string "stopped" and exit code 1 if one is not. The latter checks whether the current command is executing inside the bottle. It returns the string "inside" and exit code 0 if so; it returns the string "outside" and exit code 1 if one is not. If no bottle exists, it returns the string "no-bottle" and exit code 2. 169 | 170 | **NOTE 4:** _genie -r_ and _genie -b_ cannot, obviously, look above the systemd process in the process tree, since that would pass out of the bottle's PID namespace. As such, running these commands on a non-genie system (why would you do that?) will detect the system systemd instance and indicate that genie is running and you are within the bottle. Since the behavior of such a system should be indistinguishable from a genie bottle from within, this is not considered a bug. 171 | 172 | While running, genie stores the external PID of the systemd instance in the file _/run/genie.systemd.pid_ for use in user scripting. It does not provide a similar file for the internal PID for obvious reasons. 173 | 174 | While not compulsory, it is recommended that you shut down and restart the WSL distro before using genie again after you have used _genie -u_. See BUGS, below, for more details. 175 | 176 | ### WARNING: TIMING OUT 177 | 178 | If _genie_ (1.31+) seems to be blocked at the 179 | 180 | `"Waiting for systemd...!!!!!"` 181 | 182 | stage, this is because of the new feature in 1.31 that waits for all _systemd_ services/units to have started up before continuing, to ensure that they have started before you try and do anything that might require them. (I.e., it waits for the point at which a normal Linux system would have given you a login prompt.) It does this by waiting for _systemd_ to reach the "running" state. 183 | 184 | If it appears to have blocked, wait until the timeout (by default, 60 seconds), at which point a list of units which have not started property will be displayed. Fixing or disabling those units such that _systemd_ can start properly will also allow _genie_ to start properly. Known-problematic units are listed on [the genie wiki](https://github.com/arkane-systems/genie/wiki). 185 | 186 | ## RECOMMENDATIONS 187 | 188 | Once you have this up and running, I suggest disabling via systemctl the _getty@tty1_ service (since logging on and using WSL is done via ptsen, not ttys). 189 | 190 | Further tips on usage from other genie users can be found on the wiki for this repo. 191 | 192 | ## BUGS 193 | 194 | 1. It is considerably clunkier than I'd like it to be, inasmuch as you have to invoke genie every time to get inside the bottle, either manually (replacing, for example, _wsl [command]_ with _wsl genie -c [command]_), or by using your own shortcut in place of the one WSL gives you for the distro, using which will put you _outside_ the bottle. Pull requests, etc. But see also [RunInGenie](https://github.com/arkane-systems/RunInGenie)! 195 | 196 | 2. genie is not idempotent; i.e., it is possible that changes made by genie or by systemd inside the bottle will not be perfectly reverted when the genie bottle is shut down with _genie -u_ . (Linux pid/mount namespaces aren't perfect containers, and systemd units and other actions inside the bottle can and will change things that affect the outside of the bottle, possibly even across distros. And note that _genie -u_ calls _systemctl poweroff_ which believes that it is shutting down the entire machine; the in-bottle systemd is a full systemd installation, not a cut-down container install.) As such, it is **strongly recommended** that you terminate the entire wsl session with _wsl -t _ or _wsl --shutdown_ in between stopping and restarting the bottle, or errors may occur; we cannot support such scenarios. 197 | 198 | 3. As of 1.38, while WSLg operates correctly with _genie_ and GUI apps can be run from inside the bottle, Linux GUI apps started from the Windows Start Menu items created by WSLg will run outside the bottle. This is being worked on. 199 | --------------------------------------------------------------------------------