Merge pull request #432 from thaJeztah/bump_console_v1.0.1
vendor: containerd/console v1.0.1, and remove golang.org/x/sys replacepull/436/head
commit
13533e359a
@ -0,0 +1,20 @@
|
|||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- structcheck
|
||||||
|
- varcheck
|
||||||
|
- staticcheck
|
||||||
|
- unconvert
|
||||||
|
- gofmt
|
||||||
|
- goimports
|
||||||
|
- golint
|
||||||
|
- ineffassign
|
||||||
|
- vet
|
||||||
|
- unused
|
||||||
|
- misspell
|
||||||
|
disable:
|
||||||
|
- errcheck
|
||||||
|
|
||||||
|
run:
|
||||||
|
timeout: 3m
|
||||||
|
skip-dirs:
|
||||||
|
- vendor
|
@ -1,27 +0,0 @@
|
|||||||
language: go
|
|
||||||
go:
|
|
||||||
- "1.12.x"
|
|
||||||
- "1.13.x"
|
|
||||||
|
|
||||||
go_import_path: github.com/containerd/console
|
|
||||||
|
|
||||||
env:
|
|
||||||
- GO111MODULE=on
|
|
||||||
|
|
||||||
install:
|
|
||||||
- pushd ..; go get -u github.com/vbatts/git-validation; popd
|
|
||||||
- pushd ..; go get -u github.com/kunalkushwaha/ltag; popd
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
- pushd ..; git clone https://github.com/containerd/project; popd
|
|
||||||
|
|
||||||
script:
|
|
||||||
- DCO_VERBOSITY=-q ../project/script/validate/dco
|
|
||||||
- ../project/script/validate/fileheader ../project/
|
|
||||||
- travis_wait ../project/script/validate/vendor
|
|
||||||
- go test -race
|
|
||||||
- GOOS=openbsd go build
|
|
||||||
- GOOS=openbsd go test -c
|
|
||||||
- GOOS=solaris go build
|
|
||||||
- GOOS=solaris go test -c
|
|
||||||
- GOOS=windows go test
|
|
@ -1,4 +1,4 @@
|
|||||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
|
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f h1:6Sc1XOXTulBN6imkqo6XoAXDEzoQ4/ro6xy7Vn8+rOM=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package console
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cmdTcGet = unix.TIOCGETA
|
||||||
|
cmdTcSet = unix.TIOCSETA
|
||||||
|
)
|
||||||
|
|
||||||
|
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
|
||||||
|
// unlockpt should be called before opening the slave side of a pty.
|
||||||
|
// This does not exist on NetBSD, it does not allocate controlling terminals on open
|
||||||
|
func unlockpt(f *os.File) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ptsname retrieves the name of the first available pts for the given master.
|
||||||
|
func ptsname(f *os.File) (string, error) {
|
||||||
|
ptm, err := unix.IoctlGetPtmget(int(f.Fd()), unix.TIOCPTSNAME)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return string(ptm.Sn[:bytes.IndexByte(ptm.Sn[:], 0)]), nil
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -1,159 +0,0 @@
|
|||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// mountError records an error from mount or unmount operation
|
|
||||||
type mountError struct {
|
|
||||||
op string
|
|
||||||
source, target string
|
|
||||||
flags uintptr
|
|
||||||
data string
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *mountError) Error() string {
|
|
||||||
out := e.op + " "
|
|
||||||
|
|
||||||
if e.source != "" {
|
|
||||||
out += e.source + ":" + e.target
|
|
||||||
} else {
|
|
||||||
out += e.target
|
|
||||||
}
|
|
||||||
|
|
||||||
if e.flags != uintptr(0) {
|
|
||||||
out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
|
|
||||||
}
|
|
||||||
if e.data != "" {
|
|
||||||
out += ", data: " + e.data
|
|
||||||
}
|
|
||||||
|
|
||||||
out += ": " + e.err.Error()
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cause returns the underlying cause of the error
|
|
||||||
func (e *mountError) Cause() error {
|
|
||||||
return e.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FilterFunc is a type defining a callback function
|
|
||||||
// to filter out unwanted entries. It takes a pointer
|
|
||||||
// to an Info struct (not fully populated, currently
|
|
||||||
// only Mountpoint is filled in), and returns two booleans:
|
|
||||||
// - skip: true if the entry should be skipped
|
|
||||||
// - stop: true if parsing should be stopped after the entry
|
|
||||||
type FilterFunc func(*Info) (skip, stop bool)
|
|
||||||
|
|
||||||
// PrefixFilter discards all entries whose mount points
|
|
||||||
// do not start with a prefix specified
|
|
||||||
func PrefixFilter(prefix string) FilterFunc {
|
|
||||||
return func(m *Info) (bool, bool) {
|
|
||||||
skip := !strings.HasPrefix(m.Mountpoint, prefix)
|
|
||||||
return skip, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SingleEntryFilter looks for a specific entry
|
|
||||||
func SingleEntryFilter(mp string) FilterFunc {
|
|
||||||
return func(m *Info) (bool, bool) {
|
|
||||||
if m.Mountpoint == mp {
|
|
||||||
return false, true // don't skip, stop now
|
|
||||||
}
|
|
||||||
return true, false // skip, keep going
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentsFilter returns all entries whose mount points
|
|
||||||
// can be parents of a path specified, discarding others.
|
|
||||||
// For example, given `/var/lib/docker/something`, entries
|
|
||||||
// like `/var/lib/docker`, `/var` and `/` are returned.
|
|
||||||
func ParentsFilter(path string) FilterFunc {
|
|
||||||
return func(m *Info) (bool, bool) {
|
|
||||||
skip := !strings.HasPrefix(path, m.Mountpoint)
|
|
||||||
return skip, false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetMounts retrieves a list of mounts for the current running process,
|
|
||||||
// with an optional filter applied (use nil for no filter).
|
|
||||||
func GetMounts(f FilterFunc) ([]*Info, error) {
|
|
||||||
return parseMountTable(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mounted determines if a specified mountpoint has been mounted.
|
|
||||||
// On Linux it looks at /proc/self/mountinfo.
|
|
||||||
func Mounted(mountpoint string) (bool, error) {
|
|
||||||
entries, err := GetMounts(SingleEntryFilter(mountpoint))
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return len(entries) > 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mount will mount filesystem according to the specified configuration, on the
|
|
||||||
// condition that the target path is *not* already mounted. Options must be
|
|
||||||
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
|
||||||
// flags.go for supported option flags.
|
|
||||||
func Mount(device, target, mType, options string) error {
|
|
||||||
flag, data := parseOptions(options)
|
|
||||||
if flag&REMOUNT != REMOUNT {
|
|
||||||
if mounted, err := Mounted(target); err != nil || mounted {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mount(device, target, mType, uintptr(flag), data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForceMount will mount a filesystem according to the specified configuration,
|
|
||||||
// *regardless* if the target path is not already mounted. Options must be
|
|
||||||
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
|
|
||||||
// flags.go for supported option flags.
|
|
||||||
func ForceMount(device, target, mType, options string) error {
|
|
||||||
flag, data := parseOptions(options)
|
|
||||||
return mount(device, target, mType, uintptr(flag), data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmount lazily unmounts a filesystem on supported platforms, otherwise
|
|
||||||
// does a normal unmount.
|
|
||||||
func Unmount(target string) error {
|
|
||||||
return unmount(target, mntDetach)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RecursiveUnmount unmounts the target and all mounts underneath, starting with
|
|
||||||
// the deepsest mount first.
|
|
||||||
func RecursiveUnmount(target string) error {
|
|
||||||
mounts, err := parseMountTable(PrefixFilter(target))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the deepest mount be first
|
|
||||||
sort.Slice(mounts, func(i, j int) bool {
|
|
||||||
return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint)
|
|
||||||
})
|
|
||||||
|
|
||||||
for i, m := range mounts {
|
|
||||||
logrus.Debugf("Trying to unmount %s", m.Mountpoint)
|
|
||||||
err = unmount(m.Mountpoint, mntDetach)
|
|
||||||
if err != nil {
|
|
||||||
if i == len(mounts)-1 { // last mount
|
|
||||||
if mounted, e := Mounted(m.Mountpoint); e != nil || mounted {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// This is some submount, we can ignore this error for now, the final unmount will fail if this is a real problem
|
|
||||||
logrus.WithError(err).Warnf("Failed to unmount submount %s", m.Mountpoint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugf("Unmounted %s", m.Mountpoint)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
|
||||||
|
|
||||||
func parseMountTable(f FilterFunc) ([]*Info, error) {
|
|
||||||
// Do NOT return an error!
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package system // import "github.com/docker/docker/pkg/system"
|
|
||||||
|
|
||||||
// InitLCOW does nothing since LCOW is a windows only feature
|
|
||||||
func InitLCOW(experimental bool) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContainerdRuntimeSupported returns true if the use of ContainerD runtime is supported.
|
|
||||||
func ContainerdRuntimeSupported(_ bool, _ string) bool {
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,32 +1,48 @@
|
|||||||
|
// +build windows,!no_lcow
|
||||||
|
|
||||||
package system // import "github.com/docker/docker/pkg/system"
|
package system // import "github.com/docker/docker/pkg/system"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"runtime"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Microsoft/hcsshim/osversion"
|
||||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsOSSupported determines if an operating system is supported by the host
|
var (
|
||||||
func IsOSSupported(os string) bool {
|
// lcowSupported determines if Linux Containers on Windows are supported.
|
||||||
if strings.EqualFold(runtime.GOOS, os) {
|
lcowSupported = false
|
||||||
return true
|
)
|
||||||
}
|
|
||||||
if LCOWSupported() && strings.EqualFold(os, "linux") {
|
// InitLCOW sets whether LCOW is supported or not. Requires RS5+
|
||||||
return true
|
func InitLCOW(experimental bool) {
|
||||||
|
if experimental && osversion.Build() >= osversion.RS5 {
|
||||||
|
lcowSupported = true
|
||||||
}
|
}
|
||||||
return false
|
}
|
||||||
|
|
||||||
|
func LCOWSupported() bool {
|
||||||
|
return lcowSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePlatform determines if a platform structure is valid.
|
// ValidatePlatform determines if a platform structure is valid.
|
||||||
// TODO This is a temporary windows-only function, should be replaced by
|
// TODO This is a temporary windows-only function, should be replaced by
|
||||||
// comparison of worker capabilities
|
// comparison of worker capabilities
|
||||||
func ValidatePlatform(platform specs.Platform) error {
|
func ValidatePlatform(platform specs.Platform) error {
|
||||||
if runtime.GOOS == "windows" {
|
if !IsOSSupported(platform.OS) {
|
||||||
if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) {
|
return errors.Errorf("unsupported os %s", platform.OS)
|
||||||
return errors.Errorf("unsupported os %s", platform.OS)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsOSSupported determines if an operating system is supported by the host
|
||||||
|
func IsOSSupported(os string) bool {
|
||||||
|
if strings.EqualFold("windows", os) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if LCOWSupported() && strings.EqualFold(os, "linux") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package system // import "github.com/docker/docker/pkg/system"
|
|
||||||
|
|
||||||
// LCOWSupported returns true if Linux containers on Windows are supported.
|
|
||||||
func LCOWSupported() bool {
|
|
||||||
return false
|
|
||||||
}
|
|
@ -0,0 +1,28 @@
|
|||||||
|
// +build !windows windows,no_lcow
|
||||||
|
|
||||||
|
package system // import "github.com/docker/docker/pkg/system"
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InitLCOW does nothing since LCOW is a windows only feature
|
||||||
|
func InitLCOW(_ bool) {}
|
||||||
|
|
||||||
|
// LCOWSupported returns true if Linux containers on Windows are supported.
|
||||||
|
func LCOWSupported() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePlatform determines if a platform structure is valid. This function
|
||||||
|
// is used for LCOW, and is a no-op on non-windows platforms.
|
||||||
|
func ValidatePlatform(_ specs.Platform) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOSSupported determines if an operating system is supported by the host.
|
||||||
|
func IsOSSupported(os string) bool {
|
||||||
|
return strings.EqualFold(runtime.GOOS, os)
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
package system // import "github.com/docker/docker/pkg/system"
|
|
||||||
|
|
||||||
// LCOWSupported returns true if Linux containers on Windows are supported.
|
|
||||||
func LCOWSupported() bool {
|
|
||||||
return lcowSupported
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package term // import "github.com/docker/docker/pkg/term"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ASCII list the possible supported ASCII key sequence
|
|
||||||
var ASCII = []string{
|
|
||||||
"ctrl-@",
|
|
||||||
"ctrl-a",
|
|
||||||
"ctrl-b",
|
|
||||||
"ctrl-c",
|
|
||||||
"ctrl-d",
|
|
||||||
"ctrl-e",
|
|
||||||
"ctrl-f",
|
|
||||||
"ctrl-g",
|
|
||||||
"ctrl-h",
|
|
||||||
"ctrl-i",
|
|
||||||
"ctrl-j",
|
|
||||||
"ctrl-k",
|
|
||||||
"ctrl-l",
|
|
||||||
"ctrl-m",
|
|
||||||
"ctrl-n",
|
|
||||||
"ctrl-o",
|
|
||||||
"ctrl-p",
|
|
||||||
"ctrl-q",
|
|
||||||
"ctrl-r",
|
|
||||||
"ctrl-s",
|
|
||||||
"ctrl-t",
|
|
||||||
"ctrl-u",
|
|
||||||
"ctrl-v",
|
|
||||||
"ctrl-w",
|
|
||||||
"ctrl-x",
|
|
||||||
"ctrl-y",
|
|
||||||
"ctrl-z",
|
|
||||||
"ctrl-[",
|
|
||||||
"ctrl-\\",
|
|
||||||
"ctrl-]",
|
|
||||||
"ctrl-^",
|
|
||||||
"ctrl-_",
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBytes converts a string representing a suite of key-sequence to the corresponding ASCII code.
|
|
||||||
func ToBytes(keys string) ([]byte, error) {
|
|
||||||
codes := []byte{}
|
|
||||||
next:
|
|
||||||
for _, key := range strings.Split(keys, ",") {
|
|
||||||
if len(key) != 1 {
|
|
||||||
for code, ctrl := range ASCII {
|
|
||||||
if ctrl == key {
|
|
||||||
codes = append(codes, byte(code))
|
|
||||||
continue next
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if key == "DEL" {
|
|
||||||
codes = append(codes, 127)
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("Unknown character: '%s'", key)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
codes = append(codes, key[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return codes, nil
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package term // import "github.com/docker/docker/pkg/term"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EscapeError is special error which returned by a TTY proxy reader's Read()
|
|
||||||
// method in case its detach escape sequence is read.
|
|
||||||
type EscapeError struct{}
|
|
||||||
|
|
||||||
func (EscapeError) Error() string {
|
|
||||||
return "read escape sequence"
|
|
||||||
}
|
|
||||||
|
|
||||||
// escapeProxy is used only for attaches with a TTY. It is used to proxy
|
|
||||||
// stdin keypresses from the underlying reader and look for the passed in
|
|
||||||
// escape key sequence to signal a detach.
|
|
||||||
type escapeProxy struct {
|
|
||||||
escapeKeys []byte
|
|
||||||
escapeKeyPos int
|
|
||||||
r io.Reader
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewEscapeProxy returns a new TTY proxy reader which wraps the given reader
|
|
||||||
// and detects when the specified escape keys are read, in which case the Read
|
|
||||||
// method will return an error of type EscapeError.
|
|
||||||
func NewEscapeProxy(r io.Reader, escapeKeys []byte) io.Reader {
|
|
||||||
return &escapeProxy{
|
|
||||||
escapeKeys: escapeKeys,
|
|
||||||
r: r,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *escapeProxy) Read(buf []byte) (int, error) {
|
|
||||||
nr, err := r.r.Read(buf)
|
|
||||||
|
|
||||||
if len(r.escapeKeys) == 0 {
|
|
||||||
return nr, err
|
|
||||||
}
|
|
||||||
|
|
||||||
preserve := func() {
|
|
||||||
// this preserves the original key presses in the passed in buffer
|
|
||||||
nr += r.escapeKeyPos
|
|
||||||
preserve := make([]byte, 0, r.escapeKeyPos+len(buf))
|
|
||||||
preserve = append(preserve, r.escapeKeys[:r.escapeKeyPos]...)
|
|
||||||
preserve = append(preserve, buf...)
|
|
||||||
r.escapeKeyPos = 0
|
|
||||||
copy(buf[0:nr], preserve)
|
|
||||||
}
|
|
||||||
|
|
||||||
if nr != 1 || err != nil {
|
|
||||||
if r.escapeKeyPos > 0 {
|
|
||||||
preserve()
|
|
||||||
}
|
|
||||||
return nr, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf[0] != r.escapeKeys[r.escapeKeyPos] {
|
|
||||||
if r.escapeKeyPos > 0 {
|
|
||||||
preserve()
|
|
||||||
}
|
|
||||||
return nr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if r.escapeKeyPos == len(r.escapeKeys)-1 {
|
|
||||||
return 0, EscapeError{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Looks like we've got an escape key, but we need to match again on the next
|
|
||||||
// read.
|
|
||||||
// Store the current escape key we found so we can look for the next one on
|
|
||||||
// the next read.
|
|
||||||
// Since this is an escape key, make sure we don't let the caller read it
|
|
||||||
// If later on we find that this is not the escape sequence, we'll add the
|
|
||||||
// keys back
|
|
||||||
r.escapeKeyPos++
|
|
||||||
return nr - r.escapeKeyPos, nil
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package term // import "github.com/docker/docker/pkg/term"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
func tcget(fd uintptr, p *Termios) syscall.Errno {
|
|
||||||
_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(p)))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func tcset(fd uintptr, p *Termios) syscall.Errno {
|
|
||||||
_, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(p)))
|
|
||||||
return err
|
|
||||||
}
|
|
@ -1,124 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
// Package term provides structures and helper functions to work with
|
|
||||||
// terminal (state, sizes).
|
|
||||||
package term // import "github.com/docker/docker/pkg/term"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// ErrInvalidState is returned if the state of the terminal is invalid.
|
|
||||||
ErrInvalidState = errors.New("Invalid terminal state")
|
|
||||||
)
|
|
||||||
|
|
||||||
// State represents the state of the terminal.
|
|
||||||
type State struct {
|
|
||||||
termios Termios
|
|
||||||
}
|
|
||||||
|
|
||||||
// Winsize represents the size of the terminal window.
|
|
||||||
type Winsize struct {
|
|
||||||
Height uint16
|
|
||||||
Width uint16
|
|
||||||
x uint16
|
|
||||||
y uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// StdStreams returns the standard streams (stdin, stdout, stderr).
|
|
||||||
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
|
||||||
return os.Stdin, os.Stdout, os.Stderr
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
|
|
||||||
func GetFdInfo(in interface{}) (uintptr, bool) {
|
|
||||||
var inFd uintptr
|
|
||||||
var isTerminalIn bool
|
|
||||||
if file, ok := in.(*os.File); ok {
|
|
||||||
inFd = file.Fd()
|
|
||||||
isTerminalIn = IsTerminal(inFd)
|
|
||||||
}
|
|
||||||
return inFd, isTerminalIn
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func IsTerminal(fd uintptr) bool {
|
|
||||||
var termios Termios
|
|
||||||
return tcget(fd, &termios) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestoreTerminal restores the terminal connected to the given file descriptor
|
|
||||||
// to a previous state.
|
|
||||||
func RestoreTerminal(fd uintptr, state *State) error {
|
|
||||||
if state == nil {
|
|
||||||
return ErrInvalidState
|
|
||||||
}
|
|
||||||
if err := tcset(fd, &state.termios); err != 0 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveState saves the state of the terminal connected to the given file descriptor.
|
|
||||||
func SaveState(fd uintptr) (*State, error) {
|
|
||||||
var oldState State
|
|
||||||
if err := tcget(fd, &oldState.termios); err != 0 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &oldState, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableEcho applies the specified state to the terminal connected to the file
|
|
||||||
// descriptor, with echo disabled.
|
|
||||||
func DisableEcho(fd uintptr, state *State) error {
|
|
||||||
newState := state.termios
|
|
||||||
newState.Lflag &^= unix.ECHO
|
|
||||||
|
|
||||||
if err := tcset(fd, &newState); err != 0 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
handleInterrupt(fd, state)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRawTerminal puts the terminal connected to the given file descriptor into
|
|
||||||
// raw mode and returns the previous state. On UNIX, this puts both the input
|
|
||||||
// and output into raw mode. On Windows, it only puts the input into raw mode.
|
|
||||||
func SetRawTerminal(fd uintptr) (*State, error) {
|
|
||||||
oldState, err := MakeRaw(fd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
handleInterrupt(fd, oldState)
|
|
||||||
return oldState, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRawTerminalOutput puts the output of terminal connected to the given file
|
|
||||||
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
|
|
||||||
// state. On Windows, it disables LF -> CRLF translation.
|
|
||||||
func SetRawTerminalOutput(fd uintptr) (*State, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleInterrupt(fd uintptr, state *State) {
|
|
||||||
sigchan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigchan, os.Interrupt)
|
|
||||||
go func() {
|
|
||||||
for range sigchan {
|
|
||||||
// quit cleanly and the new terminal item is on a new line
|
|
||||||
fmt.Println()
|
|
||||||
signal.Stop(sigchan)
|
|
||||||
close(sigchan)
|
|
||||||
RestoreTerminal(fd, state)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
@ -1,221 +0,0 @@
|
|||||||
package term // import "github.com/docker/docker/pkg/term"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall" // used for STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE
|
|
||||||
|
|
||||||
"github.com/Azure/go-ansiterm/winterm"
|
|
||||||
windowsconsole "github.com/docker/docker/pkg/term/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
// State holds the console mode for the terminal.
|
|
||||||
type State struct {
|
|
||||||
mode uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Winsize is used for window size.
|
|
||||||
type Winsize struct {
|
|
||||||
Height uint16
|
|
||||||
Width uint16
|
|
||||||
}
|
|
||||||
|
|
||||||
// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
|
|
||||||
var vtInputSupported bool
|
|
||||||
|
|
||||||
// StdStreams returns the standard streams (stdin, stdout, stderr).
|
|
||||||
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
|
|
||||||
// Turn on VT handling on all std handles, if possible. This might
|
|
||||||
// fail, in which case we will fall back to terminal emulation.
|
|
||||||
var emulateStdin, emulateStdout, emulateStderr bool
|
|
||||||
fd := os.Stdin.Fd()
|
|
||||||
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
|
||||||
// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
|
|
||||||
if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
|
|
||||||
emulateStdin = true
|
|
||||||
} else {
|
|
||||||
vtInputSupported = true
|
|
||||||
}
|
|
||||||
// Unconditionally set the console mode back even on failure because SetConsoleMode
|
|
||||||
// remembers invalid bits on input handles.
|
|
||||||
winterm.SetConsoleMode(fd, mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = os.Stdout.Fd()
|
|
||||||
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
|
||||||
// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
|
|
||||||
if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
|
|
||||||
emulateStdout = true
|
|
||||||
} else {
|
|
||||||
winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = os.Stderr.Fd()
|
|
||||||
if mode, err := winterm.GetConsoleMode(fd); err == nil {
|
|
||||||
// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
|
|
||||||
if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
|
|
||||||
emulateStderr = true
|
|
||||||
} else {
|
|
||||||
winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and
|
|
||||||
// STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as
|
|
||||||
// go-ansiterm hasn't switch to x/sys/windows.
|
|
||||||
// TODO: switch back to x/sys/windows once go-ansiterm has switched
|
|
||||||
if emulateStdin {
|
|
||||||
stdIn = windowsconsole.NewAnsiReader(syscall.STD_INPUT_HANDLE)
|
|
||||||
} else {
|
|
||||||
stdIn = os.Stdin
|
|
||||||
}
|
|
||||||
|
|
||||||
if emulateStdout {
|
|
||||||
stdOut = windowsconsole.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE)
|
|
||||||
} else {
|
|
||||||
stdOut = os.Stdout
|
|
||||||
}
|
|
||||||
|
|
||||||
if emulateStderr {
|
|
||||||
stdErr = windowsconsole.NewAnsiWriter(syscall.STD_ERROR_HANDLE)
|
|
||||||
} else {
|
|
||||||
stdErr = os.Stderr
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
|
|
||||||
func GetFdInfo(in interface{}) (uintptr, bool) {
|
|
||||||
return windowsconsole.GetHandleInfo(in)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWinsize returns the window size based on the specified file descriptor.
|
|
||||||
func GetWinsize(fd uintptr) (*Winsize, error) {
|
|
||||||
info, err := winterm.GetConsoleScreenBufferInfo(fd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
winsize := &Winsize{
|
|
||||||
Width: uint16(info.Window.Right - info.Window.Left + 1),
|
|
||||||
Height: uint16(info.Window.Bottom - info.Window.Top + 1),
|
|
||||||
}
|
|
||||||
|
|
||||||
return winsize, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
|
||||||
func IsTerminal(fd uintptr) bool {
|
|
||||||
return windowsconsole.IsConsole(fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RestoreTerminal restores the terminal connected to the given file descriptor
|
|
||||||
// to a previous state.
|
|
||||||
func RestoreTerminal(fd uintptr, state *State) error {
|
|
||||||
return winterm.SetConsoleMode(fd, state.mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SaveState saves the state of the terminal connected to the given file descriptor.
|
|
||||||
func SaveState(fd uintptr) (*State, error) {
|
|
||||||
mode, e := winterm.GetConsoleMode(fd)
|
|
||||||
if e != nil {
|
|
||||||
return nil, e
|
|
||||||
}
|
|
||||||
|
|
||||||
return &State{mode: mode}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableEcho disables echo for the terminal connected to the given file descriptor.
|
|
||||||
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
|
|
||||||
func DisableEcho(fd uintptr, state *State) error {
|
|
||||||
mode := state.mode
|
|
||||||
mode &^= winterm.ENABLE_ECHO_INPUT
|
|
||||||
mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
|
|
||||||
err := winterm.SetConsoleMode(fd, mode)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register an interrupt handler to catch and restore prior state
|
|
||||||
restoreAtInterrupt(fd, state)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRawTerminal puts the terminal connected to the given file descriptor into
|
|
||||||
// raw mode and returns the previous state. On UNIX, this puts both the input
|
|
||||||
// and output into raw mode. On Windows, it only puts the input into raw mode.
|
|
||||||
func SetRawTerminal(fd uintptr) (*State, error) {
|
|
||||||
state, err := MakeRaw(fd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register an interrupt handler to catch and restore prior state
|
|
||||||
restoreAtInterrupt(fd, state)
|
|
||||||
return state, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetRawTerminalOutput puts the output of terminal connected to the given file
|
|
||||||
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
|
|
||||||
// state. On Windows, it disables LF -> CRLF translation.
|
|
||||||
func SetRawTerminalOutput(fd uintptr) (*State, error) {
|
|
||||||
state, err := SaveState(fd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
|
|
||||||
// version of Windows.
|
|
||||||
winterm.SetConsoleMode(fd, state.mode|winterm.DISABLE_NEWLINE_AUTO_RETURN)
|
|
||||||
return state, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
|
|
||||||
// mode and returns the previous state of the terminal so that it can be restored.
|
|
||||||
func MakeRaw(fd uintptr) (*State, error) {
|
|
||||||
state, err := SaveState(fd)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mode := state.mode
|
|
||||||
|
|
||||||
// See
|
|
||||||
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
|
|
||||||
// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
|
|
||||||
|
|
||||||
// Disable these modes
|
|
||||||
mode &^= winterm.ENABLE_ECHO_INPUT
|
|
||||||
mode &^= winterm.ENABLE_LINE_INPUT
|
|
||||||
mode &^= winterm.ENABLE_MOUSE_INPUT
|
|
||||||
mode &^= winterm.ENABLE_WINDOW_INPUT
|
|
||||||
mode &^= winterm.ENABLE_PROCESSED_INPUT
|
|
||||||
|
|
||||||
// Enable these modes
|
|
||||||
mode |= winterm.ENABLE_EXTENDED_FLAGS
|
|
||||||
mode |= winterm.ENABLE_INSERT_MODE
|
|
||||||
mode |= winterm.ENABLE_QUICK_EDIT_MODE
|
|
||||||
if vtInputSupported {
|
|
||||||
mode |= winterm.ENABLE_VIRTUAL_TERMINAL_INPUT
|
|
||||||
}
|
|
||||||
|
|
||||||
err = winterm.SetConsoleMode(fd, mode)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return state, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func restoreAtInterrupt(fd uintptr, state *State) {
|
|
||||||
sigchan := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigchan, os.Interrupt)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
_ = <-sigchan
|
|
||||||
RestoreTerminal(fd, state)
|
|
||||||
os.Exit(0)
|
|
||||||
}()
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
// +build darwin freebsd openbsd netbsd
|
|
||||||
|
|
||||||
package term // import "github.com/docker/docker/pkg/term"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
getTermios = unix.TIOCGETA
|
|
||||||
setTermios = unix.TIOCSETA
|
|
||||||
)
|
|
||||||
|
|
||||||
// Termios is the Unix API for terminal I/O.
|
|
||||||
type Termios unix.Termios
|
|
||||||
|
|
||||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
|
||||||
// mode and returns the previous state of the terminal so that it can be
|
|
||||||
// restored.
|
|
||||||
func MakeRaw(fd uintptr) (*State, error) {
|
|
||||||
var oldState State
|
|
||||||
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
newState := oldState.termios
|
|
||||||
newState.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
|
|
||||||
newState.Oflag &^= unix.OPOST
|
|
||||||
newState.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
|
|
||||||
newState.Cflag &^= (unix.CSIZE | unix.PARENB)
|
|
||||||
newState.Cflag |= unix.CS8
|
|
||||||
newState.Cc[unix.VMIN] = 1
|
|
||||||
newState.Cc[unix.VTIME] = 0
|
|
||||||
|
|
||||||
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &oldState, nil
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package term // import "github.com/docker/docker/pkg/term"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
getTermios = unix.TCGETS
|
|
||||||
setTermios = unix.TCSETS
|
|
||||||
)
|
|
||||||
|
|
||||||
// Termios is the Unix API for terminal I/O.
|
|
||||||
type Termios unix.Termios
|
|
||||||
|
|
||||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
|
||||||
// mode and returns the previous state of the terminal so that it can be
|
|
||||||
// restored.
|
|
||||||
func MakeRaw(fd uintptr) (*State, error) {
|
|
||||||
termios, err := unix.IoctlGetTermios(int(fd), getTermios)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldState State
|
|
||||||
oldState.termios = Termios(*termios)
|
|
||||||
|
|
||||||
termios.Iflag &^= (unix.IGNBRK | unix.BRKINT | unix.PARMRK | unix.ISTRIP | unix.INLCR | unix.IGNCR | unix.ICRNL | unix.IXON)
|
|
||||||
termios.Oflag &^= unix.OPOST
|
|
||||||
termios.Lflag &^= (unix.ECHO | unix.ECHONL | unix.ICANON | unix.ISIG | unix.IEXTEN)
|
|
||||||
termios.Cflag &^= (unix.CSIZE | unix.PARENB)
|
|
||||||
termios.Cflag |= unix.CS8
|
|
||||||
termios.Cc[unix.VMIN] = 1
|
|
||||||
termios.Cc[unix.VTIME] = 0
|
|
||||||
|
|
||||||
if err := unix.IoctlSetTermios(int(fd), setTermios, termios); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &oldState, nil
|
|
||||||
}
|
|
@ -1,263 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
ansiterm "github.com/Azure/go-ansiterm"
|
|
||||||
"github.com/Azure/go-ansiterm/winterm"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
escapeSequence = ansiterm.KEY_ESC_CSI
|
|
||||||
)
|
|
||||||
|
|
||||||
// ansiReader wraps a standard input file (e.g., os.Stdin) providing ANSI sequence translation.
|
|
||||||
type ansiReader struct {
|
|
||||||
file *os.File
|
|
||||||
fd uintptr
|
|
||||||
buffer []byte
|
|
||||||
cbBuffer int
|
|
||||||
command []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAnsiReader returns an io.ReadCloser that provides VT100 terminal emulation on top of a
|
|
||||||
// Windows console input handle.
|
|
||||||
func NewAnsiReader(nFile int) io.ReadCloser {
|
|
||||||
initLogger()
|
|
||||||
file, fd := winterm.GetStdFile(nFile)
|
|
||||||
return &ansiReader{
|
|
||||||
file: file,
|
|
||||||
fd: fd,
|
|
||||||
command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
|
|
||||||
buffer: make([]byte, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the wrapped file.
|
|
||||||
func (ar *ansiReader) Close() (err error) {
|
|
||||||
return ar.file.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fd returns the file descriptor of the wrapped file.
|
|
||||||
func (ar *ansiReader) Fd() uintptr {
|
|
||||||
return ar.fd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read reads up to len(p) bytes of translated input events into p.
|
|
||||||
func (ar *ansiReader) Read(p []byte) (int, error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Previously read bytes exist, read as much as we can and return
|
|
||||||
if len(ar.buffer) > 0 {
|
|
||||||
logger.Debugf("Reading previously cached bytes")
|
|
||||||
|
|
||||||
originalLength := len(ar.buffer)
|
|
||||||
copiedLength := copy(p, ar.buffer)
|
|
||||||
|
|
||||||
if copiedLength == originalLength {
|
|
||||||
ar.buffer = make([]byte, 0, len(p))
|
|
||||||
} else {
|
|
||||||
ar.buffer = ar.buffer[copiedLength:]
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf("Read from cache p[%d]: % x", copiedLength, p)
|
|
||||||
return copiedLength, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read and translate key events
|
|
||||||
events, err := readInputEvents(ar.fd, len(p))
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
} else if len(events) == 0 {
|
|
||||||
logger.Debug("No input events detected")
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
keyBytes := translateKeyEvents(events, []byte(escapeSequence))
|
|
||||||
|
|
||||||
// Save excess bytes and right-size keyBytes
|
|
||||||
if len(keyBytes) > len(p) {
|
|
||||||
logger.Debugf("Received %d keyBytes, only room for %d bytes", len(keyBytes), len(p))
|
|
||||||
ar.buffer = keyBytes[len(p):]
|
|
||||||
keyBytes = keyBytes[:len(p)]
|
|
||||||
} else if len(keyBytes) == 0 {
|
|
||||||
logger.Debug("No key bytes returned from the translator")
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
copiedLength := copy(p, keyBytes)
|
|
||||||
if copiedLength != len(keyBytes) {
|
|
||||||
return 0, errors.New("unexpected copy length encountered")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Debugf("Read p[%d]: % x", copiedLength, p)
|
|
||||||
logger.Debugf("Read keyBytes[%d]: % x", copiedLength, keyBytes)
|
|
||||||
return copiedLength, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// readInputEvents polls until at least one event is available.
|
|
||||||
func readInputEvents(fd uintptr, maxBytes int) ([]winterm.INPUT_RECORD, error) {
|
|
||||||
// Determine the maximum number of records to retrieve
|
|
||||||
// -- Cast around the type system to obtain the size of a single INPUT_RECORD.
|
|
||||||
// unsafe.Sizeof requires an expression vs. a type-reference; the casting
|
|
||||||
// tricks the type system into believing it has such an expression.
|
|
||||||
recordSize := int(unsafe.Sizeof(*((*winterm.INPUT_RECORD)(unsafe.Pointer(&maxBytes)))))
|
|
||||||
countRecords := maxBytes / recordSize
|
|
||||||
if countRecords > ansiterm.MAX_INPUT_EVENTS {
|
|
||||||
countRecords = ansiterm.MAX_INPUT_EVENTS
|
|
||||||
} else if countRecords == 0 {
|
|
||||||
countRecords = 1
|
|
||||||
}
|
|
||||||
logger.Debugf("[windows] readInputEvents: Reading %v records (buffer size %v, record size %v)", countRecords, maxBytes, recordSize)
|
|
||||||
|
|
||||||
// Wait for and read input events
|
|
||||||
events := make([]winterm.INPUT_RECORD, countRecords)
|
|
||||||
nEvents := uint32(0)
|
|
||||||
eventsExist, err := winterm.WaitForSingleObject(fd, winterm.WAIT_INFINITE)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if eventsExist {
|
|
||||||
err = winterm.ReadConsoleInput(fd, events, &nEvents)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return a slice restricted to the number of returned records
|
|
||||||
logger.Debugf("[windows] readInputEvents: Read %v events", nEvents)
|
|
||||||
return events[:nEvents], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// KeyEvent Translation Helpers
|
|
||||||
|
|
||||||
var arrowKeyMapPrefix = map[uint16]string{
|
|
||||||
winterm.VK_UP: "%s%sA",
|
|
||||||
winterm.VK_DOWN: "%s%sB",
|
|
||||||
winterm.VK_RIGHT: "%s%sC",
|
|
||||||
winterm.VK_LEFT: "%s%sD",
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyMapPrefix = map[uint16]string{
|
|
||||||
winterm.VK_UP: "\x1B[%sA",
|
|
||||||
winterm.VK_DOWN: "\x1B[%sB",
|
|
||||||
winterm.VK_RIGHT: "\x1B[%sC",
|
|
||||||
winterm.VK_LEFT: "\x1B[%sD",
|
|
||||||
winterm.VK_HOME: "\x1B[1%s~", // showkey shows ^[[1
|
|
||||||
winterm.VK_END: "\x1B[4%s~", // showkey shows ^[[4
|
|
||||||
winterm.VK_INSERT: "\x1B[2%s~",
|
|
||||||
winterm.VK_DELETE: "\x1B[3%s~",
|
|
||||||
winterm.VK_PRIOR: "\x1B[5%s~",
|
|
||||||
winterm.VK_NEXT: "\x1B[6%s~",
|
|
||||||
winterm.VK_F1: "",
|
|
||||||
winterm.VK_F2: "",
|
|
||||||
winterm.VK_F3: "\x1B[13%s~",
|
|
||||||
winterm.VK_F4: "\x1B[14%s~",
|
|
||||||
winterm.VK_F5: "\x1B[15%s~",
|
|
||||||
winterm.VK_F6: "\x1B[17%s~",
|
|
||||||
winterm.VK_F7: "\x1B[18%s~",
|
|
||||||
winterm.VK_F8: "\x1B[19%s~",
|
|
||||||
winterm.VK_F9: "\x1B[20%s~",
|
|
||||||
winterm.VK_F10: "\x1B[21%s~",
|
|
||||||
winterm.VK_F11: "\x1B[23%s~",
|
|
||||||
winterm.VK_F12: "\x1B[24%s~",
|
|
||||||
}
|
|
||||||
|
|
||||||
// translateKeyEvents converts the input events into the appropriate ANSI string.
|
|
||||||
func translateKeyEvents(events []winterm.INPUT_RECORD, escapeSequence []byte) []byte {
|
|
||||||
var buffer bytes.Buffer
|
|
||||||
for _, event := range events {
|
|
||||||
if event.EventType == winterm.KEY_EVENT && event.KeyEvent.KeyDown != 0 {
|
|
||||||
buffer.WriteString(keyToString(&event.KeyEvent, escapeSequence))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// keyToString maps the given input event record to the corresponding string.
|
|
||||||
func keyToString(keyEvent *winterm.KEY_EVENT_RECORD, escapeSequence []byte) string {
|
|
||||||
if keyEvent.UnicodeChar == 0 {
|
|
||||||
return formatVirtualKey(keyEvent.VirtualKeyCode, keyEvent.ControlKeyState, escapeSequence)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, alt, control := getControlKeys(keyEvent.ControlKeyState)
|
|
||||||
if control {
|
|
||||||
// TODO(azlinux): Implement following control sequences
|
|
||||||
// <Ctrl>-D Signals the end of input from the keyboard; also exits current shell.
|
|
||||||
// <Ctrl>-H Deletes the first character to the left of the cursor. Also called the ERASE key.
|
|
||||||
// <Ctrl>-Q Restarts printing after it has been stopped with <Ctrl>-s.
|
|
||||||
// <Ctrl>-S Suspends printing on the screen (does not stop the program).
|
|
||||||
// <Ctrl>-U Deletes all characters on the current line. Also called the KILL key.
|
|
||||||
// <Ctrl>-E Quits current command and creates a core
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// <Alt>+Key generates ESC N Key
|
|
||||||
if !control && alt {
|
|
||||||
return ansiterm.KEY_ESC_N + strings.ToLower(string(keyEvent.UnicodeChar))
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(keyEvent.UnicodeChar)
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatVirtualKey converts a virtual key (e.g., up arrow) into the appropriate ANSI string.
|
|
||||||
func formatVirtualKey(key uint16, controlState uint32, escapeSequence []byte) string {
|
|
||||||
shift, alt, control := getControlKeys(controlState)
|
|
||||||
modifier := getControlKeysModifier(shift, alt, control)
|
|
||||||
|
|
||||||
if format, ok := arrowKeyMapPrefix[key]; ok {
|
|
||||||
return fmt.Sprintf(format, escapeSequence, modifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
if format, ok := keyMapPrefix[key]; ok {
|
|
||||||
return fmt.Sprintf(format, modifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// getControlKeys extracts the shift, alt, and ctrl key states.
|
|
||||||
func getControlKeys(controlState uint32) (shift, alt, control bool) {
|
|
||||||
shift = 0 != (controlState & winterm.SHIFT_PRESSED)
|
|
||||||
alt = 0 != (controlState & (winterm.LEFT_ALT_PRESSED | winterm.RIGHT_ALT_PRESSED))
|
|
||||||
control = 0 != (controlState & (winterm.LEFT_CTRL_PRESSED | winterm.RIGHT_CTRL_PRESSED))
|
|
||||||
return shift, alt, control
|
|
||||||
}
|
|
||||||
|
|
||||||
// getControlKeysModifier returns the ANSI modifier for the given combination of control keys.
|
|
||||||
func getControlKeysModifier(shift, alt, control bool) string {
|
|
||||||
if shift && alt && control {
|
|
||||||
return ansiterm.KEY_CONTROL_PARAM_8
|
|
||||||
}
|
|
||||||
if alt && control {
|
|
||||||
return ansiterm.KEY_CONTROL_PARAM_7
|
|
||||||
}
|
|
||||||
if shift && control {
|
|
||||||
return ansiterm.KEY_CONTROL_PARAM_6
|
|
||||||
}
|
|
||||||
if control {
|
|
||||||
return ansiterm.KEY_CONTROL_PARAM_5
|
|
||||||
}
|
|
||||||
if shift && alt {
|
|
||||||
return ansiterm.KEY_CONTROL_PARAM_4
|
|
||||||
}
|
|
||||||
if alt {
|
|
||||||
return ansiterm.KEY_CONTROL_PARAM_3
|
|
||||||
}
|
|
||||||
if shift {
|
|
||||||
return ansiterm.KEY_CONTROL_PARAM_2
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
ansiterm "github.com/Azure/go-ansiterm"
|
|
||||||
"github.com/Azure/go-ansiterm/winterm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ansiWriter wraps a standard output file (e.g., os.Stdout) providing ANSI sequence translation.
|
|
||||||
type ansiWriter struct {
|
|
||||||
file *os.File
|
|
||||||
fd uintptr
|
|
||||||
infoReset *winterm.CONSOLE_SCREEN_BUFFER_INFO
|
|
||||||
command []byte
|
|
||||||
escapeSequence []byte
|
|
||||||
inAnsiSequence bool
|
|
||||||
parser *ansiterm.AnsiParser
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAnsiWriter returns an io.Writer that provides VT100 terminal emulation on top of a
|
|
||||||
// Windows console output handle.
|
|
||||||
func NewAnsiWriter(nFile int) io.Writer {
|
|
||||||
initLogger()
|
|
||||||
file, fd := winterm.GetStdFile(nFile)
|
|
||||||
info, err := winterm.GetConsoleScreenBufferInfo(fd)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
parser := ansiterm.CreateParser("Ground", winterm.CreateWinEventHandler(fd, file))
|
|
||||||
logger.Infof("newAnsiWriter: parser %p", parser)
|
|
||||||
|
|
||||||
aw := &ansiWriter{
|
|
||||||
file: file,
|
|
||||||
fd: fd,
|
|
||||||
infoReset: info,
|
|
||||||
command: make([]byte, 0, ansiterm.ANSI_MAX_CMD_LENGTH),
|
|
||||||
escapeSequence: []byte(ansiterm.KEY_ESC_CSI),
|
|
||||||
parser: parser,
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("newAnsiWriter: aw.parser %p", aw.parser)
|
|
||||||
logger.Infof("newAnsiWriter: %v", aw)
|
|
||||||
return aw
|
|
||||||
}
|
|
||||||
|
|
||||||
func (aw *ansiWriter) Fd() uintptr {
|
|
||||||
return aw.fd
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write writes len(p) bytes from p to the underlying data stream.
|
|
||||||
func (aw *ansiWriter) Write(p []byte) (total int, err error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("Write: % x", p)
|
|
||||||
logger.Infof("Write: %s", string(p))
|
|
||||||
return aw.parser.Parse(p)
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
|
|
||||||
package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/Azure/go-ansiterm/winterm"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetHandleInfo returns file descriptor and bool indicating whether the file is a console.
|
|
||||||
func GetHandleInfo(in interface{}) (uintptr, bool) {
|
|
||||||
switch t := in.(type) {
|
|
||||||
case *ansiReader:
|
|
||||||
return t.Fd(), true
|
|
||||||
case *ansiWriter:
|
|
||||||
return t.Fd(), true
|
|
||||||
}
|
|
||||||
|
|
||||||
var inFd uintptr
|
|
||||||
var isTerminal bool
|
|
||||||
|
|
||||||
if file, ok := in.(*os.File); ok {
|
|
||||||
inFd = file.Fd()
|
|
||||||
isTerminal = IsConsole(inFd)
|
|
||||||
}
|
|
||||||
return inFd, isTerminal
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsConsole returns true if the given file descriptor is a Windows Console.
|
|
||||||
// The code assumes that GetConsoleMode will return an error for file descriptors that are not a console.
|
|
||||||
func IsConsole(fd uintptr) bool {
|
|
||||||
_, e := winterm.GetConsoleMode(fd)
|
|
||||||
return e == nil
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
// +build windows
|
|
||||||
// These files implement ANSI-aware input and output streams for use by the Docker Windows client.
|
|
||||||
// When asked for the set of standard streams (e.g., stdin, stdout, stderr), the code will create
|
|
||||||
// and return pseudo-streams that convert ANSI sequences to / from Windows Console API calls.
|
|
||||||
|
|
||||||
package windowsconsole // import "github.com/docker/docker/pkg/term/windows"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
ansiterm "github.com/Azure/go-ansiterm"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var logger *logrus.Logger
|
|
||||||
var initOnce sync.Once
|
|
||||||
|
|
||||||
func initLogger() {
|
|
||||||
initOnce.Do(func() {
|
|
||||||
logFile := ioutil.Discard
|
|
||||||
|
|
||||||
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" {
|
|
||||||
logFile, _ = os.Create("ansiReaderWriter.log")
|
|
||||||
}
|
|
||||||
|
|
||||||
logger = &logrus.Logger{
|
|
||||||
Out: logFile,
|
|
||||||
Formatter: new(logrus.TextFormatter),
|
|
||||||
Level: logrus.DebugLevel,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
// +build !windows
|
|
||||||
|
|
||||||
package term // import "github.com/docker/docker/pkg/term"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetWinsize returns the window size based on the specified file descriptor.
|
|
||||||
func GetWinsize(fd uintptr) (*Winsize, error) {
|
|
||||||
uws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
|
|
||||||
ws := &Winsize{Height: uws.Row, Width: uws.Col, x: uws.Xpixel, y: uws.Ypixel}
|
|
||||||
return ws, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetWinsize tries to set the specified window size for the specified file descriptor.
|
|
||||||
func SetWinsize(fd uintptr, ws *Winsize) error {
|
|
||||||
uws := &unix.Winsize{Row: ws.Height, Col: ws.Width, Xpixel: ws.x, Ypixel: ws.y}
|
|
||||||
return unix.IoctlSetWinsize(int(fd), unix.TIOCSWINSZ, uws)
|
|
||||||
}
|
|
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
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.
|
@ -1,4 +1,4 @@
|
|||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mount
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -1,4 +1,4 @@
|
|||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mount
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
@ -0,0 +1,8 @@
|
|||||||
|
module github.com/moby/sys/mount
|
||||||
|
|
||||||
|
go 1.14
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/moby/sys/mountinfo v0.1.0
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae
|
||||||
|
)
|
@ -0,0 +1,4 @@
|
|||||||
|
github.com/moby/sys/mountinfo v0.1.0 h1:r8vMRbMAFEAfiNptYVokP+nfxPJzvRuia5e2vzXtENo=
|
||||||
|
github.com/moby/sys/mountinfo v0.1.0/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||||
|
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
@ -0,0 +1,98 @@
|
|||||||
|
// +build go1.13
|
||||||
|
|
||||||
|
package mount
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/moby/sys/mountinfo"
|
||||||
|
)
|
||||||
|
|
||||||
|
// mountError records an error from mount or unmount operation
|
||||||
|
type mountError struct {
|
||||||
|
op string
|
||||||
|
source, target string
|
||||||
|
flags uintptr
|
||||||
|
data string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *mountError) Error() string {
|
||||||
|
out := e.op + " "
|
||||||
|
|
||||||
|
if e.source != "" {
|
||||||
|
out += e.source + ":" + e.target
|
||||||
|
} else {
|
||||||
|
out += e.target
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.flags != uintptr(0) {
|
||||||
|
out += ", flags: 0x" + strconv.FormatUint(uint64(e.flags), 16)
|
||||||
|
}
|
||||||
|
if e.data != "" {
|
||||||
|
out += ", data: " + e.data
|
||||||
|
}
|
||||||
|
|
||||||
|
out += ": " + e.err.Error()
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cause returns the underlying cause of the error.
|
||||||
|
// This is a convention used in github.com/pkg/errors
|
||||||
|
func (e *mountError) Cause() error {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unwrap returns the underlying error.
|
||||||
|
// This is a convention used in golang 1.13+
|
||||||
|
func (e *mountError) Unwrap() error {
|
||||||
|
return e.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount will mount filesystem according to the specified configuration.
|
||||||
|
// Options must be specified like the mount or fstab unix commands:
|
||||||
|
// "opt1=val1,opt2=val2". See flags.go for supported option flags.
|
||||||
|
func Mount(device, target, mType, options string) error {
|
||||||
|
flag, data := parseOptions(options)
|
||||||
|
return mount(device, target, mType, uintptr(flag), data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmount lazily unmounts a filesystem on supported platforms, otherwise
|
||||||
|
// does a normal unmount.
|
||||||
|
func Unmount(target string) error {
|
||||||
|
return unmount(target, mntDetach)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecursiveUnmount unmounts the target and all mounts underneath, starting with
|
||||||
|
// the deepsest mount first.
|
||||||
|
func RecursiveUnmount(target string) error {
|
||||||
|
mounts, err := mountinfo.GetMounts(mountinfo.PrefixFilter(target))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the deepest mount be first
|
||||||
|
sort.Slice(mounts, func(i, j int) bool {
|
||||||
|
return len(mounts[i].Mountpoint) > len(mounts[j].Mountpoint)
|
||||||
|
})
|
||||||
|
|
||||||
|
var suberr error
|
||||||
|
for i, m := range mounts {
|
||||||
|
err = unmount(m.Mountpoint, mntDetach)
|
||||||
|
if err != nil {
|
||||||
|
if i == len(mounts)-1 { // last mount
|
||||||
|
return fmt.Errorf("%w (possible cause: %s)", err, suberr)
|
||||||
|
}
|
||||||
|
// This is a submount, we can ignore the error for now,
|
||||||
|
// the final unmount will fail if this is a real problem.
|
||||||
|
// With that in mind, the _first_ failed unmount error
|
||||||
|
// might be the real error cause, so let's keep it.
|
||||||
|
if suberr == nil {
|
||||||
|
suberr = err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mount
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#include <errno.h>
|
#include <errno.h>
|
@ -1,4 +1,4 @@
|
|||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mount
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
@ -1,6 +1,6 @@
|
|||||||
// +build !linux,!freebsd freebsd,!cgo
|
// +build !linux,!freebsd freebsd,!cgo
|
||||||
|
|
||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mount
|
||||||
|
|
||||||
func mount(device, target, mType string, flag uintptr, data string) error {
|
func mount(device, target, mType string, flag uintptr, data string) error {
|
||||||
panic("Not implemented")
|
panic("Not implemented")
|
@ -1,6 +1,6 @@
|
|||||||
// +build !windows
|
// +build !windows
|
||||||
|
|
||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mount
|
||||||
|
|
||||||
import "golang.org/x/sys/unix"
|
import "golang.org/x/sys/unix"
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mount
|
||||||
|
|
||||||
func unmount(target string, flag int) error {
|
func unmount(target string, flag int) error {
|
||||||
panic("Not implemented")
|
panic("Not implemented")
|
@ -0,0 +1,202 @@
|
|||||||
|
|
||||||
|
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.
|
@ -0,0 +1,3 @@
|
|||||||
|
module github.com/moby/sys/mountinfo
|
||||||
|
|
||||||
|
go 1.14
|
@ -1,4 +1,31 @@
|
|||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mountinfo
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
// GetMounts retrieves a list of mounts for the current running process,
|
||||||
|
// with an optional filter applied (use nil for no filter).
|
||||||
|
func GetMounts(f FilterFunc) ([]*Info, error) {
|
||||||
|
return parseMountTable(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMountsFromReader retrieves a list of mounts from the
|
||||||
|
// reader provided, with an optional filter applied (use nil
|
||||||
|
// for no filter). This can be useful in tests or benchmarks
|
||||||
|
// that provide a fake mountinfo data.
|
||||||
|
func GetMountsFromReader(reader io.Reader, f FilterFunc) ([]*Info, error) {
|
||||||
|
return parseInfoFile(reader, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mounted determines if a specified mountpoint has been mounted.
|
||||||
|
// On Linux it looks at /proc/self/mountinfo.
|
||||||
|
func Mounted(mountpoint string) (bool, error) {
|
||||||
|
entries, err := GetMounts(SingleEntryFilter(mountpoint))
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(entries) > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Info reveals information about a particular mounted filesystem. This
|
// Info reveals information about a particular mounted filesystem. This
|
||||||
// struct is populated from the content in the /proc/<pid>/mountinfo file.
|
// struct is populated from the content in the /proc/<pid>/mountinfo file.
|
@ -0,0 +1,58 @@
|
|||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// FilterFunc is a type defining a callback function for GetMount(),
|
||||||
|
// used to filter out mountinfo entries we're not interested in,
|
||||||
|
// and/or stop further processing if we found what we wanted.
|
||||||
|
//
|
||||||
|
// It takes a pointer to the Info struct (not fully populated,
|
||||||
|
// currently only Mountpoint, Fstype, Source, and (on Linux)
|
||||||
|
// VfsOpts are filled in), and returns two booleans:
|
||||||
|
//
|
||||||
|
// - skip: true if the entry should be skipped
|
||||||
|
// - stop: true if parsing should be stopped after the entry
|
||||||
|
type FilterFunc func(*Info) (skip, stop bool)
|
||||||
|
|
||||||
|
// PrefixFilter discards all entries whose mount points
|
||||||
|
// do not start with a specific prefix
|
||||||
|
func PrefixFilter(prefix string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
skip := !strings.HasPrefix(m.Mountpoint, prefix)
|
||||||
|
return skip, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SingleEntryFilter looks for a specific entry
|
||||||
|
func SingleEntryFilter(mp string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
if m.Mountpoint == mp {
|
||||||
|
return false, true // don't skip, stop now
|
||||||
|
}
|
||||||
|
return true, false // skip, keep going
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParentsFilter returns all entries whose mount points
|
||||||
|
// can be parents of a path specified, discarding others.
|
||||||
|
//
|
||||||
|
// For example, given `/var/lib/docker/something`, entries
|
||||||
|
// like `/var/lib/docker`, `/var` and `/` are returned.
|
||||||
|
func ParentsFilter(path string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
skip := !strings.HasPrefix(path, m.Mountpoint)
|
||||||
|
return skip, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FstypeFilter returns all entries that match provided fstype(s).
|
||||||
|
func FstypeFilter(fstype ...string) FilterFunc {
|
||||||
|
return func(m *Info) (bool, bool) {
|
||||||
|
for _, t := range fstype {
|
||||||
|
if m.Fstype == t {
|
||||||
|
return false, false // don't skeep, keep going
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true, false // skip, keep going
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,17 @@
|
|||||||
// +build !windows,!linux,!freebsd freebsd,!cgo
|
// +build !windows,!linux,!freebsd freebsd,!cgo
|
||||||
|
|
||||||
package mount // import "github.com/docker/docker/pkg/mount"
|
package mountinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseMountTable(f FilterFunc) ([]*Info, error) {
|
func parseMountTable(_ FilterFunc) ([]*Info, error) {
|
||||||
return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
return nil, fmt.Errorf("mount.parseMountTable is not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseInfoFile(_ io.Reader, f FilterFunc) ([]*Info, error) {
|
||||||
|
return parseMountTable(f)
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package mountinfo
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func parseMountTable(_ FilterFunc) ([]*Info, error) {
|
||||||
|
// Do NOT return an error!
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseInfoFile(_ io.Reader, f FilterFunc) ([]*Info, error) {
|
||||||
|
return parseMountTable(f)
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
// Copyright 2020 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 !linux,arm
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
func archInit() {}
|
@ -0,0 +1,16 @@
|
|||||||
|
// Copyright 2020 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 ppc64 ppc64le
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
const cacheLineSize = 128
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
options = []option{
|
||||||
|
{Name: "darn", Feature: &PPC64.HasDARN},
|
||||||
|
{Name: "scv", Feature: &PPC64.HasSCV},
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2020 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.
|
||||||
|
|
||||||
|
package cpu
|
||||||
|
|
||||||
|
const cacheLineSize = 256
|
||||||
|
|
||||||
|
func initOptions() {
|
||||||
|
options = []option{
|
||||||
|
{Name: "zarch", Feature: &S390X.HasZARCH},
|
||||||
|
{Name: "stfle", Feature: &S390X.HasSTFLE},
|
||||||
|
{Name: "ldisp", Feature: &S390X.HasLDISP},
|
||||||
|
{Name: "eimm", Feature: &S390X.HasEIMM},
|
||||||
|
{Name: "dfp", Feature: &S390X.HasDFP},
|
||||||
|
{Name: "etf3eh", Feature: &S390X.HasETF3EH},
|
||||||
|
{Name: "msa", Feature: &S390X.HasMSA},
|
||||||
|
{Name: "aes", Feature: &S390X.HasAES},
|
||||||
|
{Name: "aescbc", Feature: &S390X.HasAESCBC},
|
||||||
|
{Name: "aesctr", Feature: &S390X.HasAESCTR},
|
||||||
|
{Name: "aesgcm", Feature: &S390X.HasAESGCM},
|
||||||
|
{Name: "ghash", Feature: &S390X.HasGHASH},
|
||||||
|
{Name: "sha1", Feature: &S390X.HasSHA1},
|
||||||
|
{Name: "sha256", Feature: &S390X.HasSHA256},
|
||||||
|
{Name: "sha3", Feature: &S390X.HasSHA3},
|
||||||
|
{Name: "sha512", Feature: &S390X.HasSHA512},
|
||||||
|
{Name: "vx", Feature: &S390X.HasVX},
|
||||||
|
{Name: "vxe", Feature: &S390X.HasVXE},
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue