├── .gitattributes ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── cd.yml │ └── ci.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── DEVELOPERS.md ├── LICENSE ├── LICENSES.md ├── README.md ├── build.bat ├── res ├── Alma │ └── icon.ico ├── Alpine │ └── icon.ico ├── Arch │ └── icon.ico ├── Artix │ └── icon.ico ├── BioArch │ └── icon.ico ├── CentOS │ └── icon.ico ├── Clear │ └── icon.ico ├── Debian │ └── icon.ico ├── Deepin │ └── icon.ico ├── Devuan │ └── icon.ico ├── Elementary │ └── icon.ico ├── EndeavourOS │ └── icon.ico ├── Fedora │ └── icon.ico ├── Gentoo │ └── icon.ico ├── Kali │ ├── .icon-src │ │ ├── kali-dragon-icon.svg │ │ ├── kali-logo.svg │ │ └── merge.png │ └── icon.ico ├── Manjaro │ └── icon.ico ├── Mint │ └── icon.ico ├── Openmandriva │ └── icon.ico ├── Oracle │ └── icon.ico ├── Pengwin │ └── icon.ico ├── Puppy │ └── icon.ico ├── Rhino │ └── icon.ico ├── Rocky │ └── icon.ico ├── Slackware │ └── icon.ico ├── Solus │ └── icon.ico ├── Tails │ └── icon.ico ├── Ubuntu │ └── icon.ico ├── Venom │ └── icon.ico ├── Void │ └── icon.ico └── openSUSE │ └── icon.ico └── src ├── backup ├── backup.go ├── command.go └── help.go ├── clean ├── clean.go ├── command.go └── help.go ├── config ├── command.go └── help.go ├── get ├── command.go ├── get.go └── help.go ├── go.mod ├── go.sum ├── help ├── command.go └── help.go ├── install ├── command.go ├── help.go ├── install.go └── repair.go ├── isregd └── command.go ├── lib ├── cmdline │ ├── cmdline.go │ └── model_command.go ├── preset │ ├── model_preset.go │ └── preset.go ├── utils │ ├── download.go │ └── utils.go └── wtutils │ ├── model_config.go │ └── wtutils.go ├── main.go ├── run ├── command.go ├── help.go ├── repair.go └── run.go ├── version ├── command.go └── version.go └── versioninfo.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.sh text eol=lf 3 | *.bat text eol=crlf 4 | *.cmd text eol=crlf 5 | *.png -text 6 | *.jpg -text 7 | *.ico -text -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [yuk7] 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/src" 5 | schedule: 6 | interval: "weekly" -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Deployment 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | publish-github: 10 | name: Publish on Github 11 | runs-on: windows-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Build 15 | run: | 16 | $Env:PATH = "${Env:USERPROFILE}\go\bin;${Env:PATH}" 17 | $version = ${Env:GITHUB_REF}.Replace("refs/tags/","") 18 | $Env:GO_BUILD_OPTS = "-ldflags `"-w -s -X github.com/yuk7/wsldl/version.version=${version}`"" 19 | $Env:PATH = "${Env:USERPROFILE}\go\bin;${Env:PATH}" 20 | 21 | echo AMD64 build 22 | .\build.bat all 23 | move out out_amd64 24 | 25 | echo ARM64 build 26 | $Env:GOARCH="arm64" 27 | .\build.bat all 28 | move out out_arm64 29 | - name: Create sha256sums, zip icons and rename 30 | run: | 31 | mkdir out 32 | echo "creating amd64 artifacts" 33 | move .\out_amd64\wsldl.exe .\out\wsldl.exe 34 | Get-ChildItem .\out_amd64\icons -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { $_.hash.toLower() + " " + ($_.path | Split-Path -Leaf) } | Tee-Object -FilePath .\out_amd64\icons\sha256sums.txt 35 | Compress-Archive -Path .\out_amd64\icons\* -DestinationPath .\out\icons.zip 36 | echo "creating arm64 artifacts" 37 | move .\out_arm64\wsldl.exe .\out\wsldl_arm64.exe 38 | Get-ChildItem .\out_arm64\icons -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { $_.hash.toLower() + " " + ($_.path | Split-Path -Leaf) } | Tee-Object -FilePath .\out_arm64\icons\sha256sums.txt 39 | Compress-Archive -Path .\out_arm64\icons\* -DestinationPath .\out\icons_arm64.zip 40 | echo "creating all sha256sums" 41 | Get-ChildItem .\out -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { $_.hash.toLower() + " " + ($_.path | Split-Path -Leaf) } | Tee-Object -FilePath .\out\sha256sums.txt 42 | - name: Release body 43 | run: | 44 | $version = ${Env:GITHUB_REF}.Replace("refs/tags/","") 45 | echo "![downloads](https://img.shields.io/github/downloads/yuk7/wsldl/${version}/total?style=flat-square)" > body.txt 46 | - name: Upload the release 47 | uses: softprops/action-gh-release@v1 48 | with: 49 | body_path: body.txt 50 | files: | 51 | out/wsldl.exe 52 | out/icons.zip 53 | out/wsldl_arm64.exe 54 | out/icons_arm64.zip 55 | out/sha256sums.txt 56 | env: 57 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 58 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | paths: 6 | - "src/**" 7 | - "res/**" 8 | - "build.bat" 9 | - ".github/workflows/**" 10 | pull_request: 11 | paths: 12 | - "src/**" 13 | - "res/**" 14 | - "build.bat" 15 | - ".github/workflows/**" 16 | 17 | jobs: 18 | compile-wsldl-windows: 19 | name: Compile wsldl 20 | runs-on: windows-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Build 24 | run: | 25 | $short_sha = $env:GITHUB_SHA.SubString(0, 7) 26 | $Env:GO_BUILD_OPTS = "-ldflags `"-X github.com/yuk7/wsldl/version.version=CI-${short_sha}`"" 27 | .\build.bat all 28 | Get-ChildItem .\out\icons -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { $_.hash.toLower() + " " + ($_.path | Split-Path -Leaf) } | Tee-Object -FilePath .\out\icons\sha256sums.txt 29 | Compress-Archive -Path .\out\icons\* -DestinationPath .\out\icons.zip 30 | Get-ChildItem .\out -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { $_.hash.toLower() + " " + ($_.path | Split-Path -Leaf) } | Tee-Object -FilePath .\out\sha256sums.txt 31 | - uses: actions/upload-artifact@v4 32 | with: 33 | name: Build results-Windows 34 | path: | 35 | out/wsldl.exe 36 | out/icons.zip 37 | out/sha256sums.txt 38 | if-no-files-found: error 39 | compile-wsldl-windows-arm64: 40 | name: Compile wsldl ARM64 41 | runs-on: windows-latest 42 | steps: 43 | - uses: actions/checkout@v4 44 | - name: Build 45 | run: | 46 | go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@v1.4.0 47 | $Env:PATH = "${Env:USERPROFILE}\go\bin;${Env:PATH}" 48 | $short_sha = $env:GITHUB_SHA.SubString(0, 7) 49 | $Env:GO_BUILD_OPTS = "-ldflags `"-X github.com/yuk7/wsldl/version.version=CI-${short_sha}`"" 50 | $Env:GOARCH="arm64" 51 | .\build.bat all 52 | Get-ChildItem .\out\icons -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { $_.hash.toLower() + " " + ($_.path | Split-Path -Leaf) } | Tee-Object -FilePath .\out\icons\sha256sums.txt 53 | Compress-Archive -Path .\out\icons -DestinationPath .\out\icons.zip 54 | Get-ChildItem .\out -File | Get-FileHash -Algorithm SHA256 | ForEach-Object { $_.hash.toLower() + " " + ($_.path | Split-Path -Leaf) } | Tee-Object -FilePath .\out\sha256sums.txt 55 | - uses: actions/upload-artifact@v4 56 | with: 57 | name: Build results-Windows-ARM64 58 | path: | 59 | out/wsldl.exe 60 | out/icons.zip 61 | out/sha256sums.txt 62 | if-no-files-found: error 63 | compile-wsldl-linux: 64 | name: Cross-compile wsldl from Linux 65 | runs-on: ubuntu-latest 66 | steps: 67 | - uses: actions/checkout@v4 68 | - name: Build 69 | run: | 70 | go install github.com/josephspurrier/goversioninfo/cmd/goversioninfo@latest 71 | export PATH=$PATH:~/go/bin 72 | cd src 73 | goversioninfo 74 | env GOOS=windows GOARCH=amd64 go build -ldflags "-X github.com/yuk7/wsldl/version.version=CI-Cross-${GITHUB_SHA:0:7}" 75 | - uses: actions/upload-artifact@v4 76 | with: 77 | name: Build results-Linux 78 | path: | 79 | src/wsldl.exe 80 | if-no-files-found: error 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories 15 | vendor/ 16 | 17 | # Output directory 18 | out/ 19 | # Tools directory 20 | tools/ 21 | # Binary object file 22 | *.syso -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | yukx00@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /DEVELOPERS.md: -------------------------------------------------------------------------------- 1 | # Developers Document 2 | 3 | ## 🛠How-to-Build 4 | 5 | ### Windows 6 | 7 | #### Compile using `Go` 8 | 9 | ##### Automatic `build.bat` usage 10 | 11 | ```dos 12 | Usage: 13 | /single: 14 | Build default wsldl.exe 15 | all: 16 | Build everything, including exe with icons and default wsldl.exe 17 | resources: 18 | Build only .syso files 19 | icons: 20 | Build exe with icons(must be executed after running resources) 21 | clean: 22 | Clean(remove) .syso files after building exe with icons 23 | singlewor: 24 | Build exe without any manifest/resources 25 | ``` 26 | To cross-compile ARM64 from AMD64 or vice versa, run: 27 | ```cmd 28 | set GOARCH=ArchitectureName 29 | ``` 30 | Architecture names can be:\ 31 | `arm64`\ 32 | `amd64`\ 33 | `arm`\ 34 | `386` 35 | 36 | 37 | 38 | 39 | ### MacOS, Linux (cross compile) 40 | 41 | Run this command in shell 42 | ```bash 43 | $ cd src 44 | $ env GOOS=windows GOARCH=amd64 go build -ldflags "-w -s" 45 | ``` 46 | 47 | Optionally, to add an icon to the exe, create and link a resource with 48 | ```bash 49 | $ go get github.com/josephspurrier/goversioninfo/cmd/goversioninfo 50 | $ export PATH=$PATH:~/go/bin 51 | $ goversioninfo -icon res/DistroName/icon.ico -o src/DistroName.syso 52 | $ env GOOS=windows GOARCH=ArchitectureName go build -ldflags "-w -s" -o DistroName.exe 53 | ``` 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2025 yuk7 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /LICENSES.md: -------------------------------------------------------------------------------- 1 | # Licenses 2 | 3 | --- 4 | [MIT license](http://opensource.org/licenses/mit-license.php) 5 | 6 | * [main.c](https://github.com/yuk7/wsldl) 7 | * [res//res.rc](https://github.com/yuk7/wsldl) 8 | 9 | --- 10 | [BSD 3-Clause "New" or "Revised" License](https://opensource.org/licenses/BSD-3-Clause) 11 | 12 | * [res/Rocky/icon.ico](https://github.com/rocky-linux/branding) 13 | 14 | --- 15 | [Creative Commons (Attribution 3.0 Unported)](https://creativecommons.org/licenses/by/3.0/) 16 | 17 | * [res/Arch/icon.ico](https://www.shareicon.net/archlinux-arch-linux-101867) 18 | * [res/CentOS/icon.ico](https://www.shareicon.net/centos-cent-os-101865) 19 | * [res/Debian/icon.ico](https://www.shareicon.net/debian-101872) 20 | * [res/Fedora/icon.ico](https://www.shareicon.net/fedora-101881) 21 | * [res/Gentoo/icon.ico](https://www.shareicon.net/gentoo-101885) 22 | * [res/openSUSE/icon.ico](https://www.shareicon.net/opensuse-open-suse-101897) 23 | * [res/Ubuntu/icon.ico](https://www.shareicon.net/ubuntu-101918) 24 | 25 | 26 | --- 27 | [Creative Commons (Attribution-ShareAlike 4.0)](https://creativecommons.org/licenses/by-sa/4.0/) 28 | 29 | * [res/Alpine/icon.ico](https://github.com/yuk7/wsldl) 30 | this file is modified. original by [Alpinelinux](https://commons.wikimedia.org/wiki/File:Alpine_Linux.svg) 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wsldl 2 | Advanced WSL Distribution Launcher / Installer 3 | 4 | ![wsldl logo](https://github.com/yuk7/wsldl/assets/29954265/8c8804f7-29a2-43c7-a9ed-c763a2196100) 5 | 6 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/yuk7/wsldl/ci.yml?style=flat-square)](https://github.com/yuk7/wsldl/actions/workflows/ci.yml) 7 | [![Github All Releases](https://img.shields.io/github/downloads/yuk7/wsldl/total.svg?style=flat-square)](https://github.com/yuk7/wsldl/releases/latest) 8 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 9 | ![License](https://img.shields.io/github/license/yuk7/wsldl.svg?style=flat-square) 10 | 11 | 12 | ### [Detailed documentation is here](https://git.io/wsldl-doc) 13 | 14 | ## 💻Requirements 15 | * Windows 10 1709 Fall Creators Update or later(x64/arm64). 16 | * Windows Subsystem for Linux feature is enabled. 17 | 18 | ## 📦Install with Prebuilt Packages 19 | [**You can see List on docs**](https://wsldl-pg.github.io/docs/Using-wsldl/#distros) 20 | 21 | **Note:** 22 | Exe filename is using to the instance name to register. 23 | If you rename it, you can register with a different name. 24 | 25 | 26 | ## 🔧Install with any rootfs tarball 27 | 28 | **Note:** 29 | The filesystem needs to be in the root of the tarball, some rootfs tarballs may need to be repacked. 30 | 31 | #### 1. [Download wsldl.exe](https://github.com/yuk7/wsldl/releases/latest) 32 | (wsldl.exe is x86_64, wsldl_arm64.exe is ARM64 build) 33 | #### 2. Rename it for distribution name to register. 34 | (Ex: Rename to Arch.exe if you want to use "Arch" for the Instance name) 35 | #### 3. Put your install.tar(.gz) in same directory as exe (Installation directory) 36 | #### 4. Run exe to install. This process may take a few minutes. 37 | 38 | ## 🔧Install with any ext4 vhdx disk images (WSL2 only) 39 | #### 1. [Download wsldl.exe](https://github.com/yuk7/wsldl/releases/latest) 40 | (wsldl.exe is x86_64, wsldl_arm64.exe is ARM64 build) 41 | #### 2. Rename it for distribution name to register. 42 | (Ex: Rename to Arch.exe if you want to use "Arch" for the Instance name) 43 | #### 3. Put your install.ext4.vhdx(.gz) in same directory as exe (Installation directory) 44 | #### 4. Run exe to install. This process may take a few minutes. 45 | 46 | ## 🔗Use as a Launcher for already installed distribution 47 | #### 1. [Download wsldl.exe](https://github.com/yuk7/wsldl/releases/latest) 48 | (wsldl.exe is x86_64, wsldl_arm64.exe is ARM64 build) 49 | #### 2. Rename it for registerd instance name. 50 | Please check the registered instance name of the distribution with `wslconfig /l` command. 51 | (Ex: If the instance name is "Ubuntu-20.04", rename `wsldl.exe` to `Ubuntu-20.04.exe`) 52 | #### 4. Run exe to Launch instance or configuration. 53 | For details, please see the help. (`{InstanceName}.exe help`) 54 | 55 | Note: You can distribute your distribution including wsldl exe. 56 | 57 | ## 📝How-to-Use(for Installed Instance) 58 | #### exe Usage 59 | ``` 60 | Usage : 61 | 62 | - Open a new shell with your default settings. 63 | Inherit current directory (with exception that %%USERPROFILE%% is changed to $HOME). 64 | 65 | run 66 | - Run the given command line in that instance. Inherit current directory. 67 | 68 | runp 69 | - Run the given command line in that instance after converting its path. 70 | 71 | config [setting [value]] 72 | - `--default-user `: Set the default user of this instance to . 73 | - `--default-uid `: Set the default user uid of this instance to . 74 | - `--append-path `: Switch of Append Windows PATH to $PATH 75 | - `--mount-drive `: Switch of Mount drives 76 | - `--wsl-version <1|2>`: Set the WSL version of this instance to <1 or 2> 77 | - `--default-term `: Set default type of terminal window. 78 | 79 | get [setting [value]] 80 | - `--default-uid`: Get the default user uid in this instance. 81 | - `--append-path`: Get true/false status of Append Windows PATH to $PATH. 82 | - `--mount-drive`: Get true/false status of Mount drives. 83 | - `--wsl-version`: Get the version os the WSL (1/2) of this instance. 84 | - `--default-term`: Get Default Terminal type of this instance launcher. 85 | - `--wt-profile-name`: Get Profile Name from Windows Terminal 86 | - `--lxguid`: Get WSL GUID key for this instance. 87 | 88 | backup [file name] 89 | - `*.tar`: Output backup tar file. 90 | - `*.tar.gz`: Output backup tar.gz file. 91 | - `*.ext4.vhdx`: Output backup ext4.vhdx file. (WSL2 only) 92 | - `*.ext4.vhdx.gz`: Output backup ext4.vhdx.gz file. (WSL2 only) 93 | - `*.reg`: Output settings registry file. 94 | 95 | clean 96 | - Uninstall that instance. 97 | 98 | help 99 | - Print this usage message. 100 | ``` 101 | 102 | 103 | #### Just Run exe 104 | ```cmd 105 | >{InstanceName}.exe 106 | [root@PC-NAME user]# 107 | ``` 108 | 109 | #### Run with command line 110 | ```cmd 111 | >{InstanceName}.exe run uname -r 112 | 4.4.0-43-Microsoft 113 | ``` 114 | 115 | #### Run with command line with path translation 116 | ```cmd 117 | >{InstanceName}.exe runp echo C:\Windows\System32\cmd.exe 118 | /mnt/c/Windows/System32/cmd.exe 119 | ``` 120 | 121 | #### Change Default User(id command required) 122 | ```cmd 123 | >{InstanceName}.exe config --default-user user 124 | 125 | >{InstanceName}.exe 126 | [user@PC-NAME dir]$ 127 | ``` 128 | 129 | #### Set "Windows Terminal" as default terminal 130 | ```cmd 131 | >{InstanceName}.exe config --default-term wt 132 | ``` 133 | 134 | #### How to uninstall instance 135 | ```cmd 136 | >{InstanceName}.exe clean 137 | 138 | ``` 139 | 140 | #### How-to-backup 141 | export to backup.tar.gz (WSL1 or 2) 142 | ```cmd 143 | >{InstanceName}.exe backup backup.tar.gz 144 | ``` 145 | export to backup.ext4.vhdx.gz (WSL2 only) 146 | ```cmd 147 | >{InstanceName}.exe backup backup.ext4.vhdx.gz 148 | ``` 149 | 150 | #### How-to-import 151 | .tar(.gz) (WSL1 or 2) 152 | ```cmd 153 | >{InstanceName}.exe install backup.tar.gz 154 | ``` 155 | .ext4.vhdx(.gz) (WSL2 only) 156 | ```cmd 157 | >{InstanceName}.exe install backup.ext4.vhdx.gz 158 | ``` 159 | 160 | 161 | 162 | 163 | ## 🛠How-to-Build 164 | Please see [DEVELOPERS.md](DEVELOPERS.md) 165 | 166 | ## 📄License 167 | [MIT](LICENSES.md) 168 | 169 | Copyright (c) 2017-2025 [yuk7](https://github.com/yuk7) 170 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | :: Copyright (c) 2021-2025 yuk7 2 | :: Released under the MIT license 3 | :: http://opensource.org/licenses/mit-license.php 4 | 5 | @echo off 6 | cd /d %~dp0 7 | 8 | set PATH="%GOPATH%\bin";%PATH% 9 | set PATH="%USERPROFILE%\go\bin";%PATH% 10 | set GOVERSIONINFO_PRG=goversioninfo.exe 11 | 12 | if not defined GOPRG ( 13 | set GOPRG=go 14 | ) 15 | 16 | if not defined GOARCH ( 17 | echo GOARCH is not defined, detecting cpu architecture... 18 | if "%PROCESSOR_ARCHITECTURE%"=="x86" ( 19 | echo 32bit 386 processor detected 20 | set GOARCH=386 21 | ) 22 | if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( 23 | echo 64bit amd64 processor detected 24 | set GOARCH=amd64 25 | ) 26 | if "%PROCESSOR_ARCHITECTURE%"=="ARM" ( 27 | echo 32bit arm processor detected 28 | set GOARCH=arm 29 | ) 30 | if "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( 31 | echo 64bit arm64 processor detected 32 | set GOARCH=arm64 33 | ) 34 | ) 35 | 36 | if "%GOARCH%"=="386" ( 37 | echo GOARCH is 386 38 | echo. 39 | echo CAUTION! 386 32bit is not supported architecture 40 | echo The output binary probably won't work. 41 | echo To Cross Compile, you must set GOARCH environment variable 42 | echo Supported architectures are amd64 and arm64 43 | echo. 44 | set GOVERSIONINFO_OPTS= 45 | ) else if "%GOARCH%"=="amd64" ( 46 | echo GOARCH is amd64 47 | set GOVERSIONINFO_OPTS=-64 48 | ) else if "%GOARCH%"=="arm" ( 49 | echo GOARCH is arm 50 | echo. 51 | echo CAUTION! arm 32bit is not supported architecture 52 | echo The output binary probably won't work. 53 | echo To Cross Compile, you must set GOARCH environment variable 54 | echo Supported architectures are amd64 and arm64 55 | echo. 56 | set GOVERSIONINFO_OPTS=-arm 57 | ) else if "%GOARCH%"=="arm64" ( 58 | echo GOARCH is arm64 59 | set GOVERSIONINFO_OPTS=-arm -64 60 | ) else ( 61 | echo ERROR: %GOARCH% is not supported architecturefor build target. 62 | exit /b 1 63 | ) 64 | 65 | 66 | if "%~1"=="all" ( 67 | echo Building everything 68 | call :dlgoversioninfo 69 | if %ERRORLEVEL% NEQ 0 goto :failed 70 | call :resources 71 | if %ERRORLEVEL% NEQ 0 goto :failed 72 | call :icons 73 | if %ERRORLEVEL% NEQ 0 goto :failed 74 | call :single 75 | if %ERRORLEVEL% NEQ 0 goto :failed 76 | exit /b 77 | ) 78 | if "%~1"=="resources" ( 79 | echo Building resources 80 | call :dlgoversioninfo 81 | if %ERRORLEVEL% NEQ 0 goto :failed 82 | call :resources 83 | if %ERRORLEVEL% NEQ 0 goto :failed 84 | exit /b 85 | ) 86 | if "%~1"=="icons" ( 87 | echo Building icon binaries 88 | call :icons 89 | if %ERRORLEVEL% NEQ 0 goto :failed 90 | exit /b 91 | ) 92 | if "%~1"=="single" ( 93 | echo Building binary... 94 | call :dlgoversioninfo 95 | if %ERRORLEVEL% NEQ 0 goto :failed 96 | call :single 97 | if %ERRORLEVEL% NEQ 0 goto :failed 98 | exit /b 99 | ) 100 | if "%~1"=="singlewor" ( 101 | echo Building binary without resource... 102 | call :singlewor 103 | if %ERRORLEVEL% NEQ 0 goto :failed 104 | exit /b 105 | ) 106 | if "%~1"=="clean" ( 107 | echo Removal of .syso files 108 | call :clean 109 | exit /b 110 | ) 111 | call :dlgoversioninfo 112 | if %ERRORLEVEL% NEQ 0 goto :failed 113 | call :single 114 | if %ERRORLEVEL% NEQ 0 goto :failed 115 | exit /b 116 | 117 | 118 | 119 | :resources 120 | set DOING=resources 121 | cd /d %~dp0 122 | echo Compiling all resources... 123 | FOR /D /r %%D in ("res/*") DO ( 124 | tools\goversioninfo %GOVERSIONINFO_OPTS% -icon res\%%~nxD\icon.ico -o res\%%~nxD\resource.syso src\versioninfo.json 125 | if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% 126 | ) 127 | exit /b 128 | 129 | :icons 130 | set DOING=icons 131 | cd /d %~dp0 132 | echo Building wsldl with icons... 133 | mkdir out\icons >NUL 2>&1 134 | FOR /D /r %%D in ("res/*") DO ( 135 | copy /y res\%%~nxD\resource.syso src\resource.syso 136 | cd src 137 | echo %GOPRG% build %GO_BUILD_OPTS% -o "%~dp0\out\icons\%%~nxD.exe" 138 | %GOPRG% build %GO_BUILD_OPTS% -o "%~dp0\out\icons\%%~nxD.exe" 139 | if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% 140 | cd .. 141 | del /f src\resource.syso 142 | ) 143 | exit /b 144 | 145 | :single 146 | set DOING=single 147 | cd /d %~dp0 148 | echo Compiling resource object... 149 | tools\goversioninfo %GOVERSIONINFO_OPTS% -o src\resource.syso src\versioninfo.json 150 | if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% 151 | :singlewor 152 | set DOING=singlewor 153 | cd /d %~dp0 154 | mkdir out >NUL 2>&1 155 | cd src 156 | echo Building default wsldl.exe... 157 | echo %GOPRG% build %GO_BUILD_OPTS% -o "%~dp0\out\wsldl.exe" 158 | %GOPRG% build %GO_BUILD_OPTS% -o "%~dp0\out\wsldl.exe" 159 | if %ERRORLEVEL% NEQ 0 exit /b %ERRORLEVEL% 160 | cd .. 161 | :end 162 | exit /b 163 | 164 | :clean 165 | FOR /D /r %%D in ("res/*") DO ( 166 | cd /d %~dp0 167 | del res\%%~nxD\resource.syso 168 | ) 169 | del src\resource.syso 170 | rmdir /s /q out tools 171 | exit /b 172 | 173 | :dlgoversioninfo 174 | set DOING=dlgoversioninfo 175 | cd /d %~dp0 176 | mkdir tools >NUL 2>&1 177 | if "%PROCESSOR_ARCHITECTURE%"=="x86" ( 178 | echo Downaloding goversioninfo 386... 179 | curl -sSfL https://github.com/yuk7/goversioninfo/releases/download/v1.2.0-arm/goversioninfo_386.exe -o tools\goversioninfo.exe 180 | ) else if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( 181 | echo Downaloding goversioninfo amd64... 182 | curl -sSfL https://github.com/yuk7/goversioninfo/releases/download/v1.2.0-arm/goversioninfo_amd64.exe -o tools\goversioninfo.exe 183 | ) else if "%PROCESSOR_ARCHITECTURE%"=="ARM" ( 184 | echo Downaloding goversioninfo ARM... 185 | curl -sSfL https://github.com/yuk7/goversioninfo/releases/download/v1.2.0-arm/goversioninfo_arm.exe -o tools\goversioninfo.exe 186 | ) else if "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( 187 | echo Downaloding goversioninfo ARM64... 188 | curl -sSfL https://github.com/yuk7/goversioninfo/releases/download/v1.2.0-arm/goversioninfo_arm64.exe -o tools\goversioninfo.exe 189 | ) else ( 190 | echo ERROR: %PROCESSOR_ARCHITECTURE% is not supported for build environment 191 | ) 192 | if not exist tools\goversioninfo.exe exit /b 1 193 | exit /b 194 | 195 | :failed 196 | echo ERROR in %DOING% 197 | exit /b 1 198 | -------------------------------------------------------------------------------- /res/Alma/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Alma/icon.ico -------------------------------------------------------------------------------- /res/Alpine/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Alpine/icon.ico -------------------------------------------------------------------------------- /res/Arch/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Arch/icon.ico -------------------------------------------------------------------------------- /res/Artix/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Artix/icon.ico -------------------------------------------------------------------------------- /res/BioArch/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/BioArch/icon.ico -------------------------------------------------------------------------------- /res/CentOS/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/CentOS/icon.ico -------------------------------------------------------------------------------- /res/Clear/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Clear/icon.ico -------------------------------------------------------------------------------- /res/Debian/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Debian/icon.ico -------------------------------------------------------------------------------- /res/Deepin/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Deepin/icon.ico -------------------------------------------------------------------------------- /res/Devuan/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Devuan/icon.ico -------------------------------------------------------------------------------- /res/Elementary/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Elementary/icon.ico -------------------------------------------------------------------------------- /res/EndeavourOS/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/EndeavourOS/icon.ico -------------------------------------------------------------------------------- /res/Fedora/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Fedora/icon.ico -------------------------------------------------------------------------------- /res/Gentoo/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Gentoo/icon.ico -------------------------------------------------------------------------------- /res/Kali/.icon-src/kali-dragon-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /res/Kali/.icon-src/kali-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /res/Kali/.icon-src/merge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Kali/.icon-src/merge.png -------------------------------------------------------------------------------- /res/Kali/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Kali/icon.ico -------------------------------------------------------------------------------- /res/Manjaro/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Manjaro/icon.ico -------------------------------------------------------------------------------- /res/Mint/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Mint/icon.ico -------------------------------------------------------------------------------- /res/Openmandriva/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Openmandriva/icon.ico -------------------------------------------------------------------------------- /res/Oracle/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Oracle/icon.ico -------------------------------------------------------------------------------- /res/Pengwin/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Pengwin/icon.ico -------------------------------------------------------------------------------- /res/Puppy/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Puppy/icon.ico -------------------------------------------------------------------------------- /res/Rhino/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Rhino/icon.ico -------------------------------------------------------------------------------- /res/Rocky/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Rocky/icon.ico -------------------------------------------------------------------------------- /res/Slackware/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Slackware/icon.ico -------------------------------------------------------------------------------- /res/Solus/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Solus/icon.ico -------------------------------------------------------------------------------- /res/Tails/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Tails/icon.ico -------------------------------------------------------------------------------- /res/Ubuntu/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Ubuntu/icon.ico -------------------------------------------------------------------------------- /res/Venom/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Venom/icon.ico -------------------------------------------------------------------------------- /res/Void/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/Void/icon.ico -------------------------------------------------------------------------------- /res/openSUSE/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yuk7/wsldl/f7c07ac2508752971aab1fd36b9930e7865b1bee/res/openSUSE/icon.ico -------------------------------------------------------------------------------- /src/backup/backup.go: -------------------------------------------------------------------------------- 1 | package backup 2 | 3 | import ( 4 | "compress/gzip" 5 | "errors" 6 | "io" 7 | "math/rand" 8 | "os" 9 | "os/exec" 10 | "strconv" 11 | "strings" 12 | "time" 13 | 14 | "github.com/yuk7/wsldl/lib/utils" 15 | wslreg "github.com/yuk7/wslreglib-go" 16 | ) 17 | 18 | func backupReg(name string, destFileName string) error { 19 | profile, err := wslreg.GetProfileFromName(name) 20 | if err != nil { 21 | utils.ErrorExit(err, true, true, false) 22 | } 23 | 24 | regexe := utils.GetWindowsDirectory() + "\\System32\\reg.exe" 25 | regpath := "HKEY_CURRENT_USER\\" + wslreg.LxssBaseKey + "\\" + profile.UUID 26 | _, err = exec.Command(regexe, "export", regpath, destFileName, "/y").Output() 27 | return err 28 | } 29 | 30 | func backupTar(distributionName string, destFileName string) error { 31 | // compress and copy 32 | rootPathLower := strings.ToLower(destFileName) 33 | if strings.HasSuffix(rootPathLower, ".gz") { 34 | // create temporary tar 35 | tmpTarFn := os.TempDir() 36 | if tmpTarFn == "" { 37 | return errors.New("failed to create temp directory") 38 | } 39 | rand.NewSource(time.Now().UnixNano()) 40 | tmpTarFn = tmpTarFn + "\\" + strconv.Itoa(rand.Intn(10000)) + ".tar" 41 | wslexe := utils.GetWindowsDirectory() + "\\System32\\wsl.exe" 42 | _, err := exec.Command(wslexe, "--export", distributionName, tmpTarFn).Output() 43 | defer os.Remove(tmpTarFn) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | return copyFileAndCompress(tmpTarFn, destFileName) 49 | } else { 50 | // not compressed 51 | wslexe := utils.GetWindowsDirectory() + "\\System32\\wsl.exe" 52 | _, err := exec.Command(wslexe, "--export", distributionName, destFileName).Output() 53 | return err 54 | } 55 | } 56 | 57 | func backupExt4Vhdx(name string, destFileName string) error { 58 | prof, err := wslreg.GetProfileFromName(name) 59 | if prof.BasePath != "" { 60 | 61 | } else { 62 | if err != nil { 63 | return err 64 | } 65 | return errors.New("get profile failed") 66 | } 67 | 68 | vhdxPath := prof.BasePath + "\\ext4.vhdx" 69 | 70 | return copyFileAndCompress(vhdxPath, destFileName) 71 | } 72 | 73 | func copyFileAndCompress(srcPath, destPath string) error { 74 | src, err := os.Open(srcPath) 75 | if err != nil { 76 | return err 77 | } 78 | defer src.Close() 79 | dest, err := os.Create(destPath) 80 | if err != nil { 81 | return err 82 | } 83 | defer dest.Close() 84 | 85 | // compress and copy 86 | destPathLower := strings.ToLower(destPath) 87 | if strings.HasSuffix(destPathLower, ".gz") || strings.HasSuffix(destPathLower, ".tgz") { 88 | // compressed with gzip 89 | gw := gzip.NewWriter(dest) 90 | defer gw.Close() 91 | _, err = io.Copy(gw, src) 92 | if err != nil { 93 | return err 94 | } 95 | } else { 96 | // not compressed 97 | _, err = io.Copy(dest, src) 98 | if err != nil { 99 | return err 100 | } 101 | } 102 | return nil 103 | } 104 | -------------------------------------------------------------------------------- /src/backup/command.go: -------------------------------------------------------------------------------- 1 | package backup 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | 7 | "github.com/yuk7/wsldl/lib/cmdline" 8 | "github.com/yuk7/wsldl/lib/utils" 9 | "github.com/yuk7/wsllib-go" 10 | ) 11 | 12 | // GetCommand returns the backup command structure 13 | func GetCommand() cmdline.Command { 14 | return cmdline.Command{ 15 | Names: []string{"backup"}, 16 | Help: func(distroName string, isListQuery bool) string { 17 | if wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 18 | return getHelpMessage() 19 | } 20 | return "" 21 | }, 22 | Run: execute, 23 | } 24 | } 25 | 26 | // execute is default backup entrypoint 27 | func execute(name string, args []string) { 28 | opttar := "" 29 | optvhdx := "" 30 | optreg := "" 31 | switch len(args) { 32 | case 0: 33 | _, _, flags, _ := wsllib.WslGetDistributionConfiguration(name) 34 | if flags&wsllib.FlagEnableWsl2 == wsllib.FlagEnableWsl2 { 35 | optvhdx = "backup.ext4.vhdx.gz" 36 | optreg = "backup.reg" 37 | } else { 38 | opttar = "backup.tar.gz" 39 | optreg = "backup.reg" 40 | } 41 | 42 | case 1: 43 | arg0Lower := strings.ToLower(args[0]) 44 | switch arg0Lower { 45 | case "--tar": 46 | opttar = "backup.tar" 47 | case "--tgz": 48 | opttar = "backup.tar.gz" 49 | case "--vhdx": 50 | optvhdx = "backup.ext4.vhdx" 51 | case "--vhdxgz": 52 | optvhdx = "backup.ext4.vhdx.gz" 53 | case "--reg": 54 | optreg = "backup.reg" 55 | default: 56 | if strings.HasSuffix(arg0Lower, ".tar") || strings.HasSuffix(arg0Lower, ".tar.gz") || strings.HasSuffix(arg0Lower, ".tgz") { 57 | opttar = args[0] 58 | } else if strings.HasSuffix(arg0Lower, ".ext4.vhdx") || strings.HasSuffix(arg0Lower, ".ext4.vhdx.gz") { 59 | optvhdx = args[0] 60 | } else if strings.HasSuffix(arg0Lower, ".reg") { 61 | optreg = args[0] 62 | } else { 63 | utils.ErrorExit(os.ErrInvalid, true, true, false) 64 | } 65 | } 66 | 67 | default: 68 | utils.ErrorExit(os.ErrInvalid, true, true, false) 69 | } 70 | 71 | if optreg != "" { 72 | err := backupReg(name, optreg) 73 | if err != nil { 74 | utils.ErrorExit(err, true, true, false) 75 | } 76 | } 77 | if opttar != "" { 78 | err := backupTar(name, opttar) 79 | if err != nil { 80 | utils.ErrorExit(err, true, true, false) 81 | } 82 | 83 | } 84 | if optvhdx != "" { 85 | err := backupExt4Vhdx(name, optvhdx) 86 | if err != nil { 87 | utils.ErrorExit(err, true, true, false) 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/backup/help.go: -------------------------------------------------------------------------------- 1 | package backup 2 | 3 | // getHelpMessage returns the help message 4 | func getHelpMessage() string { 5 | return "" + 6 | "backup [file name]\n" + 7 | " - `*.tar`: Output backup tar file.\n" + 8 | " - `*.tar.gz`: Output backup tar.gz file.\n" + 9 | " - `*.ext4.vhdx`: Output backup ext4.vhdx file. (WSL2 only)\n" + 10 | " - `*.ext4.vhdx.gz`: Output backup ext4.vhdx.gz file. (WSL2 only)\n" + 11 | " - `*.reg`: Output settings registry file." 12 | } 13 | -------------------------------------------------------------------------------- /src/clean/clean.go: -------------------------------------------------------------------------------- 1 | package clean 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/yuk7/wsldl/lib/utils" 8 | "github.com/yuk7/wsllib-go" 9 | ) 10 | 11 | //Clean cleans distribution 12 | func Clean(name string, showProgress bool) { 13 | if showProgress { 14 | fmt.Println("Unregistering...") 15 | } 16 | err := wsllib.WslUnregisterDistribution(name) 17 | if err != nil { 18 | utils.ErrorExit(err, showProgress, true, false) 19 | } 20 | os.Exit(0) 21 | } 22 | -------------------------------------------------------------------------------- /src/clean/command.go: -------------------------------------------------------------------------------- 1 | package clean 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/yuk7/wsldl/lib/cmdline" 8 | "github.com/yuk7/wsldl/lib/utils" 9 | "github.com/yuk7/wsllib-go" 10 | ) 11 | 12 | // GetCommand returns the clean command structure 13 | func GetCommand() cmdline.Command { 14 | return cmdline.Command{ 15 | Names: []string{"clean"}, 16 | Help: func(distroName string, isListQuery bool) string { 17 | if wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 18 | return getHelpMessage() 19 | } 20 | return "" 21 | }, 22 | Run: execute, 23 | } 24 | } 25 | 26 | // execute is default run entrypoint. 27 | func execute(name string, args []string) { 28 | showProgress := true 29 | switch len(args) { 30 | case 0: 31 | var in string 32 | fmt.Printf("This will remove this distro (%s) from the filesystem.\n", name) 33 | fmt.Printf("Are you sure you would like to proceed? (This cannot be undone)\n") 34 | fmt.Printf("Type \"y\" to continue:") 35 | fmt.Scan(&in) 36 | 37 | if in != "y" { 38 | fmt.Fprintf(os.Stderr, "Accepting is required to proceed.") 39 | utils.ErrorExit(os.ErrInvalid, false, true, false) 40 | } 41 | 42 | case 1: 43 | showProgress = false 44 | if args[0] == "-y" { 45 | showProgress = false 46 | } else { 47 | utils.ErrorExit(os.ErrInvalid, true, true, false) 48 | } 49 | 50 | default: 51 | utils.ErrorExit(os.ErrInvalid, true, true, false) 52 | } 53 | 54 | Clean(name, showProgress) 55 | } 56 | -------------------------------------------------------------------------------- /src/clean/help.go: -------------------------------------------------------------------------------- 1 | package clean 2 | 3 | // getHelpMessage returns the help message 4 | func getHelpMessage() string { 5 | return "" + 6 | "clean\n" + 7 | " - Uninstall that instance." 8 | } 9 | -------------------------------------------------------------------------------- /src/config/command.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "strconv" 7 | 8 | "github.com/yuk7/wsldl/get" 9 | "github.com/yuk7/wsldl/lib/cmdline" 10 | "github.com/yuk7/wsldl/lib/utils" 11 | "github.com/yuk7/wsldl/run" 12 | "github.com/yuk7/wsllib-go" 13 | wslreg "github.com/yuk7/wslreglib-go" 14 | ) 15 | 16 | // GetCommand returns the config set command structure 17 | func GetCommand() cmdline.Command { 18 | return cmdline.Command{ 19 | Names: []string{"config", "set"}, 20 | Help: func(distroName string, isListQuery bool) string { 21 | if wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 22 | return getHelpMessage() 23 | } 24 | return "" 25 | }, 26 | Run: execute, 27 | } 28 | } 29 | 30 | // execute is default install entrypoint 31 | func execute(name string, args []string) { 32 | var err error 33 | uid, flags := get.WslGetConfig(name) 34 | if len(args) == 2 { 35 | switch args[0] { 36 | case "--default-uid": 37 | var intUID int 38 | intUID, err = strconv.Atoi(args[1]) 39 | uid = uint64(intUID) 40 | 41 | case "--default-user": 42 | str, _, errtmp := run.ExecRead(name, "id -u "+utils.DQEscapeString(args[1])) 43 | err = errtmp 44 | if err == nil { 45 | var intUID int 46 | intUID, err = strconv.Atoi(str) 47 | uid = uint64(intUID) 48 | if err != nil { 49 | err = errors.New(str) 50 | } 51 | } 52 | 53 | case "--append-path": 54 | var b bool 55 | b, err = strconv.ParseBool(args[1]) 56 | if b { 57 | flags |= wsllib.FlagAppendNTPath 58 | } else { 59 | flags ^= wsllib.FlagAppendNTPath 60 | } 61 | 62 | case "--mount-drive": 63 | var b bool 64 | b, err = strconv.ParseBool(args[1]) 65 | if b { 66 | flags |= wsllib.FlagEnableDriveMounting 67 | } else { 68 | flags ^= wsllib.FlagEnableDriveMounting 69 | } 70 | 71 | case "--wsl-version": 72 | var intWslVer int 73 | intWslVer, err = strconv.Atoi(args[1]) 74 | if err == nil { 75 | if intWslVer == 1 || intWslVer == 2 { 76 | err = wslreg.SetWslVersion(name, intWslVer) 77 | } else { 78 | err = os.ErrInvalid 79 | break 80 | } 81 | } 82 | 83 | case "--default-term": 84 | value := 0 85 | switch args[1] { 86 | case "default", strconv.Itoa(wslreg.FlagWsldlTermDefault): 87 | value = wslreg.FlagWsldlTermDefault 88 | case "wt", strconv.Itoa(wslreg.FlagWsldlTermWT): 89 | value = wslreg.FlagWsldlTermWT 90 | case "flute", strconv.Itoa(wslreg.FlagWsldlTermFlute): 91 | value = wslreg.FlagWsldlTermFlute 92 | default: 93 | err = os.ErrInvalid 94 | break 95 | } 96 | profile, err := wslreg.GetProfileFromName(name) 97 | if err != nil { 98 | break 99 | } 100 | profile.WsldlTerm = value 101 | err = wslreg.WriteProfile(profile) 102 | 103 | case "--flags-val": 104 | var intFlags int 105 | intFlags, err = strconv.Atoi(args[1]) 106 | flags = uint32(intFlags) 107 | 108 | default: 109 | err = os.ErrInvalid 110 | } 111 | if err != nil { 112 | utils.ErrorExit(err, true, true, false) 113 | } 114 | wsllib.WslConfigureDistribution(name, uid, flags) 115 | } else { 116 | utils.ErrorExit(os.ErrInvalid, true, true, false) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/config/help.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | // getHelpMessage returns the help message 4 | func getHelpMessage() string { 5 | return "" + 6 | "config [setting [value]]\n" + 7 | " - `--default-user `: Set the default user of this instance to .\n" + 8 | " - `--default-uid `: Set the default user uid of this instance to .\n" + 9 | " - `--append-path `: Switch of Append Windows PATH to $PATH\n" + 10 | " - `--mount-drive `: Switch of Mount drives\n" + 11 | " - `--wsl-version <1|2>`: Set the WSL version of this instance to <1 or 2>\n" + 12 | " - `--default-term `: Set default type of terminal window." 13 | } 14 | -------------------------------------------------------------------------------- /src/get/command.go: -------------------------------------------------------------------------------- 1 | package get 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/yuk7/wsldl/lib/cmdline" 9 | "github.com/yuk7/wsldl/lib/utils" 10 | "github.com/yuk7/wsldl/lib/wtutils" 11 | "github.com/yuk7/wsllib-go" 12 | wslreg "github.com/yuk7/wslreglib-go" 13 | ) 14 | 15 | // GetCommand returns the get command structure 16 | func GetCommand() cmdline.Command { 17 | return cmdline.Command{ 18 | Names: []string{"get"}, 19 | Help: func(distroName string, isListQuery bool) string { 20 | if wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 21 | return getHelpMessage() 22 | } 23 | return "" 24 | }, 25 | Run: execute, 26 | } 27 | } 28 | 29 | // execute is default install entrypoint 30 | func execute(name string, args []string) { 31 | uid, flags := WslGetConfig(name) 32 | profile, proferr := wslreg.GetProfileFromName(name) 33 | if len(args) == 1 { 34 | switch args[0] { 35 | case "--default-uid": 36 | print(uid) 37 | 38 | case "--append-path": 39 | print(flags&wsllib.FlagAppendNTPath == wsllib.FlagAppendNTPath) 40 | 41 | case "--mount-drive": 42 | print(flags&wsllib.FlagEnableDriveMounting == wsllib.FlagEnableDriveMounting) 43 | 44 | case "--wsl-version": 45 | if flags&wsllib.FlagEnableWsl2 == wsllib.FlagEnableWsl2 { 46 | print("2") 47 | } else { 48 | print("1") 49 | } 50 | 51 | case "--lxguid", "--lxuid": 52 | if profile.UUID == "" { 53 | if proferr != nil { 54 | utils.ErrorExit(proferr, true, true, false) 55 | } 56 | utils.ErrorExit(errors.New("lxguid get failed"), true, true, false) 57 | } 58 | print(profile.UUID) 59 | 60 | case "--default-term", "--default-terminal": 61 | switch profile.WsldlTerm { 62 | case wslreg.FlagWsldlTermWT: 63 | print("wt") 64 | case wslreg.FlagWsldlTermFlute: 65 | print("flute") 66 | default: 67 | print("default") 68 | } 69 | 70 | case "--wt-profile-name", "--wt-profilename", "--wt-pn": 71 | if profile.DistributionName != "" { 72 | name = profile.DistributionName 73 | } 74 | 75 | conf, err := wtutils.ReadParseWTConfig() 76 | if err != nil { 77 | utils.ErrorExit(err, true, true, false) 78 | } 79 | guid := "{" + wtutils.CreateProfileGUID(name) + "}" 80 | profileName := "" 81 | for _, profile := range conf.Profiles.ProfileList { 82 | if profile.GUID == guid { 83 | profileName = profile.Name 84 | break 85 | } 86 | } 87 | if profileName != "" { 88 | print(profileName) 89 | } else { 90 | utils.ErrorExit(errors.New("profile not found"), true, true, false) 91 | } 92 | 93 | case "--flags-val": 94 | print(flags) 95 | 96 | case "--flags-bits": 97 | fmt.Printf("%04b", flags) 98 | 99 | default: 100 | utils.ErrorExit(os.ErrInvalid, true, true, false) 101 | } 102 | } else { 103 | utils.ErrorExit(os.ErrInvalid, true, true, false) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/get/get.go: -------------------------------------------------------------------------------- 1 | package get 2 | 3 | import ( 4 | "github.com/yuk7/wsldl/lib/utils" 5 | "github.com/yuk7/wsllib-go" 6 | ) 7 | 8 | //WslGetConfig is getter of distribution configuration 9 | func WslGetConfig(distributionName string) (uid uint64, flags uint32) { 10 | var err error 11 | _, uid, flags, err = wsllib.WslGetDistributionConfiguration(distributionName) 12 | if err != nil { 13 | utils.ErrorRedPrintln("ERR: Failed to GetDistributionConfiguration") 14 | utils.ErrorExit(err, true, true, false) 15 | } 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /src/get/help.go: -------------------------------------------------------------------------------- 1 | package get 2 | 3 | // getHelpMessage returns the help message 4 | func getHelpMessage() string { 5 | return "" + 6 | "get [setting [value]]\n" + 7 | " - `--default-uid`: Get the default user uid in this instance.\n" + 8 | " - `--append-path`: Get true/false status of Append Windows PATH to $PATH.\n" + 9 | " - `--mount-drive`: Get true/false status of Mount drives.\n" + 10 | " - `--wsl-version`: Get the version os the WSL (1/2) of this instance.\n" + 11 | " - `--default-term`: Get Default Terminal type of this instance launcher.\n" + 12 | " - `--wt-profile-name`: Get Profile Name from Windows Terminal\n" + 13 | " - `--lxguid`: Get WSL GUID key for this instance." 14 | } 15 | -------------------------------------------------------------------------------- /src/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yuk7/wsldl 2 | 3 | go 1.24 4 | 5 | toolchain go1.24.0 6 | 7 | require ( 8 | github.com/fatih/color v1.18.0 9 | github.com/mitchellh/go-ps v1.0.0 10 | github.com/muhammadmuzzammil1998/jsonc v1.0.0 11 | github.com/satori/go.uuid v1.2.0 12 | github.com/schollz/progressbar/v3 v3.18.0 13 | github.com/yuk7/wsllib-go v1.0.0 14 | github.com/yuk7/wslreglib-go v1.0.1 15 | golang.org/x/text v0.25.0 16 | ) 17 | 18 | require ( 19 | github.com/kr/pretty v0.3.1 // indirect 20 | github.com/mattn/go-colorable v0.1.13 // indirect 21 | github.com/mattn/go-isatty v0.0.20 // indirect 22 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect 23 | github.com/rivo/uniseg v0.4.7 // indirect 24 | golang.org/x/sys v0.29.0 // indirect 25 | golang.org/x/term v0.28.0 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /src/go.sum: -------------------------------------------------------------------------------- 1 | github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= 2 | github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= 3 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 7 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 8 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 9 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 10 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 11 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 12 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 13 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 14 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 15 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 16 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 17 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 18 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 19 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= 20 | github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= 21 | github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= 22 | github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= 23 | github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs= 24 | github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= 25 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 26 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 27 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 28 | github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= 29 | github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 30 | github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= 31 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 32 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 33 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 34 | github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA= 35 | github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec= 36 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 37 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 38 | github.com/yuk7/wsllib-go v1.0.0 h1:o10iAZb2abMUNWSfXFKJshTGjC/fEoJlQjKvBvIrQTM= 39 | github.com/yuk7/wsllib-go v1.0.0/go.mod h1:QJnZQOhjmTIljal9n0LUNJCaLZe6g03vDDxFYRAsUjY= 40 | github.com/yuk7/wslreglib-go v1.0.1 h1:Iw+VeRBt6enSSSF6hpTbYM1bSWXn9jKgH+7NsVvtkzk= 41 | github.com/yuk7/wslreglib-go v1.0.1/go.mod h1:Y06oZI/N0M/ZRCJwnmdTgabRhVIKaz5xkD9R1Y+ybEY= 42 | golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 43 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 44 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 45 | golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= 46 | golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 47 | golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= 48 | golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 49 | golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= 50 | golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 51 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 52 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 53 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 54 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 55 | -------------------------------------------------------------------------------- /src/help/command.go: -------------------------------------------------------------------------------- 1 | package help 2 | 3 | import ( 4 | "github.com/yuk7/wsldl/lib/cmdline" 5 | ) 6 | 7 | // GetCommand returns the help command structure 8 | func GetCommand() cmdline.Command { 9 | return cmdline.Command{ 10 | Names: []string{"help", "--help", "-h", "/?"}, 11 | Help: func(distroName string, isListQuery bool) string { 12 | return getHelpMessage() 13 | }, 14 | Run: func(distroName string, args []string) { 15 | println("Usage:") 16 | println(indentString(getHelpMessage())) 17 | }, 18 | } 19 | } 20 | 21 | // ShowHelp prints help message 22 | func ShowHelpFromCommands(commands []cmdline.Command, distroName string, args []string) { 23 | helpStrs := "" 24 | if len(args) > 0 { 25 | command, err := cmdline.FindCommandFromName(commands, args[0]) 26 | if err == nil { 27 | if command.Help != nil { 28 | help := command.Help(distroName, false) 29 | if help != "" { 30 | helpStrs += "\n" + indentString(help) + "\n" 31 | } 32 | } 33 | } 34 | } 35 | 36 | if len(args) == 0 || helpStrs == "" { 37 | for _, c := range commands { 38 | if c.Help != nil { 39 | help := c.Help(distroName, true) 40 | if help != "" { 41 | helpStrs += "\n" + indentString(help) + "\n" 42 | } 43 | } 44 | } 45 | } 46 | helpStrs = "Usage:" + helpStrs 47 | print(helpStrs) 48 | } 49 | -------------------------------------------------------------------------------- /src/help/help.go: -------------------------------------------------------------------------------- 1 | package help 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // getHelpMessage returns the help message 8 | func getHelpMessage() string { 9 | return "" + 10 | "help\n" + 11 | " - Print this usage message." 12 | } 13 | 14 | // indentString indents the message 15 | func indentString(message string) string { 16 | return " " + strings.ReplaceAll(message, "\n", "\n ") 17 | } 18 | -------------------------------------------------------------------------------- /src/install/command.go: -------------------------------------------------------------------------------- 1 | package install 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | "os/exec" 8 | "strconv" 9 | 10 | "github.com/yuk7/wsldl/lib/cmdline" 11 | "github.com/yuk7/wsldl/lib/preset" 12 | "github.com/yuk7/wsldl/lib/utils" 13 | "github.com/yuk7/wsllib-go" 14 | ) 15 | 16 | func GetCommandWithNoArgs() cmdline.Command { 17 | return cmdline.Command{ 18 | Names: []string{}, 19 | Help: func(distroName string, isListQuery bool) string { 20 | if !wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 21 | return getHelpMessageNoArgs() 22 | } 23 | return "" 24 | }, 25 | Run: execute, 26 | } 27 | } 28 | 29 | // GetCommand returns the install command structure 30 | func GetCommand() cmdline.Command { 31 | return cmdline.Command{ 32 | Names: []string{"install"}, 33 | Help: func(distroName string, isListQuery bool) string { 34 | if !wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 35 | return getHelpMessage() 36 | } 37 | return "" 38 | }, 39 | Run: execute, 40 | } 41 | } 42 | 43 | // execute is default install entrypoint 44 | func execute(name string, args []string) { 45 | if !wsllib.WslIsDistributionRegistered(name) { 46 | var rootPath string 47 | var rootFileSha256 string = "" 48 | var showProgress bool 49 | jsonPreset, _ := preset.ReadParsePreset() 50 | switch len(args) { 51 | case 0: 52 | rootPath = detectRootfsFiles() 53 | if jsonPreset.InstallFile != "" { 54 | rootPath = jsonPreset.InstallFile 55 | } 56 | rootFileSha256 = jsonPreset.InstallFileSha256 57 | showProgress = true 58 | 59 | case 1: 60 | showProgress = false 61 | if args[0] == "--root" { 62 | rootPath = detectRootfsFiles() 63 | if jsonPreset.InstallFile != "" { 64 | rootPath = jsonPreset.InstallFile 65 | } 66 | rootFileSha256 = jsonPreset.InstallFileSha256 67 | } else { 68 | rootPath = args[0] 69 | } 70 | 71 | default: 72 | utils.ErrorExit(os.ErrInvalid, true, true, false) 73 | } 74 | 75 | if args == nil { 76 | if isInstalledFilesExist() { 77 | var in string 78 | fmt.Printf("An old installation files has been found.\n") 79 | fmt.Printf("Do you want to rewrite and repair the installation information?\n") 80 | fmt.Printf("Type y/n:") 81 | fmt.Scan(&in) 82 | 83 | if in == "y" { 84 | err := repairRegistry(name) 85 | if err != nil { 86 | utils.ErrorExit(err, showProgress, true, showProgress) 87 | } 88 | utils.StdoutGreenPrintln("done.") 89 | return 90 | } 91 | } 92 | } 93 | 94 | err := Install(name, rootPath, rootFileSha256, showProgress) 95 | if err != nil { 96 | utils.ErrorExit(err, showProgress, true, args == nil) 97 | } 98 | 99 | if jsonPreset.WslVersion == 1 || jsonPreset.WslVersion == 2 { 100 | wslexe := utils.GetWindowsDirectory() + "\\System32\\wsl.exe" 101 | _, err = exec.Command(wslexe, "--set-version", name, strconv.Itoa(jsonPreset.WslVersion)).Output() 102 | } 103 | 104 | if err == nil { 105 | if showProgress { 106 | utils.StdoutGreenPrintln("Installation complete") 107 | } 108 | } else { 109 | utils.ErrorExit(err, showProgress, true, args == nil) 110 | } 111 | 112 | if args == nil { 113 | fmt.Fprintf(os.Stdout, "Press enter to continue...") 114 | bufio.NewReader(os.Stdin).ReadString('\n') 115 | } 116 | 117 | } else { 118 | utils.ErrorRedPrintln("ERR: [" + name + "] is already installed.") 119 | utils.ErrorExit(os.ErrInvalid, false, true, false) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/install/help.go: -------------------------------------------------------------------------------- 1 | package install 2 | 3 | // getHelpMessageNoArgs returns the help message 4 | func getHelpMessageNoArgs() string { 5 | return "" + 6 | "\n" + 7 | " - Install a new instance with default settings." 8 | } 9 | 10 | // getHelpMessage returns the help message 11 | func getHelpMessage() string { 12 | return "" + 13 | "install [rootfs file]\n" + 14 | " - Install a new instance with your given rootfs file\n" + 15 | " You can use .tar(.gz) or .ext4.vhdx(.gz)" 16 | } 17 | -------------------------------------------------------------------------------- /src/install/install.go: -------------------------------------------------------------------------------- 1 | package install 2 | 3 | import ( 4 | "compress/gzip" 5 | "crypto/sha256" 6 | "encoding/hex" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "math/rand" 11 | "os" 12 | "path/filepath" 13 | "strconv" 14 | "strings" 15 | "time" 16 | 17 | "github.com/yuk7/wsldl/lib/utils" 18 | "github.com/yuk7/wsllib-go" 19 | wslreg "github.com/yuk7/wslreglib-go" 20 | ) 21 | 22 | var ( 23 | defaultRootFiles = []string{ 24 | "install.tar", 25 | "install.tar.gz", 26 | "install.tgz", 27 | "install.tar.zst", 28 | "install.tar.xz", 29 | "install.wsl", 30 | "rootfs.tar", 31 | "rootfs.tar.gz", 32 | "rootfs.tgz", 33 | "rootfs.tar.zst", 34 | "rootfs.tar.xz", 35 | "rootfs.wsl", 36 | "install.ext4.vhdx", 37 | "install.ext4.vhdx.gz", 38 | } 39 | ) 40 | 41 | // Install installs distribution with default rootfs file names 42 | func Install(name string, rootPath string, sha256Sum string, showProgress bool) error { 43 | rootPathLower := strings.ToLower(rootPath) 44 | sha256Actual := "" 45 | if showProgress { 46 | fmt.Printf("Using: %s\n", rootPath) 47 | } 48 | 49 | if strings.HasPrefix(rootPathLower, "http://") || strings.HasPrefix(rootPathLower, "https://") { 50 | progressBarWidth := 0 51 | if showProgress { 52 | fmt.Println("Downloading...") 53 | progressBarWidth = 35 54 | } 55 | tmpRootFn := os.TempDir() 56 | if tmpRootFn == "" { 57 | return errors.New("failed to create temp directory") 58 | } 59 | rand.NewSource(time.Now().UnixNano()) 60 | tmpRootFn = tmpRootFn + "\\" + strconv.Itoa(rand.Intn(10000)) + filepath.Base(rootPath) 61 | defer os.Remove(tmpRootFn) 62 | var err error 63 | sha256Actual, err = utils.DownloadFile(rootPath, tmpRootFn, progressBarWidth) 64 | if err != nil { 65 | return err 66 | } 67 | rootPath = tmpRootFn 68 | rootPathLower = strings.ToLower(rootPath) 69 | fmt.Println() 70 | } else if sha256Sum != "" { 71 | if showProgress { 72 | fmt.Println("Calculating checksum...") 73 | } 74 | f, err := os.Open(rootPath) 75 | if err != nil { 76 | return err 77 | } 78 | defer f.Close() 79 | h := sha256.New() 80 | if _, err := io.Copy(h, f); err != nil { 81 | return err 82 | } 83 | sha256Actual = hex.EncodeToString(h.Sum(nil)) 84 | } 85 | 86 | if showProgress && sha256Actual != "" { 87 | fmt.Printf("Checksum(SHA256): %s\n", sha256Actual) 88 | } 89 | 90 | if sha256Sum != "" && sha256Actual != "" && sha256Sum != sha256Actual { 91 | return errors.New("checksum mismatch") 92 | } 93 | 94 | if showProgress { 95 | fmt.Println("Installing...") 96 | } 97 | 98 | if strings.HasSuffix(rootPathLower, "ext4.vhdx") || strings.HasSuffix(rootPathLower, "ext4.vhdx.gz") { 99 | return InstallExt4Vhdx(name, rootPath) 100 | } 101 | return InstallTar(name, rootPath) 102 | } 103 | 104 | func InstallTar(name string, rootPath string) error { 105 | err := wsllib.WslRegisterDistribution(name, rootPath) 106 | return err 107 | } 108 | 109 | func InstallExt4Vhdx(name string, rootPath string) error { 110 | // create empty tar 111 | tmptar := os.TempDir() 112 | if tmptar == "" { 113 | return errors.New("failed to create temp directory") 114 | } 115 | tmptar = tmptar + "\\em-vhdx-temp.tar" 116 | tmptarfp, err := os.Create(tmptar) 117 | if err != nil { 118 | return err 119 | } 120 | tmptarfp.Close() 121 | // initial empty instance entry 122 | err = wsllib.WslRegisterDistribution(name, tmptar) 123 | if err != nil { 124 | return err 125 | } 126 | os.Remove(tmptar) 127 | // get profile of instance 128 | prof, err := wslreg.GetProfileFromName(name) 129 | if prof.BasePath == "" { 130 | return err 131 | } 132 | // remove instance temporary 133 | err = wsllib.WslUnregisterDistribution(name) 134 | if err != nil { 135 | return err 136 | } 137 | // copy vhdx to destination directory 138 | src, err := os.Open(rootPath) 139 | if err != nil { 140 | return err 141 | } 142 | defer src.Close() 143 | dest, err := os.Create(prof.BasePath + "\\ext4.vhdx") 144 | if err != nil { 145 | return err 146 | } 147 | defer dest.Close() 148 | 149 | // uncompress and copy 150 | rootPathLower := strings.ToLower(rootPath) 151 | if strings.HasSuffix(rootPathLower, ".gz") { 152 | // compressed with gzip 153 | gr, err := gzip.NewReader(src) 154 | if err != nil { 155 | return err 156 | } 157 | _, err = io.Copy(dest, gr) 158 | if err != nil { 159 | return err 160 | } 161 | } else { 162 | // not compressed 163 | _, err = io.Copy(dest, src) 164 | if err != nil { 165 | return err 166 | } 167 | } 168 | 169 | // write registry 170 | prof.Flags |= wsllib.FlagEnableWsl2 171 | err = wslreg.WriteProfile(prof) 172 | return err 173 | } 174 | 175 | func detectRootfsFiles() string { 176 | efPath, _ := os.Executable() 177 | efDir := filepath.Dir(efPath) 178 | for _, rootFile := range defaultRootFiles { 179 | rootPath := filepath.Join(efDir, rootFile) 180 | _, err := os.Stat(rootPath) 181 | if err == nil { 182 | return rootPath 183 | } 184 | } 185 | return "rootfs.tar.gz" 186 | } 187 | -------------------------------------------------------------------------------- /src/install/repair.go: -------------------------------------------------------------------------------- 1 | package install 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | "path/filepath" 7 | 8 | "github.com/yuk7/wsllib-go" 9 | wslreg "github.com/yuk7/wslreglib-go" 10 | ) 11 | 12 | func isInstalledFilesExist() bool { 13 | efPath, _ := os.Executable() 14 | dir := filepath.Dir(efPath) 15 | 16 | _, err := os.Stat(dir + "\\ext4.vhdx") 17 | if err == nil { 18 | return true 19 | } 20 | _, err = os.Stat(dir + "\\rootfs") 21 | return err == nil 22 | } 23 | 24 | func repairRegistry(name string) error { 25 | efPath, _ := os.Executable() 26 | dir := filepath.Dir(efPath) 27 | 28 | // for rename instance 29 | prof, _ := wslreg.GetProfileFromBasePath(dir) 30 | if prof.BasePath != "" { 31 | // profile found, maybe executable renamed 32 | // write the new name to the registry 33 | prof.DistributionName = name 34 | return wslreg.WriteProfile(prof) 35 | } 36 | 37 | // for write new WSL2 configuration 38 | _, err := os.Stat(dir + "\\ext4.vhdx") 39 | if err == nil { 40 | prof := wslreg.GenerateProfile() 41 | prof.DistributionName = name 42 | prof.BasePath = dir 43 | 44 | prof.Flags |= wsllib.FlagEnableWsl2 45 | return wslreg.WriteProfile(prof) 46 | } 47 | // for write new WSL1 configuration 48 | _, err = os.Stat(dir + "\\rootfs") 49 | if err == nil { 50 | prof := wslreg.GenerateProfile() 51 | prof.DistributionName = name 52 | prof.BasePath = dir 53 | prof.Flags ^= wsllib.FlagEnableWsl2 54 | return wslreg.WriteProfile(prof) 55 | } 56 | 57 | return errors.New("repair failed") 58 | } 59 | -------------------------------------------------------------------------------- /src/isregd/command.go: -------------------------------------------------------------------------------- 1 | package isregd 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/yuk7/wsldl/lib/cmdline" 7 | "github.com/yuk7/wsllib-go" 8 | ) 9 | 10 | func GetCommand() cmdline.Command { 11 | return cmdline.Command{ 12 | Names: []string{"isregd"}, 13 | Run: execute, 14 | } 15 | } 16 | 17 | // execute is default isregd entrypoint. Exits with registerd status 18 | func execute(name string, args []string) { 19 | if wsllib.WslIsDistributionRegistered(name) { 20 | os.Exit(0) 21 | } 22 | os.Exit(1) 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/cmdline/cmdline.go: -------------------------------------------------------------------------------- 1 | package cmdline 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // RunSubCommand executes a subcommand 8 | func RunSubCommand(commands []Command, mismatch func(), distroName string, args []string) { 9 | if len(args) > 0 { 10 | command, err := FindCommandFromName(commands, args[0]) 11 | if err == nil { 12 | command.Run(distroName, args[1:]) 13 | return 14 | } 15 | } 16 | mismatch() 17 | } 18 | 19 | // FindCommandFromName finds a command by name 20 | func FindCommandFromName(commands []Command, commandName string) (Command, error) { 21 | for _, c := range commands { 22 | for _, n := range c.Names { 23 | if n == commandName { 24 | return c, nil 25 | } 26 | } 27 | } 28 | return Command{}, errors.New("command not found") 29 | } 30 | -------------------------------------------------------------------------------- /src/lib/cmdline/model_command.go: -------------------------------------------------------------------------------- 1 | package cmdline 2 | 3 | type Command struct { 4 | Names []string 5 | Help func(distroName string, isListQuery bool) string 6 | Run func(distroName string, args []string) 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/preset/model_preset.go: -------------------------------------------------------------------------------- 1 | package preset 2 | 3 | type Preset struct { 4 | WslVersion int `json:"wslversion,omitempty"` 5 | InstallFile string `json:"installfile,omitempty"` 6 | InstallFileSha256 string `json:"installfilesha256,omitempty"` 7 | } 8 | -------------------------------------------------------------------------------- /src/lib/preset/preset.go: -------------------------------------------------------------------------------- 1 | package preset 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/muhammadmuzzammil1998/jsonc" 8 | ) 9 | 10 | // ReadPresetJSON reads preset.json configuration json file 11 | func ReadPresetJSON() (res string, err error) { 12 | efPath, _ := os.Executable() 13 | dir := filepath.Dir(efPath) 14 | json := filepath.Join(dir, "preset.json") 15 | b, err := os.ReadFile(json) 16 | if err != nil { 17 | return 18 | } 19 | res = string(b) 20 | return 21 | } 22 | 23 | // ParsePresetJSON parses preset.json configuration json string 24 | func ParsePresetJSON(str string) (res Preset, err error) { 25 | var c Preset 26 | err = jsonc.Unmarshal([]byte(str), &c) 27 | if err != nil { 28 | return 29 | } 30 | res = c 31 | return 32 | } 33 | 34 | // ReadParsePreset reads and parses preset.json configuration file 35 | func ReadParsePreset() (conf Preset, err error) { 36 | json, err := ReadPresetJSON() 37 | if err != nil { 38 | return 39 | } 40 | conf, err = ParsePresetJSON(json) 41 | return 42 | } 43 | -------------------------------------------------------------------------------- /src/lib/utils/download.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/sha256" 5 | "encoding/hex" 6 | "io" 7 | "net/http" 8 | "os" 9 | 10 | "github.com/schollz/progressbar/v3" 11 | ) 12 | 13 | func DownloadFile(url, dest string, progressBarWidth int) (string, error) { 14 | req, err := http.NewRequest("GET", url, nil) 15 | if err != nil { 16 | return "", err 17 | } 18 | resp, err := http.DefaultClient.Do(req) 19 | if err != nil { 20 | return "", err 21 | } 22 | defer resp.Body.Close() 23 | 24 | f, err := os.OpenFile(dest, os.O_CREATE|os.O_WRONLY, 0644) 25 | if err != nil { 26 | return "", err 27 | } 28 | defer f.Close() 29 | 30 | sum := sha256.New() 31 | 32 | size := resp.ContentLength 33 | 34 | if size == 0 { 35 | if progressBarWidth > 0 { 36 | ErrorRedPrintln("Failed to get total file size") 37 | } 38 | size = -1 39 | } 40 | 41 | bar := progressbar.NewOptions64( 42 | size, 43 | progressbar.OptionSetVisibility(false), 44 | ) 45 | if progressBarWidth > 0 { 46 | bar = progressbar.NewOptions64( 47 | size, 48 | progressbar.OptionSetWidth(progressBarWidth), 49 | progressbar.OptionShowBytes(true), 50 | progressbar.OptionShowCount(), 51 | ) 52 | } else if progressBarWidth < 0 { 53 | bar = progressbar.NewOptions64( 54 | -1, 55 | progressbar.OptionShowBytes(true), 56 | progressbar.OptionShowCount(), 57 | ) 58 | } 59 | _, err = io.Copy(io.MultiWriter(f, bar, sum), resp.Body) 60 | if err != nil && err != io.EOF { 61 | return "", err 62 | } 63 | 64 | sha256String := hex.EncodeToString(sum.Sum(nil)) 65 | 66 | return sha256String, nil 67 | } 68 | -------------------------------------------------------------------------------- /src/lib/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | "syscall" 11 | "unsafe" 12 | 13 | "github.com/fatih/color" 14 | ps "github.com/mitchellh/go-ps" 15 | ) 16 | 17 | const ( 18 | // ConsoleProcNames is console process list for detect parent console process 19 | ConsoleProcNames = "cmd.exe,powershell.exe,wsl.exe,WindowsTerminal.exe,flute.exe,FluentTerminal.SystemTray.exe,winpty-agent.exe" 20 | // SpecialDirs is define path of special dirs 21 | SpecialDirs = "SystemDrive:,SystemRoot:,SystemRoot:System32,USERPROFILE:" 22 | ) 23 | 24 | // DQEscapeString is escape string with double quote 25 | func DQEscapeString(str string) string { 26 | if strings.Contains(str, " ") { 27 | str = strings.Replace(str, "\"", "\\\"", -1) 28 | str = "\"" + str + "\"" 29 | } 30 | return str 31 | } 32 | 33 | // IsParentConsole gets is parent process is console or not 34 | func IsParentConsole() (res bool, err error) { 35 | list := strings.Split(ConsoleProcNames, ",") 36 | info, err := ps.FindProcess(syscall.Getppid()) 37 | if err != nil { 38 | return 39 | } 40 | 41 | parentName := info.Executable() 42 | for _, item := range list { 43 | if strings.EqualFold(parentName, item) { 44 | res = true 45 | return 46 | } 47 | } 48 | 49 | _, err = ps.FindProcess(info.PPid()) 50 | if err != nil { 51 | return 52 | } 53 | for _, item := range list { 54 | if strings.EqualFold(parentName, item) { 55 | res = true 56 | return 57 | } 58 | } 59 | 60 | res = false 61 | return 62 | } 63 | 64 | // GetWindowsDirectory gets windows direcotry path 65 | func GetWindowsDirectory() string { 66 | dir := os.Getenv("SYSTEMROOT") 67 | if dir != "" { 68 | return dir 69 | } 70 | dir = os.Getenv("WINDIR") 71 | if dir != "" { 72 | return dir 73 | } 74 | return "C:\\WINDOWS" 75 | 76 | } 77 | 78 | // IsCurrentDirSpecial gets whether the current directory is special (Windows, USEPROFILE) 79 | func IsCurrentDirSpecial() bool { 80 | cdir, err := filepath.Abs(".") 81 | if err != nil { 82 | return true 83 | } 84 | sdarr := strings.Split(SpecialDirs, ",") 85 | for _, item := range sdarr { 86 | splititem := strings.Split(item, ":") 87 | itemdir := "" 88 | if splititem[0] != "" { 89 | itemdir = os.Getenv(splititem[0]) 90 | } 91 | itemdir, err = filepath.Abs(itemdir + "\\" + splititem[1]) 92 | if err != nil { 93 | return true 94 | } 95 | if strings.EqualFold(cdir, itemdir) { 96 | return true 97 | } 98 | } 99 | return false 100 | } 101 | 102 | // CreateProcessAndWait creating process and wait it 103 | func CreateProcessAndWait(commandLine string) (res int, err error) { 104 | pCommandLine, _ := syscall.UTF16PtrFromString(commandLine) 105 | si := syscall.StartupInfo{} 106 | pi := syscall.ProcessInformation{} 107 | 108 | err = syscall.CreateProcess(nil, pCommandLine, nil, nil, false, 0, nil, nil, &si, &pi) 109 | if err != nil { 110 | return 111 | } 112 | _, err = syscall.WaitForSingleObject(pi.Process, syscall.INFINITE) 113 | var exitCode = uint32(0) 114 | syscall.GetExitCodeProcess(pi.Process, &exitCode) 115 | res = int(exitCode) 116 | return 117 | } 118 | 119 | // FreeConsole calls FreeConsole API in Windows kernel32 120 | func FreeConsole() error { 121 | kernel32, _ := syscall.LoadDLL("Kernel32.dll") 122 | proc, err := kernel32.FindProc("FreeConsole") 123 | if err != nil { 124 | return err 125 | } 126 | proc.Call() 127 | return nil 128 | } 129 | 130 | // AllocConsole calls AllocConsole API in Windows kernel32 131 | func AllocConsole() { 132 | kernel32, _ := syscall.LoadDLL("Kernel32.dll") 133 | alloc, _ := kernel32.FindProc("AllocConsole") 134 | alloc.Call() 135 | 136 | hout, _ := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE) 137 | herr, _ := syscall.GetStdHandle(syscall.STD_ERROR_HANDLE) 138 | hin, _ := syscall.GetStdHandle(syscall.STD_INPUT_HANDLE) 139 | os.Stdout = os.NewFile(uintptr(hout), "/dev/stdout") 140 | os.Stderr = os.NewFile(uintptr(herr), "/dev/stderr") 141 | os.Stdin = os.NewFile(uintptr(hin), "/dev/stdin") 142 | } 143 | 144 | // SetConsoleTitle calls SetConsoleTitleW API in Windows kernel32 145 | func SetConsoleTitle(title string) { 146 | kernel32, _ := syscall.LoadDLL("Kernel32.dll") 147 | proc, _ := kernel32.FindProc("SetConsoleTitleW") 148 | pTitle, _ := syscall.UTF16PtrFromString(title) 149 | syscall.SyscallN(proc.Addr(), 1, uintptr(unsafe.Pointer(pTitle))) 150 | } 151 | 152 | // ErrorExit shows error message and exit 153 | func ErrorExit(err error, showmsg bool, showcolor bool, pause bool) { 154 | var errno syscall.Errno 155 | if err == nil { 156 | if showmsg { 157 | ErrorRedPrintln("ERR: unknown error") 158 | Exit(pause, 1) 159 | } 160 | } 161 | if showmsg { 162 | if showcolor { 163 | ErrorRedPrintln("ERR: " + err.Error()) 164 | } else { 165 | fmt.Fprintln(os.Stderr, "ERR: "+err.Error()) 166 | } 167 | 168 | } 169 | if errors.As(err, &errno) { 170 | if showmsg { 171 | fmt.Fprintf(os.Stderr, "HRESULT: 0x%x\n", int(errno)) 172 | } 173 | Exit(pause, int(errno)) 174 | } else if err == os.ErrInvalid { 175 | if showmsg { 176 | efPath, _ := os.Executable() 177 | exeName := filepath.Base(efPath) 178 | fmt.Fprintln(os.Stderr, "Your command may be incorrect.") 179 | fmt.Fprintf(os.Stderr, "You can get help with `%s help`.\n", exeName) 180 | } 181 | } else if !strings.HasPrefix(fmt.Sprintf("%#v", err), "&errors.errorString{") { 182 | // errors.errorString only contains string, so skip it 183 | } else { 184 | if showmsg { 185 | fmt.Fprintf(os.Stderr, "%#v\n", err) 186 | } 187 | } 188 | Exit(pause, 1) 189 | } 190 | 191 | // Exit exits program 192 | func Exit(pause bool, exitCode int) { 193 | if pause { 194 | fmt.Fprintf(os.Stdout, "Press enter to exit...") 195 | bufio.NewReader(os.Stdin).ReadString('\n') 196 | } 197 | os.Exit(exitCode) 198 | } 199 | 200 | // ErrorRedPrintln shows red string to stderr 201 | func ErrorRedPrintln(str string) { 202 | color.New(color.FgRed).Fprintln(color.Error, str) 203 | } 204 | 205 | // StdoutGreenPrintln shows green string to stdout 206 | func StdoutGreenPrintln(str string) { 207 | color.New(color.FgGreen).Fprintln(color.Output, str) 208 | } 209 | -------------------------------------------------------------------------------- /src/lib/wtutils/model_config.go: -------------------------------------------------------------------------------- 1 | package wtutils 2 | 3 | // Config is json root Config 4 | type Config struct { 5 | Profiles struct { 6 | ProfileList []Profile `json:"list"` 7 | } `json:"profiles"` 8 | } 9 | 10 | // Profile is profile of terminal 11 | type Profile struct { 12 | Name string `json:"name"` 13 | CommandLine string `json:"commandline"` 14 | GUID string `json:"guid"` 15 | Source string `json:"source"` 16 | } 17 | -------------------------------------------------------------------------------- /src/lib/wtutils/wtutils.go: -------------------------------------------------------------------------------- 1 | package wtutils 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/muhammadmuzzammil1998/jsonc" 7 | uuid "github.com/satori/go.uuid" 8 | "golang.org/x/text/encoding/unicode" 9 | ) 10 | 11 | const ( 12 | // WTPackageName is package name of Windows Terminal 13 | WTPackageName = "Microsoft.WindowsTerminal_8wekyb3d8bbwe" 14 | // WTProfileNameSpaceUUID is uuid of Windows Terminal Profile NameSpace 15 | WTProfileNameSpaceUUID = "2bde4a90-d05f-401c-9492-e40884ead1d8" 16 | ) 17 | 18 | // ReadWTConfigJSON reads Windows Terminal configuration json file 19 | func ReadWTConfigJSON() (res string, err error) { 20 | json := os.Getenv("LOCALAPPDATA") 21 | json = json + "\\Packages\\" + WTPackageName + "\\LocalState\\settings.json" 22 | 23 | b, err := os.ReadFile(json) 24 | if err != nil { 25 | return 26 | } 27 | res = string(b) 28 | return 29 | } 30 | 31 | // ParseWTConfigJSON parses Windows Terminal configuration json string 32 | func ParseWTConfigJSON(str string) (res Config, err error) { 33 | var c Config 34 | err = jsonc.Unmarshal([]byte(str), &c) 35 | if err != nil { 36 | return 37 | } 38 | res = c 39 | return 40 | } 41 | 42 | // ReadParseWTConfig reads and parses Windows Terminal configuration json file 43 | func ReadParseWTConfig() (conf Config, err error) { 44 | json, err := ReadWTConfigJSON() 45 | if err != nil { 46 | return 47 | } 48 | conf, err = ParseWTConfigJSON(json) 49 | return 50 | } 51 | 52 | // CreateProfileGUID creates Windows Terminal GUID (based on uuidv5 but utf16le) 53 | func CreateProfileGUID(str string) string { 54 | uuidNS, _ := uuid.FromString(WTProfileNameSpaceUUID) 55 | 56 | utf16le := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM) 57 | utfEncoder := utf16le.NewEncoder() 58 | ut16LeEncodedMessage, _ := utfEncoder.String(str) 59 | 60 | uuid := uuid.NewV5(uuidNS, ut16LeEncodedMessage) 61 | return uuid.String() 62 | } 63 | -------------------------------------------------------------------------------- /src/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/yuk7/wsldl/backup" 8 | "github.com/yuk7/wsldl/clean" 9 | "github.com/yuk7/wsldl/config" 10 | "github.com/yuk7/wsldl/get" 11 | "github.com/yuk7/wsldl/help" 12 | "github.com/yuk7/wsldl/install" 13 | "github.com/yuk7/wsldl/isregd" 14 | "github.com/yuk7/wsldl/lib/cmdline" 15 | "github.com/yuk7/wsldl/lib/utils" 16 | "github.com/yuk7/wsldl/run" 17 | "github.com/yuk7/wsldl/version" 18 | "github.com/yuk7/wsllib-go" 19 | ) 20 | 21 | // main is the entry point of the application 22 | func main() { 23 | efPath, _ := os.Executable() 24 | name := filepath.Base(efPath[:len(efPath)-len(filepath.Ext(efPath))]) 25 | 26 | var commands = []cmdline.Command{ 27 | isregd.GetCommand(), 28 | version.GetCommand(), 29 | install.GetCommandWithNoArgs(), 30 | install.GetCommand(), 31 | run.GetCommandWithNoArgs(), 32 | run.GetCommand(), 33 | run.GetCommandP(), 34 | config.GetCommand(), 35 | get.GetCommand(), 36 | backup.GetCommand(), 37 | clean.GetCommand(), 38 | } 39 | 40 | var helpCommand = help.GetCommand() 41 | var commandsWithHelp = append(commands, cmdline.Command{ 42 | Names: helpCommand.Names, 43 | Help: helpCommand.Help, 44 | Run: func(distroName string, args []string) { 45 | help.ShowHelpFromCommands( 46 | append(commands, helpCommand), distroName, os.Args[2:], 47 | ) 48 | }, 49 | }) 50 | 51 | if len(os.Args) > 1 { 52 | cmdline.RunSubCommand( 53 | commandsWithHelp, 54 | func() { 55 | utils.ErrorExit(os.ErrInvalid, true, true, false) 56 | }, 57 | name, 58 | os.Args[1:], 59 | ) 60 | 61 | } else { 62 | if !wsllib.WslIsDistributionRegistered(name) { 63 | install.GetCommand().Run(name, nil) 64 | } else { 65 | run.GetCommandWithNoArgs().Run(name, nil) 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/run/command.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/yuk7/wsldl/lib/cmdline" 9 | "github.com/yuk7/wsldl/lib/utils" 10 | "github.com/yuk7/wsllib-go" 11 | wslreg "github.com/yuk7/wslreglib-go" 12 | ) 13 | 14 | // GetCommandWithNoArgs returns the run command structure with no arguments 15 | func GetCommandWithNoArgs() cmdline.Command { 16 | return cmdline.Command{ 17 | Names: []string{}, 18 | Help: func(distroName string, isListQuery bool) string { 19 | if wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 20 | return getHelpMessageNoArgs() 21 | } else { 22 | return "" 23 | } 24 | }, 25 | Run: executeNoArgs, 26 | } 27 | } 28 | 29 | // GetCommand returns the run command structure 30 | func GetCommand() cmdline.Command { 31 | return cmdline.Command{ 32 | Names: []string{"run", "-c", "/c"}, 33 | Help: func(distroName string, isListQuery bool) string { 34 | if wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 35 | return getHelpMessage() 36 | } else { 37 | return "" 38 | } 39 | }, 40 | Run: execute, 41 | } 42 | } 43 | 44 | // GetCommandP returns the runp command structure 45 | func GetCommandP() cmdline.Command { 46 | return cmdline.Command{ 47 | Names: []string{"runp", "-p", "/p"}, 48 | Help: func(distroName string, isListQuery bool) string { 49 | if wsllib.WslIsDistributionRegistered(distroName) || !isListQuery { 50 | return getHelpMessageP() 51 | } else { 52 | return "" 53 | } 54 | }, 55 | Run: executeP, 56 | } 57 | } 58 | 59 | // execute is default run entrypoint. 60 | func execute(name string, args []string) { 61 | command := "" 62 | for _, s := range args { 63 | command = command + " " + utils.DQEscapeString(s) 64 | } 65 | var inheritpath = true 66 | if args == nil { 67 | inheritpath = !utils.IsCurrentDirSpecial() 68 | } 69 | exitCode, err := wsllib.WslLaunchInteractive(name, command, inheritpath) 70 | if err != nil { 71 | utils.ErrorExit(err, true, true, false) 72 | } else { 73 | os.Exit(int(exitCode)) 74 | } 75 | } 76 | 77 | // executeP runs execute function with Path Translator 78 | func executeP(name string, args []string) { 79 | var convArgs []string 80 | for _, s := range args { 81 | if strings.Contains(s, "\\") { 82 | s = strings.Replace(s, "\\", "/", -1) 83 | s = utils.DQEscapeString(s) 84 | out, exitCode, err := ExecRead(name, "wslpath -u "+s) 85 | if err != nil || exitCode != 0 { 86 | utils.ErrorRedPrintln("ERR: Failed to Path Translation") 87 | fmt.Fprintf(os.Stderr, "ExitCode: 0x%x\n", int(exitCode)) 88 | if err != nil { 89 | utils.ErrorExit(err, true, true, false) 90 | } 91 | os.Exit(int(exitCode)) 92 | } 93 | convArgs = append(convArgs, out) 94 | } else { 95 | convArgs = append(convArgs, s) 96 | } 97 | } 98 | 99 | execute(name, convArgs) 100 | } 101 | 102 | // executeNoArgs runs distro, but use terminal settings 103 | func executeNoArgs(name string, args []string) { 104 | efPath, _ := os.Executable() 105 | profile, _ := wslreg.GetProfileFromName(name) 106 | 107 | // repair when the installation is moved 108 | if profile.BasePath != "" { 109 | _, err := os.Stat(profile.BasePath) 110 | if os.IsNotExist(err) { 111 | if isInstalledFilesExist() { 112 | var in string 113 | fmt.Printf("This instance (%s) BasePath is not exist.\n", name) 114 | fmt.Printf("Do you want to repair the installation information?\n") 115 | fmt.Printf("Type y/n:") 116 | fmt.Scan(&in) 117 | 118 | if in == "y" { 119 | err := repairRegistry(profile) 120 | if err != nil { 121 | utils.ErrorExit(err, true, true, true) 122 | } 123 | utils.StdoutGreenPrintln("done.") 124 | utils.Exit(true, 0) 125 | } 126 | } 127 | } 128 | } 129 | 130 | b, err := utils.IsParentConsole() 131 | if err != nil { 132 | b = true 133 | } 134 | if !b { 135 | switch profile.WsldlTerm { 136 | case wslreg.FlagWsldlTermWT: 137 | utils.FreeConsole() 138 | ExecWindowsTerminal(name) 139 | os.Exit(0) 140 | 141 | case wslreg.FlagWsldlTermFlute: 142 | utils.FreeConsole() 143 | exe := os.Getenv("LOCALAPPDATA") 144 | exe = utils.DQEscapeString(exe + "\\Microsoft\\WindowsApps\\53621FSApps.FluentTerminal_87x1pks76srcp\\flute.exe") 145 | 146 | cmd := exe + " run " + utils.DQEscapeString(efPath+" run") 147 | res, err := utils.CreateProcessAndWait(cmd) 148 | if err != nil { 149 | utils.AllocConsole() 150 | fmt.Fprintln(os.Stderr, "ERR: Failed to launch the terminal process") 151 | fmt.Fprintf(os.Stderr, "%s\n", exe) 152 | utils.ErrorExit(err, true, false, true) 153 | } 154 | os.Exit(res) 155 | } 156 | 157 | // Parent isn't console, launch instance with default conhost 158 | // Get the name from the registry to be case sensitive. 159 | if profile.DistributionName != "" { 160 | name = profile.DistributionName 161 | } 162 | 163 | utils.SetConsoleTitle(name) 164 | execute(name, nil) 165 | } else { 166 | execute(name, nil) 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/run/help.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | func getHelpMessageNoArgs() string { 4 | return "" + 5 | "\n" + 6 | " - Open a new shell with your default settings. \n" + 7 | " Inherit current directory (with exception that %USERPROFILE% is changed to $HOME)." 8 | } 9 | 10 | // getHelpMessage returns the help message for the run command 11 | func getHelpMessage() string { 12 | return "" + 13 | "run \n" + 14 | " - Run the given command line in that instance. Inherit current directory." 15 | } 16 | 17 | // getHelpMessageP returns the help message for the runp command 18 | func getHelpMessageP() string { 19 | return "" + 20 | "runp \n" + 21 | " - Run the given command line in that instance after converting its path." 22 | } 23 | -------------------------------------------------------------------------------- /src/run/repair.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | wslreg "github.com/yuk7/wslreglib-go" 8 | ) 9 | 10 | func isInstalledFilesExist() bool { 11 | efPath, _ := os.Executable() 12 | dir := filepath.Dir(efPath) 13 | 14 | _, err := os.Stat(dir + "\\ext4.vhdx") 15 | if err == nil { 16 | return true 17 | } 18 | _, err = os.Stat(dir + "\\rootfs") 19 | return err == nil 20 | } 21 | 22 | func repairRegistry(profile wslreg.Profile) error { 23 | efPath, _ := os.Executable() 24 | dir := filepath.Dir(efPath) 25 | 26 | profile.BasePath = dir 27 | return wslreg.WriteProfile(profile) 28 | } 29 | -------------------------------------------------------------------------------- /src/run/run.go: -------------------------------------------------------------------------------- 1 | package run 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | "syscall" 8 | 9 | "github.com/yuk7/wsldl/lib/utils" 10 | "github.com/yuk7/wsldl/lib/wtutils" 11 | "github.com/yuk7/wsllib-go" 12 | wslreg "github.com/yuk7/wslreglib-go" 13 | ) 14 | 15 | // ExecRead execs command and read output 16 | func ExecRead(name, command string) (out string, exitCode uint32, err error) { 17 | stdin := syscall.Handle(0) 18 | stdout := syscall.Handle(0) 19 | stdintmp := syscall.Handle(0) 20 | stdouttmp := syscall.Handle(0) 21 | sa := syscall.SecurityAttributes{InheritHandle: 1, SecurityDescriptor: 0} 22 | 23 | syscall.CreatePipe(&stdin, &stdintmp, &sa, 0) 24 | syscall.CreatePipe(&stdout, &stdouttmp, &sa, 0) 25 | 26 | handle, err := wsllib.WslLaunch(name, command, true, stdintmp, stdouttmp, stdouttmp) 27 | syscall.WaitForSingleObject(handle, syscall.INFINITE) 28 | syscall.GetExitCodeProcess(handle, &exitCode) 29 | buf := make([]byte, syscall.MAX_LONG_PATH) 30 | var length uint32 31 | 32 | syscall.ReadFile(stdout, buf, &length, nil) 33 | 34 | //[]byte -> string and cut to fit the length 35 | out = string(buf)[:length] 36 | if out[len(out)-1:] == "\n" { 37 | out = out[:len(out)-1] 38 | } 39 | return 40 | } 41 | 42 | // ExecWindowsTerminal executes Windows Terminal 43 | func ExecWindowsTerminal(name string) { 44 | // Get the name from the registry to be case sensitive. 45 | profile, _ := wslreg.GetProfileFromName(name) 46 | if profile.DistributionName != "" { 47 | name = profile.DistributionName 48 | } 49 | 50 | profileName := "" 51 | conf, err := wtutils.ReadParseWTConfig() 52 | if err == nil { 53 | guid := "{" + wtutils.CreateProfileGUID(name) + "}" 54 | for _, profile := range conf.Profiles.ProfileList { 55 | if profile.GUID == guid { 56 | profileName = profile.Name 57 | break 58 | } 59 | } 60 | if profileName == "" { 61 | for _, profile := range conf.Profiles.ProfileList { 62 | if strings.EqualFold(profile.Name, name) { 63 | profileName = profile.Name 64 | break 65 | } 66 | } 67 | } 68 | } 69 | 70 | exe := os.Getenv("LOCALAPPDATA") 71 | exe = utils.DQEscapeString(exe + "\\Microsoft\\WindowsApps\\" + wtutils.WTPackageName + "\\wt.exe") 72 | cmd := exe 73 | 74 | if profileName != "" { 75 | cmd = cmd + " -p " + utils.DQEscapeString(profileName) 76 | } else { 77 | efPath, _ := os.Executable() 78 | cmd = cmd + " " + utils.DQEscapeString(efPath) + " run" 79 | } 80 | 81 | res, err := utils.CreateProcessAndWait(cmd) 82 | if err != nil { 83 | utils.AllocConsole() 84 | fmt.Fprintln(os.Stderr, "ERR: Failed to launch the terminal process") 85 | fmt.Fprintln(os.Stderr, exe) 86 | utils.ErrorExit(err, true, false, true) 87 | } 88 | os.Exit(res) 89 | } 90 | -------------------------------------------------------------------------------- /src/version/command.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | 7 | "github.com/yuk7/wsldl/lib/cmdline" 8 | ) 9 | 10 | // GetCommand returns the version command structure 11 | func GetCommand() cmdline.Command { 12 | return cmdline.Command{ 13 | Names: []string{"version", "-v", "--version"}, 14 | Run: func(distroName string, args []string) { 15 | execute() 16 | }, 17 | } 18 | } 19 | 20 | // execute is default version entrypoint. prints version information 21 | func execute() { 22 | fmt.Printf("%s, version %s (%s)\n", project, version, runtime.GOARCH) 23 | fmt.Printf("%s\n", url) 24 | } 25 | -------------------------------------------------------------------------------- /src/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var ( 4 | project string = "wsldl2" 5 | version string = "Unknown" 6 | url string = "https://git.io/wsldl" 7 | ) 8 | -------------------------------------------------------------------------------- /src/versioninfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "FixedFileInfo": { 3 | "FileVersion": { 4 | "Major": 2, 5 | "Minor": 0, 6 | "Patch": 0, 7 | "Build": 0 8 | }, 9 | "ProductVersion": { 10 | "Major": 2, 11 | "Minor": 0, 12 | "Patch": 0, 13 | "Build": 0 14 | }, 15 | "FileFlagsMask": "3f", 16 | "FileFlags ": "00", 17 | "FileOS": "040004", 18 | "FileType": "01", 19 | "FileSubType": "00" 20 | }, 21 | "StringFileInfo": { 22 | "Comments": "", 23 | "CompanyName": "wsldl authors", 24 | "FileDescription": "wsldl, an advanced WSL launcher", 25 | "FileVersion": "2.0.0.0", 26 | "InternalName": "wsldl", 27 | "LegalCopyright": "Copyright (C) 2017-2025 wsldl authors", 28 | "LegalTrademarks": "", 29 | "OriginalFilename": "wsldl.exe", 30 | "PrivateBuild": "", 31 | "ProductName": "wsldl", 32 | "ProductVersion": "2.0.0.0", 33 | "SpecialBuild": "" 34 | }, 35 | "VarFileInfo": { 36 | "Translation": { 37 | "LangID": "0409", 38 | "CharsetID": "04B0" 39 | } 40 | }, 41 | "IconPath": "", 42 | "ManifestPath": "" 43 | } --------------------------------------------------------------------------------