Skip to content

Commit

Permalink
refactor: refactor install and pair code
Browse files Browse the repository at this point in the history
  • Loading branch information
bitxeno committed Jul 1, 2024
1 parent 88aad4a commit 3fc302b
Show file tree
Hide file tree
Showing 14 changed files with 530 additions and 199 deletions.
3 changes: 2 additions & 1 deletion doc/config.yaml.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
server:
log: /data/app.log
work_dir: /data
log:
log_file: /data/app.log
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ require (
github.com/godbus/dbus/v5 v5.0.4
github.com/gofiber/contrib/websocket v1.0.0
github.com/gofiber/fiber/v2 v2.46.0
github.com/gookit/event v1.1.2
github.com/grandcat/zeroconf v1.0.0
github.com/holoplot/go-avahi v1.0.1
github.com/iineva/CgbiPngFix v0.0.0-20210523041253-b8869b346914
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/event v1.1.2 h1:cYZWKJeoJWnP1ZxW1G+36GViV+hH9ksEorLqVw901Nw=
github.com/gookit/event v1.1.2/go.mod h1:YIYR3fXnwEq1tey3JfepMt19Mzm2uxmqlpc7Dj6Ekng=
github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
Expand Down Expand Up @@ -560,6 +564,7 @@ github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
Expand Down
148 changes: 148 additions & 0 deletions internal/manager/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package manager

import (
"context"
"io"
"os/exec"
"path/filepath"
"time"

"github.com/bitxeno/atvloadly/internal/app"
"github.com/bitxeno/atvloadly/internal/log"
"github.com/gookit/event"
)

type InstallManager struct {
quietMode bool

outputStdout *outputWriter
outputStderr *outputWriter

stdin io.WriteCloser

cancel context.CancelFunc
em *event.Manager
}

func NewInstallManager() *InstallManager {
em := event.NewManager("output", event.UsePathMode)
return &InstallManager{
quietMode: true,
outputStdout: newOutputWriter(em),
outputStderr: newOutputWriter(em),

em: em,
}
}

func NewInteractiveInstallManager() *InstallManager {
ins := NewInstallManager()
ins.quietMode = false
return ins
}

func (t *InstallManager) TryStart(ctx context.Context, udid, account, password, ipaPath string) error {
err := t.Start(ctx, udid, account, password, ipaPath)
if err != nil {
// AppleTV system has reboot/lockdownd sleep, try restart usbmuxd to fix
// LOCKDOWN_E_MUX_ERROR / AFC_E_MUX_ERROR /
ipaName := filepath.Base(ipaPath)
log.Infof("Try restarting usbmuxd to fix afc connect issue. %s", ipaName)
if err = RestartUsbmuxd(); err == nil {
log.Infof("Restart usbmuxd complete, try install ipa again. %s", ipaName)
time.Sleep(5 * time.Second)
err = t.Start(ctx, udid, account, password, ipaPath)
}
}
return err
}

func (t *InstallManager) Start(ctx context.Context, udid, account, password, ipaPath string) error {
// set execute timeout 5 miniutes
timeout := 5 * time.Minute
ctx, cancel := context.WithTimeout(ctx, timeout)
t.cancel = cancel

cmd := exec.CommandContext(ctx, "sideloader", "install", "--quiet", "--nocolor", "--udid", udid, "-a", account, "-p", password, ipaPath)
if !t.quietMode {
cmd = exec.CommandContext(ctx, "sideloader", "install", "--nocolor", "--udid", udid, "-a", account, "-p", password, ipaPath)
}
cmd.Dir = app.Config.Server.DataDir
cmd.Env = []string{"SIDELOADER_CONFIG_DIR=" + app.SideloaderDataDir()}
cmd.Stdout = t.outputStdout
cmd.Stderr = t.outputStderr

var err error
t.stdin, err = cmd.StdinPipe()
if err != nil {
log.Err(err).Msg("Error creating stdin pipe: ")
return err
}
defer t.stdin.Close()

if err := cmd.Start(); err != nil {
if err == context.DeadlineExceeded {
_ = cmd.Process.Kill()
}
log.Err(err).Msg("Error start installation script.")
return err
}

err = cmd.Wait()
if err != nil {
log.Err(err).Msgf("Error executing installation script. %s", t.ErrorLog())
}
return err
}

func (t *InstallManager) Close() {
if t.cancel != nil {
t.cancel()
t.cancel = nil
}
if t.em != nil {
t.em.CloseWait()

Check failure on line 104 in internal/manager/install.go

View workflow job for this annotation

GitHub Actions / build (1.21.x)

Error return value of `t.em.CloseWait` is not checked (errcheck)
}
}

func (t *InstallManager) OnOutput(fn func(string)) {
t.em.On("output", event.ListenerFunc(func(e event.Event) error {
fn(e.Get("text").(string))
return nil
}))
}

func (t *InstallManager) Write(p []byte) {
_, _ = t.stdin.Write(p)
}

func (t *InstallManager) ErrorLog() string {
return t.outputStderr.String()
}

func (t *InstallManager) OutputLog() string {
return t.outputStdout.String()
}

type outputWriter struct {
data []byte
em *event.Manager
}

func newOutputWriter(em *event.Manager) *outputWriter {
return &outputWriter{
em: em,
}
}

func (w *outputWriter) Write(p []byte) (n int, err error) {
w.data = append(w.data, p...)
w.em.MustFire("output", event.M{"text": string(p)})

n = len(p)
return n, nil
}

func (w *outputWriter) String() string {
return string(w.data)
}
1 change: 0 additions & 1 deletion internal/manager/lockdown.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
)

