forked from TrueCloudLab/rclone
Compare commits
4 commits
master
...
adb-remote
Author | SHA1 | Date | |
---|---|---|---|
|
c3e1a0f368 | ||
|
2683939a4b | ||
|
ed88ae878e | ||
|
2ba5c35e88 |
66 changed files with 6202 additions and 308 deletions
821
backend/adb/adb.go
Normal file
821
backend/adb/adb.go
Normal file
|
@ -0,0 +1,821 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ncw/rclone/fs"
|
||||
"github.com/ncw/rclone/fs/config/configmap"
|
||||
"github.com/ncw/rclone/fs/config/configstruct"
|
||||
"github.com/ncw/rclone/fs/hash"
|
||||
"github.com/ncw/rclone/lib/readers"
|
||||
"github.com/pkg/errors"
|
||||
adb "github.com/thinkhy/go-adb"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
// Register with Fs
|
||||
func init() {
|
||||
fs.Register(&fs.RegInfo{
|
||||
Name: "adb",
|
||||
Description: "Android Debug Bridge",
|
||||
NewFs: NewFs,
|
||||
Options: []fs.Option{{
|
||||
Name: "serial",
|
||||
Help: "The device serial to use. Leave empty for auto selection.",
|
||||
Advanced: true,
|
||||
}, {
|
||||
Name: "host",
|
||||
Default: "localhost",
|
||||
Help: "The ADB server host.",
|
||||
Advanced: true,
|
||||
}, {
|
||||
Name: "port",
|
||||
Default: 5037,
|
||||
Help: "The ADB server port.",
|
||||
Advanced: true,
|
||||
}, {
|
||||
Name: "executable",
|
||||
Help: "The ADB executable path.",
|
||||
Advanced: true,
|
||||
}, {
|
||||
Name: "copy_links",
|
||||
Help: "Follow symlinks and copy the pointed to item.",
|
||||
Default: false,
|
||||
Advanced: true,
|
||||
}},
|
||||
})
|
||||
}
|
||||
|
||||
// Options defines the configuration for this backend
|
||||
type Options struct {
|
||||
Serial string
|
||||
Host string
|
||||
Port uint16
|
||||
Executable string
|
||||
FollowSymlinks bool `config:"copy_links"`
|
||||
}
|
||||
|
||||
// Fs represents a adb device
|
||||
type Fs struct {
|
||||
name string // name of this remote
|
||||
root string // the path we are working on
|
||||
opt Options // parsed options
|
||||
features *fs.Features // optional features
|
||||
client *adb.Adb
|
||||
device *execDevice
|
||||
statFunc statFunc
|
||||
statFuncMu sync.Mutex
|
||||
touchFunc touchFunc
|
||||
touchFuncMu sync.Mutex
|
||||
}
|
||||
|
||||
// Object describes a adb file
|
||||
type Object struct {
|
||||
fs *Fs // what this object is part of
|
||||
remote string // The remote path
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
|
||||
// Name of the remote (as passed into NewFs)
|
||||
func (f *Fs) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
// Root of the remote (as passed into NewFs)
|
||||
func (f *Fs) Root() string {
|
||||
return f.root
|
||||
}
|
||||
|
||||
// String converts this Fs to a string
|
||||
func (f *Fs) String() string {
|
||||
return fmt.Sprintf("ADB root '%s'", f.root)
|
||||
}
|
||||
|
||||
// Features returns the optional features of this Fs
|
||||
func (f *Fs) Features() *fs.Features {
|
||||
return f.features
|
||||
}
|
||||
|
||||
// NewFs constructs an Fs from the path, container:path
|
||||
func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) {
|
||||
// Parse config into Options struct
|
||||
opt := new(Options)
|
||||
err := configstruct.Set(m, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if root == "" {
|
||||
root = "/"
|
||||
}
|
||||
|
||||
f := &Fs{
|
||||
name: name,
|
||||
root: root,
|
||||
opt: *opt,
|
||||
statFunc: (*Object).statTry,
|
||||
touchFunc: (*Object).touchTry,
|
||||
}
|
||||
f.features = (&fs.Features{
|
||||
CanHaveEmptyDirectories: true,
|
||||
}).Fill(f)
|
||||
|
||||
f.client, err = adb.NewWithConfig(adb.ServerConfig{
|
||||
Host: opt.Host,
|
||||
Port: int(opt.Port),
|
||||
PathToAdb: opt.Executable,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Could not configure ADB server")
|
||||
}
|
||||
err = f.client.StartServer()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Could not start ADB server")
|
||||
}
|
||||
|
||||
serverVersion, err := f.client.ServerVersion()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Could not get ADB server version")
|
||||
}
|
||||
fs.Debugf(f, "ADB server version: 0x%X", serverVersion)
|
||||
|
||||
serials, err := f.client.ListDeviceSerials()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Could not get ADB devices")
|
||||
}
|
||||
descriptor := adb.AnyDevice()
|
||||
if opt.Serial != "" {
|
||||
descriptor = adb.DeviceWithSerial(opt.Serial)
|
||||
}
|
||||
if len(serials) > 1 && opt.Serial == "" {
|
||||
return nil, errors.New("Multiple ADB devices found. Use the serial config to select a specific device")
|
||||
}
|
||||
f.device = &execDevice{f.client.Device(descriptor)}
|
||||
|
||||
// follow symlinks for root pathes
|
||||
entry, err := f.newEntryFollowSymlinks("")
|
||||
switch err {
|
||||
case nil:
|
||||
case fs.ErrorObjectNotFound:
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
switch entry.(type) {
|
||||
case fs.Object:
|
||||
f.root = path.Dir(f.root)
|
||||
return f, fs.ErrorIsFile
|
||||
case nil:
|
||||
return f, nil
|
||||
case fs.Directory:
|
||||
return f, nil
|
||||
default:
|
||||
return nil, errors.Errorf("Invalid root entry type %t", entry)
|
||||
}
|
||||
}
|
||||
|
||||
// Precision of the object storage system
|
||||
func (f *Fs) Precision() time.Duration {
|
||||
return 1 * time.Second
|
||||
}
|
||||
|
||||
// Hashes returns the supported hash sets.
|
||||
func (f *Fs) Hashes() hash.Set {
|
||||
return hash.Set(hash.None)
|
||||
}
|
||||
|
||||
// List the objects and directories in dir into entries. The
|
||||
// entries can be returned in any order but should be for a
|
||||
// complete directory.
|
||||
//
|
||||
// dir should be "" to list the root, and should not have
|
||||
// trailing slashes.
|
||||
//
|
||||
// This should return ErrDirNotFound if the directory isn't
|
||||
// found.
|
||||
func (f *Fs) List(dir string) (entries fs.DirEntries, err error) {
|
||||
p := path.Join(f.root, dir)
|
||||
dirEntries, err := f.device.ListDirEntries(p)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ListDirEntries")
|
||||
}
|
||||
|
||||
defer fs.CheckClose(dirEntries, &err)
|
||||
|
||||
found := false
|
||||
for dirEntries.Next() {
|
||||
found = true
|
||||
dirEntry := dirEntries.Entry()
|
||||
switch dirEntry.Name {
|
||||
case ".", "..":
|
||||
continue
|
||||
}
|
||||
fsEntry, err := f.entryForDirEntry(path.Join(dir, dirEntry.Name), dirEntry, f.opt.FollowSymlinks)
|
||||
if err != nil {
|
||||
fs.Errorf(p, "Listing error: %q: %v", dirEntry.Name, err)
|
||||
return nil, err
|
||||
} else if fsEntry != nil {
|
||||
entries = append(entries, fsEntry)
|
||||
} else {
|
||||
fs.Debugf(f, "Skipping DirEntry %#v", dirEntry)
|
||||
}
|
||||
}
|
||||
err = dirEntries.Err()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ListDirEntries")
|
||||
}
|
||||
if !found {
|
||||
return nil, fs.ErrorDirNotFound
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Fs) entryForDirEntry(remote string, e *adb.DirEntry, followSymlinks bool) (fs.DirEntry, error) {
|
||||
o := f.newObjectWithInfo(remote, e)
|
||||
// Follow symlinks if required
|
||||
if followSymlinks && (e.Mode&os.ModeSymlink) != 0 {
|
||||
err := f.statFunc(&o)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if o.mode.IsDir() {
|
||||
return fs.NewDir(remote, o.modTime), nil
|
||||
}
|
||||
return &o, nil
|
||||
}
|
||||
|
||||
func (f *Fs) newEntry(remote string) (fs.DirEntry, error) {
|
||||
return f.newEntryWithFollow(remote, f.opt.FollowSymlinks)
|
||||
}
|
||||
func (f *Fs) newEntryFollowSymlinks(remote string) (fs.DirEntry, error) {
|
||||
return f.newEntryWithFollow(remote, true)
|
||||
}
|
||||
func (f *Fs) newEntryWithFollow(remote string, followSymlinks bool) (fs.DirEntry, error) {
|
||||
entry, err := f.device.Stat(path.Join(f.root, remote))
|
||||
if err != nil {
|
||||
if adb.HasErrCode(err, adb.FileNoExistError) {
|
||||
return nil, fs.ErrorObjectNotFound
|
||||
}
|
||||
return nil, errors.Wrapf(err, "Stat failed")
|
||||
}
|
||||
return f.entryForDirEntry(remote, entry, followSymlinks)
|
||||
}
|
||||
|
||||
func (f *Fs) newObjectWithInfo(remote string, e *adb.DirEntry) Object {
|
||||
return Object{
|
||||
fs: f,
|
||||
remote: remote,
|
||||
size: int64(e.Size),
|
||||
mode: e.Mode,
|
||||
modTime: e.ModifiedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// NewObject finds the Object at remote. If it can't be found
|
||||
// it returns the error ErrorObjectNotFound.
|
||||
func (f *Fs) NewObject(remote string) (fs.Object, error) {
|
||||
entry, err := f.newEntry(remote)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, ok := entry.(fs.Object)
|
||||
if !ok {
|
||||
return nil, fs.ErrorObjectNotFound
|
||||
}
|
||||
return obj, nil
|
||||
}
|
||||
|
||||
// Put in to the remote path with the modTime given of the given size
|
||||
//
|
||||
// When called from outside a Fs by rclone, src.Size() will always be >= 0.
|
||||
// But for unknown-sized objects (indicated by src.Size() == -1), Put should either
|
||||
// return an error or upload it properly (rather than e.g. calling panic).
|
||||
//
|
||||
// May create the object even if it returns an error - if so
|
||||
// will return the object and the error, otherwise will return
|
||||
// nil and the error
|
||||
func (f *Fs) Put(in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) (fs.Object, error) {
|
||||
remote := src.Remote()
|
||||
// Temporary Object under construction - info filled in by Update()
|
||||
o := f.newObject(remote)
|
||||
err := o.Update(in, src, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
|
||||
// newObject makes a half completed Object
|
||||
func (f *Fs) newObject(remote string) *Object {
|
||||
return &Object{
|
||||
fs: f,
|
||||
remote: remote,
|
||||
}
|
||||
}
|
||||
|
||||
// Mkdir makes the directory (container, bucket)
|
||||
//
|
||||
// Shouldn't return an error if it already exists
|
||||
func (f *Fs) Mkdir(dir string) error {
|
||||
p := path.Join(f.root, dir)
|
||||
output, code, err := f.device.execCommandWithExitCode("mkdir -p", p)
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case adb.ShellExitError:
|
||||
entry, _ := f.newEntry(p)
|
||||
if _, ok := entry.(fs.Directory); ok {
|
||||
return nil
|
||||
}
|
||||
return errors.Errorf("mkdir %q failed with %d: %q", dir, code, output)
|
||||
default:
|
||||
return errors.Wrap(err, "mkdir")
|
||||
}
|
||||
}
|
||||
|
||||
// Rmdir removes the directory (container, bucket) if empty
|
||||
//
|
||||
// Return an error if it doesn't exist or isn't empty
|
||||
func (f *Fs) Rmdir(dir string) error {
|
||||
p := path.Join(f.root, dir)
|
||||
output, code, err := f.device.execCommandWithExitCode("rmdir", p)
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case adb.ShellExitError:
|
||||
return errors.Errorf("rmdir %q failed with %d: %q", dir, code, output)
|
||||
default:
|
||||
return errors.Wrap(err, "rmdir")
|
||||
}
|
||||
}
|
||||
|
||||
// Fs returns the parent Fs
|
||||
func (o *Object) Fs() fs.Info {
|
||||
return o.fs
|
||||
}
|
||||
|
||||
// Return a string version
|
||||
func (o *Object) String() string {
|
||||
if o == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// Remote returns the remote path
|
||||
func (o *Object) Remote() string {
|
||||
return o.remote
|
||||
}
|
||||
|
||||
// ModTime returns the modification date of the file
|
||||
// It should return a best guess if one isn't available
|
||||
func (o *Object) ModTime() time.Time {
|
||||
return o.modTime
|
||||
}
|
||||
|
||||
// Size returns the size of the file
|
||||
func (o *Object) Size() int64 {
|
||||
return o.size
|
||||
}
|
||||
|
||||
// Hash returns the selected checksum of the file
|
||||
// If no checksum is available it returns ""
|
||||
func (o *Object) Hash(hash.Type) (string, error) {
|
||||
return "", hash.ErrUnsupported
|
||||
}
|
||||
|
||||
// Storable says whether this object can be stored
|
||||
func (o *Object) Storable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SetModTime sets the metadata on the object to set the modification date
|
||||
func (o *Object) SetModTime(t time.Time) error {
|
||||
return o.fs.touchFunc(o, t)
|
||||
}
|
||||
|
||||
func (o *Object) stat() error {
|
||||
return o.statStatArg(statArgC, path.Join(o.fs.root, o.remote))
|
||||
}
|
||||
|
||||
func (o *Object) setMetadata(entry *adb.DirEntry) {
|
||||
// Don't overwrite the values if we don't need to
|
||||
// this avoids upsetting the race detector
|
||||
if o.size != int64(entry.Size) {
|
||||
o.size = int64(entry.Size)
|
||||
}
|
||||
if !o.modTime.Equal(entry.ModifiedAt) {
|
||||
o.modTime = entry.ModifiedAt
|
||||
}
|
||||
if o.mode != entry.Mode {
|
||||
o.mode = decodeEntryMode(uint32(entry.Mode))
|
||||
}
|
||||
}
|
||||
|
||||
// Open opens the file for read. Call Close() on the returned io.ReadCloser
|
||||
func (o *Object) Open(options ...fs.OpenOption) (io.ReadCloser, error) {
|
||||
const blockSize = 1 << 12
|
||||
|
||||
var offset, count int64 = 0, -1
|
||||
for _, option := range options {
|
||||
switch x := option.(type) {
|
||||
case *fs.RangeOption:
|
||||
offset, count = x.Decode(o.size)
|
||||
case *fs.SeekOption:
|
||||
offset = x.Offset
|
||||
default:
|
||||
if option.Mandatory() {
|
||||
fs.Logf(o, "Unsupported mandatory option: %v", option)
|
||||
}
|
||||
}
|
||||
}
|
||||
if offset > o.size {
|
||||
offset = o.size
|
||||
}
|
||||
if count < 0 {
|
||||
count = o.size - offset
|
||||
} else if count+offset > o.size {
|
||||
count = o.size - offset
|
||||
}
|
||||
fs.Debugf(o, "Open: remote: %q offset: %d count: %d", o.remote, offset, count)
|
||||
|
||||
if count == 0 {
|
||||
return ioutil.NopCloser(bytes.NewReader(nil)), nil
|
||||
}
|
||||
offsetBlocks, offsetRest := offset/blockSize, offset%blockSize
|
||||
countBlocks := (count-1)/blockSize + 1
|
||||
|
||||
conn, err := o.fs.device.execCommand(fmt.Sprintf("sh -c 'dd \"if=$0\" bs=%d skip=%d count=%d 2>/dev/null'", blockSize, offsetBlocks, countBlocks), path.Join(o.fs.root, o.remote))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &adbReader{
|
||||
ReadCloser: readers.NewLimitedReadCloser(conn, count+offsetRest),
|
||||
skip: offsetRest,
|
||||
expected: count,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Update in to the object with the modTime given of the given size
|
||||
//
|
||||
// When called from outside a Fs by rclone, src.Size() will always be >= 0.
|
||||
// But for unknown-sized objects (indicated by src.Size() == -1), Upload should either
|
||||
// return an error or update the object properly (rather than e.g. calling panic).
|
||||
func (o *Object) Update(in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
|
||||
for _, option := range options {
|
||||
if option.Mandatory() {
|
||||
fs.Logf(option, "Unsupported mandatory option: %v", option)
|
||||
}
|
||||
}
|
||||
written, err := o.writeToFile(path.Join(o.fs.root, o.remote), in, 0666, src.ModTime())
|
||||
if err != nil {
|
||||
if removeErr := o.Remove(); removeErr != nil {
|
||||
fs.Errorf(o, "Failed to remove partially written file: %v", removeErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
expected := src.Size()
|
||||
if expected == -1 {
|
||||
expected = written
|
||||
}
|
||||
for _, t := range []int64{100, 250, 500, 1000, 2500, 5000, 10000} {
|
||||
err = o.stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if o.size == expected {
|
||||
return nil
|
||||
}
|
||||
fs.Debugf(o, "Invalid size after update, expected: %d got: %d", expected, o.size)
|
||||
time.Sleep(time.Duration(t) * time.Millisecond)
|
||||
}
|
||||
return o.stat()
|
||||
}
|
||||
|
||||
// Remove this object
|
||||
func (o *Object) Remove() error {
|
||||
p := path.Join(o.fs.root, o.remote)
|
||||
output, code, err := o.fs.device.execCommandWithExitCode("rm", p)
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case adb.ShellExitError:
|
||||
return errors.Errorf("rm %q failed with %d: %q", o.remote, code, output)
|
||||
default:
|
||||
return errors.Wrap(err, "rm")
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Object) writeToFile(path string, rd io.Reader, perms os.FileMode, modeTime time.Time) (written int64, err error) {
|
||||
dst, err := o.fs.device.OpenWrite(path, perms, modeTime)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer fs.CheckClose(dst, &err)
|
||||
return io.Copy(dst, rd)
|
||||
}
|
||||
|
||||
type statFunc func(*Object) error
|
||||
|
||||
func (o *Object) statTry() error {
|
||||
o.fs.statFuncMu.Lock()
|
||||
defer o.fs.statFuncMu.Unlock()
|
||||
|
||||
for _, f := range []statFunc{
|
||||
(*Object).statStatL, (*Object).statRealPath, (*Object).statReadLink,
|
||||
} {
|
||||
err := f(o)
|
||||
if err != nil {
|
||||
fs.Debugf(o, "%s", err)
|
||||
} else {
|
||||
o.fs.statFunc = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Errorf("unable to resolve link target")
|
||||
}
|
||||
|
||||
const (
|
||||
statArgLc = "-Lc"
|
||||
statArgC = "-c"
|
||||
)
|
||||
|
||||
func (o *Object) statStatL() error {
|
||||
return o.statStatArg(statArgLc, path.Join(o.fs.root, o.remote))
|
||||
}
|
||||
|
||||
func (o *Object) statStatArg(arg, path string) error {
|
||||
output, code, err := o.fs.device.execCommandWithExitCode(fmt.Sprintf("stat %s %s", arg, "%f,%s,%Y"), path)
|
||||
output = strings.TrimSpace(output)
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
case adb.ShellExitError:
|
||||
return errors.Errorf("stat %q failed with %d: %q", o.remote, code, output)
|
||||
default:
|
||||
return errors.Wrap(err, "stat")
|
||||
}
|
||||
|
||||
parts := strings.Split(output, ",")
|
||||
if len(parts) != 3 {
|
||||
return errors.Errorf("stat %q invalid output %q", o.remote, output)
|
||||
}
|
||||
|
||||
mode, err := strconv.ParseUint(parts[0], 16, 32)
|
||||
if err != nil {
|
||||
return errors.Errorf("stat %q invalid output %q", o.remote, output)
|
||||
}
|
||||
size, err := strconv.ParseUint(parts[1], 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("stat %q invalid output %q", o.remote, output)
|
||||
}
|
||||
modTime, err := strconv.ParseInt(parts[2], 10, 64)
|
||||
if err != nil {
|
||||
return errors.Errorf("stat %q invalid output %q", o.remote, output)
|
||||
}
|
||||
|
||||
o.size = int64(size)
|
||||
o.modTime = time.Unix(modTime, 0)
|
||||
o.mode = decodeEntryMode(uint32(mode))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Object) statReadLink() error {
|
||||
p := path.Join(o.fs.root, o.remote)
|
||||
output, code, err := o.fs.device.execCommandWithExitCode("readlink -f", p)
|
||||
output = strings.TrimSuffix(output, "\n")
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
case adb.ShellExitError:
|
||||
return errors.Errorf("readlink %q failed with %d: %q", o.remote, code, output)
|
||||
default:
|
||||
return errors.Wrap(err, "readlink")
|
||||
}
|
||||
return o.statStatArg(statArgC, output)
|
||||
}
|
||||
func (o *Object) statRealPath() error {
|
||||
p := path.Join(o.fs.root, o.remote)
|
||||
output, code, err := o.fs.device.execCommandWithExitCode("realpath", p)
|
||||
output = strings.TrimSuffix(output, "\n")
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
case adb.ShellExitError:
|
||||
return errors.Errorf("realpath %q failed with %d: %q", o.remote, code, output)
|
||||
default:
|
||||
return errors.Wrap(err, "realpath")
|
||||
}
|
||||
return o.statStatArg(statArgC, output)
|
||||
}
|
||||
|
||||
type touchFunc func(*Object, time.Time) error
|
||||
|
||||
func (o *Object) touchTry(t time.Time) error {
|
||||
o.fs.touchFuncMu.Lock()
|
||||
defer o.fs.touchFuncMu.Unlock()
|
||||
|
||||
for _, f := range []touchFunc{
|
||||
(*Object).touchCmd, (*Object).touchCd,
|
||||
} {
|
||||
err := f(o, t)
|
||||
if err != nil {
|
||||
fs.Debugf(o, "%s", err)
|
||||
} else {
|
||||
o.fs.touchFunc = f
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Errorf("unable to resolve link target")
|
||||
}
|
||||
|
||||
const (
|
||||
touchArgCmd = "-cmd"
|
||||
touchArgCd = "-cd"
|
||||
)
|
||||
|
||||
func (o *Object) touchCmd(t time.Time) error {
|
||||
return o.touchStatArg(touchArgCmd, path.Join(o.fs.root, o.remote), t)
|
||||
}
|
||||
func (o *Object) touchCd(t time.Time) error {
|
||||
return o.touchStatArg(touchArgCd, path.Join(o.fs.root, o.remote), t)
|
||||
}
|
||||
|
||||
func (o *Object) touchStatArg(arg, path string, t time.Time) error {
|
||||
output, code, err := o.fs.device.execCommandWithExitCode(fmt.Sprintf("touch %s %s", arg, t.Format(time.RFC3339Nano)), path)
|
||||
output = strings.TrimSpace(output)
|
||||
switch err := err.(type) {
|
||||
case nil:
|
||||
case adb.ShellExitError:
|
||||
return errors.Errorf("touch %q failed with %d: %q", o.remote, code, output)
|
||||
default:
|
||||
return errors.Wrap(err, "touch")
|
||||
}
|
||||
|
||||
err = o.stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if diff, ok := checkTimeEqualWithPrecision(t, o.modTime, o.fs.Precision()); !ok {
|
||||
return errors.Errorf("touch %q to %s was ineffective: %d", o.remote, t, diff)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkTimeEqualWithPrecision(t0, t1 time.Time, precision time.Duration) (time.Duration, bool) {
|
||||
dt := t0.Sub(t1)
|
||||
if dt >= precision || dt <= -precision {
|
||||
return dt, false
|
||||
}
|
||||
return dt, true
|
||||
}
|
||||
|
||||
func decodeEntryMode(entryMode uint32) os.FileMode {
|
||||
const (
|
||||
unixIFBLK = 0x6000
|
||||
unixIFMT = 0xf000
|
||||
unixIFCHR = 0x2000
|
||||
unixIFDIR = 0x4000
|
||||
unixIFIFO = 0x1000
|
||||
unixIFLNK = 0xa000
|
||||
unixIFREG = 0x8000
|
||||
unixIFSOCK = 0xc000
|
||||
unixISGID = 0x400
|
||||
unixISUID = 0x800
|
||||
unixISVTX = 0x200
|
||||
)
|
||||
|
||||
mode := os.FileMode(entryMode & 0777)
|
||||
switch entryMode & unixIFMT {
|
||||
case unixIFBLK:
|
||||
mode |= os.ModeDevice
|
||||
case unixIFCHR:
|
||||
mode |= os.ModeDevice | os.ModeCharDevice
|
||||
case unixIFDIR:
|
||||
mode |= os.ModeDir
|
||||
case unixIFIFO:
|
||||
mode |= os.ModeNamedPipe
|
||||
case unixIFLNK:
|
||||
mode |= os.ModeSymlink
|
||||
case unixIFREG:
|
||||
// nothing to do
|
||||
case unixIFSOCK:
|
||||
mode |= os.ModeSocket
|
||||
}
|
||||
if entryMode&unixISGID != 0 {
|
||||
mode |= os.ModeSetgid
|
||||
}
|
||||
if entryMode&unixISUID != 0 {
|
||||
mode |= os.ModeSetuid
|
||||
}
|
||||
if entryMode&unixISVTX != 0 {
|
||||
mode |= os.ModeSticky
|
||||
}
|
||||
return mode
|
||||
}
|
||||
|
||||
type execDevice struct {
|
||||
*adb.Device
|
||||
}
|
||||
|
||||
func (d *execDevice) execCommandWithExitCode(cmd string, arg string) (string, int, error) {
|
||||
cmdLine := fmt.Sprintf("sh -c '%s \"$0\"; echo :$?' '%s'", cmd, strings.Replace(arg, "'", "'\\''", -1))
|
||||
fs.Debugf("adb", "exec: %s", cmdLine)
|
||||
conn, err := d.execCommand(cmdLine)
|
||||
if err != nil {
|
||||
return "", -1, err
|
||||
}
|
||||
|
||||
resp, err := conn.ReadUntilEof()
|
||||
if err != nil {
|
||||
return "", -1, errors.Wrap(err, "ExecCommand")
|
||||
}
|
||||
|
||||
outStr := string(resp)
|
||||
idx := strings.LastIndexByte(outStr, ':')
|
||||
if idx == -1 {
|
||||
return outStr, -1, fmt.Errorf("adb shell aborted, can not parse exit code")
|
||||
}
|
||||
exitCode, _ := strconv.Atoi(strings.TrimSpace(outStr[idx+1:]))
|
||||
if exitCode != 0 {
|
||||
err = adb.ShellExitError{Command: cmdLine, ExitCode: exitCode}
|
||||
}
|
||||
return outStr[:idx], exitCode, err
|
||||
}
|
||||
|
||||
func (d *execDevice) execCommand(cmd string, args ...string) (*wire.Conn, error) {
|
||||
cmd = prepareCommandLineEscaped(cmd, args...)
|
||||
conn, err := d.Dial()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "ExecCommand")
|
||||
}
|
||||
defer func() {
|
||||
if err != nil && conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
req := fmt.Sprintf("exec:%s", cmd)
|
||||
|
||||
if err = conn.SendMessage([]byte(req)); err != nil {
|
||||
return nil, errors.Wrap(err, "ExecCommand")
|
||||
}
|
||||
if _, err = conn.ReadStatus(req); err != nil {
|
||||
return nil, errors.Wrap(err, "ExecCommand")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func prepareCommandLineEscaped(cmd string, args ...string) string {
|
||||
for i, arg := range args {
|
||||
args[i] = fmt.Sprintf("'%s'", strings.Replace(arg, "'", "'\\''", -1))
|
||||
}
|
||||
|
||||
// Prepend the command to the args array.
|
||||
if len(args) > 0 {
|
||||
cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " "))
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type adbReader struct {
|
||||
io.ReadCloser
|
||||
skip int64
|
||||
read int64
|
||||
expected int64
|
||||
}
|
||||
|
||||
func (r *adbReader) Read(b []byte) (n int, err error) {
|
||||
n, err = r.ReadCloser.Read(b)
|
||||
if s := r.skip; n > 0 && s > 0 {
|
||||
_n := int64(n)
|
||||
if _n <= s {
|
||||
r.skip -= _n
|
||||
return r.Read(b)
|
||||
}
|
||||
r.skip = 0
|
||||
copy(b, b[s:n])
|
||||
n -= int(s)
|
||||
}
|
||||
r.read += int64(n)
|
||||
if err == io.EOF && r.read < r.expected {
|
||||
fs.Debugf("adb", "Read: read: %d expected: %d n: %d", r.read, r.expected, n)
|
||||
return n, io.ErrUnexpectedEOF
|
||||
}
|
||||
return n, err
|
||||
}
|
20
backend/adb/adb_test.go
Normal file
20
backend/adb/adb_test.go
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Test ADB filesystem interface
|
||||
package adb_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ncw/rclone/backend/adb"
|
||||
"github.com/ncw/rclone/fstest/fstests"
|
||||
)
|
||||
|
||||
// TestIntegration runs integration tests against the remote
|
||||
func TestIntegration(t *testing.T) {
|
||||
fstests.Run(t, &fstests.Opt{
|
||||
RemoteName: "TestAdb:/data/local/tmp",
|
||||
NilObject: (*adb.Object)(nil),
|
||||
ExtraConfig: []fstests.ExtraConfigItem{
|
||||
{Name: "TestAdb", Key: "copy_links", Value: "true"},
|
||||
},
|
||||
})
|
||||
}
|
|
@ -2,6 +2,7 @@ package all
|
|||
|
||||
import (
|
||||
// Active file systems
|
||||
_ "github.com/ncw/rclone/backend/adb"
|
||||
_ "github.com/ncw/rclone/backend/alias"
|
||||
_ "github.com/ncw/rclone/backend/amazonclouddrive"
|
||||
_ "github.com/ncw/rclone/backend/azureblob"
|
||||
|
|
5
go.mod
5
go.mod
|
@ -42,6 +42,7 @@ require (
|
|||
github.com/spf13/pflag v1.0.3
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/t3rm1n4l/go-mega v0.0.0-20190205172012-55a226cf41da
|
||||
github.com/thinkhy/go-adb v0.0.0-20190123053734-b4b48de70418
|
||||
github.com/xanzy/ssh-agent v0.2.0
|
||||
github.com/yunify/qingstor-sdk-go v2.2.15+incompatible
|
||||
go.etcd.io/bbolt v1.3.2 // indirect
|
||||
|
@ -49,10 +50,12 @@ require (
|
|||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006
|
||||
golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c
|
||||
google.golang.org/api v0.1.0
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
)
|
||||
|
||||
replace github.com/thinkhy/go-adb v0.0.0-20190123053734-b4b48de70418 => ../../../github.com/thinkhy/go-adb
|
||||
|
|
23
go.sum
23
go.sum
|
@ -23,6 +23,9 @@ github.com/a8m/tree v0.0.0-20181222104329-6a0b80129de4 h1:mK1/QgFPU4osbhjJ26B1w7
|
|||
github.com/a8m/tree v0.0.0-20181222104329-6a0b80129de4/go.mod h1:FSdwKX97koS5efgm8WevNf7XS3PqtyFkKDDXrz778cg=
|
||||
github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0=
|
||||
github.com/abbot/go-http-auth v0.4.0/go.mod h1:Cz6ARTIzApMJDzh5bRMSUou6UMSp0IEXg9km/ci7TJM=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anacrolix/dms v0.0.0-20180117034613-8af4925bffb5 h1:lmyFvZXNGOmsKCYXNwzDLWafnxeewxsFwdsvTvSC1sg=
|
||||
github.com/anacrolix/dms v0.0.0-20180117034613-8af4925bffb5/go.mod h1:DGqLjaZ3ziKKNRt+U5Q9PLWJ52Q/4rxfaaH/b3QYKaE=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||
|
@ -32,6 +35,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
|||
github.com/billziss-gh/cgofuse v1.1.0 h1:tATn9ZDvuPcOVlvR4tJitGHgAqy1y18+4mKmRfdfjec=
|
||||
github.com/billziss-gh/cgofuse v1.1.0/go.mod h1:LJjoaUojlVjgo5GQoEJTcJNqZJeRU0nCR84CyxKt2YM=
|
||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
||||
github.com/cheggaaa/pb v2.0.6+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
|
@ -75,6 +79,10 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1
|
|||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
|
@ -95,6 +103,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
|
@ -169,6 +179,7 @@ github.com/spf13/cobra v0.0.4-0.20190109003409-7547e83b2d85/go.mod h1:1l0Ry5zgKv
|
|||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
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 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
|
@ -178,6 +189,8 @@ github.com/t3rm1n4l/go-mega v0.0.0-20190205172012-55a226cf41da/go.mod h1:XWL4vDy
|
|||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
||||
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
|
||||
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
|
||||
github.com/yosemite-open/go-adb v0.0.0-20181206003817-d40962019194 h1:hQ7oP/X/5JR3gGKEEEZU3uihkDePXlFoTwr0XDu5CKg=
|
||||
github.com/yosemite-open/go-adb v0.0.0-20181206003817-d40962019194/go.mod h1:OoY1zUwKq/hv/6hBuQxzSRNu1XZ289eXaDNgoHa+3lU=
|
||||
github.com/yunify/qingstor-sdk-go v2.2.15+incompatible h1:/Z0q3/eSMoPYAuRmhjWtuGSmVVciFC6hfm3yfCKuvz0=
|
||||
github.com/yunify/qingstor-sdk-go v2.2.15+incompatible/go.mod h1:w6wqLDQ5bBTzxGJ55581UrSwLrsTAsdo9N6yX/8d9RY=
|
||||
go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk=
|
||||
|
@ -217,6 +230,10 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952 h1:FDfvYgoVsA7TTZSbgiqjAbfPbK47CNHdWl3h/PJtii0=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3 h1:+KlxhGbYkFs8lMfwKn+2ojry1ID5eBSMXprS2u/wqCE=
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
||||
|
@ -244,12 +261,18 @@ google.golang.org/genproto v0.0.0-20190201180003-4b09977fb922/go.mod h1:L3J43x8/
|
|||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v2 v2.0.6/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4=
|
||||
gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/mattn/go-colorable.v0 v0.1.0/go.mod h1:BVJlBXzARQxdi3nZo6f6bnl5yR20/tOL6p+V0KejgSY=
|
||||
gopkg.in/mattn/go-isatty.v0 v0.0.4/go.mod h1:wt691ab7g0X4ilKZNmMII3egK0bTxl37fEn/Fwbd8gc=
|
||||
gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
|
|
363
vendor/github.com/hashicorp/go-cleanhttp/LICENSE
generated
vendored
Normal file
363
vendor/github.com/hashicorp/go-cleanhttp/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,363 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
30
vendor/github.com/hashicorp/go-cleanhttp/README.md
generated
vendored
Normal file
30
vendor/github.com/hashicorp/go-cleanhttp/README.md
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
# cleanhttp
|
||||
|
||||
Functions for accessing "clean" Go http.Client values
|
||||
|
||||
-------------
|
||||
|
||||
The Go standard library contains a default `http.Client` called
|
||||
`http.DefaultClient`. It is a common idiom in Go code to start with
|
||||
`http.DefaultClient` and tweak it as necessary, and in fact, this is
|
||||
encouraged; from the `http` package documentation:
|
||||
|
||||
> The Client's Transport typically has internal state (cached TCP connections),
|
||||
so Clients should be reused instead of created as needed. Clients are safe for
|
||||
concurrent use by multiple goroutines.
|
||||
|
||||
Unfortunately, this is a shared value, and it is not uncommon for libraries to
|
||||
assume that they are free to modify it at will. With enough dependencies, it
|
||||
can be very easy to encounter strange problems and race conditions due to
|
||||
manipulation of this shared value across libraries and goroutines (clients are
|
||||
safe for concurrent use, but writing values to the client struct itself is not
|
||||
protected).
|
||||
|
||||
Making things worse is the fact that a bare `http.Client` will use a default
|
||||
`http.Transport` called `http.DefaultTransport`, which is another global value
|
||||
that behaves the same way. So it is not simply enough to replace
|
||||
`http.DefaultClient` with `&http.Client{}`.
|
||||
|
||||
This repository provides some simple functions to get a "clean" `http.Client`
|
||||
-- one that uses the same default values as the Go standard library, but
|
||||
returns a client that does not share any state with other clients.
|
57
vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go
generated
vendored
Normal file
57
vendor/github.com/hashicorp/go-cleanhttp/cleanhttp.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package cleanhttp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultTransport returns a new http.Transport with similar default values to
|
||||
// http.DefaultTransport, but with idle connections and keepalives disabled.
|
||||
func DefaultTransport() *http.Transport {
|
||||
transport := DefaultPooledTransport()
|
||||
transport.DisableKeepAlives = true
|
||||
transport.MaxIdleConnsPerHost = -1
|
||||
return transport
|
||||
}
|
||||
|
||||
// DefaultPooledTransport returns a new http.Transport with similar default
|
||||
// values to http.DefaultTransport. Do not use this for transient transports as
|
||||
// it can leak file descriptors over time. Only use this for transports that
|
||||
// will be re-used for the same host(s).
|
||||
func DefaultPooledTransport() *http.Transport {
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1,
|
||||
}
|
||||
return transport
|
||||
}
|
||||
|
||||
// DefaultClient returns a new http.Client with similar default values to
|
||||
// http.Client, but with a non-shared Transport, idle connections disabled, and
|
||||
// keepalives disabled.
|
||||
func DefaultClient() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: DefaultTransport(),
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultPooledClient returns a new http.Client with similar default values to
|
||||
// http.Client, but with a shared Transport. Do not use this function for
|
||||
// transient clients as it can leak file descriptors over time. Only use this
|
||||
// for clients that will be re-used for the same host(s).
|
||||
func DefaultPooledClient() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: DefaultPooledTransport(),
|
||||
}
|
||||
}
|
20
vendor/github.com/hashicorp/go-cleanhttp/doc.go
generated
vendored
Normal file
20
vendor/github.com/hashicorp/go-cleanhttp/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
// Package cleanhttp offers convenience utilities for acquiring "clean"
|
||||
// http.Transport and http.Client structs.
|
||||
//
|
||||
// Values set on http.DefaultClient and http.DefaultTransport affect all
|
||||
// callers. This can have detrimental effects, esepcially in TLS contexts,
|
||||
// where client or root certificates set to talk to multiple endpoints can end
|
||||
// up displacing each other, leading to hard-to-debug issues. This package
|
||||
// provides non-shared http.Client and http.Transport structs to ensure that
|
||||
// the configuration will not be overwritten by other parts of the application
|
||||
// or dependencies.
|
||||
//
|
||||
// The DefaultClient and DefaultTransport functions disable idle connections
|
||||
// and keepalives. Without ensuring that idle connections are closed before
|
||||
// garbage collection, short-term clients/transports can leak file descriptors,
|
||||
// eventually leading to "too many open files" errors. If you will be
|
||||
// connecting to the same hosts repeatedly from the same client, you can use
|
||||
// DefaultPooledClient to receive a client that has connection pooling
|
||||
// semantics similar to http.DefaultClient.
|
||||
//
|
||||
package cleanhttp
|
1
vendor/github.com/hashicorp/go-cleanhttp/go.mod
generated
vendored
Normal file
1
vendor/github.com/hashicorp/go-cleanhttp/go.mod
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
module github.com/hashicorp/go-cleanhttp
|
43
vendor/github.com/hashicorp/go-cleanhttp/handlers.go
generated
vendored
Normal file
43
vendor/github.com/hashicorp/go-cleanhttp/handlers.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package cleanhttp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// HandlerInput provides input options to cleanhttp's handlers
|
||||
type HandlerInput struct {
|
||||
ErrStatus int
|
||||
}
|
||||
|
||||
// PrintablePathCheckHandler is a middleware that ensures the request path
|
||||
// contains only printable runes.
|
||||
func PrintablePathCheckHandler(next http.Handler, input *HandlerInput) http.Handler {
|
||||
// Nil-check on input to make it optional
|
||||
if input == nil {
|
||||
input = &HandlerInput{
|
||||
ErrStatus: http.StatusBadRequest,
|
||||
}
|
||||
}
|
||||
|
||||
// Default to http.StatusBadRequest on error
|
||||
if input.ErrStatus == 0 {
|
||||
input.ErrStatus = http.StatusBadRequest
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Check URL path for non-printable characters
|
||||
idx := strings.IndexFunc(r.URL.Path, func(c rune) bool {
|
||||
return !unicode.IsPrint(c)
|
||||
})
|
||||
|
||||
if idx != -1 {
|
||||
w.WriteHeader(input.ErrStatus)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
})
|
||||
}
|
3
vendor/github.com/hashicorp/go-retryablehttp/.gitignore
generated
vendored
Normal file
3
vendor/github.com/hashicorp/go-retryablehttp/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
.idea/
|
||||
*.iml
|
||||
*.test
|
12
vendor/github.com/hashicorp/go-retryablehttp/.travis.yml
generated
vendored
Normal file
12
vendor/github.com/hashicorp/go-retryablehttp/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
sudo: false
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.8.1
|
||||
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
script: make updatedeps test
|
363
vendor/github.com/hashicorp/go-retryablehttp/LICENSE
generated
vendored
Normal file
363
vendor/github.com/hashicorp/go-retryablehttp/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,363 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
|
11
vendor/github.com/hashicorp/go-retryablehttp/Makefile
generated
vendored
Normal file
11
vendor/github.com/hashicorp/go-retryablehttp/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
default: test
|
||||
|
||||
test:
|
||||
go vet ./...
|
||||
go test -race ./...
|
||||
|
||||
updatedeps:
|
||||
go get -f -t -u ./...
|
||||
go get -f -u ./...
|
||||
|
||||
.PHONY: default test updatedeps
|
46
vendor/github.com/hashicorp/go-retryablehttp/README.md
generated
vendored
Normal file
46
vendor/github.com/hashicorp/go-retryablehttp/README.md
generated
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
go-retryablehttp
|
||||
================
|
||||
|
||||
[![Build Status](http://img.shields.io/travis/hashicorp/go-retryablehttp.svg?style=flat-square)][travis]
|
||||
[![Go Documentation](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)][godocs]
|
||||
|
||||
[travis]: http://travis-ci.org/hashicorp/go-retryablehttp
|
||||
[godocs]: http://godoc.org/github.com/hashicorp/go-retryablehttp
|
||||
|
||||
The `retryablehttp` package provides a familiar HTTP client interface with
|
||||
automatic retries and exponential backoff. It is a thin wrapper over the
|
||||
standard `net/http` client library and exposes nearly the same public API. This
|
||||
makes `retryablehttp` very easy to drop into existing programs.
|
||||
|
||||
`retryablehttp` performs automatic retries under certain conditions. Mainly, if
|
||||
an error is returned by the client (connection errors, etc.), or if a 500-range
|
||||
response code is received (except 501), then a retry is invoked after a wait
|
||||
period. Otherwise, the response is returned and left to the caller to
|
||||
interpret.
|
||||
|
||||
The main difference from `net/http` is that requests which take a request body
|
||||
(POST/PUT et. al) can have the body provided in a number of ways (some more or
|
||||
less efficient) that allow "rewinding" the request body if the initial request
|
||||
fails so that the full request can be attempted again. See the
|
||||
[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp) for more
|
||||
details.
|
||||
|
||||
Example Use
|
||||
===========
|
||||
|
||||
Using this library should look almost identical to what you would do with
|
||||
`net/http`. The most simple example of a GET request is shown below:
|
||||
|
||||
```go
|
||||
resp, err := retryablehttp.Get("/foo")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
```
|
||||
|
||||
The returned response object is an `*http.Response`, the same thing you would
|
||||
usually get from `net/http`. Had the request failed one or more times, the above
|
||||
call would block and retry with exponential backoff.
|
||||
|
||||
For more usage and examples see the
|
||||
[godoc](http://godoc.org/github.com/hashicorp/go-retryablehttp).
|
528
vendor/github.com/hashicorp/go-retryablehttp/client.go
generated
vendored
Normal file
528
vendor/github.com/hashicorp/go-retryablehttp/client.go
generated
vendored
Normal file
|
@ -0,0 +1,528 @@
|
|||
// The retryablehttp package provides a familiar HTTP client interface with
|
||||
// automatic retries and exponential backoff. It is a thin wrapper over the
|
||||
// standard net/http client library and exposes nearly the same public API.
|
||||
// This makes retryablehttp very easy to drop into existing programs.
|
||||
//
|
||||
// retryablehttp performs automatic retries under certain conditions. Mainly, if
|
||||
// an error is returned by the client (connection errors etc), or if a 500-range
|
||||
// response is received, then a retry is invoked. Otherwise, the response is
|
||||
// returned and left to the caller to interpret.
|
||||
//
|
||||
// Requests which take a request body should provide a non-nil function
|
||||
// parameter. The best choice is to provide either a function satisfying
|
||||
// ReaderFunc which provides multiple io.Readers in an efficient manner, a
|
||||
// *bytes.Buffer (the underlying raw byte slice will be used) or a raw byte
|
||||
// slice. As it is a reference type, and we will wrap it as needed by readers,
|
||||
// we can efficiently re-use the request body without needing to copy it. If an
|
||||
// io.Reader (such as a *bytes.Reader) is provided, the full body will be read
|
||||
// prior to the first request, and will be efficiently re-used for any retries.
|
||||
// ReadSeeker can be used, but some users have observed occasional data races
|
||||
// between the net/http library and the Seek functionality of some
|
||||
// implementations of ReadSeeker, so should be avoided if possible.
|
||||
package retryablehttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cleanhttp "github.com/hashicorp/go-cleanhttp"
|
||||
)
|
||||
|
||||
var (
|
||||
// Default retry configuration
|
||||
defaultRetryWaitMin = 1 * time.Second
|
||||
defaultRetryWaitMax = 30 * time.Second
|
||||
defaultRetryMax = 4
|
||||
|
||||
// defaultClient is used for performing requests without explicitly making
|
||||
// a new client. It is purposely private to avoid modifications.
|
||||
defaultClient = NewClient()
|
||||
|
||||
// We need to consume response bodies to maintain http connections, but
|
||||
// limit the size we consume to respReadLimit.
|
||||
respReadLimit = int64(4096)
|
||||
)
|
||||
|
||||
// ReaderFunc is the type of function that can be given natively to NewRequest
|
||||
type ReaderFunc func() (io.Reader, error)
|
||||
|
||||
// LenReader is an interface implemented by many in-memory io.Reader's. Used
|
||||
// for automatically sending the right Content-Length header when possible.
|
||||
type LenReader interface {
|
||||
Len() int
|
||||
}
|
||||
|
||||
// Request wraps the metadata needed to create HTTP requests.
|
||||
type Request struct {
|
||||
// body is a seekable reader over the request body payload. This is
|
||||
// used to rewind the request data in between retries.
|
||||
body ReaderFunc
|
||||
|
||||
// Embed an HTTP request directly. This makes a *Request act exactly
|
||||
// like an *http.Request so that all meta methods are supported.
|
||||
*http.Request
|
||||
}
|
||||
|
||||
// WithContext returns wrapped Request with a shallow copy of underlying *http.Request
|
||||
// with its context changed to ctx. The provided ctx must be non-nil.
|
||||
func (r *Request) WithContext(ctx context.Context) *Request {
|
||||
r.Request = r.Request.WithContext(ctx)
|
||||
return r
|
||||
}
|
||||
|
||||
// BodyBytes allows accessing the request body. It is an analogue to
|
||||
// http.Request's Body variable, but it returns a copy of the underlying data
|
||||
// rather than consuming it.
|
||||
//
|
||||
// This function is not thread-safe; do not call it at the same time as another
|
||||
// call, or at the same time this request is being used with Client.Do.
|
||||
func (r *Request) BodyBytes() ([]byte, error) {
|
||||
if r.body == nil {
|
||||
return nil, nil
|
||||
}
|
||||
body, err := r.body()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = buf.ReadFrom(body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// NewRequest creates a new wrapped request.
|
||||
func NewRequest(method, url string, rawBody interface{}) (*Request, error) {
|
||||
var err error
|
||||
var body ReaderFunc
|
||||
var contentLength int64
|
||||
|
||||
if rawBody != nil {
|
||||
switch rawBody.(type) {
|
||||
// If they gave us a function already, great! Use it.
|
||||
case ReaderFunc:
|
||||
body = rawBody.(ReaderFunc)
|
||||
tmp, err := body()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lr, ok := tmp.(LenReader); ok {
|
||||
contentLength = int64(lr.Len())
|
||||
}
|
||||
if c, ok := tmp.(io.Closer); ok {
|
||||
c.Close()
|
||||
}
|
||||
|
||||
case func() (io.Reader, error):
|
||||
body = rawBody.(func() (io.Reader, error))
|
||||
tmp, err := body()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if lr, ok := tmp.(LenReader); ok {
|
||||
contentLength = int64(lr.Len())
|
||||
}
|
||||
if c, ok := tmp.(io.Closer); ok {
|
||||
c.Close()
|
||||
}
|
||||
|
||||
// If a regular byte slice, we can read it over and over via new
|
||||
// readers
|
||||
case []byte:
|
||||
buf := rawBody.([]byte)
|
||||
body = func() (io.Reader, error) {
|
||||
return bytes.NewReader(buf), nil
|
||||
}
|
||||
contentLength = int64(len(buf))
|
||||
|
||||
// If a bytes.Buffer we can read the underlying byte slice over and
|
||||
// over
|
||||
case *bytes.Buffer:
|
||||
buf := rawBody.(*bytes.Buffer)
|
||||
body = func() (io.Reader, error) {
|
||||
return bytes.NewReader(buf.Bytes()), nil
|
||||
}
|
||||
contentLength = int64(buf.Len())
|
||||
|
||||
// We prioritize *bytes.Reader here because we don't really want to
|
||||
// deal with it seeking so want it to match here instead of the
|
||||
// io.ReadSeeker case.
|
||||
case *bytes.Reader:
|
||||
buf, err := ioutil.ReadAll(rawBody.(*bytes.Reader))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = func() (io.Reader, error) {
|
||||
return bytes.NewReader(buf), nil
|
||||
}
|
||||
contentLength = int64(len(buf))
|
||||
|
||||
// Compat case
|
||||
case io.ReadSeeker:
|
||||
raw := rawBody.(io.ReadSeeker)
|
||||
body = func() (io.Reader, error) {
|
||||
raw.Seek(0, 0)
|
||||
return ioutil.NopCloser(raw), nil
|
||||
}
|
||||
if lr, ok := raw.(LenReader); ok {
|
||||
contentLength = int64(lr.Len())
|
||||
}
|
||||
|
||||
// Read all in so we can reset
|
||||
case io.Reader:
|
||||
buf, err := ioutil.ReadAll(rawBody.(io.Reader))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
body = func() (io.Reader, error) {
|
||||
return bytes.NewReader(buf), nil
|
||||
}
|
||||
contentLength = int64(len(buf))
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("cannot handle type %T", rawBody)
|
||||
}
|
||||
}
|
||||
|
||||
httpReq, err := http.NewRequest(method, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
httpReq.ContentLength = contentLength
|
||||
|
||||
return &Request{body, httpReq}, nil
|
||||
}
|
||||
|
||||
// Logger interface allows to use other loggers than
|
||||
// standard log.Logger.
|
||||
type Logger interface {
|
||||
Printf(string, ...interface{})
|
||||
}
|
||||
|
||||
// RequestLogHook allows a function to run before each retry. The HTTP
|
||||
// request which will be made, and the retry number (0 for the initial
|
||||
// request) are available to users. The internal logger is exposed to
|
||||
// consumers.
|
||||
type RequestLogHook func(Logger, *http.Request, int)
|
||||
|
||||
// ResponseLogHook is like RequestLogHook, but allows running a function
|
||||
// on each HTTP response. This function will be invoked at the end of
|
||||
// every HTTP request executed, regardless of whether a subsequent retry
|
||||
// needs to be performed or not. If the response body is read or closed
|
||||
// from this method, this will affect the response returned from Do().
|
||||
type ResponseLogHook func(Logger, *http.Response)
|
||||
|
||||
// CheckRetry specifies a policy for handling retries. It is called
|
||||
// following each request with the response and error values returned by
|
||||
// the http.Client. If CheckRetry returns false, the Client stops retrying
|
||||
// and returns the response to the caller. If CheckRetry returns an error,
|
||||
// that error value is returned in lieu of the error from the request. The
|
||||
// Client will close any response body when retrying, but if the retry is
|
||||
// aborted it is up to the CheckResponse callback to properly close any
|
||||
// response body before returning.
|
||||
type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool, error)
|
||||
|
||||
// Backoff specifies a policy for how long to wait between retries.
|
||||
// It is called after a failing request to determine the amount of time
|
||||
// that should pass before trying again.
|
||||
type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration
|
||||
|
||||
// ErrorHandler is called if retries are expired, containing the last status
|
||||
// from the http library. If not specified, default behavior for the library is
|
||||
// to close the body and return an error indicating how many tries were
|
||||
// attempted. If overriding this, be sure to close the body if needed.
|
||||
type ErrorHandler func(resp *http.Response, err error, numTries int) (*http.Response, error)
|
||||
|
||||
// Client is used to make HTTP requests. It adds additional functionality
|
||||
// like automatic retries to tolerate minor outages.
|
||||
type Client struct {
|
||||
HTTPClient *http.Client // Internal HTTP client.
|
||||
Logger Logger // Customer logger instance.
|
||||
|
||||
RetryWaitMin time.Duration // Minimum time to wait
|
||||
RetryWaitMax time.Duration // Maximum time to wait
|
||||
RetryMax int // Maximum number of retries
|
||||
|
||||
// RequestLogHook allows a user-supplied function to be called
|
||||
// before each retry.
|
||||
RequestLogHook RequestLogHook
|
||||
|
||||
// ResponseLogHook allows a user-supplied function to be called
|
||||
// with the response from each HTTP request executed.
|
||||
ResponseLogHook ResponseLogHook
|
||||
|
||||
// CheckRetry specifies the policy for handling retries, and is called
|
||||
// after each request. The default policy is DefaultRetryPolicy.
|
||||
CheckRetry CheckRetry
|
||||
|
||||
// Backoff specifies the policy for how long to wait between retries
|
||||
Backoff Backoff
|
||||
|
||||
// ErrorHandler specifies the custom error handler to use, if any
|
||||
ErrorHandler ErrorHandler
|
||||
}
|
||||
|
||||
// NewClient creates a new Client with default settings.
|
||||
func NewClient() *Client {
|
||||
return &Client{
|
||||
HTTPClient: cleanhttp.DefaultClient(),
|
||||
Logger: log.New(os.Stderr, "", log.LstdFlags),
|
||||
RetryWaitMin: defaultRetryWaitMin,
|
||||
RetryWaitMax: defaultRetryWaitMax,
|
||||
RetryMax: defaultRetryMax,
|
||||
CheckRetry: DefaultRetryPolicy,
|
||||
Backoff: DefaultBackoff,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultRetryPolicy provides a default callback for Client.CheckRetry, which
|
||||
// will retry on connection errors and server errors.
|
||||
func DefaultRetryPolicy(ctx context.Context, resp *http.Response, err error) (bool, error) {
|
||||
// do not retry on context.Canceled or context.DeadlineExceeded
|
||||
if ctx.Err() != nil {
|
||||
return false, ctx.Err()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
// Check the response code. We retry on 500-range responses to allow
|
||||
// the server time to recover, as 500's are typically not permanent
|
||||
// errors and may relate to outages on the server side. This will catch
|
||||
// invalid response codes as well, like 0 and 999.
|
||||
if resp.StatusCode == 0 || (resp.StatusCode >= 500 && resp.StatusCode != 501) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// DefaultBackoff provides a default callback for Client.Backoff which
|
||||
// will perform exponential backoff based on the attempt number and limited
|
||||
// by the provided minimum and maximum durations.
|
||||
func DefaultBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
|
||||
mult := math.Pow(2, float64(attemptNum)) * float64(min)
|
||||
sleep := time.Duration(mult)
|
||||
if float64(sleep) != mult || sleep > max {
|
||||
sleep = max
|
||||
}
|
||||
return sleep
|
||||
}
|
||||
|
||||
// LinearJitterBackoff provides a callback for Client.Backoff which will
|
||||
// perform linear backoff based on the attempt number and with jitter to
|
||||
// prevent a thundering herd.
|
||||
//
|
||||
// min and max here are *not* absolute values. The number to be multipled by
|
||||
// the attempt number will be chosen at random from between them, thus they are
|
||||
// bounding the jitter.
|
||||
//
|
||||
// For instance:
|
||||
// * To get strictly linear backoff of one second increasing each retry, set
|
||||
// both to one second (1s, 2s, 3s, 4s, ...)
|
||||
// * To get a small amount of jitter centered around one second increasing each
|
||||
// retry, set to around one second, such as a min of 800ms and max of 1200ms
|
||||
// (892ms, 2102ms, 2945ms, 4312ms, ...)
|
||||
// * To get extreme jitter, set to a very wide spread, such as a min of 100ms
|
||||
// and a max of 20s (15382ms, 292ms, 51321ms, 35234ms, ...)
|
||||
func LinearJitterBackoff(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration {
|
||||
// attemptNum always starts at zero but we want to start at 1 for multiplication
|
||||
attemptNum++
|
||||
|
||||
if max <= min {
|
||||
// Unclear what to do here, or they are the same, so return min *
|
||||
// attemptNum
|
||||
return min * time.Duration(attemptNum)
|
||||
}
|
||||
|
||||
// Seed rand; doing this every time is fine
|
||||
rand := rand.New(rand.NewSource(int64(time.Now().Nanosecond())))
|
||||
|
||||
// Pick a random number that lies somewhere between the min and max and
|
||||
// multiply by the attemptNum. attemptNum starts at zero so we always
|
||||
// increment here. We first get a random percentage, then apply that to the
|
||||
// difference between min and max, and add to min.
|
||||
jitter := rand.Float64() * float64(max-min)
|
||||
jitterMin := int64(jitter) + int64(min)
|
||||
return time.Duration(jitterMin * int64(attemptNum))
|
||||
}
|
||||
|
||||
// PassthroughErrorHandler is an ErrorHandler that directly passes through the
|
||||
// values from the net/http library for the final request. The body is not
|
||||
// closed.
|
||||
func PassthroughErrorHandler(resp *http.Response, err error, _ int) (*http.Response, error) {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// Do wraps calling an HTTP method with retries.
|
||||
func (c *Client) Do(req *Request) (*http.Response, error) {
|
||||
if c.Logger != nil {
|
||||
c.Logger.Printf("[DEBUG] %s %s", req.Method, req.URL)
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
var err error
|
||||
|
||||
for i := 0; ; i++ {
|
||||
var code int // HTTP response code
|
||||
|
||||
// Always rewind the request body when non-nil.
|
||||
if req.body != nil {
|
||||
body, err := req.body()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
if c, ok := body.(io.ReadCloser); ok {
|
||||
req.Request.Body = c
|
||||
} else {
|
||||
req.Request.Body = ioutil.NopCloser(body)
|
||||
}
|
||||
}
|
||||
|
||||
if c.RequestLogHook != nil {
|
||||
c.RequestLogHook(c.Logger, req.Request, i)
|
||||
}
|
||||
|
||||
// Attempt the request
|
||||
resp, err = c.HTTPClient.Do(req.Request)
|
||||
if resp != nil {
|
||||
code = resp.StatusCode
|
||||
}
|
||||
|
||||
// Check if we should continue with retries.
|
||||
checkOK, checkErr := c.CheckRetry(req.Request.Context(), resp, err)
|
||||
|
||||
if err != nil {
|
||||
if c.Logger != nil {
|
||||
c.Logger.Printf("[ERR] %s %s request failed: %v", req.Method, req.URL, err)
|
||||
}
|
||||
} else {
|
||||
// Call this here to maintain the behavior of logging all requests,
|
||||
// even if CheckRetry signals to stop.
|
||||
if c.ResponseLogHook != nil {
|
||||
// Call the response logger function if provided.
|
||||
c.ResponseLogHook(c.Logger, resp)
|
||||
}
|
||||
}
|
||||
|
||||
// Now decide if we should continue.
|
||||
if !checkOK {
|
||||
if checkErr != nil {
|
||||
err = checkErr
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// We do this before drainBody beause there's no need for the I/O if
|
||||
// we're breaking out
|
||||
remain := c.RetryMax - i
|
||||
if remain <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// We're going to retry, consume any response to reuse the connection.
|
||||
if err == nil && resp != nil {
|
||||
c.drainBody(resp.Body)
|
||||
}
|
||||
|
||||
wait := c.Backoff(c.RetryWaitMin, c.RetryWaitMax, i, resp)
|
||||
desc := fmt.Sprintf("%s %s", req.Method, req.URL)
|
||||
if code > 0 {
|
||||
desc = fmt.Sprintf("%s (status: %d)", desc, code)
|
||||
}
|
||||
if c.Logger != nil {
|
||||
c.Logger.Printf("[DEBUG] %s: retrying in %s (%d left)", desc, wait, remain)
|
||||
}
|
||||
time.Sleep(wait)
|
||||
}
|
||||
|
||||
if c.ErrorHandler != nil {
|
||||
return c.ErrorHandler(resp, err, c.RetryMax+1)
|
||||
}
|
||||
|
||||
// By default, we close the response body and return an error without
|
||||
// returning the response
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
return nil, fmt.Errorf("%s %s giving up after %d attempts",
|
||||
req.Method, req.URL, c.RetryMax+1)
|
||||
}
|
||||
|
||||
// Try to read the response body so we can reuse this connection.
|
||||
func (c *Client) drainBody(body io.ReadCloser) {
|
||||
defer body.Close()
|
||||
_, err := io.Copy(ioutil.Discard, io.LimitReader(body, respReadLimit))
|
||||
if err != nil {
|
||||
if c.Logger != nil {
|
||||
c.Logger.Printf("[ERR] error reading response body: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get is a shortcut for doing a GET request without making a new client.
|
||||
func Get(url string) (*http.Response, error) {
|
||||
return defaultClient.Get(url)
|
||||
}
|
||||
|
||||
// Get is a convenience helper for doing simple GET requests.
|
||||
func (c *Client) Get(url string) (*http.Response, error) {
|
||||
req, err := NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
// Head is a shortcut for doing a HEAD request without making a new client.
|
||||
func Head(url string) (*http.Response, error) {
|
||||
return defaultClient.Head(url)
|
||||
}
|
||||
|
||||
// Head is a convenience method for doing simple HEAD requests.
|
||||
func (c *Client) Head(url string) (*http.Response, error) {
|
||||
req, err := NewRequest("HEAD", url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
// Post is a shortcut for doing a POST request without making a new client.
|
||||
func Post(url, bodyType string, body interface{}) (*http.Response, error) {
|
||||
return defaultClient.Post(url, bodyType, body)
|
||||
}
|
||||
|
||||
// Post is a convenience method for doing simple POST requests.
|
||||
func (c *Client) Post(url, bodyType string, body interface{}) (*http.Response, error) {
|
||||
req, err := NewRequest("POST", url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Content-Type", bodyType)
|
||||
return c.Do(req)
|
||||
}
|
||||
|
||||
// PostForm is a shortcut to perform a POST with form data without creating
|
||||
// a new client.
|
||||
func PostForm(url string, data url.Values) (*http.Response, error) {
|
||||
return defaultClient.PostForm(url, data)
|
||||
}
|
||||
|
||||
// PostForm is a convenience method for doing simple POST operations using
|
||||
// pre-filled url.Values form data.
|
||||
func (c *Client) PostForm(url string, data url.Values) (*http.Response, error) {
|
||||
return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
|
||||
}
|
3
vendor/github.com/hashicorp/go-retryablehttp/go.mod
generated
vendored
Normal file
3
vendor/github.com/hashicorp/go-retryablehttp/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
module github.com/hashicorp/go-retryablehttp
|
||||
|
||||
require github.com/hashicorp/go-cleanhttp v0.5.0
|
2
vendor/github.com/hashicorp/go-retryablehttp/go.sum
generated
vendored
Normal file
2
vendor/github.com/hashicorp/go-retryablehttp/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
11
vendor/github.com/thinkhy/go-adb/.travis.yml
generated
vendored
Normal file
11
vendor/github.com/thinkhy/go-adb/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.6
|
||||
- tip
|
||||
|
||||
install:
|
||||
- make get-deps
|
||||
|
||||
script:
|
||||
- make test
|
201
vendor/github.com/thinkhy/go-adb/LICENSE.txt
generated
vendored
Normal file
201
vendor/github.com/thinkhy/go-adb/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
13
vendor/github.com/thinkhy/go-adb/Makefile
generated
vendored
Normal file
13
vendor/github.com/thinkhy/go-adb/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
.PHONY: test generate get-deps
|
||||
|
||||
test: generate
|
||||
go test -v -race ./...
|
||||
|
||||
generate:
|
||||
# stringer requires the packages to be installed.
|
||||
go install -v
|
||||
go generate -x ./...
|
||||
|
||||
get-deps:
|
||||
go get -t -v ./...
|
||||
go get -u golang.org/x/tools/cmd/stringer
|
8
vendor/github.com/thinkhy/go-adb/README.md
generated
vendored
Normal file
8
vendor/github.com/thinkhy/go-adb/README.md
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
#goadb
|
||||
|
||||
[![Build Status](https://travis-ci.org/zach-klippenstein/goadb.svg?branch=master)](https://travis-ci.org/zach-klippenstein/goadb)
|
||||
[![GoDoc](https://godoc.org/github.com/zach-klippenstein/goadb?status.svg)](https://godoc.org/github.com/zach-klippenstein/goadb)
|
||||
|
||||
A Golang library for interacting with the Android Debug Bridge (adb).
|
||||
|
||||
See [demo.go](cmd/demo/demo.go) for usage.
|
176
vendor/github.com/thinkhy/go-adb/adb.go
generated
vendored
Normal file
176
vendor/github.com/thinkhy/go-adb/adb.go
generated
vendored
Normal file
|
@ -0,0 +1,176 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
/*
|
||||
Adb communicates with host services on the adb server.
|
||||
|
||||
Eg.
|
||||
client := adb.New()
|
||||
client.ListDevices()
|
||||
|
||||
See list of services at https://android.googlesource.com/platform/system/core/+/master/adb/SERVICES.TXT.
|
||||
*/
|
||||
// TODO(z): Finish implementing host services.
|
||||
type Adb struct {
|
||||
server server
|
||||
}
|
||||
|
||||
// New creates a new Adb client that uses the default ServerConfig.
|
||||
func New() (*Adb, error) {
|
||||
return NewWithConfig(ServerConfig{})
|
||||
}
|
||||
|
||||
func NewWithConfig(config ServerConfig) (*Adb, error) {
|
||||
server, err := newServer(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Adb{server}, nil
|
||||
}
|
||||
|
||||
func NewWithRemoteServer(host string, port int) (*Adb, error) {
|
||||
config := ServerConfig{
|
||||
Host: host,
|
||||
Port: port,
|
||||
}
|
||||
server, err := newServer(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Adb{server}, nil
|
||||
}
|
||||
|
||||
// Dial establishes a connection with the adb server.
|
||||
func (c *Adb) Dial() (*wire.Conn, error) {
|
||||
return c.server.Dial()
|
||||
}
|
||||
|
||||
// Starts the adb server if it’s not running.
|
||||
func (c *Adb) StartServer() error {
|
||||
return c.server.Start()
|
||||
}
|
||||
|
||||
func (c *Adb) Device(descriptor DeviceDescriptor) *Device {
|
||||
return &Device{
|
||||
server: c.server,
|
||||
descriptor: descriptor,
|
||||
deviceListFunc: c.ListDevices,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Adb) NewDeviceWatcher() *DeviceWatcher {
|
||||
return newDeviceWatcher(c.server)
|
||||
}
|
||||
|
||||
// ServerVersion asks the ADB server for its internal version number.
|
||||
func (c *Adb) ServerVersion() (int, error) {
|
||||
resp, err := roundTripSingleResponse(c.server, "host:version")
|
||||
if err != nil {
|
||||
return 0, wrapClientError(err, c, "GetServerVersion")
|
||||
}
|
||||
|
||||
version, err := c.parseServerVersion(resp)
|
||||
if err != nil {
|
||||
return 0, wrapClientError(err, c, "GetServerVersion")
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
/*
|
||||
KillServer tells the server to quit immediately.
|
||||
|
||||
Corresponds to the command:
|
||||
adb kill-server
|
||||
*/
|
||||
func (c *Adb) KillServer() error {
|
||||
conn, err := c.server.Dial()
|
||||
if err != nil {
|
||||
return wrapClientError(err, c, "KillServer")
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
if err = wire.SendMessageString(conn, "host:kill"); err != nil {
|
||||
return wrapClientError(err, c, "KillServer")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
ListDeviceSerials returns the serial numbers of all attached devices.
|
||||
|
||||
Corresponds to the command:
|
||||
adb devices
|
||||
*/
|
||||
func (c *Adb) ListDeviceSerials() ([]string, error) {
|
||||
resp, err := roundTripSingleResponse(c.server, "host:devices")
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "ListDeviceSerials")
|
||||
}
|
||||
|
||||
devices, err := parseDeviceList(string(resp), parseDeviceShort)
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "ListDeviceSerials")
|
||||
}
|
||||
|
||||
serials := make([]string, len(devices))
|
||||
for i, dev := range devices {
|
||||
serials[i] = dev.Serial
|
||||
}
|
||||
return serials, nil
|
||||
}
|
||||
|
||||
/*
|
||||
ListDevices returns the list of connected devices.
|
||||
|
||||
Corresponds to the command:
|
||||
adb devices -l
|
||||
*/
|
||||
func (c *Adb) ListDevices() ([]*DeviceInfo, error) {
|
||||
resp, err := roundTripSingleResponse(c.server, "host:devices-l")
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "ListDevices")
|
||||
}
|
||||
|
||||
devices, err := parseDeviceList(string(resp), parseDeviceLong)
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "ListDevices")
|
||||
}
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func (c *Adb) ListDevicesInfo() ([]string, []string, error) {
|
||||
resp, err := roundTripSingleResponse(c.server, "host:devices")
|
||||
if err != nil {
|
||||
return nil, nil, wrapClientError(err, c, "ListDeviceSerials")
|
||||
}
|
||||
|
||||
devices, err := parseDeviceList(string(resp), parseDeviceShort)
|
||||
if err != nil {
|
||||
return nil, nil, wrapClientError(err, c, "ListDeviceSerials")
|
||||
}
|
||||
|
||||
serials := make([]string, len(devices))
|
||||
status := make([]string, len(devices))
|
||||
for i, dev := range devices {
|
||||
serials[i] = dev.Serial
|
||||
status[i] = dev.Status
|
||||
}
|
||||
return serials, status, nil
|
||||
}
|
||||
|
||||
func (c *Adb) parseServerVersion(versionRaw []byte) (int, error) {
|
||||
versionStr := string(versionRaw)
|
||||
version, err := strconv.ParseInt(versionStr, 16, 32)
|
||||
if err != nil {
|
||||
return 0, errors.WrapErrorf(err, errors.ParseError,
|
||||
"error parsing server version: %s", versionStr)
|
||||
}
|
||||
return int(version), nil
|
||||
}
|
111
vendor/github.com/thinkhy/go-adb/asyncwriter.go
generated
vendored
Normal file
111
vendor/github.com/thinkhy/go-adb/asyncwriter.go
generated
vendored
Normal file
|
@ -0,0 +1,111 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type AsyncWriter struct {
|
||||
Done chan bool
|
||||
DoneCopy chan bool // for debug
|
||||
C chan bool
|
||||
err error
|
||||
dst io.WriteCloser
|
||||
dstPath string
|
||||
TotalSize int64
|
||||
dev *Device
|
||||
bytesCompleted int64
|
||||
copyErrC chan error
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func newAsyncWriter(dev *Device, dst io.WriteCloser, dstPath string, totalSize int64) *AsyncWriter {
|
||||
return &AsyncWriter{
|
||||
Done: make(chan bool),
|
||||
DoneCopy: make(chan bool, 1),
|
||||
C: make(chan bool),
|
||||
dst: dst,
|
||||
dstPath: dstPath,
|
||||
dev: dev,
|
||||
TotalSize: totalSize,
|
||||
copyErrC: make(chan error, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// BytesCompleted returns the total number of bytes which have been copied to the destination
|
||||
func (a *AsyncWriter) BytesCompleted() int64 {
|
||||
return a.bytesCompleted
|
||||
}
|
||||
|
||||
func (a *AsyncWriter) Progress() float64 {
|
||||
if (a.TotalSize) == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return float64(a.bytesCompleted) / float64(a.TotalSize)
|
||||
}
|
||||
|
||||
// Err return error immediately
|
||||
func (a *AsyncWriter) Err() error {
|
||||
return a.err
|
||||
}
|
||||
|
||||
func (a *AsyncWriter) Cancel() error {
|
||||
return a.dst.Close()
|
||||
}
|
||||
|
||||
// Wait blocks until sync is completed
|
||||
func (a *AsyncWriter) Wait() {
|
||||
<-a.Done
|
||||
}
|
||||
|
||||
func (a *AsyncWriter) doCopy(reader io.Reader) {
|
||||
a.wg.Add(1)
|
||||
defer a.wg.Done()
|
||||
|
||||
go a.darinProgress()
|
||||
written, err := io.Copy(a.dst, reader)
|
||||
if err != nil {
|
||||
a.err = err
|
||||
a.copyErrC <- err
|
||||
}
|
||||
a.TotalSize = written
|
||||
defer a.dst.Close()
|
||||
a.DoneCopy <- true
|
||||
}
|
||||
|
||||
func (a *AsyncWriter) darinProgress() {
|
||||
t := time.NewTicker(time.Millisecond * 500)
|
||||
defer func() {
|
||||
t.Stop()
|
||||
a.wg.Wait()
|
||||
a.Done <- true
|
||||
}()
|
||||
var lastSize int32
|
||||
for {
|
||||
select {
|
||||
case <-t.C:
|
||||
finfo, err := a.dev.Stat(a.dstPath)
|
||||
if err != nil && !HasErrCode(err, FileNoExistError) {
|
||||
a.err = err
|
||||
return
|
||||
}
|
||||
if finfo == nil {
|
||||
continue
|
||||
}
|
||||
if lastSize != finfo.Size {
|
||||
lastSize = finfo.Size
|
||||
select {
|
||||
case a.C <- true:
|
||||
default:
|
||||
}
|
||||
}
|
||||
a.bytesCompleted = int64(finfo.Size)
|
||||
if a.TotalSize != 0 && a.bytesCompleted >= a.TotalSize {
|
||||
return
|
||||
}
|
||||
case <-a.copyErrC:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
540
vendor/github.com/thinkhy/go-adb/device.go
generated
vendored
Normal file
540
vendor/github.com/thinkhy/go-adb/device.go
generated
vendored
Normal file
|
@ -0,0 +1,540 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
// MtimeOfClose should be passed to OpenWrite to set the file modification time to the time the Close
|
||||
// method is called.
|
||||
var MtimeOfClose = time.Time{}
|
||||
|
||||
// Device communicates with a specific Android device.
|
||||
// To get an instance, call Device() on an Adb.
|
||||
type Device struct {
|
||||
server server
|
||||
descriptor DeviceDescriptor
|
||||
|
||||
// Used to get device info.
|
||||
deviceListFunc func() ([]*DeviceInfo, error)
|
||||
}
|
||||
|
||||
func (c *Device) String() string {
|
||||
return c.descriptor.String()
|
||||
}
|
||||
|
||||
// get-product is documented, but not implemented, in the server.
|
||||
// TODO(z): Make product exported if get-product is ever implemented in adb.
|
||||
func (c *Device) product() (string, error) {
|
||||
attr, err := c.getAttribute("get-product")
|
||||
return attr, wrapClientError(err, c, "Product")
|
||||
}
|
||||
|
||||
func (c *Device) Serial() (string, error) {
|
||||
attr, err := c.getAttribute("get-serialno")
|
||||
return attr, wrapClientError(err, c, "Serial")
|
||||
}
|
||||
|
||||
func (c *Device) DevicePath() (string, error) {
|
||||
attr, err := c.getAttribute("get-devpath")
|
||||
return attr, wrapClientError(err, c, "DevicePath")
|
||||
}
|
||||
|
||||
func (c *Device) State() (DeviceState, error) {
|
||||
attr, err := c.getAttribute("get-state")
|
||||
state, err := parseDeviceState(attr)
|
||||
return state, wrapClientError(err, c, "State")
|
||||
}
|
||||
|
||||
var (
|
||||
FProtocolTcp = "tcp"
|
||||
FProtocolAbstract = "localabstract"
|
||||
FProtocolReserved = "localreserved"
|
||||
FProtocolFilesystem = "localfilesystem"
|
||||
)
|
||||
|
||||
type ForwardSpec struct {
|
||||
Protocol string
|
||||
PortOrName string
|
||||
}
|
||||
|
||||
func (f ForwardSpec) String() string {
|
||||
return fmt.Sprintf("%s:%s", f.Protocol, f.PortOrName)
|
||||
}
|
||||
|
||||
func (f ForwardSpec) Port() (int, error) {
|
||||
if f.Protocol != FProtocolTcp {
|
||||
return 0, fmt.Errorf("protocal is not tcp")
|
||||
}
|
||||
return strconv.Atoi(f.PortOrName)
|
||||
}
|
||||
|
||||
func (f *ForwardSpec) parseString(s string) error {
|
||||
fields := strings.Split(s, ":")
|
||||
if len(fields) != 2 {
|
||||
return fmt.Errorf("expect string contains only one ':', str = %s", s)
|
||||
}
|
||||
f.Protocol = fields[0]
|
||||
f.PortOrName = fields[1]
|
||||
return nil
|
||||
}
|
||||
|
||||
type ForwardPair struct {
|
||||
Serial string
|
||||
Local ForwardSpec
|
||||
Remote ForwardSpec
|
||||
}
|
||||
|
||||
// ForwardList returns list with struct ForwardPair
|
||||
// If no device serial specified all devices's forward list will returned
|
||||
func (c *Device) ForwardList() (fs []ForwardPair, err error) {
|
||||
attr, err := c.getAttribute("list-forward")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fields := strings.Fields(attr)
|
||||
if len(fields)%3 != 0 {
|
||||
return nil, fmt.Errorf("list forward parse error")
|
||||
}
|
||||
fs = make([]ForwardPair, 0)
|
||||
for i := 0; i < len(fields)/3; i++ {
|
||||
var local, remote ForwardSpec
|
||||
var serial = fields[i*3]
|
||||
// skip other device serial forwards
|
||||
if c.descriptor.descriptorType == DeviceSerial && c.descriptor.serial != serial {
|
||||
continue
|
||||
}
|
||||
if err = local.parseString(fields[i*3+1]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = remote.parseString(fields[i*3+2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fs = append(fs, ForwardPair{serial, local, remote})
|
||||
}
|
||||
return fs, nil
|
||||
}
|
||||
|
||||
// ForwardRemove specified forward
|
||||
func (c *Device) ForwardRemove(local ForwardSpec) error {
|
||||
err := roundTripSingleNoResponse(c.server,
|
||||
fmt.Sprintf("%s:killforward:%v", c.descriptor.getHostPrefix(), local))
|
||||
return wrapClientError(err, c, "ForwardRemove")
|
||||
}
|
||||
|
||||
// ForwardRemoveAll cancel all exists forwards
|
||||
func (c *Device) ForwardRemoveAll() error {
|
||||
err := roundTripSingleNoResponse(c.server,
|
||||
fmt.Sprintf("%s:killforward-all", c.descriptor.getHostPrefix()))
|
||||
return wrapClientError(err, c, "ForwardRemoveAll")
|
||||
}
|
||||
|
||||
// Forward remote connection to local
|
||||
func (c *Device) Forward(local, remote ForwardSpec) error {
|
||||
err := roundTripSingleNoResponse(c.server,
|
||||
fmt.Sprintf("%s:forward:%v;%v", c.descriptor.getHostPrefix(), local, remote))
|
||||
return wrapClientError(err, c, "Forward")
|
||||
}
|
||||
|
||||
// ForwardToFreePort return random generated port
|
||||
// If forward already exists, just return current forworded port
|
||||
func (c *Device) ForwardToFreePort(remote ForwardSpec) (port int, err error) {
|
||||
fws, err := c.ForwardList()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, fw := range fws {
|
||||
if fw.Remote == remote {
|
||||
return fw.Local.Port()
|
||||
}
|
||||
}
|
||||
port, err = getFreePort()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.Forward(ForwardSpec{FProtocolTcp, strconv.Itoa(port)}, remote)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Device) DeviceInfo() (*DeviceInfo, error) {
|
||||
// Adb doesn't actually provide a way to get this for an individual device,
|
||||
// so we have to just list devices and find ourselves.
|
||||
|
||||
serial, err := c.Serial()
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "GetDeviceInfo(GetSerial)")
|
||||
}
|
||||
|
||||
devices, err := c.deviceListFunc()
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "DeviceInfo(ListDevices)")
|
||||
}
|
||||
|
||||
for _, deviceInfo := range devices {
|
||||
if deviceInfo.Serial == serial {
|
||||
return deviceInfo, nil
|
||||
}
|
||||
}
|
||||
|
||||
err = errors.Errorf(errors.DeviceNotFound, "device list doesn't contain serial %s", serial)
|
||||
return nil, wrapClientError(err, c, "DeviceInfo")
|
||||
}
|
||||
|
||||
/*
|
||||
RunCommand runs the specified commands on a shell on the device.
|
||||
|
||||
From the Android docs:
|
||||
Run 'command arg1 arg2 ...' in a shell on the device, and return
|
||||
its output and error streams. Note that arguments must be separated
|
||||
by spaces. If an argument contains a space, it must be quoted with
|
||||
double-quotes. Arguments cannot contain double quotes or things
|
||||
will go very wrong.
|
||||
|
||||
Note that this is the non-interactive version of "adb shell"
|
||||
Source: https://android.googlesource.com/platform/system/core/+/master/adb/SERVICES.TXT
|
||||
|
||||
This method quotes the arguments for you, and will return an error if any of them
|
||||
contain double quotes.
|
||||
|
||||
Because the adb shell converts all "\n" into "\r\n",
|
||||
so here we convert it back (maybe not good for binary output)
|
||||
*/
|
||||
func (c *Device) RunCommand(cmd string, args ...string) (string, error) {
|
||||
conn, err := c.OpenCommand(cmd, args...)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
resp, err := conn.ReadUntilEof()
|
||||
if err != nil {
|
||||
return "", wrapClientError(err, c, "RunCommand")
|
||||
}
|
||||
outStr := strings.Replace(string(resp), "\r\n", "\n", -1)
|
||||
return outStr, nil
|
||||
}
|
||||
|
||||
func (c *Device) OpenCommand(cmd string, args ...string) (conn *wire.Conn, err error) {
|
||||
cmd, err = prepareCommandLine(cmd, args...)
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "RunCommand")
|
||||
}
|
||||
conn, err = c.dialDevice()
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "RunCommand")
|
||||
}
|
||||
defer func() {
|
||||
if err != nil && conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
req := fmt.Sprintf("shell:%s", cmd)
|
||||
|
||||
// Shell responses are special, they don't include a length header.
|
||||
// We read until the stream is closed.
|
||||
// So, we can't use conn.RoundTripSingleResponse.
|
||||
if err = conn.SendMessage([]byte(req)); err != nil {
|
||||
return nil, wrapClientError(err, c, "Command")
|
||||
}
|
||||
if _, err = conn.ReadStatus(req); err != nil {
|
||||
return nil, wrapClientError(err, c, "Command")
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Device) RunCommandInContext(conn *wire.Conn, cmd string, args ...string) (*wire.Conn, string, error) {
|
||||
conn, err := c.OpenCmdInContext(conn, cmd, args...)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
resp, err := conn.ReadUntilEof()
|
||||
if err != nil {
|
||||
return nil, "", wrapClientError(err, c, "RunCommand")
|
||||
}
|
||||
outStr := strings.Replace(string(resp), "\r\n", "\n", -1)
|
||||
return conn, outStr, nil
|
||||
}
|
||||
|
||||
func (c *Device) OpenCmdInContext(conn *wire.Conn, cmd string, args ...string) (*wire.Conn, error) {
|
||||
cmd, err := prepareCommandLine(cmd, args...)
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "RunCommand")
|
||||
}
|
||||
if conn == nil {
|
||||
conn, err = c.dialDevice()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "RunCommand")
|
||||
}
|
||||
|
||||
req := fmt.Sprintf("shell:%s", cmd)
|
||||
|
||||
// Shell responses are special, they don't include a length header.
|
||||
// We read until the stream is closed.
|
||||
// So, we can't use conn.RoundTripSingleResponse.
|
||||
if err = conn.SendMessage([]byte(req)); err != nil {
|
||||
return nil, wrapClientError(err, c, "Command")
|
||||
}
|
||||
if _, err = conn.ReadStatus(req); err != nil {
|
||||
return nil, wrapClientError(err, c, "Command")
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
/*
|
||||
Remount, from the official adb command’s docs:
|
||||
Ask adbd to remount the device's filesystem in read-write mode,
|
||||
instead of read-only. This is usually necessary before performing
|
||||
an "adb sync" or "adb push" request.
|
||||
This request may not succeed on certain builds which do not allow
|
||||
that.
|
||||
Source: https://android.googlesource.com/platform/system/core/+/master/adb/SERVICES.TXT
|
||||
*/
|
||||
func (c *Device) Remount() (string, error) {
|
||||
conn, err := c.dialDevice()
|
||||
if err != nil {
|
||||
return "", wrapClientError(err, c, "Remount")
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
resp, err := conn.RoundTripSingleResponse([]byte("remount"))
|
||||
return string(resp), wrapClientError(err, c, "Remount")
|
||||
}
|
||||
|
||||
func (c *Device) ListDirEntries(path string) (*DirEntries, error) {
|
||||
conn, err := c.getSyncConn()
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "ListDirEntries(%s)", path)
|
||||
}
|
||||
|
||||
entries, err := listDirEntries(conn, path)
|
||||
return entries, wrapClientError(err, c, "ListDirEntries(%s)", path)
|
||||
}
|
||||
|
||||
func (c *Device) Stat(path string) (*DirEntry, error) {
|
||||
conn, err := c.getSyncConn()
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "Stat(%s)", path)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
entry, err := stat(conn, path)
|
||||
return entry, wrapClientError(err, c, "Stat(%s)", path)
|
||||
}
|
||||
|
||||
func (c *Device) OpenRead(path string) (io.ReadCloser, error) {
|
||||
conn, err := c.getSyncConn()
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "OpenRead(%s)", path)
|
||||
}
|
||||
|
||||
reader, err := receiveFile(conn, path)
|
||||
return reader, wrapClientError(err, c, "OpenRead(%s)", path)
|
||||
}
|
||||
|
||||
// OpenWrite opens the file at path on the device, creating it with the permissions specified
|
||||
// by perms if necessary, and returns a writer that writes to the file.
|
||||
// The files modification time will be set to mtime when the WriterCloser is closed. The zero value
|
||||
// is TimeOfClose, which will use the time the Close method is called as the modification time.
|
||||
func (c *Device) OpenWrite(path string, perms os.FileMode, mtime time.Time) (io.WriteCloser, error) {
|
||||
conn, err := c.getSyncConn()
|
||||
if err != nil {
|
||||
return nil, wrapClientError(err, c, "OpenWrite(%s)", path)
|
||||
}
|
||||
|
||||
writer, err := sendFile(conn, path, perms, mtime)
|
||||
return writer, wrapClientError(err, c, "OpenWrite(%s)", path)
|
||||
}
|
||||
|
||||
// getAttribute returns the first message returned by the server by running
|
||||
// <host-prefix>:<attr>, where host-prefix is determined from the DeviceDescriptor.
|
||||
func (c *Device) getAttribute(attr string) (string, error) {
|
||||
resp, err := roundTripSingleResponse(c.server,
|
||||
fmt.Sprintf("%s:%s", c.descriptor.getHostPrefix(), attr))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(resp), nil
|
||||
}
|
||||
|
||||
func (c *Device) getSyncConn() (*wire.SyncConn, error) {
|
||||
conn, err := c.dialDevice()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Switch the connection to sync mode.
|
||||
if err := wire.SendMessageString(conn, "sync:"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := conn.ReadStatus("sync"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn.NewSyncConn(), nil
|
||||
}
|
||||
|
||||
// dialDevice switches the connection to communicate directly with the device
|
||||
// by requesting the transport defined by the DeviceDescriptor.
|
||||
func (c *Device) dialDevice() (*wire.Conn, error) {
|
||||
conn, err := c.server.Dial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req := fmt.Sprintf("host:%s", c.descriptor.getTransportDescriptor())
|
||||
if err = wire.SendMessageString(conn, req); err != nil {
|
||||
conn.Close()
|
||||
return nil, errors.WrapErrf(err, "error connecting to device '%s'", c.descriptor)
|
||||
}
|
||||
|
||||
if _, err = conn.ReadStatus(req); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func (c *Device) Dial() (*wire.Conn, error) {
|
||||
return c.dialDevice()
|
||||
}
|
||||
|
||||
// prepareCommandLine validates the command and argument strings, quotes
|
||||
// arguments if required, and joins them into a valid adb command string.
|
||||
func prepareCommandLine(cmd string, args ...string) (string, error) {
|
||||
if isBlank(cmd) {
|
||||
return "", errors.AssertionErrorf("command cannot be empty")
|
||||
}
|
||||
|
||||
for i, arg := range args {
|
||||
if strings.ContainsRune(arg, '"') {
|
||||
return "", errors.Errorf(errors.ParseError, "arg at index %d contains an invalid double quote: %s", i, arg)
|
||||
}
|
||||
if containsWhitespace(arg) {
|
||||
args[i] = fmt.Sprintf("\"%s\"", arg)
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend the command to the args array.
|
||||
if len(args) > 0 {
|
||||
cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " "))
|
||||
}
|
||||
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (c *Device) Push(localPath, remotePath string) int64 {
|
||||
if remotePath == "" {
|
||||
return 1
|
||||
}
|
||||
|
||||
var (
|
||||
localFile io.ReadCloser
|
||||
size int
|
||||
perms os.FileMode
|
||||
mtime time.Time
|
||||
)
|
||||
if localPath == "" {
|
||||
localFile = os.Stdin
|
||||
// 0 size will hide the progress bar.
|
||||
perms = os.FileMode(0660)
|
||||
mtime = MtimeOfClose
|
||||
} else {
|
||||
var err error
|
||||
localFile, err = os.Open(localPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening local file %s: %s\n", localPath, err)
|
||||
return 1
|
||||
}
|
||||
info, err := os.Stat(localPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error reading local file %s: %s\n", localPath, err)
|
||||
return 1
|
||||
}
|
||||
size = int(info.Size())
|
||||
perms = info.Mode().Perm()
|
||||
mtime = info.ModTime()
|
||||
}
|
||||
defer localFile.Close()
|
||||
|
||||
client := c
|
||||
writer, err := client.OpenWrite(remotePath, perms, mtime)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening remote file %s: %s\n", remotePath, err)
|
||||
return 1
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
n, err := io.Copy(writer, localFile)
|
||||
/*if err := copyWithProgressAndStats(writer, localFile, size, showProgress); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error pushing file:", err)
|
||||
return 1
|
||||
}*/
|
||||
if n == int64(size) {
|
||||
return 0
|
||||
} else {
|
||||
return n
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Device) Pull(remotePath, localPath string) (int64, error) {
|
||||
if remotePath == "" {
|
||||
fmt.Fprintln(os.Stderr, "error: must specify remote file")
|
||||
return 0, fmt.Errorf("error: must specify remote file")
|
||||
}
|
||||
|
||||
if localPath == "" {
|
||||
localPath = filepath.Base(remotePath)
|
||||
}
|
||||
|
||||
client := c
|
||||
// client := client.Device(device)
|
||||
|
||||
info, err := client.Stat(remotePath)
|
||||
if HasErrCode(err, ErrCode(FileNoExistError)) {
|
||||
// fmt.Fprintf(os.Stderr, "file %v does not exist on device ", remotePath)
|
||||
return 0, err
|
||||
} else if err != nil {
|
||||
// fmt.Fprintf(os.Stderr, "error reading remote file %s: %s\n", remotePath, err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
remoteFile, err := client.OpenRead(remotePath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening remote file %s: %s\n", remotePath, ErrorWithCauseChain(err))
|
||||
return 0, err
|
||||
}
|
||||
defer remoteFile.Close()
|
||||
|
||||
var localFile io.WriteCloser
|
||||
localFile, err = os.Create(localPath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error opening local file %s: %s\n", localPath, err)
|
||||
return 0, err
|
||||
}
|
||||
defer localFile.Close()
|
||||
|
||||
n, err := io.Copy(localFile, remoteFile)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "error pulling file:", err)
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if n == int64(info.Size) {
|
||||
return int64(info.Size), nil
|
||||
} else {
|
||||
return n, fmt.Errorf("error: file size: %v, pull size: %v", info.Size, n)
|
||||
}
|
||||
}
|
80
vendor/github.com/thinkhy/go-adb/device_descriptor.go
generated
vendored
Normal file
80
vendor/github.com/thinkhy/go-adb/device_descriptor.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
package adb
|
||||
|
||||
import "fmt"
|
||||
|
||||
//go:generate stringer -type=deviceDescriptorType
|
||||
type deviceDescriptorType int
|
||||
|
||||
const (
|
||||
// host:transport-any and host:<request>
|
||||
DeviceAny deviceDescriptorType = iota
|
||||
// host:transport:<serial> and host-serial:<serial>:<request>
|
||||
DeviceSerial
|
||||
// host:transport-usb and host-usb:<request>
|
||||
DeviceUsb
|
||||
// host:transport-local and host-local:<request>
|
||||
DeviceLocal
|
||||
)
|
||||
|
||||
type DeviceDescriptor struct {
|
||||
descriptorType deviceDescriptorType
|
||||
|
||||
// Only used if Type is DeviceSerial.
|
||||
serial string
|
||||
}
|
||||
|
||||
func AnyDevice() DeviceDescriptor {
|
||||
return DeviceDescriptor{descriptorType: DeviceAny}
|
||||
}
|
||||
|
||||
func AnyUsbDevice() DeviceDescriptor {
|
||||
return DeviceDescriptor{descriptorType: DeviceUsb}
|
||||
}
|
||||
|
||||
func AnyLocalDevice() DeviceDescriptor {
|
||||
return DeviceDescriptor{descriptorType: DeviceLocal}
|
||||
}
|
||||
|
||||
func DeviceWithSerial(serial string) DeviceDescriptor {
|
||||
return DeviceDescriptor{
|
||||
descriptorType: DeviceSerial,
|
||||
serial: serial,
|
||||
}
|
||||
}
|
||||
|
||||
func (d DeviceDescriptor) String() string {
|
||||
if d.descriptorType == DeviceSerial {
|
||||
return fmt.Sprintf("%s[%s]", d.descriptorType, d.serial)
|
||||
}
|
||||
return d.descriptorType.String()
|
||||
}
|
||||
|
||||
func (d DeviceDescriptor) getHostPrefix() string {
|
||||
switch d.descriptorType {
|
||||
case DeviceAny:
|
||||
return "host"
|
||||
case DeviceUsb:
|
||||
return "host-usb"
|
||||
case DeviceLocal:
|
||||
return "host-local"
|
||||
case DeviceSerial:
|
||||
return fmt.Sprintf("host-serial:%s", d.serial)
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid DeviceDescriptorType: %v", d.descriptorType))
|
||||
}
|
||||
}
|
||||
|
||||
func (d DeviceDescriptor) getTransportDescriptor() string {
|
||||
switch d.descriptorType {
|
||||
case DeviceAny:
|
||||
return "transport-any"
|
||||
case DeviceUsb:
|
||||
return "transport-usb"
|
||||
case DeviceLocal:
|
||||
return "transport-local"
|
||||
case DeviceSerial:
|
||||
return fmt.Sprintf("transport:%s", d.serial)
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid DeviceDescriptorType: %v", d.descriptorType))
|
||||
}
|
||||
}
|
278
vendor/github.com/thinkhy/go-adb/device_extra.go
generated
vendored
Normal file
278
vendor/github.com/thinkhy/go-adb/device_extra.go
generated
vendored
Normal file
|
@ -0,0 +1,278 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
retryablehttp "github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
type Process struct {
|
||||
User string
|
||||
Pid int
|
||||
Name string
|
||||
}
|
||||
|
||||
// ListProcesses return list of Process
|
||||
func (c *Device) ListProcesses() (ps []Process, err error) {
|
||||
reader, err := c.OpenCommand("ps")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer reader.Close()
|
||||
var fieldNames []string
|
||||
bufrd := bufio.NewReader(reader)
|
||||
for {
|
||||
line, _, err := bufrd.ReadLine()
|
||||
fields := strings.Fields(strings.TrimSpace(string(line)))
|
||||
if len(fields) == 0 {
|
||||
break
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if fieldNames == nil {
|
||||
fieldNames = fields
|
||||
continue
|
||||
}
|
||||
var process Process
|
||||
/* example output of command "ps"
|
||||
USER PID PPID VSIZE RSS WCHAN PC NAME
|
||||
root 1 0 684 540 ffffffff 00000000 S /init
|
||||
root 2 0 0 0 ffffffff 00000000 S kthreadd
|
||||
*/
|
||||
if len(fields) != len(fieldNames)+1 {
|
||||
continue
|
||||
}
|
||||
for index, name := range fieldNames {
|
||||
value := fields[index]
|
||||
switch strings.ToUpper(name) {
|
||||
case "PID":
|
||||
process.Pid, _ = strconv.Atoi(value)
|
||||
case "NAME":
|
||||
process.Name = fields[len(fields)-1]
|
||||
case "USER":
|
||||
process.User = value
|
||||
}
|
||||
}
|
||||
if process.Pid == 0 {
|
||||
continue
|
||||
}
|
||||
ps = append(ps, process)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// KillProcessByName return if killed success
|
||||
func (c *Device) KillProcessByName(name string, sig int) error {
|
||||
ps, err := c.ListProcesses()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range ps {
|
||||
if p.Name != name {
|
||||
continue
|
||||
}
|
||||
// log.Printf("kill %s with pid: %d", p.Name, p.Pid)
|
||||
_, _, er := c.RunCommandWithExitCode("kill", "-"+strconv.Itoa(sig), strconv.Itoa(p.Pid))
|
||||
if er != nil {
|
||||
return er
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PackageInfo struct {
|
||||
Name string
|
||||
Path string
|
||||
Version struct {
|
||||
Code int
|
||||
Name string
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
rePkgPath = regexp.MustCompile(`codePath=([^\s]+)`)
|
||||
reVerCode = regexp.MustCompile(`versionCode=(\d+)`)
|
||||
reVerName = regexp.MustCompile(`versionName=([^\s]+)`)
|
||||
)
|
||||
|
||||
// StatPackage returns PackageInfo
|
||||
// If package not found, err will be ErrPackageNotExist
|
||||
func (c *Device) StatPackage(packageName string) (pi PackageInfo, err error) {
|
||||
pi.Name = packageName
|
||||
out, err := c.RunCommand("dumpsys", "package", packageName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
matches := rePkgPath.FindStringSubmatch(out)
|
||||
if len(matches) == 0 {
|
||||
err = ErrPackageNotExist
|
||||
return
|
||||
}
|
||||
pi.Path = matches[1]
|
||||
|
||||
matches = reVerCode.FindStringSubmatch(out)
|
||||
if len(matches) == 0 {
|
||||
err = ErrPackageNotExist
|
||||
return
|
||||
}
|
||||
pi.Version.Code, _ = strconv.Atoi(matches[1])
|
||||
|
||||
matches = reVerName.FindStringSubmatch(out)
|
||||
if len(matches) == 0 {
|
||||
err = ErrPackageNotExist
|
||||
return
|
||||
}
|
||||
pi.Version.Name = matches[1]
|
||||
return
|
||||
}
|
||||
|
||||
// Properties extract info from $ adb shell getprop
|
||||
func (c *Device) Properties() (props map[string]string, err error) {
|
||||
propOutput, err := c.RunCommand("getprop")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
re := regexp.MustCompile(`\[(.*?)\]:\s*\[(.*?)\]`)
|
||||
matches := re.FindAllStringSubmatch(propOutput, -1)
|
||||
props = make(map[string]string)
|
||||
for _, m := range matches {
|
||||
var key = m[1]
|
||||
var val = m[2]
|
||||
props[key] = val
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
RunCommandWithExitCode use a little tricky to get exit code
|
||||
|
||||
The tricky is append "; echo :$?" to the command,
|
||||
and parse out the exit code from output
|
||||
*/
|
||||
func (c *Device) RunCommandWithExitCode(cmd string, args ...string) (string, int, error) {
|
||||
exArgs := append(args, ";", "echo", ":$?")
|
||||
outStr, err := c.RunCommand(cmd, exArgs...)
|
||||
if err != nil {
|
||||
return outStr, 0, err
|
||||
}
|
||||
idx := strings.LastIndexByte(outStr, ':')
|
||||
if idx == -1 {
|
||||
return outStr, 0, fmt.Errorf("adb shell aborted, can not parse exit code")
|
||||
}
|
||||
exitCode, _ := strconv.Atoi(strings.TrimSpace(outStr[idx+1:]))
|
||||
if exitCode != 0 {
|
||||
commandLine, _ := prepareCommandLine(cmd, args...)
|
||||
err = ShellExitError{commandLine, exitCode}
|
||||
}
|
||||
outStr = strings.Replace(outStr[0:idx], "\r\n", "\n", -1) // put somewhere else
|
||||
return outStr, exitCode, err
|
||||
}
|
||||
|
||||
type ShellExitError struct {
|
||||
Command string
|
||||
ExitCode int
|
||||
}
|
||||
|
||||
func (s ShellExitError) Error() string {
|
||||
return fmt.Sprintf("shell %s exit code %d", strconv.Quote(s.Command), s.ExitCode)
|
||||
}
|
||||
|
||||
// DoWriteFile return an object, use this object can Cancel write and get Process
|
||||
func (c *Device) DoSyncFile(path string, rd io.ReadCloser, size int64, perms os.FileMode) (aw *AsyncWriter, err error) {
|
||||
dst, err := c.OpenWrite(path, perms, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
awr := newAsyncWriter(c, dst, path, size)
|
||||
go func() {
|
||||
awr.doCopy(rd)
|
||||
rd.Close()
|
||||
}()
|
||||
return awr, nil
|
||||
}
|
||||
|
||||
func (c *Device) DoSyncLocalFile(dst string, src string, perms os.FileMode) (aw *AsyncWriter, err error) {
|
||||
f, err := os.Open(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
finfo, err := f.Stat()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return c.DoSyncFile(dst, f, finfo.Size(), perms)
|
||||
}
|
||||
|
||||
func (c *Device) DoSyncHTTPFile(dst string, srcUrl string, perms os.FileMode) (aw *AsyncWriter, err error) {
|
||||
res, err := retryablehttp.Get(srcUrl)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var length int64
|
||||
fmt.Sscanf(res.Header.Get("Content-Length"), "%d", &length)
|
||||
return c.DoSyncFile(dst, res.Body, length, perms)
|
||||
}
|
||||
|
||||
// WriteToFile write a reader stream to device
|
||||
func (c *Device) WriteToFile(path string, rd io.Reader, perms os.FileMode) (written int64, err error) {
|
||||
dst, err := c.OpenWrite(path, perms, time.Now())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
dst.Close()
|
||||
if err != nil || written == 0 {
|
||||
return
|
||||
}
|
||||
// wait until write finished.
|
||||
fromTime := time.Now()
|
||||
for {
|
||||
if time.Since(fromTime) > time.Second*600 {
|
||||
err = fmt.Errorf("write file to device timeout (10min)")
|
||||
return
|
||||
}
|
||||
finfo, er := c.Stat(path)
|
||||
if er != nil && !HasErrCode(er, FileNoExistError) {
|
||||
err = er
|
||||
return
|
||||
}
|
||||
if finfo == nil {
|
||||
err = fmt.Errorf("target file %s not created", strconv.Quote(path))
|
||||
return
|
||||
}
|
||||
if finfo != nil && finfo.Size == int32(written) {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Duration(200+rand.Intn(100)) * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
written, err = io.Copy(dst, rd)
|
||||
return
|
||||
}
|
||||
|
||||
// WriteHttpToFile download http resource to device
|
||||
func (c *Device) WriteHttpToFile(path string, urlStr string, perms os.FileMode) (written int64, err error) {
|
||||
res, err := retryablehttp.Get(urlStr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
err = fmt.Errorf("http download <%s> status %v", urlStr, res.Status)
|
||||
return
|
||||
}
|
||||
return c.WriteToFile(path, res.Body, perms)
|
||||
}
|
93
vendor/github.com/thinkhy/go-adb/device_info.go
generated
vendored
Normal file
93
vendor/github.com/thinkhy/go-adb/device_info.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strings"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
)
|
||||
|
||||
type DeviceInfo struct {
|
||||
// Always set.
|
||||
Serial string
|
||||
Status string
|
||||
|
||||
// Product, device, and model are not set in the short form.
|
||||
Product string
|
||||
Model string
|
||||
DeviceInfo string
|
||||
|
||||
// Only set for devices connected via USB.
|
||||
Usb string
|
||||
}
|
||||
|
||||
// IsUsb returns true if the device is connected via USB.
|
||||
func (d *DeviceInfo) IsUsb() bool {
|
||||
return d.Usb != ""
|
||||
}
|
||||
|
||||
func newDevice(serial string, status string, attrs map[string]string) (*DeviceInfo, error) {
|
||||
if serial == "" {
|
||||
return nil, errors.AssertionErrorf("device serial cannot be blank")
|
||||
}
|
||||
|
||||
return &DeviceInfo{
|
||||
Serial: serial,
|
||||
Status: status,
|
||||
Product: attrs["product"],
|
||||
Model: attrs["model"],
|
||||
DeviceInfo: attrs["device"],
|
||||
Usb: attrs["usb"],
|
||||
}, nil
|
||||
}
|
||||
|
||||
func parseDeviceList(list string, lineParseFunc func(string) (*DeviceInfo, error)) ([]*DeviceInfo, error) {
|
||||
devices := []*DeviceInfo{}
|
||||
scanner := bufio.NewScanner(strings.NewReader(list))
|
||||
|
||||
for scanner.Scan() {
|
||||
device, err := lineParseFunc(scanner.Text())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
devices = append(devices, device)
|
||||
}
|
||||
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
func parseDeviceShort(line string) (*DeviceInfo, error) {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) != 2 {
|
||||
return nil, errors.Errorf(errors.ParseError,
|
||||
"malformed device line, expected 2 fields but found %d", len(fields))
|
||||
}
|
||||
|
||||
return newDevice(fields[0], fields[1], map[string]string{})
|
||||
}
|
||||
|
||||
func parseDeviceLong(line string) (*DeviceInfo, error) {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 5 {
|
||||
return nil, errors.Errorf(errors.ParseError,
|
||||
"malformed device line, expected at least 5 fields but found %d", len(fields))
|
||||
}
|
||||
|
||||
attrs := parseDeviceAttributes(fields[2:])
|
||||
return newDevice(fields[0], fields[1], attrs)
|
||||
}
|
||||
|
||||
func parseDeviceAttributes(fields []string) map[string]string {
|
||||
attrs := map[string]string{}
|
||||
for _, field := range fields {
|
||||
key, val := parseKeyVal(field)
|
||||
attrs[key] = val
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
// Parses a key:val pair and returns key, val.
|
||||
func parseKeyVal(pair string) (string, string) {
|
||||
split := strings.Split(pair, ":")
|
||||
return split[0], split[1]
|
||||
}
|
36
vendor/github.com/thinkhy/go-adb/device_state.go
generated
vendored
Normal file
36
vendor/github.com/thinkhy/go-adb/device_state.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package adb
|
||||
|
||||
import "github.com/thinkhy/go-adb/internal/errors"
|
||||
|
||||
// DeviceState represents one of the 3 possible states adb will report devices.
|
||||
// A device can be communicated with when it's in StateOnline.
|
||||
// A USB device will make the following state transitions:
|
||||
// Plugged in: StateDisconnected->StateOffline->StateOnline
|
||||
// Unplugged: StateOnline->StateDisconnected
|
||||
//go:generate stringer -type=DeviceState
|
||||
type DeviceState int8
|
||||
|
||||
const (
|
||||
StateInvalid DeviceState = iota
|
||||
StateUnauthorized
|
||||
StateDisconnected
|
||||
StateOffline
|
||||
StateConnecting
|
||||
StateOnline
|
||||
)
|
||||
|
||||
var deviceStateStrings = map[string]DeviceState{
|
||||
"": StateDisconnected,
|
||||
"offline": StateOffline,
|
||||
"connecting": StateConnecting,
|
||||
"device": StateOnline,
|
||||
"unauthorized": StateUnauthorized,
|
||||
}
|
||||
|
||||
func parseDeviceState(str string) (DeviceState, error) {
|
||||
state, ok := deviceStateStrings[str]
|
||||
if !ok {
|
||||
return StateInvalid, errors.Errorf(errors.ParseError, "invalid device state: %q", state)
|
||||
}
|
||||
return state, nil
|
||||
}
|
233
vendor/github.com/thinkhy/go-adb/device_watcher.go
generated
vendored
Normal file
233
vendor/github.com/thinkhy/go-adb/device_watcher.go
generated
vendored
Normal file
|
@ -0,0 +1,233 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"log"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
/*
|
||||
DeviceWatcher publishes device status change events.
|
||||
If the server dies while listening for events, it restarts the server.
|
||||
*/
|
||||
type DeviceWatcher struct {
|
||||
*deviceWatcherImpl
|
||||
}
|
||||
|
||||
// DeviceStateChangedEvent represents a device state transition.
|
||||
// Contains the device’s old and new states, but also provides methods to query the
|
||||
// type of state transition.
|
||||
type DeviceStateChangedEvent struct {
|
||||
Serial string
|
||||
OldState DeviceState
|
||||
NewState DeviceState
|
||||
}
|
||||
|
||||
// CameOnline returns true if this event represents a device coming online.
|
||||
func (s DeviceStateChangedEvent) CameOnline() bool {
|
||||
return s.OldState != StateOnline && s.NewState == StateOnline
|
||||
}
|
||||
|
||||
// WentOffline returns true if this event represents a device going offline.
|
||||
func (s DeviceStateChangedEvent) WentOffline() bool {
|
||||
return s.OldState == StateOnline && s.NewState != StateOnline
|
||||
}
|
||||
|
||||
type deviceWatcherImpl struct {
|
||||
server server
|
||||
|
||||
// If an error occurs, it is stored here and eventChan is close immediately after.
|
||||
err atomic.Value
|
||||
|
||||
eventChan chan DeviceStateChangedEvent
|
||||
}
|
||||
|
||||
func newDeviceWatcher(server server) *DeviceWatcher {
|
||||
watcher := &DeviceWatcher{&deviceWatcherImpl{
|
||||
server: server,
|
||||
eventChan: make(chan DeviceStateChangedEvent),
|
||||
}}
|
||||
|
||||
runtime.SetFinalizer(watcher, func(watcher *DeviceWatcher) {
|
||||
watcher.Shutdown()
|
||||
})
|
||||
|
||||
go publishDevices(watcher.deviceWatcherImpl)
|
||||
|
||||
return watcher
|
||||
}
|
||||
|
||||
/*
|
||||
C returns a channel than can be received on to get events.
|
||||
If an unrecoverable error occurs, or Shutdown is called, the channel will be closed.
|
||||
*/
|
||||
func (w *DeviceWatcher) C() <-chan DeviceStateChangedEvent {
|
||||
return w.eventChan
|
||||
}
|
||||
|
||||
// Err returns the error that caused the channel returned by C to be closed, if C is closed.
|
||||
// If C is not closed, its return value is undefined.
|
||||
func (w *DeviceWatcher) Err() error {
|
||||
if err, ok := w.err.Load().(error); ok {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Shutdown stops the watcher from listening for events and closes the channel returned
|
||||
// from C.
|
||||
func (w *DeviceWatcher) Shutdown() {
|
||||
// TODO(z): Implement.
|
||||
}
|
||||
|
||||
func (w *deviceWatcherImpl) reportErr(err error) {
|
||||
w.err.Store(err)
|
||||
}
|
||||
|
||||
/*
|
||||
publishDevices reads device lists from scanner, calculates diffs, and publishes events on
|
||||
eventChan.
|
||||
Returns when scanner returns an error.
|
||||
Doesn't refer directly to a *DeviceWatcher so it can be GCed (which will,
|
||||
in turn, close Scanner and stop this goroutine).
|
||||
|
||||
TODO: to support shutdown, spawn a new goroutine each time a server connection is established.
|
||||
This goroutine should read messages and send them to a message channel. Can write errors directly
|
||||
to errVal. publishDevicesUntilError should take the msg chan and the scanner and select on the msg chan and stop chan, and if the stop
|
||||
chan sends, close the scanner and return true. If the msg chan closes, just return false.
|
||||
publishDevices can look at ret val: if false and err == EOF, reconnect. If false and other error, report err
|
||||
and abort. If true, report no error and stop.
|
||||
*/
|
||||
func publishDevices(watcher *deviceWatcherImpl) {
|
||||
defer close(watcher.eventChan)
|
||||
|
||||
var lastKnownStates map[string]DeviceState
|
||||
finished := false
|
||||
|
||||
for {
|
||||
scanner, err := connectToTrackDevices(watcher.server)
|
||||
if err != nil {
|
||||
watcher.reportErr(err)
|
||||
return
|
||||
}
|
||||
|
||||
finished, err = publishDevicesUntilError(scanner, watcher.eventChan, &lastKnownStates)
|
||||
|
||||
if finished {
|
||||
scanner.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if HasErrCode(err, ConnectionResetError) {
|
||||
// The server died, restart and reconnect.
|
||||
|
||||
// Delay by a random [0ms, 500ms) in case multiple DeviceWatchers are trying to
|
||||
// start the same server.
|
||||
delay := time.Duration(rand.Intn(500)) * time.Millisecond
|
||||
|
||||
log.Printf("[DeviceWatcher] server died, restarting in %s…", delay)
|
||||
time.Sleep(delay)
|
||||
if err := watcher.server.Start(); err != nil {
|
||||
log.Println("[DeviceWatcher] error restarting server, giving up")
|
||||
watcher.reportErr(err)
|
||||
return
|
||||
} // Else server should be running, continue listening.
|
||||
} else {
|
||||
// Unknown error, don't retry.
|
||||
watcher.reportErr(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func connectToTrackDevices(server server) (wire.Scanner, error) {
|
||||
conn, err := server.Dial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := wire.SendMessageString(conn, "host:track-devices"); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := conn.ReadStatus("host:track-devices"); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
func publishDevicesUntilError(scanner wire.Scanner, eventChan chan<- DeviceStateChangedEvent, lastKnownStates *map[string]DeviceState) (finished bool, err error) {
|
||||
for {
|
||||
msg, err := scanner.ReadMessage()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
deviceStates, err := parseDeviceStates(string(msg))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, event := range calculateStateDiffs(*lastKnownStates, deviceStates) {
|
||||
eventChan <- event
|
||||
}
|
||||
*lastKnownStates = deviceStates
|
||||
}
|
||||
}
|
||||
|
||||
func parseDeviceStates(msg string) (states map[string]DeviceState, err error) {
|
||||
states = make(map[string]DeviceState)
|
||||
|
||||
for lineNum, line := range strings.Split(msg, "\n") {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.Split(line, "\t")
|
||||
if len(fields) != 2 {
|
||||
err = errors.Errorf(errors.ParseError, "invalid device state line %d: %s", lineNum, line)
|
||||
return
|
||||
}
|
||||
|
||||
serial, stateString := fields[0], fields[1]
|
||||
var state DeviceState
|
||||
state, err = parseDeviceState(stateString)
|
||||
states[serial] = state
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func calculateStateDiffs(oldStates, newStates map[string]DeviceState) (events []DeviceStateChangedEvent) {
|
||||
for serial, oldState := range oldStates {
|
||||
newState, ok := newStates[serial]
|
||||
|
||||
if oldState != newState {
|
||||
if ok {
|
||||
// Device present in both lists: state changed.
|
||||
events = append(events, DeviceStateChangedEvent{serial, oldState, newState})
|
||||
} else {
|
||||
// Device only present in old list: device removed.
|
||||
events = append(events, DeviceStateChangedEvent{serial, oldState, StateDisconnected})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for serial, newState := range newStates {
|
||||
if _, ok := oldStates[serial]; !ok {
|
||||
// Device only present in new list: device added.
|
||||
events = append(events, DeviceStateChangedEvent{serial, StateDisconnected, newState})
|
||||
}
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
16
vendor/github.com/thinkhy/go-adb/devicedescriptortype_string.go
generated
vendored
Normal file
16
vendor/github.com/thinkhy/go-adb/devicedescriptortype_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Code generated by "stringer -type=deviceDescriptorType"; DO NOT EDIT
|
||||
|
||||
package adb
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _deviceDescriptorType_name = "DeviceAnyDeviceSerialDeviceUsbDeviceLocal"
|
||||
|
||||
var _deviceDescriptorType_index = [...]uint8{0, 9, 21, 30, 41}
|
||||
|
||||
func (i deviceDescriptorType) String() string {
|
||||
if i < 0 || i >= deviceDescriptorType(len(_deviceDescriptorType_index)-1) {
|
||||
return fmt.Sprintf("deviceDescriptorType(%d)", i)
|
||||
}
|
||||
return _deviceDescriptorType_name[_deviceDescriptorType_index[i]:_deviceDescriptorType_index[i+1]]
|
||||
}
|
16
vendor/github.com/thinkhy/go-adb/devicestate_string.go
generated
vendored
Normal file
16
vendor/github.com/thinkhy/go-adb/devicestate_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Code generated by "stringer -type=DeviceState"; DO NOT EDIT
|
||||
|
||||
package adb
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _DeviceState_name = "StateInvalidStateUnauthorizedStateDisconnectedStateOfflineStateOnline"
|
||||
|
||||
var _DeviceState_index = [...]uint8{0, 12, 29, 46, 58, 69}
|
||||
|
||||
func (i DeviceState) String() string {
|
||||
if i < 0 || i >= DeviceState(len(_DeviceState_index)-1) {
|
||||
return fmt.Sprintf("DeviceState(%d)", i)
|
||||
}
|
||||
return _DeviceState_name[_DeviceState_index[i]:_DeviceState_index[i+1]]
|
||||
}
|
44
vendor/github.com/thinkhy/go-adb/dialer.go
generated
vendored
Normal file
44
vendor/github.com/thinkhy/go-adb/dialer.go
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
// Dialer knows how to create connections to an adb server.
|
||||
type Dialer interface {
|
||||
Dial(address string) (*wire.Conn, error)
|
||||
}
|
||||
|
||||
type tcpDialer struct{}
|
||||
|
||||
// Dial connects to the adb server on the host and port set on the netDialer.
|
||||
// The zero-value will connect to the default, localhost:5037.
|
||||
func (tcpDialer) Dial(address string) (*wire.Conn, error) {
|
||||
netConn, err := net.Dial("tcp", address)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.WrapErrorf(err, errors.ServerNotAvailable, "error dialing %s", address)
|
||||
}
|
||||
|
||||
// net.Conn can't be closed more than once, but wire.Conn will try to close both sender and scanner
|
||||
// so we need to wrap it to make it safe.
|
||||
safeConn := wire.MultiCloseable(netConn)
|
||||
|
||||
// Prevent leaking the network connection, not sure if TCPConn does this itself.
|
||||
// Note that the network connection may still be in use after the conn isn't (scanners/senders
|
||||
// can give their underlying connections to other scanner/sender types), so we can't
|
||||
// set the finalizer on conn.
|
||||
runtime.SetFinalizer(safeConn, func(conn io.ReadWriteCloser) {
|
||||
conn.Close()
|
||||
})
|
||||
|
||||
return &wire.Conn{
|
||||
Scanner: wire.NewScanner(safeConn),
|
||||
Sender: wire.NewSender(safeConn),
|
||||
}, nil
|
||||
}
|
119
vendor/github.com/thinkhy/go-adb/dir_entries.go
generated
vendored
Normal file
119
vendor/github.com/thinkhy/go-adb/dir_entries.go
generated
vendored
Normal file
|
@ -0,0 +1,119 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
// DirEntry holds information about a directory entry on a device.
|
||||
type DirEntry struct {
|
||||
Name string
|
||||
Mode os.FileMode
|
||||
Size int32
|
||||
ModifiedAt time.Time
|
||||
}
|
||||
|
||||
// DirEntries iterates over directory entries.
|
||||
type DirEntries struct {
|
||||
scanner wire.SyncScanner
|
||||
|
||||
currentEntry *DirEntry
|
||||
err error
|
||||
}
|
||||
|
||||
// ReadAllDirEntries reads all the remaining directory entries into a slice,
|
||||
// closes self, and returns any error.
|
||||
// If err is non-nil, result will contain any entries read until the error occurred.
|
||||
func (entries *DirEntries) ReadAll() (result []*DirEntry, err error) {
|
||||
defer entries.Close()
|
||||
|
||||
for entries.Next() {
|
||||
result = append(result, entries.Entry())
|
||||
}
|
||||
err = entries.Err()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (entries *DirEntries) Next() bool {
|
||||
if entries.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
entry, done, err := readNextDirListEntry(entries.scanner)
|
||||
if err != nil {
|
||||
entries.err = err
|
||||
entries.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
entries.currentEntry = entry
|
||||
if done {
|
||||
entries.Close()
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (entries *DirEntries) Entry() *DirEntry {
|
||||
return entries.currentEntry
|
||||
}
|
||||
|
||||
func (entries *DirEntries) Err() error {
|
||||
return entries.err
|
||||
}
|
||||
|
||||
// Close closes the connection to the adb.
|
||||
// Next() will call Close() before returning false.
|
||||
func (entries *DirEntries) Close() error {
|
||||
return entries.scanner.Close()
|
||||
}
|
||||
|
||||
func readNextDirListEntry(s wire.SyncScanner) (entry *DirEntry, done bool, err error) {
|
||||
status, err := s.ReadStatus("dir-entry")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if status == "DONE" {
|
||||
done = true
|
||||
return
|
||||
} else if status != "DENT" {
|
||||
err = fmt.Errorf("error reading dir entries: expected dir entry ID 'DENT', but got '%s'", status)
|
||||
return
|
||||
}
|
||||
|
||||
mode, err := s.ReadFileMode()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error reading dir entries: error reading file mode: %v", err)
|
||||
return
|
||||
}
|
||||
size, err := s.ReadInt32()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error reading dir entries: error reading file size: %v", err)
|
||||
return
|
||||
}
|
||||
mtime, err := s.ReadTime()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error reading dir entries: error reading file time: %v", err)
|
||||
return
|
||||
}
|
||||
name, err := s.ReadString()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error reading dir entries: error reading file name: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
done = false
|
||||
entry = &DirEntry{
|
||||
Name: name,
|
||||
Mode: mode,
|
||||
Size: size,
|
||||
ModifiedAt: mtime,
|
||||
}
|
||||
return
|
||||
}
|
12
vendor/github.com/thinkhy/go-adb/doc.go
generated
vendored
Normal file
12
vendor/github.com/thinkhy/go-adb/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
package adb is a Go interface to the Android Debug Bridge (adb).
|
||||
|
||||
See cmd/demo/demo.go for an example of how to use this library.
|
||||
|
||||
The client/server spec is defined at https://android.googlesource.com/platform/system/core/+/master/adb/OVERVIEW.TXT.
|
||||
|
||||
WARNING This library is under heavy development, and its API is likely to change without notice.
|
||||
*/
|
||||
package adb
|
||||
|
||||
// TODO(z): Write method-specific examples.
|
38
vendor/github.com/thinkhy/go-adb/error.go
generated
vendored
Normal file
38
vendor/github.com/thinkhy/go-adb/error.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
package adb
|
||||
|
||||
import "github.com/thinkhy/go-adb/internal/errors"
|
||||
import sysErrors "errors"
|
||||
|
||||
type ErrCode errors.ErrCode
|
||||
|
||||
var ErrPackageNotExist = sysErrors.New("package not exist")
|
||||
|
||||
const (
|
||||
AssertionError = ErrCode(errors.AssertionError)
|
||||
ParseError = ErrCode(errors.ParseError)
|
||||
// The server was not available on the requested port.
|
||||
ServerNotAvailable = ErrCode(errors.ServerNotAvailable)
|
||||
// General network error communicating with the server.
|
||||
NetworkError = ErrCode(errors.NetworkError)
|
||||
// The connection to the server was reset in the middle of an operation. Server probably died.
|
||||
ConnectionResetError = ErrCode(errors.ConnectionResetError)
|
||||
// The server returned an error message, but we couldn't parse it.
|
||||
AdbError = ErrCode(errors.AdbError)
|
||||
// The server returned a "device not found" error.
|
||||
DeviceNotFound = ErrCode(errors.DeviceNotFound)
|
||||
// Tried to perform an operation on a path that doesn't exist on the device.
|
||||
FileNoExistError = ErrCode(errors.FileNoExistError)
|
||||
)
|
||||
|
||||
// HasErrCode returns true if err is an *errors.Err and err.Code == code.
|
||||
func HasErrCode(err error, code ErrCode) bool {
|
||||
return errors.HasErrCode(err, errors.ErrCode(code))
|
||||
}
|
||||
|
||||
/*
|
||||
ErrorWithCauseChain formats err and all its causes if it's an *errors.Err, else returns
|
||||
err.Error().
|
||||
*/
|
||||
func ErrorWithCauseChain(err error) string {
|
||||
return errors.ErrorWithCauseChain(err)
|
||||
}
|
21
vendor/github.com/thinkhy/go-adb/go.mod
generated
vendored
Normal file
21
vendor/github.com/thinkhy/go-adb/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
module github.com/thinkhy/go-adb
|
||||
|
||||
require (
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
|
||||
github.com/cheggaaa/pb v2.0.6+incompatible
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2
|
||||
github.com/mattn/go-colorable v0.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.4 // indirect
|
||||
github.com/stretchr/objx v0.1.1 // indirect
|
||||
github.com/stretchr/testify v1.3.0
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3
|
||||
gopkg.in/VividCortex/ewma.v1 v1.1.1 // indirect
|
||||
gopkg.in/cheggaaa/pb.v2 v2.0.6 // indirect
|
||||
gopkg.in/fatih/color.v1 v1.7.0 // indirect
|
||||
gopkg.in/mattn/go-colorable.v0 v0.1.0 // indirect
|
||||
gopkg.in/mattn/go-isatty.v0 v0.0.4 // indirect
|
||||
gopkg.in/mattn/go-runewidth.v0 v0.0.4 // indirect
|
||||
)
|
42
vendor/github.com/thinkhy/go-adb/go.sum
generated
vendored
Normal file
42
vendor/github.com/thinkhy/go-adb/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/cheggaaa/pb v2.0.6+incompatible h1:sutSx+mRaNbeJUMCAtyqNWU/tQ0B/xBm+hyb1JQmQYs=
|
||||
github.com/cheggaaa/pb v2.0.6+incompatible/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0 h1:wvCrVc9TjDls6+YGAF2hAifE1E5U1+b4tH6KdvN3Gig=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2 h1:AoISa4P4IsW0/m4T6St8Yw38gTl5GtBAgfkhYh1xAz4=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503 h1:5SvYFrOM3W8Mexn9/oA44Ji7vhXAZQ9hiP+1Q/DMrWg=
|
||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3 h1:+KlxhGbYkFs8lMfwKn+2ojry1ID5eBSMXprS2u/wqCE=
|
||||
golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
gopkg.in/VividCortex/ewma.v1 v1.1.1 h1:tWHEKkKq802K/JT9RiqGCBU5fW3raAPnJGTE9ostZvg=
|
||||
gopkg.in/VividCortex/ewma.v1 v1.1.1/go.mod h1:TekXuFipeiHWiAlO1+wSS23vTcyFau5u3rxXUSXj710=
|
||||
gopkg.in/cheggaaa/pb.v2 v2.0.6 h1:L2KAo2l2ZQTzxmh8b9RdQpzgLpK2mX3paGCMJSUugBk=
|
||||
gopkg.in/cheggaaa/pb.v2 v2.0.6/go.mod h1:0CiZ1p8pvtxBlQpLXkHuUTpdJ1shm3OqCF1QugkjHL4=
|
||||
gopkg.in/fatih/color.v1 v1.7.0 h1:bYGjb+HezBM6j/QmgBfgm1adxHpzzrss6bj4r9ROppk=
|
||||
gopkg.in/fatih/color.v1 v1.7.0/go.mod h1:P7yosIhqIl/sX8J8UypY5M+dDpD2KmyfP5IRs5v/fo0=
|
||||
gopkg.in/mattn/go-colorable.v0 v0.1.0 h1:WYuADWvfvYC07fm8ygYB3LMcsc5CunpxfMGKawHkAos=
|
||||
gopkg.in/mattn/go-colorable.v0 v0.1.0/go.mod h1:BVJlBXzARQxdi3nZo6f6bnl5yR20/tOL6p+V0KejgSY=
|
||||
gopkg.in/mattn/go-isatty.v0 v0.0.4 h1:NtS1rQGQr4IaFWBGz4Cz4BhB///gyys4gDVtKA7hIsc=
|
||||
gopkg.in/mattn/go-isatty.v0 v0.0.4/go.mod h1:wt691ab7g0X4ilKZNmMII3egK0bTxl37fEn/Fwbd8gc=
|
||||
gopkg.in/mattn/go-runewidth.v0 v0.0.4 h1:r0P71TnzQDlNIcizCqvPSSANoFa3WVGtcNJf3TWurcY=
|
||||
gopkg.in/mattn/go-runewidth.v0 v0.0.4/go.mod h1:BmXejnxvhwdaATwiJbB1vZ2dtXkQKZGu9yLFCZb4msQ=
|
16
vendor/github.com/thinkhy/go-adb/internal/errors/errcode_string.go
generated
vendored
Normal file
16
vendor/github.com/thinkhy/go-adb/internal/errors/errcode_string.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Code generated by "stringer -type=ErrCode"; DO NOT EDIT
|
||||
|
||||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _ErrCode_name = "AssertionErrorParseErrorServerNotAvailableNetworkErrorConnectionResetErrorAdbErrorDeviceNotFoundFileNoExistError"
|
||||
|
||||
var _ErrCode_index = [...]uint8{0, 14, 24, 42, 54, 74, 82, 96, 112}
|
||||
|
||||
func (i ErrCode) String() string {
|
||||
if i >= ErrCode(len(_ErrCode_index)-1) {
|
||||
return fmt.Sprintf("ErrCode(%d)", i)
|
||||
}
|
||||
return _ErrCode_name[_ErrCode_index[i]:_ErrCode_index[i+1]]
|
||||
}
|
188
vendor/github.com/thinkhy/go-adb/internal/errors/error.go
generated
vendored
Normal file
188
vendor/github.com/thinkhy/go-adb/internal/errors/error.go
generated
vendored
Normal file
|
@ -0,0 +1,188 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
Err is the implementation of error that all goadb functions return.
|
||||
|
||||
Best Practice
|
||||
|
||||
External errors should be wrapped using WrapErrorf, as soon as they are known about.
|
||||
|
||||
Intermediate code should pass *Errs up until they will be returned outside the library.
|
||||
Errors should *not* be wrapped at every return site.
|
||||
|
||||
Just before returning an *Err outside the library, it can be wrapped again, preserving the
|
||||
ErrCode (e.g. with WrapErrf).
|
||||
*/
|
||||
type Err struct {
|
||||
// Code is the high-level "type" of error.
|
||||
Code ErrCode
|
||||
// Message is a human-readable description of the error.
|
||||
Message string
|
||||
// Details is optional, and can be used to associate any auxiliary data with an error.
|
||||
Details interface{}
|
||||
// Cause is optional, and points to the more specific error that caused this one.
|
||||
Cause error
|
||||
}
|
||||
|
||||
var _ error = &Err{}
|
||||
|
||||
// Keep this in sync with ../error.go.
|
||||
//go:generate stringer -type=ErrCode
|
||||
type ErrCode byte
|
||||
|
||||
const (
|
||||
AssertionError ErrCode = iota
|
||||
ParseError
|
||||
// The server was not available on the requested port.
|
||||
ServerNotAvailable
|
||||
// General network error communicating with the server.
|
||||
NetworkError
|
||||
// The connection to the server was reset in the middle of an operation. Server probably died.
|
||||
ConnectionResetError
|
||||
// The server returned an error message, but we couldn't parse it.
|
||||
AdbError
|
||||
// The server returned a "device not found" error.
|
||||
DeviceNotFound
|
||||
// Tried to perform an operation on a path that doesn't exist on the device.
|
||||
FileNoExistError
|
||||
)
|
||||
|
||||
func Errorf(code ErrCode, format string, args ...interface{}) error {
|
||||
return &Err{
|
||||
Code: code,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
WrapErrf returns an *Err that wraps another *Err and has the same ErrCode.
|
||||
Panics if cause is not an *Err.
|
||||
|
||||
To wrap generic errors, use WrapErrorf.
|
||||
*/
|
||||
func WrapErrf(cause error, format string, args ...interface{}) error {
|
||||
if cause == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := cause.(*Err)
|
||||
return &Err{
|
||||
Code: err.Code,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
Cause: err,
|
||||
}
|
||||
}
|
||||
|
||||
// CombineErrs returns an error that wraps all the non-nil errors passed to it.
|
||||
// If all errors are nil, returns nil.
|
||||
// If there's only one non-nil error, returns that error without wrapping.
|
||||
// Else, returns an error with the message and code as passed, with the cause set to an error
|
||||
// that contains all the non-nil errors and for which Error() returns the concatenation of all their messages.
|
||||
func CombineErrs(msg string, code ErrCode, errs ...error) error {
|
||||
var nonNilErrs []error
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
nonNilErrs = append(nonNilErrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
switch len(nonNilErrs) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return nonNilErrs[0]
|
||||
default:
|
||||
return WrapErrorf(multiError(nonNilErrs), code, "%s", msg)
|
||||
}
|
||||
}
|
||||
|
||||
type multiError []error
|
||||
|
||||
func (errs multiError) Error() string {
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintf(&buf, "%d errors: [", len(errs))
|
||||
for i, err := range errs {
|
||||
buf.WriteString(err.Error())
|
||||
if i < len(errs)-1 {
|
||||
buf.WriteString(" ∪ ")
|
||||
}
|
||||
}
|
||||
buf.WriteRune(']')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
/*
|
||||
WrapErrorf returns an *Err that wraps another arbitrary error with an ErrCode and a message.
|
||||
|
||||
If cause is nil, returns nil, so you can use it like
|
||||
return util.WrapErrorf(DoSomethingDangerous(), util.NetworkError, "well that didn't work")
|
||||
|
||||
If cause is known to be of type *Err, use WrapErrf.
|
||||
*/
|
||||
func WrapErrorf(cause error, code ErrCode, format string, args ...interface{}) error {
|
||||
if cause == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Err{
|
||||
Code: code,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
Cause: cause,
|
||||
}
|
||||
}
|
||||
|
||||
func AssertionErrorf(format string, args ...interface{}) error {
|
||||
return &Err{
|
||||
Code: AssertionError,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (err *Err) Error() string {
|
||||
msg := fmt.Sprintf("%s: %s", err.Code, err.Message)
|
||||
if err.Details != nil {
|
||||
msg = fmt.Sprintf("%s (%+v)", msg, err.Details)
|
||||
}
|
||||
return msg
|
||||
}
|
||||
|
||||
// HasErrCode returns true if err is an *Err and err.Code == code.
|
||||
func HasErrCode(err error, code ErrCode) bool {
|
||||
switch err := err.(type) {
|
||||
case *Err:
|
||||
return err.Code == code
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
ErrorWithCauseChain formats err and all its causes if it's an *Err, else returns
|
||||
err.Error().
|
||||
*/
|
||||
func ErrorWithCauseChain(err error) string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for {
|
||||
if wrappedErr, ok := err.(*Err); ok && wrappedErr.Cause != nil {
|
||||
fmt.Fprintln(&buffer, wrappedErr.Error())
|
||||
fmt.Fprint(&buffer, "caused by ")
|
||||
err = wrappedErr.Cause
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
buffer.WriteString(err.Error())
|
||||
} else {
|
||||
buffer.WriteString("<err=nil>")
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
136
vendor/github.com/thinkhy/go-adb/server.go
generated
vendored
Normal file
136
vendor/github.com/thinkhy/go-adb/server.go
generated
vendored
Normal file
|
@ -0,0 +1,136 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
const (
|
||||
AdbExecutableName = "adb"
|
||||
|
||||
// Default port the adb server listens on.
|
||||
AdbPort = 5037
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
// Path to the adb executable. If empty, the PATH environment variable will be searched.
|
||||
PathToAdb string
|
||||
|
||||
// Host and port the adb server is listening on.
|
||||
// If not specified, will use the default port on localhost.
|
||||
Host string
|
||||
Port int
|
||||
|
||||
// Dialer used to connect to the adb server.
|
||||
Dialer
|
||||
|
||||
fs *filesystem
|
||||
}
|
||||
|
||||
// Server knows how to start the adb server and connect to it.
|
||||
type server interface {
|
||||
Start() error
|
||||
Dial() (*wire.Conn, error)
|
||||
}
|
||||
|
||||
func roundTripSingleResponse(s server, req string) ([]byte, error) {
|
||||
conn, err := s.Dial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
return conn.RoundTripSingleResponse([]byte(req))
|
||||
}
|
||||
|
||||
func roundTripSingleNoResponse(s server, req string) error {
|
||||
conn, err := s.Dial()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
return conn.RoundTripSingleNoResponse([]byte(req))
|
||||
}
|
||||
|
||||
type realServer struct {
|
||||
config ServerConfig
|
||||
|
||||
// Caches Host:Port so they don't have to be concatenated for every dial.
|
||||
address string
|
||||
}
|
||||
|
||||
func newServer(config ServerConfig) (server, error) {
|
||||
if config.Dialer == nil {
|
||||
config.Dialer = tcpDialer{}
|
||||
}
|
||||
|
||||
if config.Host == "" {
|
||||
// localhost becames very slow on windows(1s dial delay), use 127.0.0.1 works well
|
||||
config.Host = "127.0.0.1"
|
||||
}
|
||||
if config.Port == 0 {
|
||||
config.Port = AdbPort
|
||||
}
|
||||
|
||||
if config.fs == nil {
|
||||
config.fs = localFilesystem
|
||||
}
|
||||
|
||||
if config.PathToAdb == "" {
|
||||
path, err := config.fs.LookPath(AdbExecutableName)
|
||||
if err != nil {
|
||||
return nil, errors.WrapErrorf(err, errors.ServerNotAvailable, "could not find %s in PATH", AdbExecutableName)
|
||||
}
|
||||
config.PathToAdb = path
|
||||
}
|
||||
|
||||
return &realServer{
|
||||
config: config,
|
||||
address: fmt.Sprintf("%s:%d", config.Host, config.Port),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Dial tries to connect to the server. If the first attempt fails, tries starting the server before
|
||||
// retrying. If the second attempt fails, returns the error.
|
||||
func (s *realServer) Dial() (*wire.Conn, error) {
|
||||
conn, err := s.config.Dial(s.address)
|
||||
if err != nil {
|
||||
// Attempt to start the server and try again.
|
||||
if err = s.Start(); err != nil {
|
||||
return nil, errors.WrapErrorf(err, errors.ServerNotAvailable, "error starting server for dial")
|
||||
}
|
||||
|
||||
conn, err = s.config.Dial(s.address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// StartServer ensures there is a server running.
|
||||
func (s *realServer) Start() error {
|
||||
output, err := s.config.fs.CmdCombinedOutput(s.config.PathToAdb, "start-server")
|
||||
outputStr := strings.TrimSpace(string(output))
|
||||
return errors.WrapErrorf(err, errors.ServerNotAvailable, "error starting server: %s\noutput:\n%s", err, outputStr)
|
||||
}
|
||||
|
||||
// filesystem abstracts interactions with the local filesystem for testability.
|
||||
type filesystem struct {
|
||||
// Wraps exec.LookPath.
|
||||
LookPath func(string) (string, error)
|
||||
|
||||
// Wraps exec.Command().CombinedOutput()
|
||||
CmdCombinedOutput func(name string, arg ...string) ([]byte, error)
|
||||
}
|
||||
|
||||
var localFilesystem = &filesystem{
|
||||
LookPath: exec.LookPath,
|
||||
CmdCombinedOutput: func(name string, arg ...string) ([]byte, error) {
|
||||
return exec.Command(name, arg...).CombinedOutput()
|
||||
},
|
||||
}
|
100
vendor/github.com/thinkhy/go-adb/sync_client.go
generated
vendored
Normal file
100
vendor/github.com/thinkhy/go-adb/sync_client.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
var zeroTime = time.Unix(0, 0).UTC()
|
||||
|
||||
func stat(conn *wire.SyncConn, path string) (*DirEntry, error) {
|
||||
if err := conn.SendOctetString("STAT"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := conn.SendBytes([]byte(path)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, err := conn.ReadStatus("stat")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if id != "STAT" {
|
||||
return nil, errors.Errorf(errors.AssertionError, "expected stat ID 'STAT', but got '%s'", id)
|
||||
}
|
||||
|
||||
return readStat(conn)
|
||||
}
|
||||
|
||||
func listDirEntries(conn *wire.SyncConn, path string) (entries *DirEntries, err error) {
|
||||
if err = conn.SendOctetString("LIST"); err != nil {
|
||||
return
|
||||
}
|
||||
if err = conn.SendBytes([]byte(path)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return &DirEntries{scanner: conn}, nil
|
||||
}
|
||||
|
||||
func receiveFile(conn *wire.SyncConn, path string) (io.ReadCloser, error) {
|
||||
if err := conn.SendOctetString("RECV"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := conn.SendBytes([]byte(path)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newSyncFileReader(conn)
|
||||
}
|
||||
|
||||
// sendFile returns a WriteCloser than will write to the file at path on device.
|
||||
// The file will be created with permissions specified by mode.
|
||||
// The file's modified time will be set to mtime, unless mtime is 0, in which case the time the writer is
|
||||
// closed will be used.
|
||||
func sendFile(conn *wire.SyncConn, path string, mode os.FileMode, mtime time.Time) (io.WriteCloser, error) {
|
||||
if err := conn.SendOctetString("SEND"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pathAndMode := encodePathAndMode(path, mode)
|
||||
if err := conn.SendBytes(pathAndMode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newSyncFileWriter(conn, mtime), nil
|
||||
}
|
||||
|
||||
func readStat(s wire.SyncScanner) (entry *DirEntry, err error) {
|
||||
mode, err := s.ReadFileMode()
|
||||
if err != nil {
|
||||
err = errors.WrapErrf(err, "error reading file mode: %v", err)
|
||||
return
|
||||
}
|
||||
size, err := s.ReadInt32()
|
||||
if err != nil {
|
||||
err = errors.WrapErrf(err, "error reading file size: %v", err)
|
||||
return
|
||||
}
|
||||
mtime, err := s.ReadTime()
|
||||
if err != nil {
|
||||
err = errors.WrapErrf(err, "error reading file time: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// adb doesn't indicate when a file doesn't exist, but will return all zeros.
|
||||
// Theoretically this could be an actual file, but that's very unlikely.
|
||||
if mode == os.FileMode(0) && size == 0 && mtime == zeroTime {
|
||||
return nil, errors.Errorf(errors.FileNoExistError, "file doesn't exist")
|
||||
}
|
||||
|
||||
entry = &DirEntry{
|
||||
Mode: mode,
|
||||
Size: size,
|
||||
ModifiedAt: mtime,
|
||||
}
|
||||
return
|
||||
}
|
108
vendor/github.com/thinkhy/go-adb/sync_file_reader.go
generated
vendored
Normal file
108
vendor/github.com/thinkhy/go-adb/sync_file_reader.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
// syncFileReader wraps a SyncConn that has requested to receive a file.
|
||||
type syncFileReader struct {
|
||||
// Reader used to read data from the adb connection.
|
||||
scanner wire.SyncScanner
|
||||
|
||||
// Reader for the current chunk only.
|
||||
chunkReader io.Reader
|
||||
|
||||
// False until the DONE chunk is encountered.
|
||||
eof bool
|
||||
}
|
||||
|
||||
var _ io.ReadCloser = &syncFileReader{}
|
||||
|
||||
func newSyncFileReader(s wire.SyncScanner) (r io.ReadCloser, err error) {
|
||||
r = &syncFileReader{
|
||||
scanner: s,
|
||||
}
|
||||
|
||||
// Read the header for the first chunk to consume any errors.
|
||||
if _, err = r.Read([]byte{}); err != nil {
|
||||
if err == io.EOF {
|
||||
// EOF means the file was empty. This still means the file was opened successfully,
|
||||
// and the next time the caller does a read they'll get the EOF and handle it themselves.
|
||||
err = nil
|
||||
} else {
|
||||
r.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *syncFileReader) Read(buf []byte) (n int, err error) {
|
||||
if r.eof {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
if r.chunkReader == nil {
|
||||
chunkReader, err := readNextChunk(r.scanner)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
// We just read the last chunk, set our flag before passing it up.
|
||||
r.eof = true
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
r.chunkReader = chunkReader
|
||||
}
|
||||
|
||||
if len(buf) == 0 {
|
||||
// Read can be called with an empty buffer to read the next chunk and check for errors.
|
||||
// However, net.Conn.Read seems to return EOF when given an empty buffer, so we need to
|
||||
// handle that case ourselves.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
n, err = r.chunkReader.Read(buf)
|
||||
if err == io.EOF {
|
||||
// End of current chunk, don't return an error, the next chunk will be
|
||||
// read on the next call to this method.
|
||||
r.chunkReader = nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *syncFileReader) Close() error {
|
||||
return r.scanner.Close()
|
||||
}
|
||||
|
||||
// readNextChunk creates an io.LimitedReader for the next chunk of data,
|
||||
// and returns io.EOF if the last chunk has been read.
|
||||
func readNextChunk(r wire.SyncScanner) (io.Reader, error) {
|
||||
status, err := r.ReadStatus("read-chunk")
|
||||
if err != nil {
|
||||
if wire.IsAdbServerErrorMatching(err, readFileNotFoundPredicate) {
|
||||
return nil, errors.Errorf(errors.FileNoExistError, "no such file or directory")
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch status {
|
||||
case wire.StatusSyncData:
|
||||
return r.ReadBytes()
|
||||
case wire.StatusSyncDone:
|
||||
return nil, io.EOF
|
||||
default:
|
||||
return nil, errors.Errorf(errors.AssertionError, "expected chunk id '%s' or '%s', but got '%s'",
|
||||
wire.StatusSyncData, wire.StatusSyncDone, []byte(status))
|
||||
}
|
||||
}
|
||||
|
||||
// readFileNotFoundPredicate returns true if s is the adb server error message returned
|
||||
// when trying to open a file that doesn't exist.
|
||||
func readFileNotFoundPredicate(s string) bool {
|
||||
return s == "No such file or directory"
|
||||
}
|
86
vendor/github.com/thinkhy/go-adb/sync_file_writer.go
generated
vendored
Normal file
86
vendor/github.com/thinkhy/go-adb/sync_file_writer.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
"github.com/thinkhy/go-adb/wire"
|
||||
)
|
||||
|
||||
// syncFileWriter wraps a SyncConn that has requested to send a file.
|
||||
type syncFileWriter struct {
|
||||
// The modification time to write in the footer.
|
||||
// If 0, use the current time.
|
||||
mtime time.Time
|
||||
|
||||
// Reader used to read data from the adb connection.
|
||||
sender wire.SyncSender
|
||||
}
|
||||
|
||||
var _ io.WriteCloser = &syncFileWriter{}
|
||||
|
||||
func newSyncFileWriter(s wire.SyncSender, mtime time.Time) io.WriteCloser {
|
||||
return &syncFileWriter{
|
||||
mtime: mtime,
|
||||
sender: s,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
encodePathAndMode encodes a path and file mode as required for starting a send file stream.
|
||||
|
||||
From https://android.googlesource.com/platform/system/core/+/master/adb/SYNC.TXT:
|
||||
The remote file name is split into two parts separated by the last
|
||||
comma (","). The first part is the actual path, while the second is a decimal
|
||||
encoded file mode containing the permissions of the file on device.
|
||||
*/
|
||||
func encodePathAndMode(path string, mode os.FileMode) []byte {
|
||||
return []byte(fmt.Sprintf("%s,%d", path, uint32(mode.Perm())))
|
||||
}
|
||||
|
||||
// Write writes the min of (len(buf), 64k).
|
||||
func (w *syncFileWriter) Write(buf []byte) (n int, err error) {
|
||||
written := 0
|
||||
|
||||
// If buf > 64k we'll have to send multiple chunks.
|
||||
// TODO Refactor this into something that can coalesce smaller writes into a single chukn.
|
||||
for len(buf) > 0 {
|
||||
// Writes < 64k have a one-to-one mapping to chunks.
|
||||
// If buffer is larger than the max, we'll return the max size and leave it up to the
|
||||
// caller to handle correctly.
|
||||
partialBuf := buf
|
||||
if len(partialBuf) > wire.SyncMaxChunkSize {
|
||||
partialBuf = partialBuf[:wire.SyncMaxChunkSize]
|
||||
}
|
||||
|
||||
if err := w.sender.SendOctetString(wire.StatusSyncData); err != nil {
|
||||
return written, err
|
||||
}
|
||||
if err := w.sender.SendBytes(partialBuf); err != nil {
|
||||
return written, err
|
||||
}
|
||||
|
||||
written += len(partialBuf)
|
||||
buf = buf[len(partialBuf):]
|
||||
}
|
||||
|
||||
return written, nil
|
||||
}
|
||||
|
||||
func (w *syncFileWriter) Close() error {
|
||||
if w.mtime.IsZero() {
|
||||
w.mtime = time.Now()
|
||||
}
|
||||
|
||||
if err := w.sender.SendOctetString(wire.StatusSyncDone); err != nil {
|
||||
return errors.WrapErrf(err, "error sending done chunk to close stream")
|
||||
}
|
||||
if err := w.sender.SendTime(w.mtime); err != nil {
|
||||
return errors.WrapErrf(err, "error writing file modification time")
|
||||
}
|
||||
|
||||
return errors.WrapErrf(w.sender.Close(), "error closing FileWriter")
|
||||
}
|
59
vendor/github.com/thinkhy/go-adb/util.go
generated
vendored
Normal file
59
vendor/github.com/thinkhy/go-adb/util.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package adb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
whitespaceRegex = regexp.MustCompile(`^\s*$`)
|
||||
)
|
||||
|
||||
func containsWhitespace(str string) bool {
|
||||
return strings.ContainsAny(str, " \t\v")
|
||||
}
|
||||
|
||||
func isBlank(str string) bool {
|
||||
return whitespaceRegex.MatchString(str)
|
||||
}
|
||||
|
||||
func wrapClientError(err error, client interface{}, operation string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if _, ok := err.(*errors.Err); !ok {
|
||||
panic("err is not a *Err: " + err.Error())
|
||||
}
|
||||
|
||||
clientType := reflect.TypeOf(client)
|
||||
|
||||
return &errors.Err{
|
||||
Code: err.(*errors.Err).Code,
|
||||
Cause: err,
|
||||
Message: fmt.Sprintf("error performing %s on %s", fmt.Sprintf(operation, args...), clientType),
|
||||
Details: client,
|
||||
}
|
||||
}
|
||||
|
||||
// Get a free port.
|
||||
func getFreePort() (port int, err error) {
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
addr := listener.Addr().String()
|
||||
_, portString, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return strconv.Atoi(portString)
|
||||
}
|
96
vendor/github.com/thinkhy/go-adb/wire/conn.go
generated
vendored
Normal file
96
vendor/github.com/thinkhy/go-adb/wire/conn.go
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
package wire
|
||||
|
||||
import (
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// The official implementation of adb imposes an undocumented 255-byte limit
|
||||
// on messages.
|
||||
MaxMessageLength = 255
|
||||
)
|
||||
|
||||
/*
|
||||
Conn is a normal connection to an adb server.
|
||||
|
||||
For most cases, usage looks something like:
|
||||
conn := wire.Dial()
|
||||
conn.SendMessage(data)
|
||||
conn.ReadStatus() == StatusSuccess || StatusFailure
|
||||
conn.ReadMessage()
|
||||
conn.Close()
|
||||
|
||||
For some messages, the server will return more than one message (but still a single
|
||||
status). Generally, after calling ReadStatus once, you should call ReadMessage until
|
||||
it returns an io.EOF error. Note: the protocol docs seem to suggest that connections will be
|
||||
kept open for multiple commands, but this is not the case. The official client closes
|
||||
a connection immediately after its read the response, in most cases. The docs might be
|
||||
referring to the connection between the adb server and the device, but I haven't confirmed
|
||||
that.
|
||||
|
||||
For most commands, the server will close the connection after sending the response.
|
||||
You should still always call Close() when you're done with the connection.
|
||||
*/
|
||||
type Conn struct {
|
||||
Scanner
|
||||
Sender
|
||||
}
|
||||
|
||||
func NewConn(scanner Scanner, sender Sender) *Conn {
|
||||
return &Conn{scanner, sender}
|
||||
}
|
||||
|
||||
// NewSyncConn returns connection that can operate in sync mode.
|
||||
// The connection must already have been switched (by sending the sync command
|
||||
// to a specific device), or the return connection will return an error.
|
||||
func (c *Conn) NewSyncConn() *SyncConn {
|
||||
return &SyncConn{
|
||||
SyncScanner: c.Scanner.NewSyncScanner(),
|
||||
SyncSender: c.Sender.NewSyncSender(),
|
||||
}
|
||||
}
|
||||
|
||||
// RoundTripSingleResponse sends a message to the server, and reads a single
|
||||
// message response. If the reponse has a failure status code, returns it as an error.
|
||||
func (conn *Conn) RoundTripSingleResponse(req []byte) (resp []byte, err error) {
|
||||
if err = conn.SendMessage(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = conn.ReadStatus(string(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn.ReadMessage()
|
||||
}
|
||||
|
||||
// RoundTripSingleResponse sends a message to the server
|
||||
// Only read status
|
||||
func (conn *Conn) RoundTripSingleNoResponse(req []byte) (err error) {
|
||||
err = conn.SendMessage(req)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = conn.ReadStatus(string(req))
|
||||
return
|
||||
}
|
||||
|
||||
func (conn *Conn) Close() error {
|
||||
errs := struct {
|
||||
SenderErr error
|
||||
ScannerErr error
|
||||
}{
|
||||
SenderErr: conn.Sender.Close(),
|
||||
ScannerErr: conn.Scanner.Close(),
|
||||
}
|
||||
|
||||
if errs.ScannerErr != nil || errs.SenderErr != nil {
|
||||
return &errors.Err{
|
||||
Code: errors.NetworkError,
|
||||
Message: "error closing connection",
|
||||
Details: errs,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
13
vendor/github.com/thinkhy/go-adb/wire/doc.go
generated
vendored
Normal file
13
vendor/github.com/thinkhy/go-adb/wire/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
Package wire implements the low-level part of the client/server wire protocol.
|
||||
It also implements the "sync" wire format for file transfers.
|
||||
|
||||
This package is not intended to be used directly. adb.Adb and adb.Device
|
||||
use it to abstract away the bit-twiddling details of the protocol. You should only ever
|
||||
need to work with the goadb package. Also, this package's API may change more frequently
|
||||
than goadb's.
|
||||
|
||||
The protocol spec can be found at
|
||||
https://android.googlesource.com/platform/system/core/+/master/adb/OVERVIEW.TXT.
|
||||
*/
|
||||
package wire
|
33
vendor/github.com/thinkhy/go-adb/wire/filemode.go
generated
vendored
Normal file
33
vendor/github.com/thinkhy/go-adb/wire/filemode.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
package wire
|
||||
|
||||
import "os"
|
||||
|
||||
// ADB file modes seem to only be 16 bits.
|
||||
// Values are taken from http://linux.die.net/include/bits/stat.h.
|
||||
const (
|
||||
ModeDir uint32 = 0040000
|
||||
ModeSymlink = 0120000
|
||||
ModeSocket = 0140000
|
||||
ModeFifo = 0010000
|
||||
ModeCharDevice = 0020000
|
||||
)
|
||||
|
||||
func ParseFileModeFromAdb(modeFromSync uint32) (filemode os.FileMode) {
|
||||
// The ADB filemode uses the permission bits defined in Go's os package, but
|
||||
// we need to parse the other bits manually.
|
||||
switch {
|
||||
case modeFromSync&ModeSymlink == ModeSymlink:
|
||||
filemode = os.ModeSymlink
|
||||
case modeFromSync&ModeDir == ModeDir:
|
||||
filemode = os.ModeDir
|
||||
case modeFromSync&ModeSocket == ModeSocket:
|
||||
filemode = os.ModeSocket
|
||||
case modeFromSync&ModeFifo == ModeFifo:
|
||||
filemode = os.ModeNamedPipe
|
||||
case modeFromSync&ModeCharDevice == ModeCharDevice:
|
||||
filemode = os.ModeCharDevice
|
||||
}
|
||||
|
||||
filemode |= os.FileMode(modeFromSync).Perm()
|
||||
return
|
||||
}
|
187
vendor/github.com/thinkhy/go-adb/wire/scanner.go
generated
vendored
Normal file
187
vendor/github.com/thinkhy/go-adb/wire/scanner.go
generated
vendored
Normal file
|
@ -0,0 +1,187 @@
|
|||
package wire
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
)
|
||||
|
||||
// TODO(zach): All EOF errors returned from networoking calls should use ConnectionResetError.
|
||||
|
||||
// StatusCodes are returned by the server. If the code indicates failure, the
|
||||
// next message will be the error.
|
||||
const (
|
||||
StatusSuccess string = "OKAY"
|
||||
StatusFailure = "FAIL"
|
||||
StatusSyncData = "DATA"
|
||||
StatusSyncDone = "DONE"
|
||||
StatusNone = ""
|
||||
)
|
||||
|
||||
func isFailureStatus(status string) bool {
|
||||
return status == StatusFailure
|
||||
}
|
||||
|
||||
type StatusReader interface {
|
||||
// Reads a 4-byte status string and returns it.
|
||||
// If the status string is StatusFailure, reads the error message from the server
|
||||
// and returns it as an AdbError.
|
||||
ReadStatus(req string) (string, error)
|
||||
}
|
||||
|
||||
/*
|
||||
Scanner reads tokens from a server.
|
||||
See Conn for more details.
|
||||
*/
|
||||
type Scanner interface {
|
||||
io.Closer
|
||||
StatusReader
|
||||
Read([]byte) (int, error)
|
||||
ReadMessage() ([]byte, error)
|
||||
ReadUntilEof() ([]byte, error)
|
||||
|
||||
NewSyncScanner() SyncScanner
|
||||
}
|
||||
|
||||
type realScanner struct {
|
||||
reader io.ReadCloser
|
||||
}
|
||||
|
||||
func NewScanner(r io.ReadCloser) Scanner {
|
||||
return &realScanner{r}
|
||||
}
|
||||
|
||||
func ReadMessageString(s Scanner) (string, error) {
|
||||
msg, err := s.ReadMessage()
|
||||
if err != nil {
|
||||
return string(msg), err
|
||||
}
|
||||
return string(msg), nil
|
||||
}
|
||||
|
||||
func (s *realScanner) ReadStatus(req string) (string, error) {
|
||||
return readStatusFailureAsError(s.reader, req, readHexLength)
|
||||
}
|
||||
|
||||
func (s *realScanner) ReadMessage() ([]byte, error) {
|
||||
return readMessage(s.reader, readHexLength)
|
||||
}
|
||||
|
||||
func (s *realScanner) ReadUntilEof() ([]byte, error) {
|
||||
data, err := ioutil.ReadAll(s.reader)
|
||||
if err != nil {
|
||||
return nil, errors.WrapErrorf(err, errors.NetworkError, "error reading until EOF")
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// wrap for shell
|
||||
func (s *realScanner) Read(p []byte) (n int, err error) {
|
||||
return s.reader.Read(p)
|
||||
}
|
||||
|
||||
func (s *realScanner) NewSyncScanner() SyncScanner {
|
||||
return NewSyncScanner(s.reader)
|
||||
}
|
||||
|
||||
func (s *realScanner) Close() error {
|
||||
return errors.WrapErrorf(s.reader.Close(), errors.NetworkError, "error closing scanner")
|
||||
}
|
||||
|
||||
var _ Scanner = &realScanner{}
|
||||
|
||||
// lengthReader is a func that readMessage uses to read message length.
|
||||
// See readHexLength and readInt32.
|
||||
type lengthReader func(io.Reader) (int, error)
|
||||
|
||||
// Reads the status, and if failure, reads the message and returns it as an error.
|
||||
// If the status is success, doesn't read the message.
|
||||
// req is just used to populate the AdbError, and can be nil.
|
||||
// messageLengthReader is the function passed to readMessage if the status is failure.
|
||||
func readStatusFailureAsError(r io.Reader, req string, messageLengthReader lengthReader) (string, error) {
|
||||
status, err := readOctetString(req, r)
|
||||
if err != nil {
|
||||
return "", errors.WrapErrorf(err, errors.NetworkError, "error reading status for %s", req)
|
||||
}
|
||||
|
||||
if isFailureStatus(status) {
|
||||
msg, err := readMessage(r, messageLengthReader)
|
||||
if err != nil {
|
||||
return "", errors.WrapErrorf(err, errors.NetworkError,
|
||||
"server returned error for %s, but couldn't read the error message", req)
|
||||
}
|
||||
|
||||
return "", adbServerError(req, string(msg))
|
||||
}
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
func readOctetString(description string, r io.Reader) (string, error) {
|
||||
octet := make([]byte, 4)
|
||||
n, err := io.ReadFull(r, octet)
|
||||
|
||||
if err == io.ErrUnexpectedEOF {
|
||||
return "", errIncompleteMessage(description, n, 4)
|
||||
} else if err != nil {
|
||||
return "", errors.WrapErrorf(err, errors.NetworkError, "error reading "+description)
|
||||
}
|
||||
|
||||
return string(octet), nil
|
||||
}
|
||||
|
||||
// readMessage reads a length from r, then reads length bytes and returns them.
|
||||
// lengthReader is the function used to read the length. Most operations encode
|
||||
// length as a hex string (readHexLength), but sync operations use little-endian
|
||||
// binary encoding (readInt32).
|
||||
func readMessage(r io.Reader, lengthReader lengthReader) ([]byte, error) {
|
||||
var err error
|
||||
|
||||
length, err := lengthReader(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := make([]byte, length)
|
||||
n, err := io.ReadFull(r, data)
|
||||
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return data, errors.WrapErrorf(err, errors.NetworkError, "error reading message data")
|
||||
} else if err == io.ErrUnexpectedEOF {
|
||||
return data, errIncompleteMessage("message data", n, length)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// readHexLength reads the next 4 bytes from r as an ASCII hex-encoded length and parses them into an int.
|
||||
func readHexLength(r io.Reader) (int, error) {
|
||||
lengthHex := make([]byte, 4)
|
||||
n, err := io.ReadFull(r, lengthHex)
|
||||
if err != nil {
|
||||
return 0, errIncompleteMessage("length", n, 4)
|
||||
}
|
||||
|
||||
length, err := strconv.ParseInt(string(lengthHex), 16, 64)
|
||||
if err != nil {
|
||||
return 0, errors.WrapErrorf(err, errors.NetworkError, "could not parse hex length %v", lengthHex)
|
||||
}
|
||||
|
||||
// COMMENT(ssx): comment the below code because I encounter message length > 255
|
||||
// Clip the length to 255, as per the Google implementation.
|
||||
// if length > MaxMessageLength {
|
||||
// length = MaxMessageLength
|
||||
// }
|
||||
|
||||
return int(length), nil
|
||||
}
|
||||
|
||||
// readInt32 reads the next 4 bytes from r as a little-endian integer.
|
||||
// Returns an int instead of an int32 to match the lengthReader type.
|
||||
func readInt32(r io.Reader) (int, error) {
|
||||
var value int32
|
||||
err := binary.Read(r, binary.LittleEndian, &value)
|
||||
return int(value), err
|
||||
}
|
52
vendor/github.com/thinkhy/go-adb/wire/sender.go
generated
vendored
Normal file
52
vendor/github.com/thinkhy/go-adb/wire/sender.go
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
package wire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
)
|
||||
|
||||
// Sender sends messages to the server.
|
||||
type Sender interface {
|
||||
Write([]byte) (int, error)
|
||||
SendMessage(msg []byte) error
|
||||
NewSyncSender() SyncSender
|
||||
|
||||
Close() error
|
||||
}
|
||||
|
||||
type realSender struct {
|
||||
writer io.WriteCloser
|
||||
}
|
||||
|
||||
func NewSender(w io.WriteCloser) Sender {
|
||||
return &realSender{w}
|
||||
}
|
||||
|
||||
func SendMessageString(s Sender, msg string) error {
|
||||
return s.SendMessage([]byte(msg))
|
||||
}
|
||||
|
||||
func (s *realSender) Write(data []byte) (n int, err error) {
|
||||
return s.writer.Write(data)
|
||||
}
|
||||
|
||||
func (s *realSender) SendMessage(msg []byte) error {
|
||||
if len(msg) > MaxMessageLength {
|
||||
return errors.AssertionErrorf("message length exceeds maximum: %d", len(msg))
|
||||
}
|
||||
|
||||
lengthAndMsg := fmt.Sprintf("%04x%s", len(msg), msg)
|
||||
return writeFully(s.writer, []byte(lengthAndMsg))
|
||||
}
|
||||
|
||||
func (s *realSender) NewSyncSender() SyncSender {
|
||||
return NewSyncSender(s.writer)
|
||||
}
|
||||
|
||||
func (s *realSender) Close() error {
|
||||
return errors.WrapErrorf(s.writer.Close(), errors.NetworkError, "error closing sender")
|
||||
}
|
||||
|
||||
var _ Sender = &realSender{}
|
37
vendor/github.com/thinkhy/go-adb/wire/sync_conn.go
generated
vendored
Normal file
37
vendor/github.com/thinkhy/go-adb/wire/sync_conn.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
package wire
|
||||
|
||||
import "github.com/thinkhy/go-adb/internal/errors"
|
||||
|
||||
const (
|
||||
// Chunks cannot be longer than 64k.
|
||||
SyncMaxChunkSize = 64 * 1024
|
||||
)
|
||||
|
||||
/*
|
||||
SyncConn is a connection to the adb server in sync mode.
|
||||
Assumes the connection has been put into sync mode (by sending "sync" in transport mode).
|
||||
|
||||
The adb sync protocol is defined at
|
||||
https://android.googlesource.com/platform/system/core/+/master/adb/SYNC.TXT.
|
||||
|
||||
Unlike the normal adb protocol (implemented in Conn), the sync protocol is binary.
|
||||
Lengths are binary-encoded (little-endian) instead of hex.
|
||||
|
||||
Notes on Encoding
|
||||
|
||||
Length headers and other integers are encoded in little-endian, with 32 bits.
|
||||
|
||||
File mode seems to be encoded as POSIX file mode.
|
||||
|
||||
Modification time seems to be the Unix timestamp format, i.e. seconds since Epoch UTC.
|
||||
*/
|
||||
type SyncConn struct {
|
||||
SyncScanner
|
||||
SyncSender
|
||||
}
|
||||
|
||||
// Close closes both the sender and the scanner, and returns any errors.
|
||||
func (c SyncConn) Close() error {
|
||||
return errors.CombineErrs("error closing SyncConn", errors.NetworkError,
|
||||
c.SyncScanner.Close(), c.SyncSender.Close())
|
||||
}
|
92
vendor/github.com/thinkhy/go-adb/wire/sync_scanner.go
generated
vendored
Normal file
92
vendor/github.com/thinkhy/go-adb/wire/sync_scanner.go
generated
vendored
Normal file
|
@ -0,0 +1,92 @@
|
|||
package wire
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
)
|
||||
|
||||
type SyncScanner interface {
|
||||
io.Closer
|
||||
StatusReader
|
||||
ReadInt32() (int32, error)
|
||||
ReadFileMode() (os.FileMode, error)
|
||||
ReadTime() (time.Time, error)
|
||||
|
||||
// Reads an octet length, followed by length bytes.
|
||||
ReadString() (string, error)
|
||||
|
||||
// Reads an octet length, and returns a reader that will read length
|
||||
// bytes (see io.LimitReader). The returned reader should be fully
|
||||
// read before reading anything off the Scanner again.
|
||||
ReadBytes() (io.Reader, error)
|
||||
}
|
||||
|
||||
type realSyncScanner struct {
|
||||
io.Reader
|
||||
}
|
||||
|
||||
func NewSyncScanner(r io.Reader) SyncScanner {
|
||||
return &realSyncScanner{r}
|
||||
}
|
||||
|
||||
func (s *realSyncScanner) ReadStatus(req string) (string, error) {
|
||||
return readStatusFailureAsError(s.Reader, req, readInt32)
|
||||
}
|
||||
|
||||
func (s *realSyncScanner) ReadInt32() (int32, error) {
|
||||
value, err := readInt32(s.Reader)
|
||||
return int32(value), errors.WrapErrorf(err, errors.NetworkError, "error reading int from sync scanner")
|
||||
}
|
||||
func (s *realSyncScanner) ReadFileMode() (os.FileMode, error) {
|
||||
var value uint32
|
||||
err := binary.Read(s.Reader, binary.LittleEndian, &value)
|
||||
if err != nil {
|
||||
return 0, errors.WrapErrorf(err, errors.NetworkError, "error reading filemode from sync scanner")
|
||||
}
|
||||
return ParseFileModeFromAdb(value), nil
|
||||
|
||||
}
|
||||
func (s *realSyncScanner) ReadTime() (time.Time, error) {
|
||||
seconds, err := s.ReadInt32()
|
||||
if err != nil {
|
||||
return time.Time{}, errors.WrapErrorf(err, errors.NetworkError, "error reading time from sync scanner")
|
||||
}
|
||||
|
||||
return time.Unix(int64(seconds), 0).UTC(), nil
|
||||
}
|
||||
|
||||
func (s *realSyncScanner) ReadString() (string, error) {
|
||||
length, err := s.ReadInt32()
|
||||
if err != nil {
|
||||
return "", errors.WrapErrorf(err, errors.NetworkError, "error reading length from sync scanner")
|
||||
}
|
||||
|
||||
bytes := make([]byte, length)
|
||||
n, rawErr := io.ReadFull(s.Reader, bytes)
|
||||
if rawErr != nil && rawErr != io.ErrUnexpectedEOF {
|
||||
return "", errors.WrapErrorf(rawErr, errors.NetworkError, "error reading string from sync scanner")
|
||||
} else if rawErr == io.ErrUnexpectedEOF {
|
||||
return "", errIncompleteMessage("bytes", n, int(length))
|
||||
}
|
||||
|
||||
return string(bytes), nil
|
||||
}
|
||||
func (s *realSyncScanner) ReadBytes() (io.Reader, error) {
|
||||
length, err := s.ReadInt32()
|
||||
if err != nil {
|
||||
return nil, errors.WrapErrorf(err, errors.NetworkError, "error reading bytes from sync scanner")
|
||||
}
|
||||
|
||||
return io.LimitReader(s.Reader, int64(length)), nil
|
||||
}
|
||||
|
||||
func (s *realSyncScanner) Close() error {
|
||||
if closer, ok := s.Reader.(io.Closer); ok {
|
||||
return errors.WrapErrorf(closer.Close(), errors.NetworkError, "error closing sync scanner")
|
||||
}
|
||||
return nil
|
||||
}
|
79
vendor/github.com/thinkhy/go-adb/wire/sync_sender.go
generated
vendored
Normal file
79
vendor/github.com/thinkhy/go-adb/wire/sync_sender.go
generated
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
package wire
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
)
|
||||
|
||||
type SyncSender interface {
|
||||
io.Closer
|
||||
|
||||
// SendOctetString sends a 4-byte string.
|
||||
SendOctetString(string) error
|
||||
SendInt32(int32) error
|
||||
SendFileMode(os.FileMode) error
|
||||
SendTime(time.Time) error
|
||||
|
||||
// Sends len(data) as an octet, followed by the bytes.
|
||||
// If data is bigger than SyncMaxChunkSize, it returns an assertion error.
|
||||
SendBytes(data []byte) error
|
||||
}
|
||||
|
||||
type realSyncSender struct {
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func NewSyncSender(w io.Writer) SyncSender {
|
||||
return &realSyncSender{w}
|
||||
}
|
||||
|
||||
func (s *realSyncSender) SendOctetString(str string) error {
|
||||
if len(str) != 4 {
|
||||
return errors.AssertionErrorf("octet string must be exactly 4 bytes: '%s'", str)
|
||||
}
|
||||
|
||||
wrappedErr := errors.WrapErrorf(writeFully(s.Writer, []byte(str)),
|
||||
errors.NetworkError, "error sending octet string on sync sender")
|
||||
|
||||
return wrappedErr
|
||||
}
|
||||
|
||||
func (s *realSyncSender) SendInt32(val int32) error {
|
||||
return errors.WrapErrorf(binary.Write(s.Writer, binary.LittleEndian, val),
|
||||
errors.NetworkError, "error sending int on sync sender")
|
||||
}
|
||||
|
||||
func (s *realSyncSender) SendFileMode(mode os.FileMode) error {
|
||||
return errors.WrapErrorf(binary.Write(s.Writer, binary.LittleEndian, mode),
|
||||
errors.NetworkError, "error sending filemode on sync sender")
|
||||
}
|
||||
|
||||
func (s *realSyncSender) SendTime(t time.Time) error {
|
||||
return errors.WrapErrorf(s.SendInt32(int32(t.Unix())),
|
||||
errors.NetworkError, "error sending time on sync sender")
|
||||
}
|
||||
|
||||
func (s *realSyncSender) SendBytes(data []byte) error {
|
||||
length := len(data)
|
||||
if length > SyncMaxChunkSize {
|
||||
// This limit might not apply to filenames, but it's big enough
|
||||
// that I don't think it will be a problem.
|
||||
return errors.AssertionErrorf("data must be <= %d in length", SyncMaxChunkSize)
|
||||
}
|
||||
|
||||
if err := s.SendInt32(int32(length)); err != nil {
|
||||
return errors.WrapErrorf(err, errors.NetworkError, "error sending data length on sync sender")
|
||||
}
|
||||
return writeFully(s.Writer, data)
|
||||
}
|
||||
|
||||
func (s *realSyncSender) Close() error {
|
||||
if closer, ok := s.Writer.(io.Closer); ok {
|
||||
return errors.WrapErrorf(closer.Close(), errors.NetworkError, "error closing sync sender")
|
||||
}
|
||||
return nil
|
||||
}
|
101
vendor/github.com/thinkhy/go-adb/wire/util.go
generated
vendored
Normal file
101
vendor/github.com/thinkhy/go-adb/wire/util.go
generated
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
package wire
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"sync"
|
||||
|
||||
"github.com/thinkhy/go-adb/internal/errors"
|
||||
)
|
||||
|
||||
// ErrorResponseDetails is an error message returned by the server for a particular request.
|
||||
type ErrorResponseDetails struct {
|
||||
Request string
|
||||
ServerMsg string
|
||||
}
|
||||
|
||||
// deviceNotFoundMessagePattern matches all possible error messages returned by adb servers to
|
||||
// report that a matching device was not found. Used to set the DeviceNotFound error code on
|
||||
// error values.
|
||||
//
|
||||
// Old servers send "device not found", and newer ones "device 'serial' not found".
|
||||
var deviceNotFoundMessagePattern = regexp.MustCompile(`device( '.*')? not found`)
|
||||
|
||||
func adbServerError(request string, serverMsg string) error {
|
||||
var msg string
|
||||
if request == "" {
|
||||
msg = fmt.Sprintf("server error: %s", serverMsg)
|
||||
} else {
|
||||
msg = fmt.Sprintf("server error for %s request: %s", request, serverMsg)
|
||||
}
|
||||
|
||||
errCode := errors.AdbError
|
||||
if deviceNotFoundMessagePattern.MatchString(serverMsg) {
|
||||
errCode = errors.DeviceNotFound
|
||||
}
|
||||
|
||||
return &errors.Err{
|
||||
Code: errCode,
|
||||
Message: msg,
|
||||
Details: ErrorResponseDetails{
|
||||
Request: request,
|
||||
ServerMsg: serverMsg,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// IsAdbServerErrorMatching returns true if err is an *Err with code AdbError and for which
|
||||
// predicate returns true when passed Details.ServerMsg.
|
||||
func IsAdbServerErrorMatching(err error, predicate func(string) bool) bool {
|
||||
if err, ok := err.(*errors.Err); ok && err.Code == errors.AdbError {
|
||||
return predicate(err.Details.(ErrorResponseDetails).ServerMsg)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func errIncompleteMessage(description string, actual int, expected int) error {
|
||||
return &errors.Err{
|
||||
Code: errors.ConnectionResetError,
|
||||
Message: fmt.Sprintf("incomplete %s: read %d bytes, expecting %d", description, actual, expected),
|
||||
Details: struct {
|
||||
ActualReadBytes int
|
||||
ExpectedBytes int
|
||||
}{
|
||||
ActualReadBytes: actual,
|
||||
ExpectedBytes: expected,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// writeFully writes all of data to w.
|
||||
// Inverse of io.ReadFully().
|
||||
func writeFully(w io.Writer, data []byte) error {
|
||||
offset := 0
|
||||
for offset < len(data) {
|
||||
n, err := w.Write(data[offset:])
|
||||
if err != nil {
|
||||
return errors.WrapErrorf(err, errors.NetworkError, "error writing %d bytes at offset %d", len(data), offset)
|
||||
}
|
||||
offset += n
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MultiCloseable wraps c in a ReadWriteCloser that can be safely closed multiple times.
|
||||
func MultiCloseable(c io.ReadWriteCloser) io.ReadWriteCloser {
|
||||
return &multiCloseable{ReadWriteCloser: c}
|
||||
}
|
||||
|
||||
type multiCloseable struct {
|
||||
io.ReadWriteCloser
|
||||
closeOnce sync.Once
|
||||
err error
|
||||
}
|
||||
|
||||
func (c *multiCloseable) Close() error {
|
||||
c.closeOnce.Do(func() {
|
||||
c.err = c.ReadWriteCloser.Close()
|
||||
})
|
||||
return c.err
|
||||
}
|
16
vendor/golang.org/x/sys/unix/README.md
generated
vendored
16
vendor/golang.org/x/sys/unix/README.md
generated
vendored
|
@ -32,7 +32,7 @@ To build the files for your current OS and architecture, make sure GOOS and
|
|||
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
|
||||
your specific system. Running `mkall.sh -n` shows the commands that will be run.
|
||||
|
||||
Requirements: bash, perl, go
|
||||
Requirements: bash, go
|
||||
|
||||
### New Build System (currently for `GOOS == "linux"`)
|
||||
|
||||
|
@ -52,14 +52,14 @@ system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
|
|||
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
|
||||
system. Running `mkall.sh -n` shows the commands that will be run.
|
||||
|
||||
Requirements: bash, perl, go, docker
|
||||
Requirements: bash, go, docker
|
||||
|
||||
## Component files
|
||||
|
||||
This section describes the various files used in the code generation process.
|
||||
It also contains instructions on how to modify these files to add a new
|
||||
architecture/OS or to add additional syscalls, types, or constants. Note that
|
||||
if you are using the new build system, the scripts cannot be called normally.
|
||||
if you are using the new build system, the scripts/programs cannot be called normally.
|
||||
They must be called from within the docker container.
|
||||
|
||||
### asm files
|
||||
|
@ -81,8 +81,8 @@ each GOOS/GOARCH pair.
|
|||
|
||||
### mksysnum
|
||||
|
||||
Mksysnum is a script located at `${GOOS}/mksysnum.pl` (or `mksysnum_${GOOS}.pl`
|
||||
for the old system). This script takes in a list of header files containing the
|
||||
Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go`
|
||||
for the old system). This program takes in a list of header files containing the
|
||||
syscall number declarations and parses them to produce the corresponding list of
|
||||
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
|
||||
constants.
|
||||
|
@ -92,14 +92,14 @@ new installation of the target OS (or updating the source checkouts for the
|
|||
new build system). However, depending on the OS, you make need to update the
|
||||
parsing in mksysnum.
|
||||
|
||||
### mksyscall.pl
|
||||
### mksyscall.go
|
||||
|
||||
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
|
||||
hand-written Go files which implement system calls (for unix, the specific OS,
|
||||
or the specific OS/Architecture pair respectively) that need special handling
|
||||
and list `//sys` comments giving prototypes for ones that can be generated.
|
||||
|
||||
The mksyscall.pl script takes the `//sys` and `//sysnb` comments and converts
|
||||
The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts
|
||||
them into syscalls. This requires the name of the prototype in the comment to
|
||||
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
|
||||
prototype can be exported (capitalized) or not.
|
||||
|
@ -160,7 +160,7 @@ signal numbers, and constants. Generated by `mkerrors.sh` (see above).
|
|||
### `zsyscall_${GOOS}_${GOARCH}.go`
|
||||
|
||||
A file containing all the generated syscalls for a specific GOOS and GOARCH.
|
||||
Generated by `mksyscall.pl` (see above).
|
||||
Generated by `mksyscall.go` (see above).
|
||||
|
||||
### `zsysnum_${GOOS}_${GOARCH}.go`
|
||||
|
||||
|
|
2
vendor/golang.org/x/sys/unix/mkall.sh
generated
vendored
2
vendor/golang.org/x/sys/unix/mkall.sh
generated
vendored
|
@ -170,7 +170,7 @@ openbsd_arm)
|
|||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
solaris_amd64)
|
||||
mksyscall="./mksyscall_solaris.pl"
|
||||
mksyscall="go run mksyscall_solaris.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksysnum=
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
|
|
4
vendor/golang.org/x/sys/unix/mksyscall.go
generated
vendored
4
vendor/golang.org/x/sys/unix/mksyscall.go
generated
vendored
|
@ -88,6 +88,10 @@ func parseParam(p string) Param {
|
|||
func main() {
|
||||
// Get the OS and architecture (using GOARCH_TARGET if it exists)
|
||||
goos := os.Getenv("GOOS")
|
||||
if goos == "" {
|
||||
fmt.Fprintln(os.Stderr, "GOOS not defined in environment")
|
||||
os.Exit(1)
|
||||
}
|
||||
goarch := os.Getenv("GOARCH_TARGET")
|
||||
if goarch == "" {
|
||||
goarch = os.Getenv("GOARCH")
|
||||
|
|
335
vendor/golang.org/x/sys/unix/mksyscall_solaris.go
generated
vendored
Normal file
335
vendor/golang.org/x/sys/unix/mksyscall_solaris.go
generated
vendored
Normal file
|
@ -0,0 +1,335 @@
|
|||
// Copyright 2019 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
This program reads a file containing function prototypes
|
||||
(like syscall_solaris.go) and generates system call bodies.
|
||||
The prototypes are marked by lines beginning with "//sys"
|
||||
and read like func declarations if //sys is replaced by func, but:
|
||||
* The parameter lists must give a name for each argument.
|
||||
This includes return parameters.
|
||||
* The parameter lists must give a type for each argument:
|
||||
the (x, y, z int) shorthand is not allowed.
|
||||
* If the return parameter is an error number, it must be named err.
|
||||
* If go func name needs to be different than its libc name,
|
||||
* or the function is not in libc, name could be specified
|
||||
* at the end, after "=" sign, like
|
||||
//sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
b32 = flag.Bool("b32", false, "32bit big-endian")
|
||||
l32 = flag.Bool("l32", false, "32bit little-endian")
|
||||
tags = flag.String("tags", "", "build tags")
|
||||
)
|
||||
|
||||
// cmdLine returns this programs's commandline arguments
|
||||
func cmdLine() string {
|
||||
return "go run mksyscall_solaris.go " + strings.Join(os.Args[1:], " ")
|
||||
}
|
||||
|
||||
// buildTags returns build tags
|
||||
func buildTags() string {
|
||||
return *tags
|
||||
}
|
||||
|
||||
// Param is function parameter
|
||||
type Param struct {
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
// usage prints the program usage
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: go run mksyscall_solaris.go [-b32 | -l32] [-tags x,y] [file ...]\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// parseParamList parses parameter list and returns a slice of parameters
|
||||
func parseParamList(list string) []string {
|
||||
list = strings.TrimSpace(list)
|
||||
if list == "" {
|
||||
return []string{}
|
||||
}
|
||||
return regexp.MustCompile(`\s*,\s*`).Split(list, -1)
|
||||
}
|
||||
|
||||
// parseParam splits a parameter into name and type
|
||||
func parseParam(p string) Param {
|
||||
ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p)
|
||||
if ps == nil {
|
||||
fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
|
||||
os.Exit(1)
|
||||
}
|
||||
return Param{ps[1], ps[2]}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if len(flag.Args()) <= 0 {
|
||||
fmt.Fprintf(os.Stderr, "no files to parse provided\n")
|
||||
usage()
|
||||
}
|
||||
|
||||
endianness := ""
|
||||
if *b32 {
|
||||
endianness = "big-endian"
|
||||
} else if *l32 {
|
||||
endianness = "little-endian"
|
||||
}
|
||||
|
||||
pack := ""
|
||||
text := ""
|
||||
dynimports := ""
|
||||
linknames := ""
|
||||
var vars []string
|
||||
for _, path := range flag.Args() {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
s := bufio.NewScanner(file)
|
||||
for s.Scan() {
|
||||
t := s.Text()
|
||||
t = strings.TrimSpace(t)
|
||||
t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `)
|
||||
if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" {
|
||||
pack = p[1]
|
||||
}
|
||||
nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t)
|
||||
if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Line must be of the form
|
||||
// func Open(path string, mode int, perm int) (fd int, err error)
|
||||
// Split into name, in params, out params.
|
||||
f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t)
|
||||
if f == nil {
|
||||
fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
|
||||
os.Exit(1)
|
||||
}
|
||||
funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6]
|
||||
|
||||
// Split argument lists on comma.
|
||||
in := parseParamList(inps)
|
||||
out := parseParamList(outps)
|
||||
|
||||
inps = strings.Join(in, ", ")
|
||||
outps = strings.Join(out, ", ")
|
||||
|
||||
// Try in vain to keep people from editing this file.
|
||||
// The theory is that they jump into the middle of the file
|
||||
// without reading the header.
|
||||
text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
|
||||
|
||||
// So file name.
|
||||
if modname == "" {
|
||||
modname = "libc"
|
||||
}
|
||||
|
||||
// System call name.
|
||||
if sysname == "" {
|
||||
sysname = funct
|
||||
}
|
||||
|
||||
// System call pointer variable name.
|
||||
sysvarname := fmt.Sprintf("proc%s", sysname)
|
||||
|
||||
strconvfunc := "BytePtrFromString"
|
||||
strconvtype := "*byte"
|
||||
|
||||
sysname = strings.ToLower(sysname) // All libc functions are lowercase.
|
||||
|
||||
// Runtime import of function to allow cross-platform builds.
|
||||
dynimports += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"%s.so\"\n", sysname, sysname, modname)
|
||||
// Link symbol to proc address variable.
|
||||
linknames += fmt.Sprintf("//go:linkname %s libc_%s\n", sysvarname, sysname)
|
||||
// Library proc address variable.
|
||||
vars = append(vars, sysvarname)
|
||||
|
||||
// Go function header.
|
||||
outlist := strings.Join(out, ", ")
|
||||
if outlist != "" {
|
||||
outlist = fmt.Sprintf(" (%s)", outlist)
|
||||
}
|
||||
if text != "" {
|
||||
text += "\n"
|
||||
}
|
||||
text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outlist)
|
||||
|
||||
// Check if err return available
|
||||
errvar := ""
|
||||
for _, param := range out {
|
||||
p := parseParam(param)
|
||||
if p.Type == "error" {
|
||||
errvar = p.Name
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare arguments to Syscall.
|
||||
var args []string
|
||||
n := 0
|
||||
for _, param := range in {
|
||||
p := parseParam(param)
|
||||
if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
|
||||
args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))")
|
||||
} else if p.Type == "string" && errvar != "" {
|
||||
text += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype)
|
||||
text += fmt.Sprintf("\t_p%d, %s = %s(%s)\n", n, errvar, strconvfunc, p.Name)
|
||||
text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
|
||||
args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
|
||||
n++
|
||||
} else if p.Type == "string" {
|
||||
fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
|
||||
text += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype)
|
||||
text += fmt.Sprintf("\t_p%d, _ = %s(%s)\n", n, strconvfunc, p.Name)
|
||||
args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
|
||||
n++
|
||||
} else if s := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); s != nil {
|
||||
// Convert slice into pointer, length.
|
||||
// Have to be careful not to take address of &a[0] if len == 0:
|
||||
// pass nil in that case.
|
||||
text += fmt.Sprintf("\tvar _p%d *%s\n", n, s[1])
|
||||
text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name)
|
||||
args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n), fmt.Sprintf("uintptr(len(%s))", p.Name))
|
||||
n++
|
||||
} else if p.Type == "int64" && endianness != "" {
|
||||
if endianness == "big-endian" {
|
||||
args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
|
||||
} else {
|
||||
args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
|
||||
}
|
||||
} else if p.Type == "bool" {
|
||||
text += fmt.Sprintf("\tvar _p%d uint32\n", n)
|
||||
text += fmt.Sprintf("\tif %s {\n\t\t_p%d = 1\n\t} else {\n\t\t_p%d = 0\n\t}\n", p.Name, n, n)
|
||||
args = append(args, fmt.Sprintf("uintptr(_p%d)", n))
|
||||
n++
|
||||
} else {
|
||||
args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
|
||||
}
|
||||
}
|
||||
nargs := len(args)
|
||||
|
||||
// Determine which form to use; pad args with zeros.
|
||||
asm := "sysvicall6"
|
||||
if nonblock != nil {
|
||||
asm = "rawSysvicall6"
|
||||
}
|
||||
if len(args) <= 6 {
|
||||
for len(args) < 6 {
|
||||
args = append(args, "0")
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "%s: too many arguments to system call\n", path)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Actual call.
|
||||
arglist := strings.Join(args, ", ")
|
||||
call := fmt.Sprintf("%s(uintptr(unsafe.Pointer(&%s)), %d, %s)", asm, sysvarname, nargs, arglist)
|
||||
|
||||
// Assign return values.
|
||||
body := ""
|
||||
ret := []string{"_", "_", "_"}
|
||||
doErrno := false
|
||||
for i := 0; i < len(out); i++ {
|
||||
p := parseParam(out[i])
|
||||
reg := ""
|
||||
if p.Name == "err" {
|
||||
reg = "e1"
|
||||
ret[2] = reg
|
||||
doErrno = true
|
||||
} else {
|
||||
reg = fmt.Sprintf("r%d", i)
|
||||
ret[i] = reg
|
||||
}
|
||||
if p.Type == "bool" {
|
||||
reg = fmt.Sprintf("%d != 0", reg)
|
||||
}
|
||||
if p.Type == "int64" && endianness != "" {
|
||||
// 64-bit number in r1:r0 or r0:r1.
|
||||
if i+2 > len(out) {
|
||||
fmt.Fprintf(os.Stderr, "%s: not enough registers for int64 return\n", path)
|
||||
os.Exit(1)
|
||||
}
|
||||
if endianness == "big-endian" {
|
||||
reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1)
|
||||
} else {
|
||||
reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i)
|
||||
}
|
||||
ret[i] = fmt.Sprintf("r%d", i)
|
||||
ret[i+1] = fmt.Sprintf("r%d", i+1)
|
||||
}
|
||||
if reg != "e1" {
|
||||
body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
|
||||
}
|
||||
}
|
||||
if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" {
|
||||
text += fmt.Sprintf("\t%s\n", call)
|
||||
} else {
|
||||
text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call)
|
||||
}
|
||||
text += body
|
||||
|
||||
if doErrno {
|
||||
text += "\tif e1 != 0 {\n"
|
||||
text += "\t\terr = e1\n"
|
||||
text += "\t}\n"
|
||||
}
|
||||
text += "\treturn\n"
|
||||
text += "}\n"
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
imp := ""
|
||||
if pack != "unix" {
|
||||
imp = "import \"golang.org/x/sys/unix\"\n"
|
||||
|
||||
}
|
||||
vardecls := "\t" + strings.Join(vars, ",\n\t")
|
||||
vardecls += " syscallFunc"
|
||||
fmt.Printf(srcTemplate, cmdLine(), buildTags(), pack, imp, dynimports, linknames, vardecls, text)
|
||||
}
|
||||
|
||||
const srcTemplate = `// %s
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
// +build %s
|
||||
|
||||
package %s
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
%s
|
||||
%s
|
||||
%s
|
||||
var (
|
||||
%s
|
||||
)
|
||||
|
||||
%s
|
||||
`
|
294
vendor/golang.org/x/sys/unix/mksyscall_solaris.pl
generated
vendored
294
vendor/golang.org/x/sys/unix/mksyscall_solaris.pl
generated
vendored
|
@ -1,294 +0,0 @@
|
|||
#!/usr/bin/env perl
|
||||
# Copyright 2009 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# This program reads a file containing function prototypes
|
||||
# (like syscall_solaris.go) and generates system call bodies.
|
||||
# The prototypes are marked by lines beginning with "//sys"
|
||||
# and read like func declarations if //sys is replaced by func, but:
|
||||
# * The parameter lists must give a name for each argument.
|
||||
# This includes return parameters.
|
||||
# * The parameter lists must give a type for each argument:
|
||||
# the (x, y, z int) shorthand is not allowed.
|
||||
# * If the return parameter is an error number, it must be named err.
|
||||
# * If go func name needs to be different than its libc name,
|
||||
# * or the function is not in libc, name could be specified
|
||||
# * at the end, after "=" sign, like
|
||||
# //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
|
||||
|
||||
use strict;
|
||||
|
||||
my $cmdline = "mksyscall_solaris.pl " . join(' ', @ARGV);
|
||||
my $errors = 0;
|
||||
my $_32bit = "";
|
||||
my $tags = ""; # build tags
|
||||
|
||||
binmode STDOUT;
|
||||
|
||||
if($ARGV[0] eq "-b32") {
|
||||
$_32bit = "big-endian";
|
||||
shift;
|
||||
} elsif($ARGV[0] eq "-l32") {
|
||||
$_32bit = "little-endian";
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-tags") {
|
||||
shift;
|
||||
$tags = $ARGV[0];
|
||||
shift;
|
||||
}
|
||||
|
||||
if($ARGV[0] =~ /^-/) {
|
||||
print STDERR "usage: mksyscall_solaris.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub parseparamlist($) {
|
||||
my ($list) = @_;
|
||||
$list =~ s/^\s*//;
|
||||
$list =~ s/\s*$//;
|
||||
if($list eq "") {
|
||||
return ();
|
||||
}
|
||||
return split(/\s*,\s*/, $list);
|
||||
}
|
||||
|
||||
sub parseparam($) {
|
||||
my ($p) = @_;
|
||||
if($p !~ /^(\S*) (\S*)$/) {
|
||||
print STDERR "$ARGV:$.: malformed parameter: $p\n";
|
||||
$errors = 1;
|
||||
return ("xx", "int");
|
||||
}
|
||||
return ($1, $2);
|
||||
}
|
||||
|
||||
my $package = "";
|
||||
my $text = "";
|
||||
my $dynimports = "";
|
||||
my $linknames = "";
|
||||
my @vars = ();
|
||||
while(<>) {
|
||||
chomp;
|
||||
s/\s+/ /g;
|
||||
s/^\s+//;
|
||||
s/\s+$//;
|
||||
$package = $1 if !$package && /^package (\S+)$/;
|
||||
my $nonblock = /^\/\/sysnb /;
|
||||
next if !/^\/\/sys / && !$nonblock;
|
||||
|
||||
# Line must be of the form
|
||||
# func Open(path string, mode int, perm int) (fd int, err error)
|
||||
# Split into name, in params, out params.
|
||||
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
|
||||
print STDERR "$ARGV:$.: malformed //sys declaration\n";
|
||||
$errors = 1;
|
||||
next;
|
||||
}
|
||||
my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
|
||||
|
||||
# Split argument lists on comma.
|
||||
my @in = parseparamlist($in);
|
||||
my @out = parseparamlist($out);
|
||||
|
||||
# Try in vain to keep people from editing this file.
|
||||
# The theory is that they jump into the middle of the file
|
||||
# without reading the header.
|
||||
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
|
||||
|
||||
# So file name.
|
||||
if($modname eq "") {
|
||||
$modname = "libc";
|
||||
}
|
||||
|
||||
# System call name.
|
||||
if($sysname eq "") {
|
||||
$sysname = "$func";
|
||||
}
|
||||
|
||||
# System call pointer variable name.
|
||||
my $sysvarname = "proc$sysname";
|
||||
|
||||
my $strconvfunc = "BytePtrFromString";
|
||||
my $strconvtype = "*byte";
|
||||
|
||||
$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase.
|
||||
|
||||
# Runtime import of function to allow cross-platform builds.
|
||||
$dynimports .= "//go:cgo_import_dynamic libc_${sysname} ${sysname} \"$modname.so\"\n";
|
||||
# Link symbol to proc address variable.
|
||||
$linknames .= "//go:linkname ${sysvarname} libc_${sysname}\n";
|
||||
# Library proc address variable.
|
||||
push @vars, $sysvarname;
|
||||
|
||||
# Go function header.
|
||||
$out = join(', ', @out);
|
||||
if($out ne "") {
|
||||
$out = " ($out)";
|
||||
}
|
||||
if($text ne "") {
|
||||
$text .= "\n"
|
||||
}
|
||||
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out;
|
||||
|
||||
# Check if err return available
|
||||
my $errvar = "";
|
||||
foreach my $p (@out) {
|
||||
my ($name, $type) = parseparam($p);
|
||||
if($type eq "error") {
|
||||
$errvar = $name;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# Prepare arguments to Syscall.
|
||||
my @args = ();
|
||||
my $n = 0;
|
||||
foreach my $p (@in) {
|
||||
my ($name, $type) = parseparam($p);
|
||||
if($type =~ /^\*/) {
|
||||
push @args, "uintptr(unsafe.Pointer($name))";
|
||||
} elsif($type eq "string" && $errvar ne "") {
|
||||
$text .= "\tvar _p$n $strconvtype\n";
|
||||
$text .= "\t_p$n, $errvar = $strconvfunc($name)\n";
|
||||
$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))";
|
||||
$n++;
|
||||
} elsif($type eq "string") {
|
||||
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
|
||||
$text .= "\tvar _p$n $strconvtype\n";
|
||||
$text .= "\t_p$n, _ = $strconvfunc($name)\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))";
|
||||
$n++;
|
||||
} elsif($type =~ /^\[\](.*)/) {
|
||||
# Convert slice into pointer, length.
|
||||
# Have to be careful not to take address of &a[0] if len == 0:
|
||||
# pass nil in that case.
|
||||
$text .= "\tvar _p$n *$1\n";
|
||||
$text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))";
|
||||
$n++;
|
||||
} elsif($type eq "int64" && $_32bit ne "") {
|
||||
if($_32bit eq "big-endian") {
|
||||
push @args, "uintptr($name >> 32)", "uintptr($name)";
|
||||
} else {
|
||||
push @args, "uintptr($name)", "uintptr($name >> 32)";
|
||||
}
|
||||
} elsif($type eq "bool") {
|
||||
$text .= "\tvar _p$n uint32\n";
|
||||
$text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n";
|
||||
push @args, "uintptr(_p$n)";
|
||||
$n++;
|
||||
} else {
|
||||
push @args, "uintptr($name)";
|
||||
}
|
||||
}
|
||||
my $nargs = @args;
|
||||
|
||||
# Determine which form to use; pad args with zeros.
|
||||
my $asm = "sysvicall6";
|
||||
if ($nonblock) {
|
||||
$asm = "rawSysvicall6";
|
||||
}
|
||||
if(@args <= 6) {
|
||||
while(@args < 6) {
|
||||
push @args, "0";
|
||||
}
|
||||
} else {
|
||||
print STDERR "$ARGV:$.: too many arguments to system call\n";
|
||||
}
|
||||
|
||||
# Actual call.
|
||||
my $args = join(', ', @args);
|
||||
my $call = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $args)";
|
||||
|
||||
# Assign return values.
|
||||
my $body = "";
|
||||
my $failexpr = "";
|
||||
my @ret = ("_", "_", "_");
|
||||
my @pout= ();
|
||||
my $do_errno = 0;
|
||||
for(my $i=0; $i<@out; $i++) {
|
||||
my $p = $out[$i];
|
||||
my ($name, $type) = parseparam($p);
|
||||
my $reg = "";
|
||||
if($name eq "err") {
|
||||
$reg = "e1";
|
||||
$ret[2] = $reg;
|
||||
$do_errno = 1;
|
||||
} else {
|
||||
$reg = sprintf("r%d", $i);
|
||||
$ret[$i] = $reg;
|
||||
}
|
||||
if($type eq "bool") {
|
||||
$reg = "$reg != 0";
|
||||
}
|
||||
if($type eq "int64" && $_32bit ne "") {
|
||||
# 64-bit number in r1:r0 or r0:r1.
|
||||
if($i+2 > @out) {
|
||||
print STDERR "$ARGV:$.: not enough registers for int64 return\n";
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
|
||||
} else {
|
||||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
|
||||
}
|
||||
$ret[$i] = sprintf("r%d", $i);
|
||||
$ret[$i+1] = sprintf("r%d", $i+1);
|
||||
}
|
||||
if($reg ne "e1") {
|
||||
$body .= "\t$name = $type($reg)\n";
|
||||
}
|
||||
}
|
||||
if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
|
||||
$text .= "\t$call\n";
|
||||
} else {
|
||||
$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
|
||||
}
|
||||
$text .= $body;
|
||||
|
||||
if ($do_errno) {
|
||||
$text .= "\tif e1 != 0 {\n";
|
||||
$text .= "\t\terr = e1\n";
|
||||
$text .= "\t}\n";
|
||||
}
|
||||
$text .= "\treturn\n";
|
||||
$text .= "}\n";
|
||||
}
|
||||
|
||||
if($errors) {
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
// $cmdline
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
// +build $tags
|
||||
|
||||
package $package
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
EOF
|
||||
|
||||
print "import \"golang.org/x/sys/unix\"\n" if $package ne "unix";
|
||||
|
||||
my $vardecls = "\t" . join(",\n\t", @vars);
|
||||
$vardecls .= " syscallFunc";
|
||||
|
||||
chomp($_=<<EOF);
|
||||
|
||||
$dynimports
|
||||
$linknames
|
||||
var (
|
||||
$vardecls
|
||||
)
|
||||
|
||||
$text
|
||||
EOF
|
||||
print $_;
|
||||
exit 0;
|
1
vendor/golang.org/x/sys/unix/syscall_darwin.go
generated
vendored
1
vendor/golang.org/x/sys/unix/syscall_darwin.go
generated
vendored
|
@ -416,6 +416,7 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err e
|
|||
//sys Chmod(path string, mode uint32) (err error)
|
||||
//sys Chown(path string, uid int, gid int) (err error)
|
||||
//sys Chroot(path string) (err error)
|
||||
//sys ClockGettime(clockid int32, time *Timespec) (err error)
|
||||
//sys Close(fd int) (err error)
|
||||
//sys Dup(fd int) (nfd int, err error)
|
||||
//sys Dup2(from int, to int) (err error)
|
||||
|
|
15
vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go
generated
vendored
15
vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.go
generated
vendored
|
@ -943,6 +943,21 @@ func libc_chroot_trampoline()
|
|||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func ClockGettime(clockid int32, time *Timespec) (err error) {
|
||||
_, _, e1 := syscall_syscall(funcPC(libc_clock_gettime_trampoline), uintptr(clockid), uintptr(unsafe.Pointer(time)), 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func libc_clock_gettime_trampoline()
|
||||
|
||||
//go:linkname libc_clock_gettime libc_clock_gettime
|
||||
//go:cgo_import_dynamic libc_clock_gettime clock_gettime "/usr/lib/libSystem.B.dylib"
|
||||
|
||||
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
|
||||
|
||||
func Close(fd int) (err error) {
|
||||
_, _, e1 := syscall_syscall(funcPC(libc_close_trampoline), uintptr(fd), 0, 0)
|
||||
if e1 != 0 {
|
||||
|
|
2
vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s
generated
vendored
2
vendor/golang.org/x/sys/unix/zsyscall_darwin_amd64.s
generated
vendored
|
@ -108,6 +108,8 @@ TEXT ·libc_chown_trampoline(SB),NOSPLIT,$0-0
|
|||
JMP libc_chown(SB)
|
||||
TEXT ·libc_chroot_trampoline(SB),NOSPLIT,$0-0
|
||||
JMP libc_chroot(SB)
|
||||
TEXT ·libc_clock_gettime_trampoline(SB),NOSPLIT,$0-0
|
||||
JMP libc_clock_gettime(SB)
|
||||
TEXT ·libc_close_trampoline(SB),NOSPLIT,$0-0
|
||||
JMP libc_close(SB)
|
||||
TEXT ·libc_dup_trampoline(SB),NOSPLIT,$0-0
|
||||
|
|
2
vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go
generated
vendored
2
vendor/golang.org/x/sys/unix/zsyscall_solaris_amd64.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
// mksyscall_solaris.pl -tags solaris,amd64 syscall_solaris.go syscall_solaris_amd64.go
|
||||
// go run mksyscall_solaris.go -tags solaris,amd64 syscall_solaris.go syscall_solaris_amd64.go
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
// +build solaris,amd64
|
||||
|
|
6
vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go
generated
vendored
6
vendor/golang.org/x/sys/unix/zsysnum_darwin_amd64.go
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
// go run mksysnum.go /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/include/sys/syscall.h
|
||||
// go run mksysnum.go /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk/usr/include/sys/syscall.h
|
||||
// Code generated by the command above; see README.md. DO NOT EDIT.
|
||||
|
||||
// +build amd64,darwin
|
||||
|
@ -431,6 +431,8 @@ const (
|
|||
SYS_NTP_ADJTIME = 527
|
||||
SYS_NTP_GETTIME = 528
|
||||
SYS_OS_FAULT_WITH_PAYLOAD = 529
|
||||
SYS_MAXSYSCALL = 530
|
||||
SYS_KQUEUE_WORKLOOP_CTL = 530
|
||||
SYS___MACH_BRIDGE_REMOTE_TIME = 531
|
||||
SYS_MAXSYSCALL = 532
|
||||
SYS_INVALID = 63
|
||||
)
|
||||
|
|
10
vendor/modules.txt
vendored
10
vendor/modules.txt
vendored
|
@ -90,6 +90,10 @@ github.com/goftp/server
|
|||
github.com/golang/protobuf/proto
|
||||
# github.com/google/go-querystring v1.0.0
|
||||
github.com/google/go-querystring/query
|
||||
# github.com/hashicorp/go-cleanhttp v0.5.0
|
||||
github.com/hashicorp/go-cleanhttp
|
||||
# github.com/hashicorp/go-retryablehttp v0.5.2
|
||||
github.com/hashicorp/go-retryablehttp
|
||||
# github.com/inconshreveable/mousetrap v1.0.0
|
||||
github.com/inconshreveable/mousetrap
|
||||
# github.com/jlaffaye/ftp v0.0.0-20190126081051-8019e6774408
|
||||
|
@ -142,6 +146,10 @@ github.com/stretchr/testify/assert
|
|||
github.com/stretchr/testify/require
|
||||
# github.com/t3rm1n4l/go-mega v0.0.0-20190205172012-55a226cf41da
|
||||
github.com/t3rm1n4l/go-mega
|
||||
# github.com/thinkhy/go-adb v0.0.0-20190123053734-b4b48de70418 => ../../../github.com/thinkhy/go-adb
|
||||
github.com/thinkhy/go-adb
|
||||
github.com/thinkhy/go-adb/wire
|
||||
github.com/thinkhy/go-adb/internal/errors
|
||||
# github.com/xanzy/ssh-agent v0.2.0
|
||||
github.com/xanzy/ssh-agent
|
||||
# github.com/yunify/qingstor-sdk-go v2.2.15+incompatible
|
||||
|
@ -197,7 +205,7 @@ golang.org/x/oauth2/jws
|
|||
golang.org/x/oauth2/jwt
|
||||
# golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4
|
||||
golang.org/x/sync/errgroup
|
||||
# golang.org/x/sys v0.0.0-20190204203706-41f3e6584952
|
||||
# golang.org/x/sys v0.0.0-20190213121743-983097b1a8a3
|
||||
golang.org/x/sys/unix
|
||||
golang.org/x/sys/windows
|
||||
# golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2
|
||||
|
|
Loading…
Add table
Reference in a new issue