diff --git a/device.go b/device.go index 880bba4..6b4671e 100644 --- a/device.go +++ b/device.go @@ -41,6 +41,7 @@ type device struct { instruments Instruments afc Afc houseArrest HouseArrest + syslogRelay SyslogRelay } func (d *device) Properties() DeviceProperties { @@ -435,6 +436,35 @@ func (d *device) HouseArrestService() (houseArrest HouseArrest, err error) { return } +func (d *device) syslogRelayService() (syslogRelay SyslogRelay, err error) { + if d.syslogRelay != nil { + return d.syslogRelay, nil + } + if _, err = d.lockdownService(); err != nil { + return nil, err + } + if d.syslogRelay, err = d.lockdown.SyslogRelayService(); err != nil { + return nil, err + } + syslogRelay = d.syslogRelay + return +} + +func (d *device) Syslog() (lines <-chan string, err error) { + if _, err = d.syslogRelayService(); err != nil { + return nil, err + } + return d.syslogRelay.Lines(), nil +} + +func (d *device) SyslogStop() (err error) { + if _, err = d.syslogRelayService(); err != nil { + return err + } + d.syslogRelay.Stop() + return nil +} + func (d *device) XCTest(bundleID string) (out <-chan string, cancel context.CancelFunc, err error) { ctx, cancelFunc := context.WithCancel(context.TODO()) _out := make(chan string) diff --git a/device_test.go b/device_test.go index 0f57681..5901c73 100644 --- a/device_test.go +++ b/device_test.go @@ -5,6 +5,7 @@ import ( "os" "os/signal" "testing" + "time" ) var dev Device @@ -113,3 +114,32 @@ func Test_device_AppUninstall(t *testing.T) { t.Fatal(err) } } + +func Test_device_Syslog(t *testing.T) { + setupLockdownSrv(t) + + lines, err := dev.Syslog() + if err != nil { + t.Fatal(err) + } + + done := make(chan os.Signal, 1) + + go func() { + for line := range lines { + fmt.Println(line) + } + done <- os.Interrupt + t.Log("DONE!!!") + }() + + signal.Notify(done, os.Interrupt, os.Kill) + + // <-done + time.Sleep(3 * time.Second) + err = dev.SyslogStop() + if err != nil { + t.Fatal(err) + } + time.Sleep(200 * time.Millisecond) +} diff --git a/idevice.go b/idevice.go index fcb22c8..81c4874 100644 --- a/idevice.go +++ b/idevice.go @@ -57,6 +57,10 @@ type Device interface { HouseArrestService() (houseArrest HouseArrest, err error) + syslogRelayService() (syslogRelay SyslogRelay, err error) + Syslog() (lines <-chan string, err error) + SyslogStop() (err error) + XCTest(bundleID string) (out <-chan string, cancel context.CancelFunc, err error) } @@ -83,6 +87,7 @@ type Lockdown interface { TestmanagerdService() (testmanagerd Testmanagerd, err error) AfcService() (afc Afc, err error) HouseArrestService() (houseArrest HouseArrest, err error) + SyslogRelayService() (syslogRelay SyslogRelay, err error) } type ImageMounter interface { @@ -177,6 +182,11 @@ type XCTestManagerDaemon interface { close() } +type SyslogRelay interface { + Lines() <-chan string + Stop() +} + type InnerConn = libimobiledevice.InnerConn type LockdownType = libimobiledevice.LockdownType diff --git a/lockdown.go b/lockdown.go index 54d24f4..6527b2f 100644 --- a/lockdown.go +++ b/lockdown.go @@ -437,6 +437,16 @@ func (c *lockdown) HouseArrestService() (houseArrest HouseArrest, err error) { return } +func (c *lockdown) SyslogRelayService() (syslogRelay SyslogRelay, err error) { + var innerConn InnerConn + if innerConn, err = c._startService(libimobiledevice.SyslogRelayServiceName, nil); err != nil { + return nil, err + } + syslogRelayClient := libimobiledevice.NewSyslogRelayClient(innerConn) + syslogRelay = newSyslogRelay(syslogRelayClient) + return +} + func (c *lockdown) _startService(serviceName string, escrowBag []byte) (innerConn InnerConn, err error) { if err = c.handshake(); err != nil { return nil, err @@ -455,7 +465,7 @@ func (c *lockdown) _startService(serviceName string, escrowBag []byte) (innerCon return nil, err } - if innerConn, err = c.dev.NewConnect(dynamicPort); err != nil { + if innerConn, err = c.dev.NewConnect(dynamicPort, 0); err != nil { return nil, err } // clean deadline diff --git a/lockdown_test.go b/lockdown_test.go index 1423378..f5a899b 100644 --- a/lockdown_test.go +++ b/lockdown_test.go @@ -1,7 +1,11 @@ package giDevice import ( + "fmt" + "os" + "os/signal" "testing" + "time" ) var lockdownSrv Lockdown @@ -41,3 +45,30 @@ func Test_lockdown_GetValue(t *testing.T) { t.Log(v) } + +func Test_lockdown_SyslogRelayService(t *testing.T) { + setupLockdownSrv(t) + + syslogRelaySrv, err := lockdownSrv.SyslogRelayService() + if err != nil { + t.Fatal(err) + } + + lines := syslogRelaySrv.Lines() + + done := make(chan os.Signal, 1) + + go func() { + for line := range lines { + fmt.Println(line) + } + done <- os.Interrupt + fmt.Println("DONE!!!") + }() + + signal.Notify(done, os.Interrupt, os.Kill) + + <-done + syslogRelaySrv.Stop() + time.Sleep(time.Second) +} diff --git a/pkg/libimobiledevice/syslogrelay.go b/pkg/libimobiledevice/syslogrelay.go new file mode 100644 index 0000000..2901f2d --- /dev/null +++ b/pkg/libimobiledevice/syslogrelay.go @@ -0,0 +1,21 @@ +package libimobiledevice + +const SyslogRelayServiceName = "com.apple.syslog_relay" + +func NewSyslogRelayClient(innerConn InnerConn) *SyslogRelayClient { + return &SyslogRelayClient{ + newServicePacketClient(innerConn), + } +} + +type SyslogRelayClient struct { + client *servicePacketClient +} + +func (c *SyslogRelayClient) InnerConn() InnerConn { + return c.client.innerConn +} + +func (c *SyslogRelayClient) Close() { + c.client.innerConn.Close() +} diff --git a/syslogrelay.go b/syslogrelay.go new file mode 100644 index 0000000..03680bc --- /dev/null +++ b/syslogrelay.go @@ -0,0 +1,76 @@ +package giDevice + +import ( + "bufio" + "fmt" + "github.com/electricbubble/gidevice/pkg/libimobiledevice" + "io" + "strings" +) + +var _ SyslogRelay = (*syslogRelay)(nil) + +func newSyslogRelay(client *libimobiledevice.SyslogRelayClient) *syslogRelay { + r := &syslogRelay{ + client: client, + stop: make(chan bool), + } + r.reader = bufio.NewReader(r.client.InnerConn().RawConn()) + return r +} + +type syslogRelay struct { + client *libimobiledevice.SyslogRelayClient + + reader *bufio.Reader + stop chan bool +} + +func (r *syslogRelay) Lines() <-chan string { + out := make(chan string) + + go func() { + for { + select { + case <-r.stop: + close(out) + return + default: + bs, err := r.readLine() + if err != nil { + if strings.Contains(err.Error(), io.EOF.Error()) { + return + } + debugLog(fmt.Sprintf("syslog: %s", err)) + } + if len(bs) > 1 && bs[0] == 0 { + bs = bs[1:] + } + out <- string(bs) + } + } + }() + return out +} + +func (r *syslogRelay) Stop() { + r.stop <- true +} + +func (r *syslogRelay) readLine() ([]byte, error) { + var line []byte + for { + l, more, err := r.reader.ReadLine() + if err != nil { + return nil, err + } + if line == nil && !more { + return l, nil + } + line = append(line, l...) + if !more { + break + } + } + return line, nil +}