func loadLockdownDevices() (map[string]model.LockdownDevice, error) {
log.Infof("Load lockdown from path: %s", app.Config.App.LockdownDir)
files, err := os.ReadDir(app.Config.App.LockdownDir)
if err != nil {
log.Err(err).Msg("Read lockdown dir error: ")
Expand Down
118 changes: 118 additions & 0 deletions internal/manager/pair.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package manager

import (
"context"
"io"
"os/exec"
"time"

"github.com/bitxeno/atvloadly/internal/app"
"github.com/bitxeno/atvloadly/internal/log"
"github.com/gookit/event"
)

type PairManager struct {
outputStdout *pairOutputWriter
outputStderr *pairOutputWriter

stdin io.WriteCloser

cancel context.CancelFunc
em *event.Manager
}

func NewPairManager() *PairManager {
em := event.NewManager("output", event.UsePathMode)
return &PairManager{
outputStdout: newPairOutputWriter(em),
outputStderr: newPairOutputWriter(em),

em: em,
}
}

func (t *PairManager) Start(ctx context.Context, udid string) error {
// set execute timeout 1 miniutes
timeout := time.Minute
ctx, cancel := context.WithTimeout(ctx, timeout)
t.cancel = cancel

cmd := exec.CommandContext(ctx, "idevicepair", "pair", "-u", udid, "-w")
cmd.Dir = app.Config.Server.DataDir
cmd.Stdout = t.outputStdout
cmd.Stderr = t.outputStderr

var err error
t.stdin, err = cmd.StdinPipe()
if err != nil {
log.Err(err).Msg("Error creating stdin pipe: ")
return err
}
defer t.stdin.Close()

if err := cmd.Start(); err != nil {
if err == context.DeadlineExceeded {
_ = cmd.Process.Kill()
}
log.Err(err).Msg("Error start pair script.")
return err
}

err = cmd.Wait()
if err != nil {
log.Err(err).Msgf("Error executing pair script. %s", t.ErrorLog())
}
return err
}

func (t *PairManager) Close() {
if t.cancel != nil {
t.cancel()
t.cancel = nil
}
if t.em != nil {
t.em.CloseWait()

Check failure on line 74 in internal/manager/pair.go

View workflow job for this annotation

GitHub Actions / build (1.21.x)

Error return value of `t.em.CloseWait` is not checked (errcheck)
}
}

func (t *PairManager) OnOutput(fn func(string)) {
t.em.On("output", event.ListenerFunc(func(e event.Event) error {
fn(e.Get("text").(string))
return nil
}))
}

func (t *PairManager) Write(p []byte) {
_, _ = t.stdin.Write(p)
}

func (t *PairManager) ErrorLog() string {
return t.outputStderr.String()
}

func (t *PairManager) OutputLog() string {
return t.outputStdout.String()
}

type pairOutputWriter struct {
data []byte
em *event.Manager
}

func newPairOutputWriter(em *event.Manager) *pairOutputWriter {
return &pairOutputWriter{
em: em,
}
}

func (w *pairOutputWriter) Write(p []byte) (n int, err error) {
w.data = append(w.data, p...)
w.em.MustFire("output", event.M{"text": string(p)})

n = len(p)
return n, nil
}

func (w *pairOutputWriter) String() string {
return string(w.data)
}
18 changes: 18 additions & 0 deletions internal/model/message.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package model

import "encoding/json"

// Message.Type
const (
MessageTypeInstall = 1
MessageType2FA = 2

MessageTypePair = 1
MessageTypePairConfirm = 2
)

// Message Websocket Communication data format
type Message struct {
Type int `json:"t"`
Data json.RawMessage `json:"d"`
}
Loading

0 comments on commit 3fc302b

Please sign in to comment.