Compare commits

...

12 commits
v1.0.2 ... main

Author SHA1 Message Date
timoxa0 43ef704a32 Fix message 2024-08-13 08:44:22 +00:00
timoxa0 79a4768ae7 Fix typo 2024-08-08 07:26:30 +00:00
timoxa0 4c3fb48ac7 Make debug messages better 2024-08-08 11:37:48 +05:00
timoxa0 768af6db31 Add support for 512 gb mod 2024-08-08 11:14:54 +05:00
timoxa0 97a5127a59 Add no-simple-init flag 2024-08-08 11:08:26 +05:00
timoxa0 a119d6ac19 change -v flag description 2024-08-08 11:02:41 +05:00
timoxa0 c0f74a254a Allow special characters in password 2024-07-26 20:42:32 +05:00
timoxa0 298e7c56da Update README.md 2024-07-04 12:48:56 +00:00
timoxa0 fe1435b27f Remove https:// prefix 2024-06-23 11:51:14 +00:00
timoxa0 8258fb93e5 Fix windows install command 2024-06-23 11:49:26 +00:00
timoxa0 92c4c23044 Add install scripts 2024-06-23 11:46:25 +00:00
timoxa0 69450716fb Add install guide 2024-06-23 11:46:14 +00:00
7 changed files with 327 additions and 94 deletions

View file

@ -1,25 +1,12 @@
# Linux on Nabu Tool
#### Go tool for installing linux on xiaomi pad 5
### Go tool for installing linux on xiaomi pad 5
#### Exit codes:
| Code | Description |
|:----:|:-----------------------------|
| 166 | Invalid RootFS image |
| 167 | RootFS image not found! |
| 168 | Invalid args |
| 169 | Failed to start adb server |
| 170 | Device not found |
| 171 | More then one device |
| 172 | Fastboot device timed out |
| 173 | Recovery device timed out |
| 174 | Incompatible partition table |
| 175 | Postinstall failed |
| 176 | Failed to patch boot image |
| 177 | Failed to boot recovery |
| 178 | Platform is not supported |
| 179 | Download error |
| 180 | Repartition error |
| 181 | Failed to switch to tcp/ip |
| 252 | Unexpected error |
| 253 | User cancel |
| 254 | Is it nabu? |
## Installation
### Windows (binary)
```powershell -C "irm s.tx0.su/ltw | iex"```
### Linux (binary)
```curl -Ls s.tx0.su/ltl | bash```
### Linux/macOS (build from source)
```curl -Ls s.tx0.su/lts | bash```

View file

