utils.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. package main
  2. import (
  3. "bufio"
  4. "bytes"
  5. "crypto/rand"
  6. "encoding/hex"
  7. "fmt"
  8. "image"
  9. "io"
  10. "log"
  11. "net"
  12. "net/http"
  13. "os"
  14. "net/url"
  15. "os/exec"
  16. "path/filepath"
  17. "regexp"
  18. "strconv"
  19. "strings"
  20. "time"
  21. "github.com/codeskyblue/goreq"
  22. "github.com/codeskyblue/procfs"
  23. shellquote "github.com/kballard/go-shellquote"
  24. "github.com/openatx/androidutils"
  25. "github.com/pkg/errors"
  26. "github.com/shogo82148/androidbinary/apk"
  27. )
  28. // TempFileName generates a temporary filename for use in testing or whatever
  29. func TempFileName(dir, suffix string) string {
  30. randBytes := make([]byte, 16)
  31. rand.Read(randBytes)
  32. return filepath.Join(dir, hex.EncodeToString(randBytes)+suffix)
  33. }
  34. func fileExists(path string) bool {
  35. _, err := os.Stat(path)
  36. return err == nil
  37. }
  38. // Command add timeout support for os/exec
  39. type Command struct {
  40. Args []string
  41. Timeout time.Duration
  42. Shell bool
  43. ShellQuote bool
  44. Stdout io.Writer
  45. Stderr io.Writer
  46. }
  47. func (c *Command) shellPath() string {
  48. sh := os.Getenv("SHELL")
  49. if sh == "" {
  50. sh, err := exec.LookPath("sh")
  51. if err == nil {
  52. return sh
  53. }
  54. sh = "/system/bin/sh"
  55. }
  56. return sh
  57. }
  58. func (c *Command) computedArgs() (name string, args []string) {
  59. if c.Shell {
  60. var cmdline string
  61. if c.ShellQuote {
  62. cmdline = shellquote.Join(c.Args...)
  63. } else {
  64. cmdline = strings.Join(c.Args, " ") // simple, but works well with ">". eg Args("echo", "hello", ">output.txt")
  65. }
  66. args = append(args, "-c", cmdline)
  67. return c.shellPath(), args
  68. }
  69. return c.Args[0], c.Args[1:]
  70. }
  71. func (c Command) newCommand() *exec.Cmd {
  72. name, args := c.computedArgs()
  73. cmd := exec.Command(name, args...)
  74. if c.Stdout != nil {
  75. cmd.Stdout = c.Stdout
  76. }
  77. if c.Stderr != nil {
  78. cmd.Stderr = c.Stderr
  79. }
  80. return cmd
  81. }
  82. func (c Command) Run() error {
  83. cmd := c.newCommand()
  84. if c.Timeout > 0 {
  85. timer := time.AfterFunc(c.Timeout, func() {
  86. if cmd.Process != nil {
  87. cmd.Process.Kill()
  88. }
  89. })
  90. defer timer.Stop()
  91. }
  92. return cmd.Run()
  93. }
  94. func (c Command) Output() (output []byte, err error) {
  95. var b bytes.Buffer
  96. c.Stdout = &b
  97. c.Stderr = nil
  98. err = c.Run()
  99. return b.Bytes(), err
  100. }
  101. func (c Command) CombinedOutput() (output []byte, err error) {
  102. var b bytes.Buffer
  103. c.Stdout = &b
  104. c.Stderr = &b
  105. err = c.Run()
  106. return b.Bytes(), err
  107. }
  108. func (c Command) CombinedOutputString() (output string, err error) {
  109. bytesOutput, err := c.CombinedOutput()
  110. return string(bytesOutput), err
  111. }
  112. // need add timeout
  113. func runShell(args ...string) (output []byte, err error) {
  114. return Command{
  115. Args: args,
  116. Shell: true,
  117. ShellQuote: false,
  118. Timeout: 10 * time.Minute,
  119. }.CombinedOutput()
  120. }
  121. func runShellOutput(args ...string) (output []byte, err error) {
  122. return Command{
  123. Args: args,
  124. Shell: true,
  125. ShellQuote: false,
  126. Timeout: 10 * time.Minute,
  127. }.Output()
  128. }
  129. func runShellTimeout(duration time.Duration, args ...string) (output []byte, err error) {
  130. return Command{
  131. Args: args,
  132. Shell: true,
  133. Timeout: duration,
  134. }.CombinedOutput()
  135. }
  136. type fakeWriter struct {
  137. writeFunc func([]byte) (int, error)
  138. Err chan error
  139. }
  140. func (w *fakeWriter) Write(data []byte) (int, error) {
  141. n, err := w.writeFunc(data)
  142. if err != nil {
  143. select {
  144. case w.Err <- err:
  145. default:
  146. }
  147. }
  148. return n, err
  149. }
  150. func newFakeWriter(f func([]byte) (int, error)) *fakeWriter {
  151. return &fakeWriter{
  152. writeFunc: f,
  153. Err: make(chan error, 1),
  154. }
  155. }
  156. // pidof
  157. func pidOf(packageName string) (pid int, err error) {
  158. fs, err := procfs.NewFS(procfs.DefaultMountPoint)
  159. if err != nil {
  160. return
  161. }
  162. procs, err := fs.AllProcs()
  163. if err != nil {
  164. return
  165. }
  166. for _, proc := range procs {
  167. cmdline, _ := proc.CmdLine()
  168. if len(cmdline) == 1 && cmdline[0] == packageName {
  169. return proc.PID, nil
  170. }
  171. }
  172. return 0, errors.New("package not found")
  173. }
  174. type PackageInfo struct {
  175. MainActivity string `json:"mainActivity"`
  176. Label string `json:"label"`
  177. VersionName string `json:"versionName"`
  178. VersionCode int `json:"versionCode"`
  179. Size int64 `json:"size"`
  180. Icon image.Image `json:"-"`
  181. }
  182. func pkgInfo(packageName string) (info PackageInfo, err error) {
  183. outbyte, err := runShell("pm", "path", packageName)
  184. output := strings.TrimSpace(string(outbyte))
  185. if !strings.HasPrefix(output, "package:") {
  186. err = errors.New("package " + strconv.Quote(packageName) + " not found")
  187. return
  188. }
  189. apkpath := output[len("package:"):]
  190. finfo, err := os.Stat(apkpath)
  191. if err != nil {
  192. return
  193. }
  194. info.Size = finfo.Size()
  195. pkg, err := apk.OpenFile(apkpath)
  196. if err != nil {
  197. err = errors.Wrap(err, packageName)
  198. return
  199. }
  200. info.Label, _ = pkg.Label(nil)
  201. info.MainActivity, _ = pkg.MainActivity()
  202. info.Icon, _ = pkg.Icon(nil)
  203. info.VersionCode = pkg.Manifest().VersionCode
  204. info.VersionName = pkg.Manifest().VersionName
  205. return
  206. }
  207. func procWalk(fn func(p procfs.Proc)) error {
  208. fs, err := procfs.NewFS(procfs.DefaultMountPoint)
  209. if err != nil {
  210. return err
  211. }
  212. procs, err := fs.AllProcs()
  213. for _, proc := range procs {
  214. fn(proc)
  215. }
  216. return nil
  217. }
  218. // get main activity with packageName
  219. func mainActivityOf(packageName string) (activity string, err error) {
  220. output, err := runShellOutput("pm", "list", "packages", "-f", packageName)
  221. if err != nil {
  222. log.Println("pm list err:", err)
  223. return
  224. }
  225. matches := regexp.MustCompile(`package:(.+)=([.\w]+)`).FindAllStringSubmatch(string(output), -1)
  226. for _, match := range matches {
  227. if match[2] != packageName {
  228. continue
  229. }
  230. pkg, err := apk.OpenFile(match[1])
  231. if err != nil {
  232. return "", err
  233. }
  234. return pkg.MainActivity()
  235. }
  236. return "", errors.New("package not found")
  237. }
  238. // download minicap or minitouch apk, etc...
  239. func httpDownload(path string, urlStr string, perms os.FileMode) (written int64, err error) {
  240. resp, err := goreq.Request{
  241. Uri: urlStr,
  242. RedirectHeaders: true,
  243. MaxRedirects: 10,
  244. }.Do()
  245. if err != nil {
  246. return
  247. }
  248. defer resp.Body.Close()
  249. if resp.StatusCode != http.StatusOK {
  250. err = fmt.Errorf("http download <%s> status %v", urlStr, resp.Status)
  251. return
  252. }
  253. file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, perms)
  254. if err != nil {
  255. return
  256. }
  257. defer file.Close()
  258. written, err = io.Copy(file, resp.Body)
  259. log.Println("http download:", written)
  260. return
  261. }
  262. func hijackHTTPRequest(w http.ResponseWriter) (conn net.Conn, err error) {
  263. hj, ok := w.(http.Hijacker)
  264. if !ok {
  265. err = errors.New("webserver don't support hijacking")
  266. return
  267. }
  268. hjconn, bufrw, err := hj.Hijack()
  269. if err != nil {
  270. return nil, err
  271. }
  272. conn = newHijackReadWriteCloser(hjconn.(*net.TCPConn), bufrw)
  273. return
  274. }
  275. type hijactRW struct {
  276. *net.TCPConn
  277. bufrw *bufio.ReadWriter
  278. }
  279. func (this *hijactRW) Write(data []byte) (int, error) {
  280. nn, err := this.bufrw.Write(data)
  281. this.bufrw.Flush()
  282. return nn, err
  283. }
  284. func (this *hijactRW) Read(p []byte) (int, error) {
  285. return this.bufrw.Read(p)
  286. }
  287. func newHijackReadWriteCloser(conn *net.TCPConn, bufrw *bufio.ReadWriter) net.Conn {
  288. return &hijactRW{
  289. bufrw: bufrw,
  290. TCPConn: conn,
  291. }
  292. }
  293. func getCachedProperty(name string) string {
  294. return androidutils.CachedProperty(name)
  295. }
  296. func getProperty(name string) string {
  297. return androidutils.Property(name)
  298. }
  299. func copyToFile(rd io.Reader, dst string) error {
  300. fd, err := os.Create(dst)
  301. if err != nil {
  302. return err
  303. }
  304. defer fd.Close()
  305. _, err = io.Copy(fd, rd)
  306. return err
  307. }
  308. // Get preferred outbound ip of this machine
  309. func GetOutboundIP() (net.IP, error) {
  310. conn, err := net.Dial("udp", "8.8.8.8:80")
  311. if err != nil {
  312. return nil, err
  313. }
  314. defer conn.Close()
  315. localAddr := conn.LocalAddr().(*net.UDPAddr)
  316. return localAddr.IP, nil
  317. }
  318. // Generate Addr with http:// and ws://
  319. type NetAddr url.URL
  320. // NewNetAddr accept http://.... or example.com or 192.168.0.1:3000
  321. func NewNetAddr(addr string) *NetAddr {
  322. // var host string
  323. if !regexp.MustCompile(`^(http|ws)s?://`).MatchString(addr){
  324. addr = "http://"+addr
  325. }
  326. u, err := url.Parse(addr)
  327. if err != nil {
  328. panic(err)
  329. }
  330. return (*NetAddr)(u)
  331. }
  332. func (n *NetAddr) HTTPAddr(paths ...string) string {
  333. return n.Scheme + "://"+n.Host + strings.Join(paths, "")
  334. }
  335. func (n *NetAddr) WebSocketAddr(paths ...string) string {
  336. scheme := "ws"
  337. if n.Scheme == "https" {
  338. scheme = "wss"
  339. }
  340. return scheme + "://"+n.Host+strings.Join(paths, "")
  341. }