Skip to content

Commit

Permalink
tweak: support tvOS 16.5
Browse files Browse the repository at this point in the history
  • Loading branch information
bitxeno committed Jul 24, 2023
1 parent 12d8c3c commit 303d1c7
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 32 deletions.
9 changes: 7 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,12 @@ RUN chmod +x /usr/bin/${APP_NAME}
RUN rm -rf /var/lib/lockdown && mkdir -p /data/lockdown && ln -s /data/lockdown /var/lib/lockdown



# 生成启动脚本
COPY ./scripts/anisette-server /etc/init.d/anisette-server
RUN chmod +x /etc/init.d/anisette-server
COPY ./scripts/usbmuxd /etc/init.d/usbmuxd
RUN chmod +x /etc/init.d/usbmuxd
RUN printf '#!/bin/sh \n\n\
mkdir -p /data/lockdown \n\
Expand All @@ -74,8 +79,8 @@ if [ ! -f "/data/config.yaml" ]; then \n\
cp /doc/config.yaml /data/config.yaml \n\
fi \n\
nohup /usr/sbin/usbmuxd & \n\
nohup /usr/bin/anisette-server --adi-path /data/Provision & \n\
/etc/init.d/usbmuxd start \n\
/etc/init.d/anisette-server start \n\
/usr/bin/%s server -p ${SERVICE_PORT:-80} -c /data/config.yaml \n\
\n\
Expand Down
36 changes: 36 additions & 0 deletions doc/scripts/anisette-server
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/sh

set -e
. /lib/lsb/init-functions

DAEMON=/usr/bin/anisette-server
PIDFILE=/var/run/anisette-server.pid
DAEMON_OPTS="--adi-path /data/Provision"

start_it_up()
{
start-stop-daemon --start --background --quiet --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS
}

shut_it_down()
{
start-stop-daemon --stop --pidfile $PIDFILE
}


case "$1" in
start)
start_it_up
;;
stop)
shut_it_down
;;
restart)
shut_it_down
start_it_up
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart}" >&2
exit 2
;;
esac
36 changes: 36 additions & 0 deletions doc/scripts/usbmuxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/sh

set -e
. /lib/lsb/init-functions

DAEMON=/usr/sbin/usbmuxd
PIDFILE=/var/run/usbmuxd.pid
DAEMON_OPTS=""

start_it_up()
{
start-stop-daemon --start --background --quiet --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- $DAEMON_OPTS
}

shut_it_down()
{
start-stop-daemon --stop --pidfile $PIDFILE
}


case "$1" in
start)
start_it_up
;;
stop)
shut_it_down
;;
restart)
shut_it_down
start_it_up
;;
*)
echo "Usage: /etc/init.d/$NAME {start|stop|restart}" >&2
exit 2
;;
esac
35 changes: 29 additions & 6 deletions manager/device_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os/exec"
"strings"
"sync"
"time"