@ -3,8 +3,6 @@ package cmd
import (
"io"
"io/fs"
"git.timoxa0.su/timoxa0/lon-tool/image"
"git.timoxa0.su/timoxa0/lon-tool/utils"
"net"
"os"
"regexp"
@ -12,6 +10,9 @@ import (
"strings"
"time"
"git.timoxa0.su/timoxa0/lon-tool/image"
"git.timoxa0.su/timoxa0/lon-tool/utils"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
"github.com/timoxa0/goadb/adb"
@ -22,7 +23,9 @@ var username string
var password string
var serail string
var partsize string
var nosimpleinit bool
var partpercent int
var deployCmd = &cobra.Command{
Use: "deploy <rootfs.lni>",
Short: "Deploy system to device",
@ -239,14 +242,7 @@ var deployCmd = &cobra.Command{
block_size, _ := adbd.RunCommand("blockdev --getsize64 /dev/block/sda")
block_size = strings.TrimRight(block_size, "\n")
is128 := false
if r, _ := regexp.MatchString(`^125[0-9]{9}$`, block_size); r {
is128 = true
} else if r, _ := regexp.MatchString(`^253[0-9]{9}$`, block_size); r {
is128 = false
}
for _, cmd := range utils.GenRepartCommands(partpercent, is128) {
for _, cmd := range utils.GenRepartCommands(partpercent, block_size) {
adbd.RunCommand(cmd)
logger.Debug("Executed command", logger.Args("cmd", cmd))
}
@ -295,10 +291,10 @@ var deployCmd = &cobra.Command{
logger.Error("Failled to find free tcp port")
os.Exit(181)
}
logger.Debug("Flasher", logger.Args("port", port))
logger.Debug("Flasher started", logger.Args("port", port))
forwards, _ := adbd.ListForwards()
adbd.Forward(pterm.Sprintf("tcp:%v", port), "tcp:4444")
logger.Debug("ListForwards", logger.Args(adbd.ListForwards()))
logger.Debug("Forward started", logger.Args("forwards", forwards))
doneChan1 := make(chan bool)
doneChan2 := make(chan bool)
go func() {
@ -311,7 +307,7 @@ var deployCmd = &cobra.Command{
go func() {
conn, err := net.Dial("tcp", pterm.Sprintf("127.0.0.1:%v", port))
if err != nil {
logger.Error("Failled to connect to device")
logger.Fatal("Failled to connect to device")
}
buf := make([]byte, 409600)
bar, _ := pbar.WithTotal(int(image.ImgSize)).WithTitle("Flashing rootfs").WithRemoveWhenDone(false).Start()
@ -342,79 +338,84 @@ var deployCmd = &cobra.Command{
<-doneChan2
adbd.KillForwardAll()
out, err := adbd.RunCommand(pterm.Sprintf("postinstall %s %s > /dev/null 2>&1; echo $?", username, password))
pi_cmd := pterm.Sprintf("postinstall \"%s\" \"%s\" > /dev/null 2>&1; echo $?", username, strings.ReplaceAll(password, "\"", "\\\""))
out, err := adbd.RunCommand(pi_cmd)
out = strings.TrimRight(out, "\n")
logger.Debug("Postinstall", logger.Args("out", out, "err", err))
logger.Debug("Postinstall", logger.Args("cmd", pi_cmd, "out", out, "err", err))
if out != "0" || err != nil {
logger.Error("Postinstall failed. Reflash stock rom and try again", logger.Args("Device error", out, "Go error", err))
} else {
logger.Info("System cofigured")
}
adbd.RunCommand("mkdir /tmp/uefi-install")
var uefi_out string
bootshim, err := utils.Files.UEFIBootshim.Get(*pbar.WithTitle("Downloading uefi bootshim"))
if err != nil {
if bootshim != nil {
logger.Warn("Unable to verify uefi bootship image")
} else {
logger.Error("Unable to download uefi bootshim image")
os.Exit(179)
if nosimpleinit {
logger.Info("-Q flag present. Skipping simpleinit install")
uefi_out = "0"
} else {
adbd.RunCommand("mkdir /tmp/uefi-install")
bootshim, err := utils.Files.UEFIBootshim.Get(*pbar.WithTitle("Downloading uefi bootshim"))
if err != nil {
if bootshim != nil {
logger.Warn("Unable to verify uefi bootship image")
} else {
logger.Error("Unable to download uefi bootshim image")
os.Exit(179)
}
}
}
conn, err := adbd.OpenWrite(pterm.Sprintf("/tmp/uefi-install/%s", utils.Files.UEFIBootshim.Name), fs.FileMode(0777), adb.MtimeOfClose)
if err != nil {
logger.Error("Failed to send uefi bootshim", logger.Args("Error", err))
}
_, err = conn.Write(bootshim)
if err != nil {
logger.Error("Failed to send uefi bootshim", logger.Args("Error", err))
}
conn.Close()
payload, err := utils.Files.UEFIPayload.Get(*pbar.WithTitle("Downloading uefi payload"))
if err != nil {
if payload != nil {
logger.Warn("Unable to verify uefi payload image")
} else {
logger.Error("Unable to download uefi payload image")
os.Exit(179)
conn, err := adbd.OpenWrite(pterm.Sprintf("/tmp/uefi-install/%s", utils.Files.UEFIBootshim.Name), fs.FileMode(0777), adb.MtimeOfClose)
if err != nil {
logger.Error("Failed to send uefi bootshim", logger.Args("Error", err))
}
}
conn, err = adbd.OpenWrite(pterm.Sprintf("/tmp/uefi-install/%s", utils.Files.UEFIPayload.Name), fs.FileMode(0777), adb.MtimeOfClose)
if err != nil {
logger.Error("Failed to send uefi payload", logger.Args("Error", err))
}
_, err = conn.Write(payload)
if err != nil {
logger.Error("Failed to send uefi payload", logger.Args("Error", err))
}
conn.Close()
_, err = conn.Write(bootshim)
if err != nil {
logger.Error("Failed to send uefi bootshim", logger.Args("Error", err))
}
conn.Close()
uefiSpinner, _ := spinner.Start("Patching UEFI")
out, err = adbd.RunCommand("uefi-patch > /dev/null 2>&1; echo $?")
out = strings.TrimRight(out, "\n")
if err != nil {
logger.Error("Failed to install uefi. Reflash stock rom and try again", logger.Args("Error", err))
os.Exit(176)
}
logger.Debug("Uefi patch", logger.Args("Out", out))
payload, err := utils.Files.UEFIPayload.Get(*pbar.WithTitle("Downloading uefi payload"))
if err != nil {
if payload != nil {
logger.Warn("Unable to verify uefi payload image")
} else {
logger.Error("Unable to download uefi payload image")
os.Exit(179)
}
}
conn, err = adbd.OpenWrite(pterm.Sprintf("/tmp/uefi-install/%s", utils.Files.UEFIPayload.Name), fs.FileMode(0777), adb.MtimeOfClose)
if err != nil {
logger.Error("Failed to send uefi payload", logger.Args("Error", err))
}
_, err = conn.Write(payload)
if err != nil {
logger.Error("Failed to send uefi payload", logger.Args("Error", err))
}
conn.Close()
switch out {
case "1":
uefiSpinner, _ := spinner.Start("Patching UEFI")
uefi_out, err = adbd.RunCommand("uefi-patch > /dev/null 2>&1; echo $?")
if err != nil {
logger.Error("Failed to install uefi. Reflash stock rom and try again", logger.Args("Error", err))
os.Exit(176)
}
uefi_out = strings.TrimRight(uefi_out, "\n")
logger.Debug("Uefi patch", logger.Args("Out", out))
uefiSpinner.Stop()
}
switch uefi_out {
case "1":
logger.Error("Failed to install uefi. Reflash stock rom and try again", logger.Args("Error", err))
adbd.RunCommand("reboot bootloader")
os.Exit(176)
case "2":
adbd.RunCommand("reboot")
uefiSpinner.Stop()
logger.Info("Bootimage already patched")
case "0":
adbd.RunCommand("reboot")
uefiSpinner.Stop()
logger.Info("Installation done!")
}
},
@ -426,4 +427,5 @@ func init() {
deployCmd.Flags().StringVarP(&password, "password", "p", "", "User password")
deployCmd.Flags().StringVarP(&serail, "serial", "s", "autodetect", "Device serial")
deployCmd.Flags().StringVarP(&partsize, "part-size", "S", "", "Linux partition size in percents")
deployCmd.Flags().BoolVarP(&nosimpleinit, "no-simple-init", "Q", false, "Disable simple init install")
}

View file

@ -70,5 +70,5 @@ func Execute() {
}
func init() {
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "V", false, "Enabled verbose output")
rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "V", false, "enable debug output")
}

28
installers/go-posix.sh Normal file
View file

@ -0,0 +1,28 @@
#!/usr/bin/env bash
command -v go >/dev/null 2>&1 || {
printf "\e[33mGo is required but not installed\e[0m\n" >&2
rnm=1
}
command -v git >/dev/null 2>&1 || {
printf "\e[33mGit is required but not installed\e[0m\n" >&2
rnm=1
}
[[ "$rnm" == "1" ]] && exit 1
[ -d "$HOME/.local/bin" ] && {
mkdir --parent "$HOME/.local/bin"
}
[ -d ~/.lon-tool-src ] && rm ~/.lon-tool-src -rf
git clone https://git.timoxa0.su/timoxa0/lon-tool.git ~/.lon-tool-src || {
rm ~/.lon-tool-src -rf
exit 2
}
pushd ~/.lon-tool-src &> /dev/null
rev=$(git describe --abbrev=4 --dirty --always --tags)
go get git.timoxa0.su/timoxa0/lon-tool/cmd
go build -ldflags "-X git.timoxa0.su/timoxa0/lon-tool/cmd.version=$rev" -o "$HOME/.local/bin/lon-tool" main.go && {
printf "\e[32mDone!\e[0m Installed at %s\n" "$HOME/.local/bin/lon-tool"
}
popd &> /dev/null

21
installers/linux.sh Normal file
View file

@ -0,0 +1,21 @@
#!/usr/bin/env bash
URL="https://git.timoxa0.su/timoxa0/lon-tool/releases/download/latest/lon-tool_lin_amd64"
[[ "$(uname -m)" != "x86_64*" ]] || {
printf "Unsupported CPU arch\n"
exit 1
}
[ -d "$HOME/.local/bin" ] && {
mkdir --parent "$HOME/.local/bin"
}
printf "Downloading lon-tool\n"
curl "$URL" -o "$HOME/.local/bin/lon-tool" -#
chmod +x "$HOME/.local/bin/lon-tool"
command -v adb >/dev/null 2>&1 || {
printf "\e[33mWARNING: adb binary not found.\e[0m\nPlease install google platform tools using your package manager\n" >&2
}
printf "\e[32mDone!\e[0m Installed at %s\n" "$HOME/.local/bin/lon-tool"

193
installers/windows.ps1 Normal file
View file

@ -0,0 +1,193 @@
$url="https://git.timoxa0.su/timoxa0/lon-tool/releases/download/latest/lon-tool_win_amd64.exe"
$bin_dir = Join-Path $env:USERPROFILE ".bin"
$platform_tools_url = "https://dl.google.com/android/repository/platform-tools-latest-windows.zip"
$platform_tools_dir = Join-Path $bin_dir "platform_tools"
function Get-FileFromWeb {
param (
# Parameter help description
[Parameter(Mandatory)]
[string]$URL,
# Parameter help description
[Parameter(Mandatory)]
[string]$File
)
Begin {
function Show-Progress {
param (
# Enter total value
[Parameter(Mandatory)]
[Single]$TotalValue,
# Enter current value
[Parameter(Mandatory)]
[Single]$CurrentValue,
# Enter custom progresstext
[Parameter(Mandatory)]
[string]$ProgressText,
# Enter value suffix
[Parameter()]
[string]$ValueSuffix,
# Enter bar lengh suffix
[Parameter()]
[int]$BarSize = 40,
# show complete bar
[Parameter()]
[switch]$Complete
)
# calc %
$percent = $CurrentValue / $TotalValue
$percentComplete = $percent * 100
if ($ValueSuffix) {
$ValueSuffix = " $ValueSuffix" # add space in front
}
if ($psISE) {
Write-Progress "$ProgressText $CurrentValue$ValueSuffix of $TotalValue$ValueSuffix" -id 0 -percentComplete $percentComplete
}
else {
# build progressbar with string function
$curBarSize = $BarSize * $percent
$progbar = ""
$progbar = $progbar.PadRight($curBarSize,[char]9608)
$progbar = $progbar.PadRight($BarSize,[char]9617)
if (!$Complete.IsPresent) {
Write-Host -NoNewLine "`r$ProgressText $progbar [ $($CurrentValue.ToString("#.###").PadLeft($TotalValue.ToString("#.###").Length))$ValueSuffix / $($TotalValue.ToString("#.###"))$ValueSuffix ] $($percentComplete.ToString("##0.00").PadLeft(6)) % complete"
}
else {
Write-Host -NoNewLine "`r$ProgressText $progbar [ $($TotalValue.ToString("#.###").PadLeft($TotalValue.ToString("#.###").Length))$ValueSuffix / $($TotalValue.ToString("#.###"))$ValueSuffix ] $($percentComplete.ToString("##0.00").PadLeft(6)) % complete"
}
}
}
}
Process {
try {
$storeEAP = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
# invoke request
$request = [System.Net.HttpWebRequest]::Create($URL)
$response = $request.GetResponse()
if ($response.StatusCode -eq 401 -or $response.StatusCode -eq 403 -or $response.StatusCode -eq 404) {
throw "Remote file either doesn't exist, is unauthorized, or is forbidden for '$URL'."
}
if($File -match '^\.\\') {
$File = Join-Path (Get-Location -PSProvider "FileSystem") ($File -Split '^\.')[1]
}
if($File -and !(Split-Path $File)) {
$File = Join-Path (Get-Location -PSProvider "FileSystem") $File
}
if ($File) {
$fileDirectory = $([System.IO.Path]::GetDirectoryName($File))
if (!(Test-Path($fileDirectory))) {
[System.IO.Directory]::CreateDirectory($fileDirectory) | Out-Null
}
}
[long]$fullSize = $response.ContentLength
$fullSizeMB = $fullSize / 1024 / 1024
# define buffer
[byte[]]$buffer = new-object byte[] 1048576
[long]$total = [long]$count = 0
# create reader / writer
$reader = $response.GetResponseStream()
$writer = new-object System.IO.FileStream $File, "Create"
# start download
$finalBarCount = 0 #show final bar only one time
do {
$count = $reader.Read($buffer, 0, $buffer.Length)
$writer.Write($buffer, 0, $count)
$total += $count
$totalMB = $total / 1024 / 1024
if ($fullSize -gt 0) {
Show-Progress -TotalValue $fullSizeMB -CurrentValue $totalMB -ProgressText "Downloading $($File.Name)" -ValueSuffix "MB"
}
if ($total -eq $fullSize -and $count -eq 0 -and $finalBarCount -eq 0) {
Show-Progress -TotalValue $fullSizeMB -CurrentValue $totalMB -ProgressText "Downloading $($File.Name)" -ValueSuffix "MB" -Complete
$finalBarCount++
#Write-Host "$finalBarCount"
}
} while ($count -gt 0)
}
catch {
$ExeptionMsg = $_.Exception.Message
Write-Host "Download breaks with error : $ExeptionMsg"
}
finally {
# cleanup
if ($reader) { $reader.Close() }
if ($writer) { $writer.Flush(); $writer.Close() }
$ErrorActionPreference = $storeEAP
[GC]::Collect()
}
}
}
function Install-Tool {
if (-not (Test-Path $bin_dir -PathType Container)) {
New-Item -Path $bin_dir -ItemType Directory | Out-Null
}
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User") -split ";"
if ($currentPath -notcontains $bin_dir) {
[Environment]::SetEnvironmentVariable("PATH", "$env:PATH;$bin_dir", "User")
$env:PATH="$env:PATH;$bin_dir"
Write-Host "$bin_dir added to PATH."
}
Get-FileFromWeb "$url" (Join-Path $bin_dir "lon-tool.exe")
Write-Host
}
function Install-Platoform_tools {
if (-not (Test-Path $platform_tools_dir -PathType Container)) {
New-Item -Path $platform_tools_dir -ItemType Directory | Out-Null
}
$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User") -split ";"
if ($currentPath -notcontains $platform_tools_dir) {
[Environment]::SetEnvironmentVariable("PATH", "$env:PATH;$platform_tools_dir", "User")
$env:PATH="$env:PATH;$platform_tools_dir"
Write-Host "$platform_tools_dir added to PATH."
}
Get-FileFromWeb "$platform_tools_url" (Join-Path $platform_tools_dir "tools.zip")
Write-Host
Expand-Archive -Path (Join-Path $platform_tools_dir "tools.zip") -DestinationPath $platform_tools_dir
Move-Item (Join-Path $platform_tools_dir "platform-tools\*") $platform_tools_dir
Remove-Item (Join-Path $platform_tools_dir "platform-tools\*")
}
Install-Tool
if (-not (Get-Command "adb.exe" -ErrorAction SilentlyContinue)) {
$decision = $Host.UI.PromptForChoice("Adb executable not found in PATH", "Do you wand to install android platform tools?", ("&Yes", "&No"), 1)
if ($decision -eq 0) {
Install-Platoform_tools
}
}
Write-Host "Done!" -ForegroundColor Green

View file

@ -4,14 +4,17 @@ import (
"fmt"
"math"
"net"
"regexp"
)
func GenRepartCommands(percent int, is128 bool) []string {
var maxsize uint8
if is128 {
func GenRepartCommands(percent int, blocksize string) []string {
var maxsize uint16
if r, _ := regexp.MatchString(`^125[0-9]{9}$`, blocksize); r {
maxsize = 126
} else {
} else if r, _ := regexp.MatchString(`^253[0-9]{9}$`, blocksize); r {
maxsize = 254
} else if r, _ := regexp.MatchString(`^509[0-9]{9}$`, blocksize); r {
maxsize = 509
}
linux_max := maxsize - 12
size := math.Round(float64(linux_max)*float64(percent)) / 100
@ -26,7 +29,6 @@ func GenRepartCommands(percent int, is128 bool) []string {
}
}
func GetFreePort() (int, error) {
listener, err := net.Listen("tcp", ":0")
if err != nil {