diff --git a/.gitignore b/.gitignore index 984e540..3f4bd7a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .idea/ dist/ -.sib/ \ No newline at end of file +.sib/ +sonic-ios-bridge.exe +sonic-ios-bridge +vendor/ diff --git a/cmd/devmode.go b/cmd/devmode.go new file mode 100644 index 0000000..566d435 --- /dev/null +++ b/cmd/devmode.go @@ -0,0 +1,20 @@ +package cmd + +import ( + "github.com/SonicCloudOrg/sonic-ios-bridge/cmd/devmode" + "github.com/spf13/cobra" +) + +var devmodeCmd = &cobra.Command{ + Use: "devmode", + Short: "Enable Developer Mode on iOS 16+ devices or print the current status.", + Long: "Enable Developer Mode on iOS 16+ devices or print the current status.", + Run: func(cmd *cobra.Command, args []string) { + cmd.Help() + }, +} + +func init() { + rootCmd.AddCommand(devmodeCmd) + devmode.InitDevmode(devmodeCmd) +} diff --git a/cmd/devmode/arm.go b/cmd/devmode/arm.go new file mode 100644 index 0000000..5679f00 --- /dev/null +++ b/cmd/devmode/arm.go @@ -0,0 +1,51 @@ +package devmode + +import ( + "fmt" + "net/http" + + "github.com/SonicCloudOrg/sonic-ios-bridge/src/util" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "golang.org/x/xerrors" +) + +var devmodeArmCmd = &cobra.Command{ + Use: "arm", + Short: "Arm the Developer Mode (device will reboot)", + Long: "Arm the Developer Mode (device will reboot)", + RunE: func(cmd *cobra.Command, args []string) error { + util.InitLogger() + if bCan, eCan := canToggleDevMode(udid); eCan != nil { + strErrMsg := fmt.Sprintf("Failed to check device %s iOS version", udid) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } else if !bCan { + strErrMsg := fmt.Sprintf("Device %s iOS version below 16", udid) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } + amfi, errAmfi := getAmfiServer() + if errAmfi != nil { + return errAmfi + } + res, errArm := amfi.DevModeArm() + if errArm != nil { + return errArm + } + if res == http.StatusOK { + logrus.Infof("Developer Mode armed.") + return nil + } else { + strErrMsg := fmt.Sprintf("Failed to arm Developer Mode (%d).", res) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } + }, +} + +func initDevModeArmCmd() { + devmodeRootCMD.AddCommand(devmodeArmCmd) + devmodeArmCmd.Flags().StringVarP(&udid, "udid", "u", "", "device's serialNumber") + devmodeArmCmd.MarkFlagRequired("udid") +} diff --git a/cmd/devmode/confirm.go b/cmd/devmode/confirm.go new file mode 100644 index 0000000..0ef3327 --- /dev/null +++ b/cmd/devmode/confirm.go @@ -0,0 +1,53 @@ +package devmode + +import ( + "fmt" + "net/http" + + "github.com/SonicCloudOrg/sonic-ios-bridge/src/util" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "golang.org/x/xerrors" +) + +var devmodeConfirmCmd = &cobra.Command{ + Use: "confirm", + Short: "Confirm enabling of Developer Mode", + Long: "Confirm enabling of Developer Mode", + RunE: func(cmd *cobra.Command, args []string) error { + util.InitLogger() + if bPreCheckIOSVer { + if bCan, eCan := canToggleDevMode(udid); eCan != nil { + strErrMsg := fmt.Sprintf("Failed to check device %s iOS version", udid) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } else if !bCan { + strErrMsg := fmt.Sprintf("Device %s iOS version below 16", udid) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } + } + amfi, errAmfi := getAmfiServer() + if errAmfi != nil { + return errAmfi + } + res, errReveal := amfi.DevModeEnable() + if errReveal != nil { + return errReveal + } + if res == http.StatusOK { + logrus.Infof("Developer Mode menu enabled successfully.") + return nil + } else { + strErrMsg := fmt.Sprintf("Failed to enable Developer Mode menu (%d).", res) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } + }, +} + +func initDevModeConfirmCmd() { + devmodeRootCMD.AddCommand(devmodeConfirmCmd) + devmodeConfirmCmd.Flags().StringVarP(&udid, "udid", "u", "", "device's serialNumber") + devmodeConfirmCmd.MarkFlagRequired("udid") +} diff --git a/cmd/devmode/devmodeinit.go b/cmd/devmode/devmodeinit.go new file mode 100644 index 0000000..8b6c4a7 --- /dev/null +++ b/cmd/devmode/devmodeinit.go @@ -0,0 +1,64 @@ +package devmode + +import ( + "encoding/json" + "os" + "reflect" + + giDevice "github.com/SonicCloudOrg/sonic-gidevice" + "github.com/SonicCloudOrg/sonic-ios-bridge/src/entity" + "github.com/SonicCloudOrg/sonic-ios-bridge/src/util" + "github.com/spf13/cobra" + "golang.org/x/xerrors" +) + +var devmodeRootCMD *cobra.Command +var bPreCheckIOSVer bool = true + +// option bindings +var udid string +var bIsOutputJson bool + +func InitDevmode(devmodeCMD *cobra.Command) { + devmodeRootCMD = devmodeCMD + initDevModeListCmd() + initDevModeArmCmd() + initDevModeRevealCmd() + initDevModeEnableCmd() + initDevModeConfirmCmd() +} + +func getAmfiServer() (giDevice.Amfi, error) { + device := util.GetDeviceByUdId(udid) + if device == nil { + os.Exit(55 /* https://git.islam.gov.my/mohdrizal/fabric/-/blob/v2.3.3/vendor/golang.org/x/sys/windows/zerrors_windows.go#L195 */) + } + return device.AmfiService() +} + +func canToggleDevMode(udid string) (bool, error) { + gidevice := util.GetDeviceByUdId(udid) + if gidevice == nil { + return false, xerrors.Errorf("Device %s not found", udid) + } + device := entity.Device{} + deviceByte, _ := json.Marshal(gidevice.Properties()) + json.Unmarshal(deviceByte, &device) + detail, err2 := entity.GetDetail(gidevice) + if err2 != nil { + return false, err2 + } else { + device.DeviceDetail = *detail + } + devmode := entity.DevMode{Device: device} + b, e := devmode.CanCheck() + if e != nil { + return false, e + } + return b, nil +} + +func __PACKAGE__() string { // https://www.appsloveworld.com/go/3/how-to-get-name-of-current-package-in-go?expand_article=1 + type dummy struct{} + return reflect.TypeOf(dummy{}).PkgPath() +} diff --git a/cmd/devmode/enable.go b/cmd/devmode/enable.go new file mode 100644 index 0000000..81dbf04 --- /dev/null +++ b/cmd/devmode/enable.go @@ -0,0 +1,89 @@ +package devmode + +import ( + "os" + "strings" + "sync" + "time" + + giDevice "github.com/SonicCloudOrg/sonic-gidevice" + "github.com/SonicCloudOrg/sonic-ios-bridge/src/entity" + "github.com/SonicCloudOrg/sonic-ios-bridge/src/util" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +// option bindings +var bWaitReboot, bAutoConfirm bool +var intEnableWaitTimeout int + +var devmodeEnableCmd = &cobra.Command{ + Use: "enable", + Short: "Enable Developer Mode (device will reboot)", + Long: "Enable Developer Mode (device will reboot)", + RunE: func(cmd *cobra.Command, args []string) error { + //util.InitLogger() + errArm := devmodeArmCmd.RunE(cmd, args) + if errArm != nil { + return errArm + } + if bWaitReboot { + bIsDeviceOnline := true + wg := new(sync.WaitGroup) + wg.Add(1) + shutDownFun, errListen := util.UsbmuxListen(func(gidevice *giDevice.Device, device *entity.Device, e error, cancelFunc func()) { + if device == nil { + return + } + funcDone := func() { + cancelFunc() + bIsDeviceOnline = true + logrus.Infof("Device %s is online.", udid) + wg.Done() + } + if device.Status == "offline" { + bIsDeviceOnline = false + logrus.Infof("Device %s is offline.", udid) + } else if !bIsDeviceOnline && device.Status == "online" { + if device.SerialNumber == udid { + funcDone() + return + } + detail, _ := entity.GetDetail(*gidevice) + if detail != nil && detail.UniqueDeviceID == udid { + funcDone() + return + } + } + }) + if errListen != nil { + return errListen + } + go func() { + time.Sleep(time.Duration(intEnableWaitTimeout) * time.Second) + logrus.Warnf("Timeout waiting for device %s to reboot.", udid) + shutDownFun() + wg.Done() + }() + wg.Wait() + if bIsDeviceOnline && bAutoConfirm { + bPreCheckIOSVer = false + devmodeConfirmCmd.RunE(cmd, args) + } else { + executable, _ := os.Executable() + pkgPath := strings.Split(__PACKAGE__(), "/") + logrus.Infof("Please check the device %s is online and then run '%s %s %s -u %s'.", udid, executable, pkgPath[len(pkgPath)-1], devmodeConfirmCmd.Use, udid) + } + } + return nil + }, +} + +func initDevModeEnableCmd() { + devmodeRootCMD.AddCommand(devmodeEnableCmd) + devmodeEnableCmd.Flags().StringVarP(&udid, "udid", "u", "", "device's serialNumber") + devmodeEnableCmd.MarkFlagRequired("udid") + devmodeEnableCmd.Flags().BoolVar(&bWaitReboot, "wait", false, "wait for reboot to complete") + devmodeEnableCmd.Flags().IntVar(&intEnableWaitTimeout, "wait-timeout", 60, "wait timeout in seconds") + devmodeEnableCmd.Flags().BoolVarP(&bAutoConfirm, "confirm", "y", false, "automatically confirm after reboot") +} diff --git a/cmd/devmode/list.go b/cmd/devmode/list.go new file mode 100644 index 0000000..21049d5 --- /dev/null +++ b/cmd/devmode/list.go @@ -0,0 +1,143 @@ +package devmode + +import ( + "encoding/json" + "fmt" + + giDevice "github.com/SonicCloudOrg/sonic-gidevice" + "github.com/SonicCloudOrg/sonic-ios-bridge/src/entity" + "github.com/SonicCloudOrg/sonic-ios-bridge/src/util" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var devmodeListCmd = &cobra.Command{ + Use: "list", + Short: "Print the Developer Mode status of connected devices", + Long: "Print the Developer Mode status of connected devices", + RunE: func(cmd *cobra.Command, args []string) error { + util.InitLogger() + usbMuxClient, err := giDevice.NewUsbmux() + if err != nil { + return util.NewErrorPrint(util.ErrConnect, "usbMux", err) + } + allErrors := []error{} + localList, errReadLocal := usbMuxClient.Devices() + if errReadLocal != nil { + allErrors = append(allErrors, errReadLocal) + } + remoteList, _ := util.ReadRemote() + devices := []entity.DevMode{} + if len(localList) != 0 { + for _, d := range localList { + deviceByte, _ := json.Marshal(d.Properties()) + device := &entity.Device{} + json.Unmarshal(deviceByte, device) + if len(udid) > 0 && device.SerialNumber != udid { + continue + } + detail, err2 := entity.GetDetail(d) + if err2 != nil { + allErrors = append(allErrors, err2) + } else { + device.DeviceDetail = *detail + } + devmode := entity.DevMode{Device: *device} + devices = append(devices, devmode) + } + } + if len(remoteList) != 0 { + for _, d := range remoteList { + deviceByte, _ := json.Marshal(d.Properties()) + device := &entity.Device{} + json.Unmarshal(deviceByte, device) + if len(udid) > 0 && device.SerialNumber != udid { + continue + } + detail, err2 := entity.GetDetail(d) + if err2 != nil { + allErrors = append(allErrors, err2) + } else { + device.DeviceDetail = *detail + } + devmode := entity.DevMode{Device: *device} + devices = append(devices, devmode) + } + } + jsonResults := []map[string]any{} + for _, devmode := range devices { + jsonObj := map[string]any{"udid": devmode.SerialNumber, "status": "N/A"} + bCanCheck, errChk := devmode.CanCheck() + if errChk != nil { + if bIsOutputJson { + jsonObj["error"] = errChk.Error() + } else { + fmt.Printf("%s\t%s\n", devmode.SerialNumber, "Error: "+errChk.Error()) + } + } else if !bCanCheck { + if bIsOutputJson { + jsonObj["error"] = "iOS version below 16" + } else { + fmt.Printf("%s\tN/A\n", devmode.SerialNumber) + } + } else { + device := util.GetDeviceByUdId(devmode.SerialNumber) + if device == nil { + continue + } + interResult, errInfo := device.GetValue("com.apple.security.mac.amfi", "DeveloperModeStatus") + if errInfo != nil { + if bIsOutputJson { + jsonObj["error"] = errInfo.Error() + } else { + fmt.Printf("%s\t%s\n", devmode.SerialNumber, "Error: "+errInfo.Error()) + } + } else { + strDevModeStatus := "N/A" + switch interResult := interResult.(type) { + case bool: + if interResult { + strDevModeStatus = "enabled" + } else { + strDevModeStatus = "disabled" + } + } + if bIsOutputJson { + jsonObj["status"] = strDevModeStatus + } else { + fmt.Printf("%s\t%s\n", devmode.SerialNumber, strDevModeStatus) + } + } + } + if bIsOutputJson { + jsonResults = append(jsonResults, jsonObj) + } + } + if len(jsonResults) > 0 && bIsOutputJson { + if len(udid) > 0 { + b, _ := json.Marshal(jsonResults[0]) + fmt.Println(string(b)) + } else { + b, _ := json.Marshal(jsonResults) + fmt.Println(string(b)) + } + } + if len(allErrors) > 0 { + for _, e := range allErrors { + logrus.Warnf("%+v\n", e) + } + } + return nil + }, +} + +func initDevModeListCmd() { + devmodeRootCMD.AddCommand(devmodeListCmd) + devmodeListCmd.Flags().StringVarP(&udid, "udid", "u", "", "device's serialNumber") + devmodeListCmd.Flags().BoolVarP(&bIsOutputJson, "json", "j", false, "output in JSON format") +} + +/* +References: +https://github.com/libimobiledevice/libimobiledevice/blob/master/tools/idevicedevmodectl.c#L99 +*/ diff --git a/cmd/devmode/reveal.go b/cmd/devmode/reveal.go new file mode 100644 index 0000000..49e894c --- /dev/null +++ b/cmd/devmode/reveal.go @@ -0,0 +1,56 @@ +package devmode + +import ( + "fmt" + "net/http" + + "github.com/SonicCloudOrg/sonic-ios-bridge/src/util" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "golang.org/x/xerrors" +) + +var devmodeRevealCmd = &cobra.Command{ + Use: "reveal", + Short: "Reveal the Developer Mode menu on the device", + Long: "Reveal the Developer Mode menu on the device", + RunE: func(cmd *cobra.Command, args []string) error { + util.InitLogger() + if bCan, eCan := canToggleDevMode(udid); eCan != nil { + strErrMsg := fmt.Sprintf("Failed to check device %s iOS version", udid) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } else if !bCan { + strErrMsg := fmt.Sprintf("Device %s iOS version below 16", udid) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } + amfi, errAmfi := getAmfiServer() + if errAmfi != nil { + return errAmfi + } + res, errReveal := amfi.DevModeReveal() + if errReveal != nil { + return errReveal + } + if res == http.StatusOK { + logrus.Infof("Developer Mode menu revealed successfully.") + return nil + } else { + strErrMsg := fmt.Sprintf("Failed to reveal Developer Mode menu (%d).", res) + logrus.Warn(strErrMsg) + return xerrors.New(strErrMsg) + } + }, +} + +func initDevModeRevealCmd() { + devmodeRootCMD.AddCommand(devmodeRevealCmd) + devmodeRevealCmd.Flags().StringVarP(&udid, "udid", "u", "", "device's serialNumber") + devmodeRevealCmd.MarkFlagRequired("udid") +} + +/* +References: +https://github.com/libimobiledevice/libimobiledevice/blob/master/tools/idevicedevmodectl.c#L440 +*/ diff --git a/cmd/root.go b/cmd/root.go index 2d0710f..be9ca2c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -26,8 +26,12 @@ import ( var isJson, isDetail, isFormat bool var udid string +var strExeccutable string = (func() string { + exe, _ := os.Executable() + return exe +})() var rootCmd = &cobra.Command{ - Use: "sib", + Use: strExeccutable, Short: "Bridge of iOS Devices", Long: ` ▄▄▄▄ ▄▄▄▄ ▄▄▄ ▄▄ ▄▄▄▄▄▄ ▄▄▄▄ diff --git a/go.mod b/go.mod index 415f009..f7a83cd 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,8 @@ module github.com/SonicCloudOrg/sonic-ios-bridge go 1.18 require ( - github.com/SonicCloudOrg/sonic-gidevice v0.7.5 + github.com/Masterminds/semver v1.5.0 + github.com/SonicCloudOrg/sonic-gidevice v0.7.6 github.com/SonicCloudOrg/sonic-ios-webkit-adapter v0.0.7 github.com/gin-gonic/gin v1.8.1 github.com/google/gopacket v1.1.19 @@ -11,7 +12,10 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/mitchellh/mapstructure v1.5.0 github.com/satori/go.uuid v1.2.0 + github.com/sirupsen/logrus v1.9.3 github.com/spf13/cobra v1.4.0 + github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 howett.net/plist v1.0.0 ) @@ -21,6 +25,7 @@ require ( github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/validator/v10 v10.10.0 // indirect github.com/goccy/go-json v0.9.7 // indirect + github.com/google/go-cmp v0.5.8 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/leodido/go-urn v1.2.1 // indirect @@ -39,7 +44,7 @@ require ( github.com/yezihack/e v1.0.0 // indirect golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect - golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 // indirect + golang.org/x/sys v0.14.0 // indirect golang.org/x/text v0.3.6 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index 037f43d..be2eaa5 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ -github.com/SonicCloudOrg/sonic-gidevice v0.7.3 h1:1+p2vE+mr79ZOhhtSnQDls1bYk5o8mWlfgEI1IiU7w4= -github.com/SonicCloudOrg/sonic-gidevice v0.7.3/go.mod h1:sww8Tk3iZ/FMhCJtZT+gQgWg7F4u5M2858OARwjC3Yc= -github.com/SonicCloudOrg/sonic-gidevice v0.7.4 h1:Yt9lCzZdziQr4efBWV+fUWakqvxqb/ksT9T6LULrHDY= -github.com/SonicCloudOrg/sonic-gidevice v0.7.4/go.mod h1:sww8Tk3iZ/FMhCJtZT+gQgWg7F4u5M2858OARwjC3Yc= -github.com/SonicCloudOrg/sonic-gidevice v0.7.5 h1:8liJHks2EQoA7INfHeid5+/9GgGV3/KAnyPEPQw8XKE= -github.com/SonicCloudOrg/sonic-gidevice v0.7.5/go.mod h1:sww8Tk3iZ/FMhCJtZT+gQgWg7F4u5M2858OARwjC3Yc= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/SonicCloudOrg/sonic-gidevice v0.7.6 h1:zSHY1aCrMEQNtnsKsfsOUp7erxf5ocl/eoxjfvKSpNg= +github.com/SonicCloudOrg/sonic-gidevice v0.7.6/go.mod h1:SEquP5doc1Xa9Y0556SJBpFg7Pex+jXk+y4XQ4i0yxo= github.com/SonicCloudOrg/sonic-ios-webkit-adapter v0.0.7 h1:s4OTcJ0VG4mg3501Ec+aSR6gQ8eTWz26fdgCUjxlmJQ= github.com/SonicCloudOrg/sonic-ios-webkit-adapter v0.0.7/go.mod h1:q5dSyO/kX8Mkypy6DkFuqgWnBmHZE7oU7m+pW7kdT+8= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -31,8 +29,9 @@ github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= @@ -50,6 +49,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= @@ -86,6 +86,9 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= @@ -95,12 +98,16 @@ github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816 h1:J6v8awz+me+xeb/cUTotKgceAYouhIB3pjzgRd6IlGk= +github.com/t-tomalak/logrus-easy-formatter v0.0.0-20190827215021-c074f06c5816/go.mod h1:tzym/CEb5jnFI+Q0k4Qq3+LvRF4gO3E2pxS8fHP8jcA= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw= github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -131,12 +138,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069 h1:siQdpVirKtzPhKl3lZWozZraCFObP8S1v6PRp0bLrtU= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -147,8 +157,9 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= diff --git a/src/entity/devices.go b/src/entity/devices.go index 7f14d30..4ef3d2f 100644 --- a/src/entity/devices.go +++ b/src/entity/devices.go @@ -21,6 +21,8 @@ import ( "encoding/json" "fmt" "strings" + + "github.com/Masterminds/semver" ) type DeviceList struct { @@ -79,3 +81,16 @@ func (deviceList DeviceList) ToFormat() string { result, _ := json.MarshalIndent(deviceList, "", "\t") return string(result) } + +type DevMode struct { + Device + DevModeStatus string `json:"status,omitempty"` +} + +func (devmode DevMode) CanCheck() (bool, error) { + v, err := semver.NewVersion(devmode.DeviceDetail.ProductVersion) + if err != nil { + return false, err + } + return v.Major() >= 16, nil +} diff --git a/src/util/log.go b/src/util/log.go new file mode 100644 index 0000000..f2da35c --- /dev/null +++ b/src/util/log.go @@ -0,0 +1,16 @@ +package util + +import ( + "os" + + "github.com/sirupsen/logrus" + easy "github.com/t-tomalak/logrus-easy-formatter" +) + +func InitLogger() { + logrus.SetOutput(os.Stderr) + logrus.SetFormatter(&easy.Formatter{ + TimestampFormat: "2006-01-02 15:04:05", + LogFormat: "[%lvl%]: %time% - %msg%\n", + }) +} diff --git a/src/util/usbmux.go b/src/util/usbmux.go new file mode 100644 index 0000000..85c30e1 --- /dev/null +++ b/src/util/usbmux.go @@ -0,0 +1,51 @@ +package util + +import ( + "encoding/json" + "os" + "os/signal" + + giDevice "github.com/SonicCloudOrg/sonic-gidevice" + "github.com/SonicCloudOrg/sonic-ios-bridge/src/entity" +) + +func UsbmuxListen(cb func(gidevice *giDevice.Device, device *entity.Device, e error, cancelFunc func())) (func(), error) { + usbMuxClient, err := giDevice.NewUsbmux() + if err != nil { + return nil, NewErrorPrint(ErrConnect, "usbMux", err) + } + usbmuxInput := make(chan giDevice.Device) + shutdownUsbmuxFun, err2 := usbMuxClient.Listen(usbmuxInput) + if err2 != nil { + return nil, NewErrorPrint(ErrSendCommand, "listen", err2) + } + shutDown := make(chan os.Signal, 1) + signal.Notify(shutDown, os.Interrupt, os.Kill) + go func() { + for { + select { + case d, ok := <-usbmuxInput: + if !ok { // usbmux channel closed + close(shutDown) + return + } + if d == nil { + continue + } + deviceByte, _ := json.Marshal(d.Properties()) + device := &entity.Device{} + errDecode := json.Unmarshal(deviceByte, device) + if errDecode != nil { + cb(nil, nil, errDecode, shutdownUsbmuxFun) + continue + } + device.Status = device.GetStatus() + cb(&d, device, nil, shutdownUsbmuxFun) + case <-shutDown: + shutdownUsbmuxFun() + return + } + } + }() + return shutdownUsbmuxFun, nil +}