"github.com/bitxeno/atvloadly/config"
"github.com/bitxeno/atvloadly/internal/log"
Expand Down Expand Up @@ -93,14 +94,25 @@ func (dm *DeviceManager) GetMountImageInfo(udid string) (*model.UsbmuxdImage, er

imageInfo := model.NewUsbmuxdImage(*devInfo, config.Settings.DeveloperDiskImage.ImageSource)
imageMounted, err := dm.CheckHasMountImage(udid)
if err != nil {
log.Err(err).Msg("Cannot get image signature: ")
return nil, err
if err == nil {
imageInfo.ImageMounted = imageMounted
return imageInfo, nil
}

// AppleTV system has reboot, need restart usbmuxd????
// Error: lookup_image returned -256
if strings.Contains(err.Error(), "lookup_image returned -256") {
if err = dm.RestartUsbmuxd(); err == nil {
time.Sleep(5 * time.Second)
if imageMounted, err = dm.CheckHasMountImage(udid); err != nil {
imageInfo.ImageMounted = imageMounted
return imageInfo, nil
}
}
}
imageInfo.ImageMounted = imageMounted
return imageInfo, nil

// return nil, fmt.Errorf("Device pairing state not valid. Please try to pair again.")
log.Err(err).Msg("Cannot get image signature: ")
return nil, err
}

func (dm *DeviceManager) GetUsbmuxdDevice(udid string) (*model.UsbmuxdDevice, error) {
Expand Down Expand Up @@ -141,6 +153,17 @@ func (dm *DeviceManager) CheckHasMountImage(udid string) (bool, error) {
}

output := string(data)

if strings.Contains(output, "ERROR") {
return false, fmt.Errorf("%s", output)
}

// fmt.Println(output)
return strings.Contains(output, "ImageSignature") && !strings.Contains(output, "ImageSignature[0]"), nil
}

func (dm *DeviceManager) RestartUsbmuxd() error {
cmd := exec.Command("/etc/init.d/usbmuxd", "restart")
_, err := cmd.CombinedOutput()
return err
}
35 changes: 25 additions & 10 deletions model/usbmuxd_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,44 @@ package model
import (
"fmt"
"strings"

"github.com/bitxeno/atvloadly/internal/utils"
)

type UsbmuxdImage struct {
Device UsbmuxdDevice

ImageMounted bool `json:"ImageMounted,omitempty"`
DeveloperDiskImageUrl string `json:"DeveloperDiskImageUrl,omitempty"`
DeveloperDiskImageVersion string `json:"DeveloperDiskImageVersion,omitempty"`
ImageMounted bool `json:"ImageMounted,omitempty"`
DeveloperDiskImageUrl string `json:"DeveloperDiskImageUrl,omitempty"`
DeveloperDiskImageVersion string `json:"DeveloperDiskImageVersion,omitempty"`
DowngradeDeveloperDiskImageUrl string `json:"DowngradeDeveloperDiskImageUrl,omitempty"`
DowngradeDeveloperDiskImageVersion string `json:"DowngradeDeveloperDiskImageVersion,omitempty"`
}

func NewUsbmuxdImage(device UsbmuxdDevice, imageSource string) *UsbmuxdImage {
arr := strings.Split(device.ProductVersion, ".")
tvOSVersion := ""
version := ""
downgradeVersion := ""
major := 0
minor := 0
if len(arr) < 2 {
tvOSVersion = fmt.Sprintf("%s.0", arr[0])
major = utils.MustParseInt(arr[0])
minor = 0
version = fmt.Sprintf("%s.0", arr[0])
downgradeVersion = fmt.Sprintf("%d.%d", major-1, minor)
} else {
tvOSVersion = fmt.Sprintf("%s.%s", arr[0], arr[1])
major = utils.MustParseInt(arr[0])
minor = utils.MustParseInt(arr[1])
version = fmt.Sprintf("%s.%s", arr[0], arr[1])
downgradeVersion = fmt.Sprintf("%d.%d", major, minor-1)
}

return &UsbmuxdImage{
Device: device,
ImageMounted: false,
DeveloperDiskImageUrl: strings.Replace(imageSource, "{0}", tvOSVersion, -1),
DeveloperDiskImageVersion: tvOSVersion,
Device: device,
ImageMounted: false,
DeveloperDiskImageUrl: strings.Replace(imageSource, "{0}", version, -1),
DeveloperDiskImageVersion: version,
DowngradeDeveloperDiskImageUrl: strings.Replace(imageSource, "{0}", downgradeVersion, -1),
DowngradeDeveloperDiskImageVersion: downgradeVersion,
}
}
71 changes: 57 additions & 14 deletions service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,25 +87,12 @@ func MountDeveloperDiskImage(ctx context.Context, id string) error {

// 尝试挂载
// 对应版本的DeveloperDiskImage不存在的话,尝试下载
imageDir := filepath.Join(cfg.Server.WorkDir, "DeveloperDiskImage")
imageVersionDir := filepath.Join(cfg.Server.WorkDir, "DeveloperDiskImage", imageInfo.DeveloperDiskImageVersion)
if _, err := os.Stat(imageVersionDir); os.IsNotExist(err) {
tmpPath := filepath.Join(cfg.Server.WorkDir, "tmp", "DeveloperDiskImage.zip")
resp, err := http.NewClient().R().SetOutput(tmpPath).Get(imageInfo.DeveloperDiskImageUrl)
if err != nil {
if err := downloadDeveloperDiskImage(imageInfo, imageVersionDir); err != nil {
log.Err(err).Msg("Download Developer disk image error: ")
return err
}
if !resp.IsSuccess() {
return fmt.Errorf("Developer disk image could not found. os: tvOS %s url: %s status: %d", imageInfo.Device.ProductVersion, imageInfo.DeveloperDiskImageUrl, resp.StatusCode())
}

// unzip
uz := unzip.New()
if _, err = uz.Extract(tmpPath, imageDir); err != nil {
log.Err(err).Msg("Unzip Developer disk image error: ")
return err
}
}

// 开始执行挂载
Expand All @@ -126,6 +113,62 @@ func MountDeveloperDiskImage(ctx context.Context, id string) error {
return nil
}

func downloadDeveloperDiskImage(imageInfo *model.UsbmuxdImage, imageVersionDir string) error {
tmpPath := filepath.Join(cfg.Server.WorkDir, "tmp", "DeveloperDiskImage.zip")
tmpUnzipPath := filepath.Join(cfg.Server.WorkDir, "tmp", "DeveloperDiskImage")
_ = os.RemoveAll(tmpUnzipPath)

resp, err := http.NewClient().R().SetOutput(tmpPath).Get(imageInfo.DeveloperDiskImageUrl)
if err == nil && resp.IsSuccess() {
// unzip
uz := unzip.New()
files, err := uz.Extract(tmpPath, tmpUnzipPath)
if err != nil {
log.Err(err).Msg("Unzip Developer disk image error: ")
return err
}
_ = os.MkdirAll(imageVersionDir, os.ModePerm)
for _, f := range files {
if filepath.Base(f) == "DeveloperDiskImage.dmg" || filepath.Base(f) == "DeveloperDiskImage.dmg.signature" {
_ = os.Rename(f, filepath.Join(imageVersionDir, filepath.Base(f)))
}
}
return nil
}

// 镜像找不到,尝试降级获取上一版本(有时可以使用前一版本的镜像进行mount)
if resp.StatusCode() == 404 {
log.Warnf("try downgrade Developer disk image to version: %s", imageInfo.DowngradeDeveloperDiskImageVersion)
resp, err = http.NewClient().R().SetOutput(tmpPath).Get(imageInfo.DowngradeDeveloperDiskImageUrl)
if err == nil && resp.IsSuccess() {
// unzip
uz := unzip.New()
files, err := uz.Extract(tmpPath, tmpUnzipPath)
if err != nil {
log.Err(err).Msg("Unzip Developer disk image error: ")
return err
}
_ = os.MkdirAll(imageVersionDir, os.ModePerm)
for _, f := range files {
if filepath.Base(f) == "DeveloperDiskImage.dmg" || filepath.Base(f) == "DeveloperDiskImage.dmg.signature" {
_ = os.Rename(f, filepath.Join(imageVersionDir, filepath.Base(f)))
}
}
return nil
}
}

if err != nil {
log.Err(err).Msg("Download Developer disk image error: ")
return err
}
if !resp.IsSuccess() {
return fmt.Errorf("Developer disk image could not found. os: tvOS %s url: %s status: %d", imageInfo.Device.ProductVersion, imageInfo.DeveloperDiskImageUrl, resp.StatusCode())
}

return nil
}

func GetValidName(name string) string {
return strings.ToLower(regValidName.ReplaceAllString(name, ""))
}
13 changes: 13 additions & 0 deletions task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/bitxeno/atvloadly/internal/app"
"github.com/bitxeno/atvloadly/internal/cfg"
"github.com/bitxeno/atvloadly/internal/log"
"github.com/bitxeno/atvloadly/manager"
"github.com/bitxeno/atvloadly/model"
"github.com/bitxeno/atvloadly/notify"
"github.com/bitxeno/atvloadly/service"
Expand Down Expand Up @@ -175,6 +176,18 @@ func (t *Task) runInternal(v model.InstalledApp) error {
return fmt.Errorf("任务帐号,密码,UDID为空")
}

// 检查developer disk image是否已mounted
imageInfo, err := manager.GetDeviceMountImageInfo(v.UDID)
if err != nil {
log.Err(err).Msg("Check DeveloperDiskImage mounted error: ")
return err
}

if !imageInfo.ImageMounted {
log.Error("DeveloperDiskImage not mounted.")
return err
}

// 为每个appleid创建对应的工作目录,用于存储AltServer生成的签名证书
dirName := regValidName.ReplaceAllString(strings.ToLower(v.Account), "")
workdir := filepath.Join(cfg.Server.WorkDir, "AltServer", dirName)
Expand Down

0 comments on commit 303d1c7

Please sign in to comment.