├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG ├── LICENSE.md ├── Makefile ├── PKGBUILD ├── README.md ├── binsrc ├── Makefile ├── imp-executor │ ├── __main__.py │ ├── configuration.py │ ├── mountie.py │ └── requirements.txt ├── imp-generator │ ├── __main__.py │ ├── configuration.py │ ├── definitions.py │ ├── generatees.py │ ├── genhelper.py │ └── requirements.txt ├── imp-wrapper │ ├── Makefile │ ├── imp.c │ └── test-as-root ├── imp │ ├── __main__.py │ ├── configuration.py │ ├── helpers.py │ └── requirements.txt └── shared │ └── configuration.py ├── debian ├── bottle-imp.lintian-overrides ├── bottle-imp.manpages ├── changelog ├── control ├── copyright ├── docs ├── rules └── source │ └── format └── othersrc ├── docs ├── LICENSE.md ├── README.md └── imp.8 └── scripts ├── imp-user-runtime-dir.sh └── wait-forever.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ['cerebrate'] 2 | ko_fi: arkanesystems 3 | 4 | -------------------------------------------------------------------------------- /.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-22.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-latest 34 | 35 | steps: 36 | - name: Update apt db 37 | run: sudo apt update 38 | 39 | - name: Install Cross-Compiler 40 | run: sudo apt install -y gcc-aarch64-linux-gnu 41 | 42 | - name: Checkout 43 | uses: actions/checkout@v3 44 | 45 | - name: Build & Make Tarball 46 | run: make package-tar 47 | 48 | - name: Upload Results 49 | uses: actions/upload-artifact@v3 50 | with: 51 | name: tar 52 | path: out/tar/* 53 | 54 | build-arch: 55 | runs-on: ubuntu-latest 56 | container: 57 | image: cerebrate/fuckarch:right-in-the-ear 58 | 59 | steps: 60 | - name: Checkout 61 | uses: actions/checkout@v2 62 | 63 | - name: Permissions fixup 64 | run: sudo chown -R build . 65 | 66 | - name: Build Package 67 | run: sudo -u build make package-arch 68 | 69 | - name: Upload Results 70 | uses: actions/upload-artifact@v3 71 | with: 72 | name: zst 73 | path: out/arch/* 74 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Results 2 | binsrc/imp-wrapper/imp 3 | out/ 4 | 5 | # Debian build intermediates 6 | 7 | debian/bottle-imp 8 | debian/.debhelper/ 9 | debian/debhelper-build-stamp 10 | debian/files 11 | debian/bottle-imp.debhelper.log 12 | debian/bottle-imp.postinst.debhelper 13 | debian/bottle-imp.postrm.debhelper 14 | debian/bottle-imp.substvars 15 | debian/bottle-imp 16 | 17 | # Tarball build intermediates 18 | tarball/ 19 | 20 | # Byte-compiled / optimized / DLL files 21 | __pycache__/ 22 | *.py[cod] 23 | *$py.class 24 | 25 | # C extensions 26 | *.so 27 | 28 | # Distribution / packaging 29 | .Python 30 | build/ 31 | develop-eggs/ 32 | dist/ 33 | downloads/ 34 | eggs/ 35 | .eggs/ 36 | lib/ 37 | lib64/ 38 | parts/ 39 | sdist/ 40 | var/ 41 | wheels/ 42 | pip-wheel-metadata/ 43 | share/python-wheels/ 44 | *.egg-info/ 45 | .installed.cfg 46 | *.egg 47 | MANIFEST 48 | 49 | # Dependencies 50 | dp/ 51 | 52 | # PyInstaller 53 | # Usually these files are written by a python script from a template 54 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 55 | *.manifest 56 | *.spec 57 | 58 | # Installer logs 59 | pip-log.txt 60 | pip-delete-this-directory.txt 61 | 62 | # Unit test / coverage reports 63 | htmlcov/ 64 | .tox/ 65 | .nox/ 66 | .coverage 67 | .coverage.* 68 | .cache 69 | nosetests.xml 70 | coverage.xml 71 | *.cover 72 | *.py,cover 73 | .hypothesis/ 74 | .pytest_cache/ 75 | 76 | # Translations 77 | *.mo 78 | *.pot 79 | 80 | # Django stuff: 81 | *.log 82 | local_settings.py 83 | db.sqlite3 84 | db.sqlite3-journal 85 | 86 | # Flask stuff: 87 | instance/ 88 | .webassets-cache 89 | 90 | # Scrapy stuff: 91 | .scrapy 92 | 93 | # Sphinx documentation 94 | docs/_build/ 95 | 96 | # PyBuilder 97 | target/ 98 | 99 | # Jupyter Notebook 100 | .ipynb_checkpoints 101 | 102 | # IPython 103 | profile_default/ 104 | ipython_config.py 105 | 106 | # pyenv 107 | .python-version 108 | 109 | # pipenv 110 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 111 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 112 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 113 | # install all needed dependencies. 114 | #Pipfile.lock 115 | 116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 117 | __pypackages__/ 118 | 119 | # Celery stuff 120 | celerybeat-schedule 121 | celerybeat.pid 122 | 123 | # SageMath parsed files 124 | *.sage.py 125 | 126 | # Environments 127 | .env 128 | .venv 129 | env/ 130 | venv/ 131 | ENV/ 132 | env.bak/ 133 | venv.bak/ 134 | 135 | # Spyder project settings 136 | .spyderproject 137 | .spyproject 138 | 139 | # Rope project settings 140 | .ropeproject 141 | 142 | # mkdocs documentation 143 | /site 144 | 145 | # mypy 146 | .mypy_cache/ 147 | .dmypy.json 148 | dmypy.json 149 | 150 | # Pyre type checker 151 | .pyre/ 152 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | debian/changelog -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | othersrc/docs/LICENSE.md -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This Makefile builds and packages bottle-imp by invoking relevant sub-makefiles. 3 | # 4 | 5 | # Bottle-Imp version 6 | IMPVERSION = 1.0 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/bottle-imp 15 | BINDIR = $(DESTDIR)/usr/bin 16 | USRLIBDIR = $(DESTDIR)/usr/lib 17 | GENDIR = $(DESTDIR)/usr/lib/systemd/system-generators 18 | 19 | # used only by TAR installer 20 | MAN8DIR = $(DESTDIR)/usr/share/man/man8 21 | DOCDIR = $(DESTDIR)/usr/share/doc/bottle-imp 22 | 23 | # 24 | # Default target: list options 25 | # 26 | 27 | default: 28 | # Build options include: 29 | # 30 | # Build binaries only. 31 | # 32 | # make build-binaries 33 | # 34 | # Package 35 | # 36 | # make package 37 | # make package-debian 38 | # make package-debian-amd64 39 | # make package-debian-arm64 40 | # make package-tar 41 | # make package-tar-amd64 42 | # make package-tar-arm64 43 | # 44 | # make package-arch 45 | # 46 | # Clean up 47 | # 48 | # make clean 49 | # make clean-debian 50 | # make clean-tar 51 | # 52 | # make clean-arch 53 | 54 | # 55 | # Targets: individual end-product build. 56 | # 57 | 58 | clean: clean-debian clean-tar 59 | make -C binsrc clean 60 | rm -rf out 61 | 62 | package: package-debian package-tar 63 | 64 | 65 | # 66 | # Debian packaging 67 | # 68 | 69 | package-debian: package-debian-amd64 package-debian-arm64 70 | 71 | package-debian-amd64: make-output-directory 72 | mkdir -p out/debian 73 | debuild --no-sign 74 | mv ../bottle-imp_* out/debian 75 | 76 | package-debian-arm64: make-output-directory 77 | mkdir -p out/debian 78 | debuild -aarm64 -b --no-sign 79 | mv ../bottle-imp_* out/debian 80 | 81 | clean-debian: 82 | debuild -- clean 83 | 84 | # Internal packaging functions 85 | 86 | internal-debian-package: 87 | mkdir -p debian/bottle-imp 88 | @$(MAKE) -f $(THIS_FILE) DESTDIR=debian/bottle-imp internal-package 89 | 90 | 91 | # 92 | # Tarball packaging 93 | # 94 | 95 | package-tar: package-tar-amd64 package-tar-arm64 96 | 97 | package-tar-amd64: make-output-directory 98 | mkdir -p out/tar 99 | rm -rf tarball 100 | mkdir -p tarball 101 | 102 | $(MAKE) -f $(THIS_FILE) build-binaries 103 | 104 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball internal-package 105 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball internal-supplement 106 | 107 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball TARCH=amd64 archive-tarfile 108 | 109 | mv bottle-imp-*-amd64.tar.gz out/tar 110 | 111 | package-tar-arm64: make-output-directory 112 | mkdir -p out/tar 113 | rm -rf tarball 114 | mkdir -p tarball 115 | 116 | DEB_TARGET_ARCH=arm64 $(MAKE) -f $(THIS_FILE) build-binaries 117 | 118 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball internal-package 119 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball internal-supplement 120 | 121 | fakeroot $(MAKE) -f $(THIS_FILE) DESTDIR=tarball TARCH=arm64 archive-tarfile 122 | 123 | mv bottle-imp-*-arm64.tar.gz out/tar 124 | 125 | clean-tar: 126 | rm -rf tarball 127 | 128 | # Internal packaging functions 129 | 130 | archive-tarfile: 131 | # tar it up 132 | tar zcvf bottle-imp-$(IMPVERSION)-$(TARCH).tar.gz tarball/* --transform='s/^tarball//' 133 | 134 | 135 | # 136 | # Arch packaging 137 | # 138 | 139 | package-arch: 140 | mkdir -p out/arch 141 | updpkgsums 142 | BUILDDIR=/tmp PKDEST=$(PWD)/out/arch makepkg 143 | rm -rf $(PWD)/bottle-imp 144 | mv *.zst out/arch 145 | 146 | clean-arch: 147 | rm -rf $(PWD)/genie 148 | rm -rf out/arch 149 | 150 | 151 | # 152 | # Helpers: intermediate build stages. 153 | # 154 | 155 | # We can assume DESTDIR is set, due to how the following are called. 156 | 157 | internal-package: 158 | # binaries 159 | mkdir -p "$(BINDIR)" 160 | install -Dm 6755 -o root "binsrc/imp-wrapper/imp" -t "$(BINDIR)" 161 | install -Dm 0755 -o root "binsrc/out/imp" -t "$(INSTALLDIR)" 162 | install -Dm 0755 -o root "binsrc/out/imp-generator" -t "$(GENDIR)" 163 | install -Dm 0755 -o root "binsrc/out/imp-executor" -t "$(INSTALLDIR)" 164 | 165 | # scripts 166 | install -Dm 0755 -o root "othersrc/scripts/imp-user-runtime-dir.sh" -t "$(INSTALLDIR)" 167 | install -Dm 0755 -o root "othersrc/scripts/wait-forever.sh" -t "$(INSTALLDIR)" 168 | 169 | internal-clean: 170 | make -C binsrc clean 171 | 172 | # internal-supplement: TMPBUILDDIR = $(shell mktemp -d -t bit-XXXXXX) 173 | internal-supplement: TMPBUILDDIR = /tmp/bi-build 174 | internal-supplement: 175 | # Do the things that debuild would do if debuild was doing things. 176 | mkdir -p $(TMPBUILDDIR) 177 | 178 | # Documentation. 179 | /usr/bin/cp debian/changelog $(TMPBUILDDIR)/changelog 180 | /usr/bin/cp othersrc/docs/README.md $(TMPBUILDDIR)/README.md 181 | 182 | gzip $(TMPBUILDDIR)/changelog 183 | gzip $(TMPBUILDDIR)/README.md 184 | 185 | mkdir -p "$(DOCDIR)" 186 | install -Dm 0644 -o root $(TMPBUILDDIR)/changelog.gz -t "$(DOCDIR)" 187 | install -Dm 0644 -o root debian/copyright -t "$(DOCDIR)" 188 | install -Dm 0644 -o root othersrc/docs/LICENSE.md -t "$(DOCDIR)" 189 | install -Dm 0644 -o root $(TMPBUILDDIR)/README.md.gz -t "$(DOCDIR)" 190 | 191 | # Man page. 192 | /usr/bin/cp othersrc/docs/imp.8 $(TMPBUILDDIR)/imp.8 193 | gzip -f9 $(TMPBUILDDIR)/imp.8 194 | 195 | mkdir -p $(MAN8DIR) 196 | install -Dm 0644 -o root "$(TMPBUILDDIR)/imp.8.gz" -t $(MAN8DIR) 197 | 198 | # Cleanup temporary directory 199 | rm -rf $(TMPBUILDDIR) 200 | 201 | make-output-directory: 202 | mkdir -p out 203 | 204 | build-binaries: 205 | make -C binsrc 206 | -------------------------------------------------------------------------------- /PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: Alistair Young 2 | pkgname=bottle-imp 3 | pkgver=1.0 4 | pkgrel=1 5 | pkgdesc="A helper for WSL's native systemd support." 6 | arch=('x86_64') 7 | url="https://github.com/arkane-systems/bottle-imp" 8 | license=('Unlicense') 9 | 10 | depends=('python>=3.7' 'python-psutil' 'systemd>=232.25' 'inetutils') 11 | conflicts=('systemd-genie') 12 | makedepends=('git' 'python-pip') 13 | options=(!strip) 14 | source=("git+https://github.com/arkane-systems/bottle-imp.git") 15 | sha256sums=('SKIP') 16 | 17 | # pkgver() { 18 | # git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//g' 19 | # } 20 | 21 | build() { 22 | cd bottle-imp 23 | make build-binaries 24 | } 25 | 26 | package() { 27 | cd bottle-imp 28 | make DESTDIR=${pkgdir} internal-package 29 | make DESTDIR=${pkgdir} internal-supplement 30 | } 31 | -------------------------------------------------------------------------------- /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-imp build-executor build-generator 9 | 10 | build-wrapper: 11 | mkdir -p out 12 | make -C imp-wrapper 13 | 14 | build-imp: 15 | mkdir -p out 16 | python3 -m pip install -r imp/requirements.txt --target imp/dp --upgrade 17 | python3 -m zipapp -o out/imp -p "/usr/bin/env python3" -c imp 18 | 19 | build-executor: 20 | mkdir -p out 21 | python3 -m pip install -r imp-executor/requirements.txt --target imp-executor/dp --upgrade 22 | python3 -m zipapp -o out/imp-executor -p "/usr/bin/env python3" -c imp-executor 23 | 24 | build-generator: 25 | mkdir -p out 26 | python3 -m pip install -r imp-generator/requirements.txt --target imp-generator/dp --upgrade 27 | python3 -m zipapp -o out/imp-generator -p "/usr/bin/env python3" -c imp-generator 28 | 29 | # 30 | # clean: clean up after a build/package 31 | # 32 | clean: clean-wrapper clean-imp clean-executor clean-generator 33 | rm -rf out 34 | 35 | clean-wrapper: 36 | make -C imp-wrapper clean 37 | 38 | clean-imp: 39 | rm -f out/imp 40 | 41 | clean-executor: 42 | rm -f out/imp-executor 43 | 44 | clean-generator: 45 | rm -f out/imp-generator 46 | -------------------------------------------------------------------------------- /binsrc/imp-executor/__main__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | import mountie 7 | 8 | def print_help_message(): 9 | """Display a help message for imp-executor.""" 10 | 11 | print ("""imp-executor: internal functions for bottle-imp 12 | 13 | You should not use this directly under normal circumstances. 14 | It should only be called by bottle-imp provided services. 15 | 16 | Operations: 17 | 18 | help Display this message. 19 | 20 | rrfs Remount root filesystem shared. 21 | devshm Fix /dev/shm mount. 22 | pstore Mount pstore filesystem. 23 | security Mount security filesystem. 24 | wslg Bind mount WSL .X11-unix. 25 | """) 26 | 27 | 28 | def fix_dev_shm(): 29 | """Move the tmpfs for shared memory to /dev/shm and bind mount it from /run/shm.""" 30 | os.unlink ("/dev/shm") 31 | os.mkdir ("/dev/shm") 32 | mountie.mount ("/run/shm", "/dev/shm", "", mountie.MS_MOVE) 33 | mountie.mount ("/dev/shm", "/run/shm", "", mountie.MS_BIND) 34 | # os.rmdir ("/run/shm") 35 | # os.symlink ("/dev/shm", "/run/shm") 36 | 37 | 38 | def mount_pstore_filesystem(): 39 | """Mount the pstore filesystem.""" 40 | mountie.mount ("pstore", "/sys/fs/pstore", "pstore", 41 | mountie.MS_NOSUID | mountie.MS_NODEV | mountie.MS_NOEXEC) 42 | 43 | 44 | def mount_security_filesystem(): 45 | """Mount the security filesystem.""" 46 | mountie.mount ("securityfs", "/sys/kernel/security", "securityfs", 47 | mountie.MS_NOSUID | mountie.MS_NODEV | mountie.MS_NOEXEC) 48 | 49 | 50 | def remount_root_shared(): 51 | """Remount the root filesystem shared.""" 52 | mountie.mount ("none", "/", "", mountie.MS_REC | mountie.MS_SHARED, "") 53 | 54 | 55 | def remount_wslg(): 56 | """Remount the WSLg socket in the appropriate place.""" 57 | mountie.mount ("/mnt/wslg/.X11-unix", "/tmp/.X11-unix", "", 58 | mountie.MS_BIND | mountie.MS_RDONLY) 59 | # required because flags other than MS_BIND ignored in first call. 60 | mountie.mount ("none", "/tmp/.X11-unix", "", 61 | mountie.MS_REMOUNT | mountie.MS_BIND | mountie.MS_RDONLY) 62 | 63 | 64 | def entrypoint(): 65 | """Entry point for the imp-executor.""" 66 | 67 | # Check the command-line arguments. 68 | if (len (sys.argv) < 2): 69 | sys.exit ("imp-executor requires the operation to perform") 70 | 71 | operation = sys.argv[1] 72 | 73 | if operation == "help": 74 | print_help_message() 75 | elif operation == "rrfs": 76 | remount_root_shared() 77 | elif operation == "pstore": 78 | mount_pstore_filesystem() 79 | elif operation == "security": 80 | mount_security_filesystem() 81 | elif operation == "devshm": 82 | fix_dev_shm() 83 | elif operation == "wslg": 84 | remount_wslg() 85 | else: 86 | print ("imp-executor: operation not recognized") 87 | 88 | 89 | entrypoint() 90 | 91 | # End of file. 92 | -------------------------------------------------------------------------------- /binsrc/imp-executor/configuration.py: -------------------------------------------------------------------------------- 1 | ../shared/configuration.py -------------------------------------------------------------------------------- /binsrc/imp-executor/mountie.py: -------------------------------------------------------------------------------- 1 | # Mount/unmount helper functions 2 | 3 | import ctypes 4 | import ctypes.util 5 | import os 6 | 7 | 8 | # Mount flags 9 | MS_NONE = 0 # No flags. 10 | MS_RDONLY = 1 # Mount read-only. 11 | MS_NOSUID = 2 # Ignore suid and sgid bits. 12 | MS_NODEV = 4 # Disallow access to device special files. 13 | MS_NOEXEC = 8 # Disallow program execution. 14 | MS_SYNCHRONOUS = 16 # Writes are synced at once. 15 | MS_REMOUNT = 32 # Alter flags of a mounted filesystem. 16 | MS_MANDLOCK = 64 # Allow mandatory locks on an FS. 17 | MS_DIRSYNC = 128 # Directory modifications are synchronous. 18 | MS_NOSYMFOLLOW = 256 # Do not follow symlinks. 19 | MS_NOATIME = 1024 # Do not update access times. 20 | MS_NODIRATIME = 2048 # Do not update directory access times. 21 | MS_BIND = 4096 # Bind directory at different place. 22 | MS_MOVE = 8192 23 | MS_REC = 16384 24 | MS_SILENT = 32768 25 | MS_POSIXACL = 1 << 16 # VFS does not apply the umask. 26 | MS_UNBINDABLE = 1 << 17 # Change to unbindable. 27 | MS_PRIVATE = 1 << 18 # Change to private. 28 | MS_SLAVE = 1 << 19 # Change to slave. 29 | MS_SHARED = 1 << 20 # Set propagation type to shared. 30 | MS_RELATIME = 1 << 21 # Update atime relative to mtime/ctime. 31 | MS_KERNMOUNT = 1 << 22 # This is a kern_mount call. 32 | MS_I_VERSION = 1 << 23 # Update inode I_version field. 33 | MS_STRICTATIME = 1 << 24 # Always perform atime updates. 34 | MS_LAZYTIME = 1 << 25 # Update the on-disk [acm]times lazily. 35 | MS_ACTIVE = 1 << 30 36 | MS_NOUSER = 1 << 31 37 | 38 | 39 | # Unmount flags 40 | MU_NONE = 0 # No flags. 41 | # ... TODO: add these 42 | 43 | 44 | libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) 45 | libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_ulong, ctypes.c_char_p) 46 | libc.umount2.argtypes = (ctypes.c_char_p, ctypes.c_int) 47 | 48 | 49 | def mount (source: str, target: str, fs: str, flags: int = MS_NONE, options=''): 50 | """Mount a filesystem.""" 51 | ret = libc.mount(source.encode(), target.encode(), fs.encode(), flags, options.encode()) 52 | if ret < 0: 53 | errno = ctypes.get_errno() 54 | raise OSError(errno, f"Error mounting {source} ({fs}) on {target} with flags {flags} and options '{options}': {os.strerror(errno)}") 55 | 56 | 57 | def unmount (target: str, flags: int = MU_NONE): 58 | """Unmount a filesystem.""" 59 | ret = libc.umount2(target.encode(), flags) 60 | if ret < 0: 61 | errno = ctypes.get_errno() 62 | raise OSError(errno, f"Error unmounting {target}: {os.strerror(errno)}") 63 | -------------------------------------------------------------------------------- /binsrc/imp-executor/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkane-systems/bottle-imp/4218cc9275ff16c4906bf69553a9e51dc15cb58c/binsrc/imp-executor/requirements.txt -------------------------------------------------------------------------------- /binsrc/imp-generator/__main__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import os 4 | import sys 5 | 6 | from definitions import Generatee 7 | 8 | import genhelper 9 | import generatees 10 | 11 | def entrypoint(): 12 | """Entry point for the imp-generator.""" 13 | 14 | # Check the command-line arguments; test the resulting normal dir path. 15 | if (len (sys.argv) < 2): 16 | sys.exit ("imp-generator requires the path of the target directory") 17 | 18 | normal_dir = os.path.abspath(sys.argv[1]) 19 | 20 | if not os.path.exists(normal_dir): 21 | sys.exit ("generated-file directory must exist") 22 | 23 | # Create generator helper. 24 | gh = genhelper.GeneratorHelper(normal_dir) 25 | 26 | # Iterate through generatees. 27 | for g in generatees.generatees: 28 | g.generate(gh) 29 | 30 | 31 | entrypoint() 32 | 33 | # End of file. 34 | -------------------------------------------------------------------------------- /binsrc/imp-generator/configuration.py: -------------------------------------------------------------------------------- 1 | ../shared/configuration.py -------------------------------------------------------------------------------- /binsrc/imp-generator/definitions.py: -------------------------------------------------------------------------------- 1 | # Class definitions for the contents. 2 | 3 | from abc import abstractmethod 4 | import genhelper 5 | 6 | class Generatee: 7 | """A generatable item.""" 8 | @abstractmethod 9 | def generate (self, gh: genhelper.GeneratorHelper): 10 | pass 11 | 12 | class BinFmt (Generatee): 13 | """Defines a binary format.""" 14 | binfmt_name: str = None 15 | binfmt_text: str = None 16 | 17 | def __init__ (self, binfmt_name: str, binfmt_text: str): 18 | self.binfmt_name = binfmt_name 19 | self.binfmt_text = binfmt_text 20 | 21 | def generate (self, gh: genhelper.GeneratorHelper): 22 | """Generate the config file.""" 23 | gh.generate_binfmt (self.binfmt_name, self.binfmt_text) 24 | 25 | class EnableSystemUnit (Generatee): 26 | """Defines an existing system unit and the target to enable it for.""" 27 | unit_name: str = None 28 | target: str = None 29 | 30 | def __init__ (self, unit_name: str, target: str): 31 | self.unit_name = unit_name 32 | self.target = target 33 | 34 | def generate (self, gh: genhelper.GeneratorHelper): 35 | """Generate the config file.""" 36 | gh.enable_system_unit (self.unit_name, self.target) 37 | 38 | class ImpUnit (Generatee): 39 | """Defines a new systemd unit and the target to enable it for.""" 40 | unit_name: str = None 41 | unit_text: str = None 42 | target: str = None 43 | 44 | def __init__ (self, unit_name: str, target: str, unit_text: str): 45 | self.unit_name = unit_name 46 | self.unit_text = unit_text 47 | self.target = target 48 | 49 | def generate (self, gh: genhelper.GeneratorHelper): 50 | """Generate the config file.""" 51 | gh.generate_and_enable_imp_unit (self.unit_name, self.unit_text, self.target) 52 | 53 | class TmpFile (Generatee): 54 | """Defines a temporary file operations file.""" 55 | tmpfile_name: str = None 56 | tmpfile_text: str = None 57 | 58 | def __init__ (self, tmpfile_name: str, tmpfile_text: str): 59 | self.tmpfile_name = tmpfile_name 60 | self.tmpfile_text = tmpfile_text 61 | 62 | def generate (self, gh: genhelper.GeneratorHelper): 63 | """Generate the config file.""" 64 | gh.generate_tmpfile (self.tmpfile_name, self.tmpfile_text) 65 | 66 | class UnitOverride (Generatee): 67 | """Defines a unit override file and the unit to override.""" 68 | unit_name: str = None 69 | override_text: str = None 70 | 71 | def __init__ (self, unit_name: str, override_text: str): 72 | self.unit_name = unit_name 73 | self.override_text = override_text 74 | 75 | def generate (self, gh: genhelper.GeneratorHelper): 76 | """Generate the config file.""" 77 | gh.generate_override_conf (self.unit_name, self.override_text) 78 | -------------------------------------------------------------------------------- /binsrc/imp-generator/generatees.py: -------------------------------------------------------------------------------- 1 | # Module containing definitions of the systemd units. 2 | 3 | from definitions import * 4 | 5 | generatees = { 6 | ## Binfmts 7 | BinFmt("WSLInterop.conf", ":WSLInterop:M::MZ::/init:PF"), 8 | 9 | ## System units to enable 10 | EnableSystemUnit("systemd-machined.service", "multi-user.target"), 11 | 12 | ## Units 13 | ### imp-fixshm.service / bottle-imp - Fix the /dev/shm symlink to be a mount 14 | ImpUnit("imp-fixshm.service", 15 | "local-fs-pre.target", 16 | """# imp-generator 17 | 18 | [Unit] 19 | Description=bottle-imp - Fix the /dev/shm symlink to be a mount 20 | DefaultDependencies=no 21 | Before=local-fs-pre.target 22 | Before=imp-remount-root-shared.service 23 | Before=procps.service syslog.service systemd-firstboot.service systemd-sysctl.service systemd-sysusers.service systemd-tmpfiles-clean.service systemd-tmpfiles-setup-dev.service systemd-tmpfiles-setup.service 24 | ConditionPathExists=/dev/shm 25 | ConditionPathIsSymbolicLink=/dev/shm 26 | ConditionPathIsMountPoint=/run/shm 27 | 28 | [Service] 29 | Type=oneshot 30 | ExecStart=/usr/lib/bottle-imp/imp-executor devshm 31 | 32 | [Install] 33 | WantedBy=local-fs-pre.target 34 | """), 35 | 36 | ### imp-pstorefs.service / bottle-imp - Kernel Persistent Storage File System 37 | ImpUnit ("imp-pstorefs.service", 38 | "local-fs-pre.target", 39 | """# imp-generator 40 | 41 | [Unit] 42 | Description=bottle-imp - Kernel Persistent Storage File System 43 | DefaultDependencies=no 44 | Before=local-fs-pre.target 45 | Before=systemd-pstore.service 46 | ConditionPathExists=/sys/fs/pstore 47 | ConditionPathIsMountPoint=!/sys/fs/pstore 48 | 49 | [Service] 50 | Type=oneshot 51 | ExecStart=/usr/lib/bottle-imp/imp-executor pstore 52 | 53 | [Install] 54 | WantedBy=local-fs-pre.target 55 | """), 56 | 57 | ### imp-remount-root-shared.service / bottle-imp - Remount Root Filesystem Shared 58 | ImpUnit ("imp-remount-root-shared.service", 59 | "local-fs-pre.target", 60 | """# imp-generator 61 | 62 | [Unit] 63 | Description=bottle-imp - Remount Root Filesystem Shared 64 | DefaultDependencies=no 65 | Before=local-fs-pre.target 66 | Before=systemd-remount-fs.service 67 | 68 | [Service] 69 | Type=oneshot 70 | ExecStart=/usr/lib/bottle-imp/imp-executor rrfs 71 | 72 | [Install] 73 | WantedBy=local-fs-pre.target 74 | """), 75 | 76 | ### imp-securityfs.service / bottle-imp - Kernel Security File System 77 | ImpUnit ("imp-securityfs.service", 78 | "local-fs-pre.target", 79 | """# imp-generator 80 | 81 | [Unit] 82 | Description=bottle-imp - Kernel Security File System 83 | DefaultDependencies=no 84 | Before=local-fs-pre.target 85 | Before=apparmor.service 86 | ConditionSecurity=apparmor 87 | ConditionPathExists=/sys/kernel/security 88 | ConditionPathIsMountPoint=!/sys/kernel/security 89 | 90 | [Service] 91 | Type=oneshot 92 | ExecStart=/usr/lib/bottle-imp/imp-executor security 93 | 94 | [Install] 95 | WantedBy=local-fs-pre.target 96 | """), 97 | 98 | ### imp-wslg-socket.service / bottle-imp - WSLg socket remount service 99 | ImpUnit ("imp-wslg-socket.service", 100 | "multi-user.target", 101 | """# imp-generator 102 | 103 | [Unit] 104 | Description=bottle-imp - WSLg socket remount service 105 | After=tmp.mount 106 | After=systemd-tmpfiles-setup.service 107 | Before=multi-user.target 108 | ConditionPathExists=/tmp/.X11-unix 109 | ConditionPathIsMountPoint=!/tmp/.X11-unix 110 | ConditionPathExists=/mnt/wslg/.X11-unix 111 | 112 | [Service] 113 | Type=oneshot 114 | ExecStart=/usr/lib/bottle-imp/imp-executor wslg 115 | 116 | [Install] 117 | WantedBy=multi-user.target 118 | """), 119 | 120 | ## Tmpfiles 121 | 122 | ### imp-x11.conf 123 | TmpFile ("imp-x11.conf", 124 | """# imp-generator 125 | 126 | # Does what systemd does but does not automatically remove an existing 127 | # /tmp/.X11-unix (protect WSLg link). 128 | 129 | # Make sure these are created by default so that nobody else can 130 | d! /tmp/.X11-unix 1777 root root 10d 131 | D! /tmp/.ICE-unix 1777 root root 10d 132 | D! /tmp/.XIM-unix 1777 root root 10d 133 | D! /tmp/.font-unix 1777 root root 10d 134 | 135 | # Unlink the X11 lock files 136 | r! /tmp/.X[0-9]*-lock 137 | """), 138 | 139 | ## Override files. 140 | 141 | ### user-runtime-dir@.service.d/override.conf - fix up user runtime directory 142 | UnitOverride ("user-runtime-dir@.service", """# imp-generator 143 | 144 | [Service] 145 | ExecStart= 146 | ExecStart=/usr/lib/bottle-imp/imp-user-runtime-dir.sh start %i 147 | ExecStop= 148 | ExecStop=/usr/lib/bottle-imp/imp-user-runtime-dir.sh stop %i 149 | """), 150 | 151 | } 152 | -------------------------------------------------------------------------------- /binsrc/imp-generator/genhelper.py: -------------------------------------------------------------------------------- 1 | # Class of functions to write out the generated files. 2 | 3 | import os 4 | 5 | class GeneratorHelper: 6 | """A class of helpers for writing systemd generators.""" 7 | unit_dir = None 8 | binfmt_dir = '/run/binfmt.d' 9 | tmpfiles_dir = '/run/tmpfiles.d' 10 | 11 | 12 | def __init__ (self, unit_dir: str): 13 | self.unit_dir = unit_dir 14 | 15 | 16 | def enable_system_unit (self, unit_name: str, target: str): 17 | """Enable an existing system unit for a specific target.""" 18 | # Create the target directory if it does not already exist. 19 | target_dir = os.path.join (self.unit_dir, target + '.wants') 20 | if not os.path.isdir (target_dir): 21 | os.mkdir (target_dir) 22 | 23 | # Link to the system unit. 24 | unit_link = os.path.join (target_dir, unit_name) 25 | unit_file = os.path.join ('/lib/systemd/system', unit_name) 26 | 27 | if os.path.exists (unit_link): 28 | os.unlink (unit_link) 29 | 30 | os.symlink (unit_file, unit_link) 31 | 32 | 33 | def generate_binfmt(self, binfmt_name: str, binfmt_text: str): 34 | """Generate a dynamic binfmt.d format definition file.""" 35 | # Create the dynamic binfmt.d if it does not already exist. 36 | if not os.path.isdir (self.binfmt_dir): 37 | os.mkdir (self.binfmt_dir) 38 | 39 | # Write out the override file. 40 | with open (os.path.join (self.binfmt_dir, binfmt_name), 'w') as b: 41 | b.write (binfmt_text) 42 | 43 | 44 | def generate_and_enable_imp_unit (self, unit_name: str, unit_text: str, target: str = ''): 45 | """Generate an imp unit and enable it for a specified target.""" 46 | # Write out the unit file. 47 | unit_file = os.path.join (self.unit_dir, unit_name) 48 | 49 | with open (unit_file, 'w') as u: 50 | u.write (unit_text) 51 | 52 | # If a target was not specified, exit. 53 | if target == '': 54 | return 55 | 56 | # Create the target directory if it does not already exist. 57 | target_dir = os.path.join (self.unit_dir, target + '.wants') 58 | 59 | if not os.path.isdir (target_dir): 60 | os.mkdir (target_dir) 61 | 62 | # Link to the just-created unit. 63 | unit_link = os.path.join (target_dir, unit_name) 64 | 65 | if os.path.exists (unit_link): 66 | os.unlink (unit_link) 67 | 68 | os.symlink (unit_file, unit_link) 69 | 70 | 71 | def generate_override_conf (self, unit_name: str, override_text: str): 72 | """Generate an override configuration file for an existing systemd unit.""" 73 | # Create the override directory if it does not already exist. 74 | override_dir = os.path.join (self.unit_dir, unit_name + '.d') 75 | 76 | if not os.path.isdir (override_dir): 77 | os.mkdir (override_dir) 78 | 79 | # Write out the override file. 80 | with open (os.path.join(override_dir, 'override.conf'), 'w') as o: 81 | o.write (override_text) 82 | 83 | 84 | def generate_tmpfile(self, tmpfile_name: str, tmpfile_text: str): 85 | """Generate a tmpfiles.d operations file.""" 86 | # Create the dynamic tmpfiles.d if it does not already exist. 87 | if not os.path.isdir (self.tmpfiles_dir): 88 | os.mkdir (self.tmpfiles_dir) 89 | 90 | # Write out the override file. 91 | with open (os.path.join (self.tmpfiles_dir, tmpfile_name), 'w') as t: 92 | t.write (tmpfile_text) 93 | -------------------------------------------------------------------------------- /binsrc/imp-generator/requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/arkane-systems/bottle-imp/4218cc9275ff16c4906bf69553a9e51dc15cb58c/binsrc/imp-generator/requirements.txt -------------------------------------------------------------------------------- /binsrc/imp-wrapper/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This Makefile produces and packages the imp 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 imp.c -o imp 15 | endif 16 | 17 | ifeq ($(DEB_TARGET_ARCH),arm64) 18 | aarch64-linux-gnu-gcc imp.c -o imp 19 | endif 20 | 21 | # 22 | # clean: delete the package interim files 23 | # 24 | 25 | clean: 26 | rm -f imp 27 | -------------------------------------------------------------------------------- /binsrc/imp-wrapper/imp.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 IMP_LOGNAME environment variable. */ 15 | struct passwd * logname = getpwuid(getuid()); 16 | int result = setenv ("IMP_LOGNAME", logname->pw_name, 1); 17 | 18 | if (result != 0) 19 | { 20 | perror ("imp-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/bottle-imp/imp", argv); 30 | // execv("/bin/sh", argv); 31 | 32 | /* Reach here if execv failed */ 33 | perror("imp-wrapper"); 34 | return 1; 35 | } 36 | -------------------------------------------------------------------------------- /binsrc/imp-wrapper/test-as-root: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | sudo chown root:root imp 3 | sudo chmod 6755 imp 4 | -------------------------------------------------------------------------------- /binsrc/imp/__main__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | import argparse 4 | import os 5 | import signal 6 | import subprocess 7 | import sys 8 | import time 9 | 10 | import configuration 11 | import helpers 12 | 13 | # Global variables 14 | version = "1.00" 15 | 16 | verbose = False 17 | login = None 18 | 19 | # Command line parser 20 | def parse_command_line(): 21 | """Create the command-line option parser and parse arguments.""" 22 | parser = argparse.ArgumentParser( 23 | description = "Helper for using WSL systemd native support.", 24 | epilog = "For more information, see https://github.com/arkane-systems/bottle-imp/" 25 | ) 26 | 27 | # Version command 28 | parser.add_argument('-V', '--version', action='version', 29 | version='%(prog)s ' + version) 30 | 31 | # Verbose option 32 | parser.add_argument('-v', '--verbose', action='store_true', 33 | help="display verbose progress messages") 34 | 35 | # Specify username option 36 | parser.add_argument('-a', '--as-user', action='store', 37 | help="specify user to run shell or command as (use with -s or -c)", dest='user') 38 | 39 | # Commands 40 | group2 = parser.add_argument_group('commands') 41 | group = group2.add_mutually_exclusive_group(required=True) 42 | 43 | group.add_argument('-i', '--initialize', action='store_true', 44 | help='initialize WSL interop for user sessions and hold WSL open until explicit shutdown') 45 | group.add_argument('-s', '--shell', action='store_true', 46 | help='open or connect to a systemd user session, and run a shell in it') 47 | group.add_argument('-l', '--login', action='store_true', 48 | help='open a login prompt for a systemd user session') 49 | group.add_argument( 50 | '-c', '--command', help='open or connect to a systemd user session, and run the specified command in it\n(preserves working directory)', nargs=argparse.REMAINDER) 51 | group.add_argument('-u', '--shutdown', action='store_true', 52 | help='shut down systemd and the WSL instance') 53 | 54 | return parser.parse_args() 55 | 56 | 57 | # Subordinate functions. 58 | def wait_for_systemd(): 59 | """Check if systemd is in the running state, and if not, wait for it.""" 60 | 61 | # Unlike in genie, we can presume here that systemd does exist. 62 | # But it may not be accessible yet. Check for system dbus. 63 | if not os.path.exists('/run/dbus/system_bus_socket'): 64 | # wait for it 65 | print("imp: dbus is not available yet, please wait...", end="", flush=True) 66 | 67 | timeout = configuration.dbus_timeout() 68 | 69 | while not os.path.exists('/run/dbus/system_bus_socket'): 70 | time.sleep(1) 71 | print(".", end="", flush=True) 72 | 73 | timeout -= 1 74 | 75 | print("") 76 | 77 | if timeout <= 0: 78 | sys.exit("imp: dbus still not available; cannot continue") 79 | 80 | # Now dbus is available, check for systemd. 81 | state = helpers.get_systemd_state() 82 | 83 | if 'stopping' in state: 84 | sys.exit("imp: systemd is shutting down, cannot proceed") 85 | 86 | if 'initializing' in state or 'starting' in state: 87 | # wait for it 88 | print("imp: systemd is starting up, please wait...", end="", flush=True) 89 | 90 | timeout = configuration.systemd_timeout() 91 | 92 | while ('running' not in state and 'degraded' not in state) and timeout > 0: 93 | time.sleep(1) 94 | state = helpers.get_systemd_state() 95 | 96 | print(".", end="", flush=True) 97 | 98 | timeout -= 1 99 | 100 | print("") 101 | 102 | if timeout <= 0: 103 | print("imp: WARNING: timeout waiting for bottle to start") 104 | 105 | if 'degraded' in state: 106 | print('imp: WARNING: systemd is in degraded state, issues may occur!') 107 | print('imp: check for failed units with "systemctl --failed".') 108 | 109 | if not ('running' in state or 'degraded' in state): 110 | sys.exit("imp: systemd in unsupported state '" 111 | + state + "'; cannot proceed") 112 | 113 | 114 | # Commands 115 | def do_initialize(): 116 | """Initialize WSL_INTEROP, then fork a blocker, holding the session open until systemctl poweroff.""" 117 | wait_for_systemd() 118 | 119 | # Update the base environment with interop-fu. 120 | subprocess.run(['systemctl', 'import-environment', 121 | 'WSL_INTEROP', 'WSL2_GUI_APPS_ENABLED', 'WSL_DISTRO_NAME', 'WSLENV', 'NAME', 'HOSTTYPE']) 122 | 123 | # Run wait-forever subprocess. 124 | subprocess.Popen(['/usr/lib/bottle-imp/wait-forever.sh'], 125 | stdin=subprocess.DEVNULL, 126 | stdout=subprocess.DEVNULL, 127 | stderr=subprocess.DEVNULL, 128 | start_new_session = True, 129 | preexec_fn=(lambda: signal.signal(signal.SIGHUP, signal.SIG_IGN))) 130 | 131 | print ("imp: systemd environment initialized and instance holding") 132 | 133 | # Exit 134 | sys.exit(0) 135 | 136 | 137 | def do_login(): 138 | """Start a systemd login prompt.""" 139 | wait_for_systemd() 140 | 141 | if not helpers.get_systemd_machined_active(): 142 | sys.exit ("imp: cannot launch login; systemd-machined is not active") 143 | 144 | if verbose: 145 | print("imp: starting login prompt") 146 | 147 | os.execv ('/usr/bin/machinectl', ['machinectl', 'login', '.host']) 148 | # never get here 149 | 150 | 151 | def do_shell(): 152 | """Start/connect to a systemd user session with a shell.""" 153 | wait_for_systemd() 154 | 155 | if not helpers.get_systemd_machined_active(): 156 | sys.exit ("imp: cannot launch shell; systemd-machined is not active") 157 | 158 | if verbose: 159 | print("imp: starting shell") 160 | 161 | if helpers.get_in_windows_terminal(): 162 | os.execv ('/usr/bin/machinectl', ['machinectl', 163 | '-E', 'WT_SESSION=' + os.environ['WT_SESSION'], 164 | '-E', 'WT_PROFILE_ID=' + os.environ['WT_PROFILE_ID'], 165 | 'shell', '-q', login + '@.host']) 166 | else: 167 | os.execv ('/usr/bin/machinectl', ['machinectl', 168 | 'shell', '-q', login + '@.host']) 169 | 170 | # never get here 171 | 172 | 173 | def do_command(commandline): 174 | """Start/connect to a systemd user session with a command.""" 175 | wait_for_systemd() 176 | 177 | if not helpers.get_systemd_machined_active(): 178 | sys.exit ("imp: cannot launch command; systemd-machined is not active") 179 | 180 | if verbose: 181 | print("imp: running command " + ' '.join(commandline)) 182 | 183 | if len(commandline) == 0: 184 | sys.exit("imp: no command specified") 185 | 186 | if helpers.get_in_windows_terminal(): 187 | command = ['machinectl', 188 | '-E', 'WT_SESSION=' + os.environ['WT_SESSION'], 189 | '-E', 'WT_PROFILE_ID=' + os.environ['WT_PROFILE_ID'], 190 | 'shell', '-q', login + '@.host', '/usr/bin/env', '-C', os.getcwd()] + commandline; 191 | else: 192 | command = ['machinectl', 193 | 'shell', '-q', login + '@.host', '/usr/bin/env', '-C', os.getcwd()] + commandline; 194 | 195 | os.execv ('/usr/bin/machinectl', command) 196 | 197 | 198 | def do_shutdown(): 199 | """Shut down systemd and the WSL instance.""" 200 | wait_for_systemd() 201 | 202 | if verbose: 203 | print ("imp: shutting down WSL instance") 204 | 205 | os.execv('/usr/bin/systemctl', ['systemctl', 'poweroff']) 206 | 207 | 208 | # Entrypoint 209 | def entrypoint(): 210 | """Entrypoint of the application.""" 211 | global verbose 212 | global login 213 | 214 | helpers.prelaunch_checks() 215 | configuration.load() 216 | arguments = parse_command_line() 217 | 218 | # Set globals. 219 | verbose = arguments.verbose 220 | login = helpers.get_login_session_user() 221 | 222 | # Check user 223 | if arguments.user is not None: 224 | 225 | # Abort if user specified and not -c or -s 226 | if not (arguments.shell or (arguments.command is not None)): 227 | sys.exit( 228 | "imp: error: argument -a/--as-user can only be used with -c/--command or -s/--shell") 229 | 230 | # Check if arguments.user is a real user 231 | helpers.validate_is_real_user(arguments.user) 232 | 233 | login = arguments.user 234 | 235 | if verbose: 236 | print(f"imp: executing as user {login}") 237 | 238 | # Decide what to do. 239 | if arguments.initialize: 240 | do_initialize() 241 | elif arguments.shell: 242 | do_shell() 243 | elif arguments.login: 244 | do_login() 245 | elif arguments.shutdown: 246 | do_shutdown() 247 | elif arguments.command is not None: 248 | do_command(arguments.command) 249 | else: 250 | sys.exit("imp: impossible argument - how did we get here?") 251 | 252 | 253 | entrypoint() 254 | 255 | # End of file. 256 | -------------------------------------------------------------------------------- /binsrc/imp/configuration.py: -------------------------------------------------------------------------------- 1 | ../shared/configuration.py -------------------------------------------------------------------------------- /binsrc/imp/helpers.py: -------------------------------------------------------------------------------- 1 | # Helper functions module 2 | 3 | import os 4 | import subprocess 5 | import sys 6 | import psutil 7 | import pwd 8 | 9 | 10 | def get_in_windows_terminal(): 11 | """Are we inside a Windows Terminal session?""" 12 | if 'WT_SESSION' in os.environ: 13 | return True 14 | else: 15 | return False 16 | 17 | 18 | def get_login_session_user(): 19 | """Get the user logged into the current session, pre-setuid.""" 20 | # This environment variable is set by the setuid wrapper. 21 | return os.environ["IMP_LOGNAME"] 22 | 23 | 24 | def get_systemd_state(): 25 | """Get the systemd state.""" 26 | sc = subprocess.run(["systemctl", "is-system-running"], 27 | capture_output=True, text=True) 28 | return sc.stdout.rstrip() 29 | 30 | 31 | def get_systemd_machined_active(): 32 | """Get the systemd-machined service state.""" 33 | sc = subprocess.run(["systemctl", "is-active", "systemd-machined.service", "--quiet"]) 34 | return (sc.returncode == 0) 35 | 36 | 37 | def prelaunch_checks(): 38 | """Check that we are on the correct platform, and as the correct user.""" 39 | 40 | # Is this Linux? 41 | if not sys.platform.startswith('linux'): 42 | sys.exit("imp: not executing on the Linux platform - how did we get here?") 43 | 44 | # Is this WSL 1? 45 | root_type = list(filter(lambda x: x.mountpoint == '/', 46 | psutil.disk_partitions(all=True)))[0].fstype 47 | if root_type == 'lxfs' or root_type == 'wslfs': 48 | sys.exit("imp: systemd is not supported under WSL 1.") 49 | 50 | # Is this WSL 2? 51 | if not os.path.exists('/run/WSL'): 52 | if 'microsoft' not in os.uname().release: 53 | sys.exit("imp: not executing under WSL 2 - how did we get here?") 54 | 55 | # Is systemd already running as pid 1? 56 | if not psutil.Process(1).name() == 'systemd': 57 | sys.exit("imp: systemd support does not appear to be enabled.") 58 | 59 | # Are we effectively root? 60 | if os.geteuid() != 0: 61 | sys.exit("imp: must execute as root - has the setuid bit gone astray?") 62 | 63 | 64 | def validate_is_real_user(username): 65 | """Check that the supplied username is a real user; otherwise exit.""" 66 | try: 67 | pwd.getpwnam(username) 68 | except KeyError: 69 | sys.exit("imp: specified user does not exist") 70 | -------------------------------------------------------------------------------- /binsrc/imp/requirements.txt: -------------------------------------------------------------------------------- 1 | # psutil>=5.9.0 (should be handled by apt package) 2 | -------------------------------------------------------------------------------- /binsrc/shared/configuration.py: -------------------------------------------------------------------------------- 1 | # Configuration data module 2 | 3 | import configparser 4 | 5 | # Global variables 6 | 7 | _config = None 8 | 9 | 10 | # functions 11 | def dbus_timeout(): 12 | """Return the configured timeout for the system dbus socket appearing.""" 13 | return _config.getint('imp', 'dbus-timeout', fallback=240) 14 | 15 | 16 | def systemd_timeout(): 17 | """Return the configured timeout for systemd to enter the running state.""" 18 | return _config.getint('imp', 'systemd-timeout', fallback=240) 19 | 20 | 21 | # Initialization 22 | def load(): 23 | """Load the configuration from the config file ('/etc/imp.ini').""" 24 | global _config 25 | 26 | _config = configparser.ConfigParser() 27 | _config.read('/etc/imp.ini') 28 | -------------------------------------------------------------------------------- /debian/bottle-imp.lintian-overrides: -------------------------------------------------------------------------------- 1 | # Must be setuid. 2 | bottle-imp: elevated-privileges 6755 root/root [usr/bin/imp] 3 | -------------------------------------------------------------------------------- /debian/bottle-imp.manpages: -------------------------------------------------------------------------------- 1 | othersrc/docs/imp.8 -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | bottle-imp (1.0) buster bullseye bookworm sid focal jammy; urgency=medium 2 | 3 | * Change over to generator-based model. 4 | * Added unit prependencies to avoid race conditions (fixes #33). 5 | * Reduced libc version requirement to ease buster install issues. 6 | * Move unit functions into imp-executor. 7 | * Move filesystem mounts to precede local-fs-pre.target. 8 | * Move rather than remake /dev/shm; bind-mount /run/shm.. 9 | * Moved WSLInterop binfmt into generator. 10 | * Added modified X11 regenerating tmpfile.d to generator. 11 | 12 | -- Alistair Young Fri, 25 Nov 2022 15:00:0o -0500 13 | 14 | bottle-imp (0.13) buster bullseye bookworm sid focal jammy; urgency=medium 15 | 16 | * See 0.12. 17 | 18 | -- Alistair Young Tue, 1 Nov 2022 09:00:01 -0500 19 | 20 | bottle-imp (0.12) buster bullseye bookworm sid focal jammy; urgency=medium 21 | 22 | * Fix issue breaking -s and -c on systemd < 250 (fixes #28). 23 | * Add configuration file for timeouts. 24 | 25 | -- Alistair Young Tue, 1 Nov 2022 09:00:00 -0500 26 | 27 | bottle-imp (0.11) buster bullseye bookworm sid focal jammy; urgency=medium 28 | 29 | * Added various environment variables to the passthrough list. 30 | * Pass through Windows Terminal variables on shell/command. 31 | * Add shutdown helper command. 32 | * Added bugs section to documentation. 33 | 34 | -- Alistair Young Mon, 31 Oct 2022 15:00:00 -0500 35 | 36 | bottle-imp (0.10) buster bullseye bookworm sid focal jammy; urgency=medium 37 | 38 | * Added fix for /dev/shm / /run/shm inversion. 39 | * Renamed systemd services with imp- prefix. 40 | * Updated documentation. 41 | 42 | -- Alistair Young Sat, 29 Oct 2022 19:00:00 -0500 43 | 44 | bottle-imp (0.9) buster bullseye bookworm sid focal jammy; urgency=medium 45 | 46 | * Handle user runtime directories other than uid=1000. 47 | 48 | -- Alistair Young Mon, 24 Oct 2022 14:00:00 -0500 49 | 50 | bottle-imp (0.8) buster bullseye bookworm sid focal jammy; urgency=medium 51 | 52 | * Added remounting of / as shared (container mode loss). 53 | 54 | -- Alistair Young Fri, 07 Oct 2022 18:00:00 -0500 55 | 56 | bottle-imp (0.7) buster bullseye bookworm sid focal jammy; urgency=medium 57 | 58 | * 0.68.2 - remove the tmpfiles config. 59 | * 0.68.2 - user runtime directory handling adapted to pre-mapping. 60 | * 0.68.2 - modified .X11-unix mounting routine. 61 | 62 | -- Alistair Young Tue, 29 Sep 2022 13:00:00 -0500 63 | 64 | bottle-imp (0.6) buster bullseye bookworm sid focal jammy; urgency=medium 65 | 66 | * Additions to tmpfiles for things disabled by systemd support. 67 | * Build package using XZ compression when using CI. 68 | * Do not use dh_installsystemd, it fucks up. 69 | * Mount .X11-unix read-only. 70 | * Add compensatory tmpfiles entries (/var/tmp, other X folders). 71 | 72 | -- Alistair Young Tue, 27 Sep 2022 23:00:00 -0500 73 | 74 | bottle-imp (0.5) buster bullseye bookworm sid focal jammy; urgency=medium 75 | 76 | * Added amd64 Arch package. 77 | * Added clarifying tarball instructions to readme. 78 | * Conformed service enablers to standard practice. 79 | * Added existence condition check to pstorefs/securityfs mounters. 80 | * Converted X11 symlink to bind mount. 81 | * Fixed assorted errors in the README.md. (Thanks to @mangkoran for proofreading.) 82 | 83 | -- Alistair Young Tue, 27 Sep 2022 11:00:00 -0500 84 | 85 | bottle-imp (0.4) buster bullseye bookworm sid focal jammy; urgency=medium 86 | 87 | * Added arm64 build for Debian. 88 | * Added amd64/arm64 tarballs. 89 | * Added additional Debian targets (also Ubuntu). 90 | * Wait for dbus to be up before checking systemd status. 91 | * Force-enable systemd-machined. 92 | 93 | -- Alistair Young Sun, 25 Sep 2022 01:00:00 -0500 94 | 95 | bottle-imp (0.3) bullseye bookworm sid; urgency=medium 96 | 97 | * First (alpha) public version. 98 | 99 | -- Alistair Young Fri, 23 Sep 2022 12:00:00 -0500 100 | 101 | bottle-imp (0.2) bullseye bookworm sid; urgency=medium 102 | 103 | * Updated dependencies of pstorefs.service. 104 | * Set up services for auto-enable. 105 | * Added prototype version of imp executable. 106 | 107 | -- Alistair Young Thu, 22 Sep 2022 19:00:00 -0500 108 | 109 | bottle-imp (0.1) bullseye bookworm sid; urgency=medium 110 | 111 | * Initial data-only hack. 112 | 113 | -- Alistair Young Thu, 22 Sep 2022 18:00:00 -0500 114 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: bottle-imp 2 | Section: contrib/misc 3 | Priority: optional 4 | Maintainer: Alistair Young 5 | Build-Depends: debhelper-compat (= 13) 6 | Standards-Version: 4.5.0 7 | Homepage: https://github.com/arkane-systems/bottle-imp 8 | Vcs-Git: https://github.com/arkane-systems/bottle-imp.git 9 | Vcs-Browser: https://github.com/arkane-systems/bottle-imp 10 | 11 | Package: bottle-imp 12 | Architecture: amd64 arm64 13 | Depends: ${misc:Depends}, ${shlibs:Depends}, libc6 (>= 2.28), systemd (>= 232-25), python3 (>= 3.7), python3-pip, python3-psutil, systemd-container (>= 232-25) 14 | Conflicts: systemd-genie 15 | Description: helper for native systemd support under WSL 16 | Fixes some secondary facilities and allows easy access to a user session 17 | when systemd is run under Windows Subsystem for Linux using its native 18 | support. A spinoff of system-genie. 19 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Source: https://github.com/arkane-systems/bottle-imp 3 | Upstream-Contact: Alistair Young 4 | Upstream-Name: bottle-imp 5 | 6 | Files: * 7 | Copyright: 2018-2022 Alistair Young 8 | License: Unlicense 9 | 10 | License: Unlicense 11 | This is free and unencumbered software released into the public domain. 12 | . 13 | Anyone is free to copy, modify, publish, use, compile, sell, or 14 | distribute this software, either in source code form or as a compiled 15 | binary, for any purpose, commercial or non-commercial, and by any 16 | means. 17 | . 18 | In jurisdictions that recognize copyright laws, the author or authors 19 | of this software dedicate any and all copyright interest in the 20 | software to the public domain. We make this dedication for the benefit 21 | of the public at large and to the detriment of our heirs and 22 | successors. We intend this dedication to be an overt act of 23 | relinquishment in perpetuity of all present and future rights to this 24 | software under copyright law. 25 | . 26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 29 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 30 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 31 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 32 | OTHER DEALINGS IN THE SOFTWARE. 33 | . 34 | For more information, please refer to 35 | 36 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | othersrc/docs/LICENSE.md 2 | othersrc/docs/README.md 3 | -------------------------------------------------------------------------------- /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 -Ximp 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 | 34 | override_dh_builddeb: 35 | dpkg-deb -Zxz --build debian/bottle-imp .. 36 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (native) 2 | 3 | -------------------------------------------------------------------------------- /othersrc/docs/LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /othersrc/docs/README.md: -------------------------------------------------------------------------------- 1 | # bottle-imp 2 | 3 | [ ![ci](https://github.com/arkane-systems/bottle-imp/workflows/ci/badge.svg?branch=master) ](https://github.com/arkane-systems/bottle-imp/actions?query=workflow%3Aci+branch%3Amaster) 4 | 5 | ## A helper for using WSL's native systemd support 6 | 7 | _bottle-imp_ is a spinoff of _[systemd-genie](http://github.com/arkane-systems/genie)_ 8 | to supplement WSL's [new built-in systemd support](https://devblogs.microsoft.com/commandline/systemd-support-is-now-available-in-wsl/). 9 | 10 | **Why is this necessary?** 11 | 12 | Well, awesome as the native _systemd_ support is, there are some things it 13 | doesn't do, and others that it does in notably different ways from _genie_ 14 | or other existing _systemd_ solutions. For more information, you can see my 15 | [migrating from systemd-genie to native WSL systemd](https://randombytes.substack.com/p/migrating-from-systemd-genie-to-native) 16 | article here. 17 | 18 | **What does it do, exactly?** 19 | 20 | The following features are provided by _bottle-imp_ (see the above article for more in-depth explanations): 21 | 22 | * Ensures that _securityfs_ (needed for AppArmor and LSMs) and _pstorefs_ (for debugging panics) are mounted. 23 | * Ensures that the shared memory area filesystem is mounted at _/dev/shm_ and _/run/shm_, not vice versa. 24 | * Ensures that the root file system is mounted with shared propagation. 25 | * Ensures that the bind mount of `/tmp/.X11-unix/X0`, which makes WSLg work, is restored after _systemd_ clears out `/tmp`. 26 | * Ensures that WSL interop is working, even after _systemd_ rebuilds the binfmts. 27 | * Ensures that the WSLg-created user runtime directory is mounted for the appropriate user, and is not mounted for other users. 28 | * Makes sure _systemd_ is up and running before proceeding. 29 | * Keeps the WSL instance running even when you have no active sessions. 30 | 31 | and the big one 32 | 33 | * Creates a login session for you, along with a user _systemd_ and a session dbus. 34 | 35 | ## REQUIREMENTS 36 | 37 | First, obviously, if you were previously a user of _systemd-genie_ or one of the other systemd solutions, uninstall it _before_ attempting to set up native _systemd_ support or _bottle-imp_. 38 | 39 | It is a good idea to set your _systemd_ default target to _multi-user.target_ before enabling _systemd_ native support. 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. 40 | 41 | If you are using a custom kernel for WSL, it should comply with the suggested means of detecting WSL given in [microsoft/WSL#423](https://github.com/microsoft/WSL/issues/423) - 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". 42 | 43 | Obviously, since native _systemd_ support only works under WSL 2, the same can be said for _imp_. 44 | 45 | Some _systemd_ units were problematic for various reasons under _genie_, and continue to be so under native support. 46 | A list of common problematic units and solutions [is available here](https://randombytes.substack.com/p/problematic-systemd-units-under-wsl). 47 | 48 | ## INSTALLATION 49 | 50 | If there is a package available for your distribution, this is the recommended method of installing _bottle-imp_. 51 | 52 | ### Debian 53 | Dependent packages on Debian are _libc6_ (>= 2.34), _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. 54 | 55 | To install, add the wsl-translinux repository here by following the instructions here: 56 | 57 | https://arkane-systems.github.io/wsl-transdebian/ 58 | 59 | then install _bottle-imp_ using the commands: 60 | 61 | ```bash 62 | sudo apt update 63 | sudo apt install -y bottle-imp 64 | ``` 65 | 66 | ### Arch 67 | 68 | An Arch package (.zst) for amd64 can be downloaded from the releases, to right. Install it manually, using `pacman -U `. 69 | 70 | ### Other Distros 71 | 72 | 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. 73 | 74 | Debian is the "native" distribution for _bottle-imp_, for which read, "what the author uses". Specifically, Debian bullseye+, with _usrmerge_ installed. 75 | 76 | #### TAR 77 | 78 | There is a .tar.gz of a complete _bottle-imp_ 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. 79 | 80 | You should use the _-p_ flag when untarring this release to preserve file permissions and the setuid flag on `/usr/bin/imp`. As some versions of _tar(1)_ always remove the high bits, you should also check the setuid status of `/usr/bin/imp` after installing. 81 | 82 | ## USAGE 83 | 84 | ``` 85 | usage: imp [-h] [-V] [-v] [-a USER] (-i | -s | -l | -c ...) 86 | 87 | Helper for using WSL systemd native support. 88 | 89 | options: 90 | -h, --help show this help message and exit 91 | -V, --version show program's version number and exit 92 | -v, --verbose display verbose progress messages 93 | -a USER, --as-user USER 94 | specify user to run shell or command as (use with -s or -c) 95 | 96 | commands: 97 | -i, --initialize initialize WSL interop for user sessions and hold WSL open until explicit shutdown 98 | -s, --shell open or connect to a systemd user session, and run a shell in it 99 | -l, --login open a login prompt for a systemd user session 100 | -c ..., --command ... 101 | open or connect to a systemd user session, and run the specified command within it (preserves working directory) 102 | -u, --shutdown shut down systemd and the WSL instance 103 | 104 | For more information, see https://github.com/arkane-systems/bottle-imp/ 105 | ``` 106 | 107 | There are four primary commands available in _bottle-imp_. 108 | 109 | _imp -i_ should be run first to set up your WSL instance. It has two effects (apart from waiting for systemd to be ready); it copies the necessary information to ensure that Windows interoperability works inside _systemd_-managed login sessions and even services, and it starts a lifetime-running process to ensure that the WSL instance does not terminate even when you have no interactive sessions open. 110 | 111 | **NOTE:** For technical reasons, it is not currently possible to separate these functions; you can't have reliable Windows interop without the lifetime-running process. 112 | 113 | **NOTE 2:** The below commands will still work even if you do not run `imp -i`; however, Windows interop will not function inside _systemd_-managed sessions, and the WSL instance will idle-terminate as soon as there are no interactive sessions (technically defined as processes that are children of the Microsoft _init_) running. 114 | 115 | _imp -s_ runs your login shell inside a _systemd_ login session; basically, Windows-side, `wsl imp -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. 116 | 117 | _imp -c [command]_ runs _command_ inside a _systemd_ login session, then exits. It follows _sudo_ semantics, and so does preserve the cwd. 118 | 119 | With either of the above, the _imp -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, _imp -a bongo -s_ would start a shell as the user _bongo_. 120 | 121 | _imp -l_ opens a login prompt. This permits you to log in to the WSL distribution via _systemd_ 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. 122 | 123 | _imp -u_ will shut down _systemd_ cleanly and exit the WSL instance. This uses the _systemctl poweroff_ command to 124 | simulate a normal Linux system shutting down. It is suggested that this be used before shutting down the Windows machine or force-terminating WSL to ensure a clean shutdown of _systemd_ services. 125 | 126 | Shutting down the WSL instance in this way causes it to exit completely. You should wait for the instance to show as stopped before attempting to restart it or execute further commands inside it. 127 | 128 | ### Configuration file 129 | 130 | While one is not supplied by default, a configuration file can be created at `/etc/imp.ini`, with contents similar to the following: 131 | 132 | ``` 133 | [imp] 134 | dbus-timeout=240 135 | systemd-timeout=240 136 | ``` 137 | 138 | The _*-timeout_ settings control how long _imp_ will wait for the system dbus socket to be available, and how long, once it is, _imp_ will wait for _systemd_ to enter either the running or degraded state before giving up. Under normal circumstances, there is no need to set either of these, since _imp_ will move immediately to the next state when it is ready. However, if engaged in extensive _systemd_ debugging or if one is using a particularly slow machine, these timeouts can be controlled here. 139 | 140 | ## BUGS 141 | 142 | 1. Using _imp_ to create a session is required for the user login session (and its concomitants, such as a user _systemd_ instance and a session dbus) to be created properly. Simply starting a process with _wsl_ (or using a Linux GUI app shortcut) does not do this, although the problem is less serious than with _genie_, since the process will still be started with _systemd_ as pid 1. 143 | 144 | For information about starting Visual Studio Code remote sessions in login sessions, see https://github.com/arkane-systems/bottle-imp/discussions/19 . 145 | 146 | 2. While the Windows Terminal environment variables, WT_SESSION and WT_PROFILE_ID, will be passed through to shell and command prompt invocations of _imp_, they will not be passed through to login sessions created with _imp -l_, due to a limitation in _machinectl_. 147 | 148 | 3. _imp_ requires the python package _psutil_, which due to technical limitations of _zipapp_ can't be wrapped into the _imp_ executable. As such, _imp_ depends on this package for the system _python_. If you are inside another python environment, _imp_ may fail unless you install the _psutil_ package into this environment also. 149 | -------------------------------------------------------------------------------- /othersrc/docs/imp.8: -------------------------------------------------------------------------------- 1 | .Dd 09/23/22 2 | .Dt imp 8 3 | .Os Linux 4 | .Sh NAME 5 | .Nm imp 6 | .Nd A helper for Windows Subsystem for Linux's native systemd support. 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op -h 10 | .Op -V 11 | .Op -v 12 | .Op -a 13 | .Ar user 14 | .Op -i 15 | .Op -s 16 | .Op -l 17 | .Op -u 18 | .Op -c 19 | .Ar command... 20 | .Sh DESCRIPTION 21 | .Nm 22 | provides a means of running inside a 23 | .Xr systemd 1 24 | user session, with full support for user 25 | .Xr systemd 1 26 | , session dbus, and Windows interoperability. 27 | .Pp 28 | .Bl -tag -width "-c ..., --command ..." 29 | .It Fl h, -help 30 | Prints a short help text and exits. 31 | .It Fl V, -version 32 | Prints the installed imp version and exits. 33 | .It Fl v, -verbose 34 | Causes any other command to print the details of the operations it is 35 | performing as it goes along. Useful mostly for debugging. 36 | .It Fl a, -as-user 37 | Permits a user to be specified (by name) to execute as when using the -c/--command 38 | or -s/--shell commands. 39 | .It Fl i, -initialize 40 | Sets up the needful for Windows interoperability support, and starts the 41 | lifetime-running process to preserve the current WSL instance. It should 42 | be run before any of the other 43 | .Nm 44 | commands. 45 | .It Fl s, -shell 46 | Runs your login shell within a systemd login session. It is intended as 47 | the standard way to start a shell within a distribution with 48 | .Nm 49 | installed. 50 | .Pp 51 | This follows login semantics, and as such does not preserve the current 52 | working directory. 53 | .It Fl l, -login 54 | Opens a login prompt. This permits you to log in to the WSL distribution 55 | as any user. The login prompt will return when you log out; to terminate 56 | the session, press ^] three times within one second. 57 | .Pp 58 | This follows login semantics, and as such does not preserve the current 59 | working directory. 60 | .It Fl c, -command 61 | Runs the specified command inside a systemd login session. It is 62 | intended as the standard way to run arbitrary commands within a 63 | distribution with 64 | .Nm 65 | installed. 66 | .Pp 67 | Unlike the other options, this preserves the current working directory. 68 | .It Fl u, -shutdown 69 | Shuts down 70 | .Xr systemd 1 71 | cleanly and exits the WSL instance. This uses the 72 | .Ar systemctl poweroff 73 | command to simulate a normal Linux system shutting down. It is suggested that 74 | this be used before shutting down the Windows machine or force-terminating WSL 75 | to ensure a clean shutdown of 76 | .Xr systemd 1 77 | services. 78 | .Pp 79 | Shutting down the WSL instance in this way causes it to exit 80 | .Ar completely. 81 | You should wait for the instance to show as stopped before attempting to restart it 82 | or execute further commands inside it. 83 | .El 84 | .Sh EXIT STATUS 85 | .Nm 86 | maintains a policy of returning zero on success, and non-zero when an error 87 | occurs. 88 | .Sh NOTES 89 | .Nm 90 | can only be used within a WSL 2 distribution, since 91 | .Xr systemd 1 92 | can only be run within a WSL 2 distribution. WSL 1 does not implement the 93 | system calls required to support it. 94 | .Pp 95 | .Nm 96 | serves no purpose on Linux running outside of the WSL environment, or 97 | within other containers. Its behavior if run in such environments is 98 | undefined. 99 | .Sh SEE ALSO 100 | .Xr systemctl 1 , 101 | .Xr systemd 1 , 102 | .Xr bootup 7 , 103 | .Xr systemd-machined 8 , 104 | .Sh BUGS 105 | 1. Using 106 | .Nm 107 | to create a session is required for the user login session (and its concomitants, such as a user 108 | .Xr systemd 1 109 | instance and a session dbus) to be created properly. Simply starting a process with 110 | .Ar wsl 111 | (or using a Linux GUI app shortcut) does not do this, although the problem is less serious than with 112 | .Ar genie 113 | , since the process will still be started with 114 | .Xr systemd 1 115 | as pid 1. 116 | .Pp 117 | 2. While the Windows Terminal environment variables, WT_SESSION and WT_PROFILE_ID, will be passed 118 | through to shell and command prompt invocations of 119 | .Nm 120 | , they will not be passed through to login sessions created with 121 | .Ar imp -l 122 | , due to a limitation in 123 | .Xr machinectl 1 124 | . 125 | .Pp 126 | 3. 127 | .Nm 128 | requires the Python package 129 | .Ar psutil 130 | , which due to technical limitations of 131 | .Ar zipapp 132 | can't be wrapped into the 133 | .Nm 134 | executable. As such, 135 | .Nm 136 | depends on this package for the system Python. If you are inside another Python environment, 137 | .Nm 138 | may fail unless you install the 139 | .Ar psutil 140 | package into this environment also. 141 | .Pp 142 | If you feel you have found a bug in 143 | .Nm 144 | please submit a bug report at 145 | .Ar http://github.com/arkane-systems/bottle-imp/issues 146 | -------------------------------------------------------------------------------- /othersrc/scripts/imp-user-runtime-dir.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -d /mnt/wslg/runtime-dir ] 4 | then 5 | # WSLg is not present, so default to doing the standard thing. 6 | /lib/systemd/systemd-user-runtime-dir $1 $2 7 | exit 8 | fi 9 | 10 | # Get the UID of the WSLg runtime directory. 11 | WSLGUID=$(stat -c "%u" /mnt/wslg/runtime-dir) 12 | 13 | if [ "$1" = "start" ] 14 | then 15 | # Setting up runtime dir. 16 | # At this point, the WSLg runtime dir will be mounted at this point anyway; 17 | # regardless of UID. 18 | if [ $2 -eq $WSLGUID ] 19 | then 20 | # We are the WSLg user, so leave the status quo. 21 | exit 22 | fi 23 | 24 | # Otherwise, unmount the runtime dir, and then default to the standard. 25 | /bin/umount /run/user/$2 26 | /lib/systemd/systemd-user-runtime-dir $1 $2 27 | exit 28 | fi 29 | 30 | if [ $1 = "stop" ] 31 | then 32 | # Unsetting up runtime dir. 33 | if [ $2 -eq $WSLGUID ] 34 | then 35 | # We are the WSLg user, so leave the status quo. 36 | exit 37 | fi 38 | 39 | # Otherwise, default to the standard. 40 | /lib/systemd/systemd-user-runtime-dir $1 $2 41 | fi 42 | -------------------------------------------------------------------------------- /othersrc/scripts/wait-forever.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | while true 3 | do 4 | sleep 1h 5 | done 6 | --------------------------------------------------------------------------------