mirror of
https://github.com/weaveworks/scope.git
synced 2026-03-03 18:20:27 +00:00
Merge pull request #1208 from weaveworks/1016-host-level-ttys
host-level ttys
This commit is contained in:
@@ -27,7 +27,16 @@ type pipe struct {
|
||||
onClose func()
|
||||
}
|
||||
|
||||
// NewPipe makes a new... pipe.
|
||||
// NewPipeFromEnds makes a new pipe specifying its ends
|
||||
func NewPipeFromEnds(local io.ReadWriter, remote io.ReadWriter) Pipe {
|
||||
return &pipe{
|
||||
port: local,
|
||||
starboard: remote,
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// NewPipe makes a new pipe
|
||||
func NewPipe() Pipe {
|
||||
r1, w1 := io.Pipe()
|
||||
r2, w2 := io.Pipe()
|
||||
|
||||
@@ -13,8 +13,6 @@ wait_for_containers $HOST1 60 alpine
|
||||
|
||||
assert "docker_on $HOST1 inspect --format='{{.State.Running}}' alpine" "true"
|
||||
PROBEID=$(docker_on $HOST1 logs weavescope 2>&1 | grep "probe starting" | sed -n 's/^.*ID \([0-9a-f]*\)$/\1/p')
|
||||
HOSTID=$(echo $HOST1 | cut -d"." -f1)
|
||||
|
||||
|
||||
# Execute 'echo foo' in a container tty and check its output
|
||||
PIPEID=$(curl -s -f -X POST "http://$HOST1:4040/api/control/$PROBEID/$CID;<container>/docker_exec_container" | jq -r '.pipe' )
|
||||
|
||||
19
integration/420_host_control_test.sh
Executable file
19
integration/420_host_control_test.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#! /bin/bash
|
||||
|
||||
. ./config.sh
|
||||
|
||||
start_suite "Test host controls"
|
||||
|
||||
weave_on $HOST1 launch
|
||||
scope_on $HOST1 launch
|
||||
|
||||
sleep 10
|
||||
|
||||
PROBEID=$(docker_on $HOST1 logs weavescope 2>&1 | grep "probe starting" | sed -n 's/^.*ID \([0-9a-f]*\)$/\1/p')
|
||||
HOSTID=$($SSH $HOST1 hostname)
|
||||
|
||||
# Execute 'echo foo' in the host tty and check its output
|
||||
PIPEID=$(curl -s -f -X POST "http://$HOST1:4040/api/control/$PROBEID/$HOSTID;<host>/host_exec" | jq -r '.pipe' )
|
||||
assert "(sleep 1 && echo \"PS1=''; echo foo\" && sleep 1) | wscat -b 'ws://$HOST1:4040/api/pipe/$PIPEID' | col -pb | tail -n 1" "foo\n"
|
||||
|
||||
scope_end_suite
|
||||
@@ -2,6 +2,7 @@ package controls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/rand"
|
||||
|
||||
"github.com/weaveworks/scope/common/xfer"
|
||||
@@ -21,11 +22,10 @@ type pipe struct {
|
||||
client PipeClient
|
||||
}
|
||||
|
||||
// NewPipe creats a new pipe and connects it to the app.
|
||||
var NewPipe = func(c PipeClient, appID string) (string, xfer.Pipe, error) {
|
||||
func newPipe(p xfer.Pipe, c PipeClient, appID string) (string, xfer.Pipe, error) {
|
||||
pipeID := fmt.Sprintf("pipe-%d", rand.Int63())
|
||||
pipe := &pipe{
|
||||
Pipe: xfer.NewPipe(),
|
||||
Pipe: p,
|
||||
appID: appID,
|
||||
id: pipeID,
|
||||
client: c,
|
||||
@@ -36,6 +36,16 @@ var NewPipe = func(c PipeClient, appID string) (string, xfer.Pipe, error) {
|
||||
return pipeID, pipe, nil
|
||||
}
|
||||
|
||||
// NewPipe creates a new pipe and connects it to the app.
|
||||
var NewPipe = func(c PipeClient, appID string) (string, xfer.Pipe, error) {
|
||||
return newPipe(xfer.NewPipe(), c, appID)
|
||||
}
|
||||
|
||||
// NewPipeFromEnds creates a new pipe from its ends and connects it to the app.
|
||||
func NewPipeFromEnds(local, remote io.ReadWriter, c PipeClient, appID string) (string, xfer.Pipe, error) {
|
||||
return newPipe(xfer.NewPipeFromEnds(local, remote), c, appID)
|
||||
}
|
||||
|
||||
func (p *pipe) Close() error {
|
||||
err1 := p.Pipe.Close()
|
||||
err2 := p.client.PipeClose(p.appID, p.id)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
// Control IDs used by the docker intergation.
|
||||
// Control IDs used by the docker integration.
|
||||
const (
|
||||
StopContainer = "docker_stop_container"
|
||||
StartContainer = "docker_start_container"
|
||||
|
||||
@@ -102,7 +102,7 @@ func (r *Reporter) containerTopology(localAddrs []net.IP) report.Topology {
|
||||
})
|
||||
result.Controls.AddControl(report.Control{
|
||||
ID: ExecContainer,
|
||||
Human: "Exec /bin/sh",
|
||||
Human: "Exec shell",
|
||||
Icon: "fa-terminal",
|
||||
})
|
||||
|
||||
|
||||
58
probe/host/controls.go
Normal file
58
probe/host/controls.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/kr/pty"
|
||||
|
||||
"github.com/weaveworks/scope/common/xfer"
|
||||
"github.com/weaveworks/scope/probe/controls"
|
||||
)
|
||||
|
||||
// Control IDs used by the host integration.
|
||||
const (
|
||||
ExecHost = "host_exec"
|
||||
)
|
||||
|
||||
func (r *Reporter) registerControls() {
|
||||
controls.Register(ExecHost, r.execHost)
|
||||
}
|
||||
|
||||
func (*Reporter) deregisterControls() {
|
||||
controls.Rm(ExecHost)
|
||||
}
|
||||
|
||||
func (r *Reporter) execHost(req xfer.Request) xfer.Response {
|
||||
cmd := exec.Command(r.hostShellCmd[0], r.hostShellCmd[1:]...)
|
||||
cmd.Env = []string{"TERM=xterm"}
|
||||
ptyPipe, err := pty.Start(cmd)
|
||||
if err != nil {
|
||||
return xfer.ResponseError(err)
|
||||
}
|
||||
|
||||
id, pipe, err := controls.NewPipeFromEnds(nil, ptyPipe, r.pipes, req.AppID)
|
||||
if err != nil {
|
||||
return xfer.ResponseError(err)
|
||||
}
|
||||
pipe.OnClose(func() {
|
||||
if err := cmd.Process.Kill(); err != nil {
|
||||
log.Errorf("Error stopping host shell: %v", err)
|
||||
}
|
||||
if err := ptyPipe.Close(); err != nil {
|
||||
log.Errorf("Error closing host shell's pty: %v", err)
|
||||
}
|
||||
log.Info("Host shell closed.")
|
||||
})
|
||||
go func() {
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Errorf("Error waiting on host shell: %v", err)
|
||||
}
|
||||
pipe.Close()
|
||||
}()
|
||||
|
||||
return xfer.Response{
|
||||
Pipe: id,
|
||||
RawTTY: true,
|
||||
}
|
||||
}
|
||||
5
probe/host/controls_darwin.go
Normal file
5
probe/host/controls_darwin.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package host
|
||||
|
||||
func getHostShellCmd() []string {
|
||||
return []string{"/bin/bash"}
|
||||
}
|
||||
88
probe/host/controls_linux.go
Normal file
88
probe/host/controls_linux.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package host
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/willdonnelly/passwd"
|
||||
)
|
||||
|
||||
func getHostShellCmd() []string {
|
||||
if isProbeContainerized() {
|
||||
// Escape the container namespaces and jump into the ones from
|
||||
// the host's init process.
|
||||
// Note: There should be no need to enter into the host network
|
||||
// and PID namespace because we should already already be there
|
||||
// but it doesn't hurt.
|
||||
readPasswdCmd := []string{"/usr/bin/nsenter", "-t1", "-m", "--no-fork", "cat", "/etc/passwd"}
|
||||
uid, gid, shell := getRootUserDetails(readPasswdCmd)
|
||||
return []string{
|
||||
"/usr/bin/nsenter", "-t1", "-m", "-i", "-n", "-p", "--no-fork",
|
||||
"--setuid", uid,
|
||||
"--setgid", gid,
|
||||
shell,
|
||||
}
|
||||
}
|
||||
|
||||
_, _, shell := getRootUserDetails([]string{"cat", "/etc/passwd"})
|
||||
return []string{shell}
|
||||
}
|
||||
|
||||
func getRootUserDetails(readPasswdCmd []string) (uid, gid, shell string) {
|
||||
uid = "0"
|
||||
gid = "0"
|
||||
shell = "/bin/sh"
|
||||
|
||||
cmd := exec.Command(readPasswdCmd[0], readPasswdCmd[1:]...)
|
||||
cmdBuffer := &bytes.Buffer{}
|
||||
cmd.Stdout = cmdBuffer
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Warnf(
|
||||
"getRootUserDetails(): error running read passwd command %q: %s",
|
||||
strings.Join(readPasswdCmd, " "),
|
||||
err,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
entries, err := passwd.ParseReader(cmdBuffer)
|
||||
if err != nil {
|
||||
log.Warnf("getRootUserDetails(): error parsing passwd: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
entry, ok := entries["root"]
|
||||
if !ok {
|
||||
log.Warnf("getRootUserDetails(): no root entry in passwd")
|
||||
return
|
||||
}
|
||||
|
||||
return entry.Uid, entry.Gid, entry.Shell
|
||||
}
|
||||
|
||||
func isProbeContainerized() bool {
|
||||
// Figure out whether we are running in a container by checking if our
|
||||
// mount namespace matches the one from init process. This works
|
||||
// because, when containerized, the Scope probes run in the host's PID
|
||||
// namespace (and if they weren't due to a configuration problem, we
|
||||
// wouldn't have a way to escape the container anyhow).
|
||||
var statT syscall.Stat_t
|
||||
|
||||
path := "/proc/self/ns/mnt"
|
||||
if err := syscall.Stat(path, &statT); err != nil {
|
||||
log.Warnf("isProbeContainerized(): stat() error on %q: %s", path, err)
|
||||
return false
|
||||
}
|
||||
selfMountNamespaceID := statT.Ino
|
||||
|
||||
path = "/proc/1/ns/mnt"
|
||||
if err := syscall.Stat(path, &statT); err != nil {
|
||||
log.Warnf("isProbeContainerized(): stat() error on %q: %s", path, err)
|
||||
return false
|
||||
}
|
||||
|
||||
return selfMountNamespaceID != statT.Ino
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/weaveworks/scope/common/mtime"
|
||||
"github.com/weaveworks/scope/probe/controls"
|
||||
"github.com/weaveworks/scope/report"
|
||||
)
|
||||
|
||||
@@ -34,17 +35,25 @@ const (
|
||||
|
||||
// Reporter generates Reports containing the host topology.
|
||||
type Reporter struct {
|
||||
hostID string
|
||||
hostName string
|
||||
hostID string
|
||||
hostName string
|
||||
probeID string
|
||||
pipes controls.PipeClient
|
||||
hostShellCmd []string
|
||||
}
|
||||
|
||||
// NewReporter returns a Reporter which produces a report containing host
|
||||
// topology for this host.
|
||||
func NewReporter(hostID, hostName string) *Reporter {
|
||||
return &Reporter{
|
||||
hostID: hostID,
|
||||
hostName: hostName,
|
||||
func NewReporter(hostID, hostName, probeID string, pipes controls.PipeClient) *Reporter {
|
||||
r := &Reporter{
|
||||
hostID: hostID,
|
||||
hostName: hostName,
|
||||
probeID: probeID,
|
||||
pipes: pipes,
|
||||
hostShellCmd: getHostShellCmd(),
|
||||
}
|
||||
r.registerControls()
|
||||
return r
|
||||
}
|
||||
|
||||
// Name of this reporter, for metrics gathering
|
||||
@@ -98,6 +107,7 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
memoryUsage, max := GetMemoryUsageBytes()
|
||||
metrics[MemoryUsage] = report.MakeMetric().Add(now, memoryUsage).WithMax(max)
|
||||
|
||||
metadata := map[string]string{report.ControlProbeID: r.probeID}
|
||||
rep.Host.AddNode(report.MakeHostNodeID(r.hostID), report.MakeNodeWith(map[string]string{
|
||||
Timestamp: mtime.Now().UTC().Format(time.RFC3339Nano),
|
||||
HostName: r.hostName,
|
||||
@@ -106,7 +116,18 @@ func (r *Reporter) Report() (report.Report, error) {
|
||||
Uptime: uptime.String(),
|
||||
}).WithSets(report.EmptySets.
|
||||
Add(LocalNetworks, report.MakeStringSet(localCIDRs...)),
|
||||
).WithMetrics(metrics))
|
||||
).WithMetrics(metrics).WithControls(ExecHost).WithLatests(metadata))
|
||||
|
||||
rep.Host.Controls.AddControl(report.Control{
|
||||
ID: ExecHost,
|
||||
Human: "Exec shell",
|
||||
Icon: "fa-terminal",
|
||||
})
|
||||
|
||||
return rep, nil
|
||||
}
|
||||
|
||||
// Stop stops the reporter.
|
||||
func (r *Reporter) Stop() {
|
||||
r.deregisterControls()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func TestReporter(t *testing.T) {
|
||||
network = "192.168.0.0/16"
|
||||
hostID = "hostid"
|
||||
hostname = "hostname"
|
||||
probeID = "abcdeadbeef"
|
||||
timestamp = time.Now()
|
||||
metrics = report.Metrics{
|
||||
host.Load1: report.MakeMetric().Add(timestamp, 1.0),
|
||||
@@ -57,7 +58,7 @@ func TestReporter(t *testing.T) {
|
||||
host.GetMemoryUsageBytes = func() (float64, float64) { return 60.0, 100.0 }
|
||||
host.GetLocalNetworks = func() ([]*net.IPNet, error) { return []*net.IPNet{ipnet}, nil }
|
||||
|
||||
rpt, err := host.NewReporter(hostID, hostname).Report()
|
||||
rpt, err := host.NewReporter(hostID, hostname, probeID, nil).Report()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -140,9 +140,11 @@ func probeMain() {
|
||||
|
||||
p := probe.New(*spyInterval, *publishInterval, clients)
|
||||
p.AddTicker(processCache)
|
||||
hostReporter := host.NewReporter(hostID, hostName, probeID, clients)
|
||||
defer hostReporter.Stop()
|
||||
p.AddReporter(
|
||||
endpointReporter,
|
||||
host.NewReporter(hostID, hostName),
|
||||
hostReporter,
|
||||
process.NewReporter(processCache, hostID, process.GetDeltaTotalJiffies),
|
||||
)
|
||||
p.AddTagger(probe.NewTopologyTagger(), host.NewTagger(hostID))
|
||||
|
||||
23
vendor/github.com/kr/pty/License
generated
vendored
Normal file
23
vendor/github.com/kr/pty/License
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
Copyright (c) 2011 Keith Rarick
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall
|
||||
be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
36
vendor/github.com/kr/pty/README.md
generated
vendored
Normal file
36
vendor/github.com/kr/pty/README.md
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# pty
|
||||
|
||||
Pty is a Go package for using unix pseudo-terminals.
|
||||
|
||||
## Install
|
||||
|
||||
go get github.com/kr/pty
|
||||
|
||||
## Example
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/kr/pty"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := exec.Command("grep", "--color=auto", "bar")
|
||||
f, err := pty.Start(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
f.Write([]byte("foo\n"))
|
||||
f.Write([]byte("bar\n"))
|
||||
f.Write([]byte("baz\n"))
|
||||
f.Write([]byte{4}) // EOT
|
||||
}()
|
||||
io.Copy(os.Stdout, f)
|
||||
}
|
||||
```
|
||||
16
vendor/github.com/kr/pty/doc.go
generated
vendored
Normal file
16
vendor/github.com/kr/pty/doc.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
// Package pty provides functions for working with Unix terminals.
|
||||
package pty
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ErrUnsupported is returned if a function is not
|
||||
// available on the current platform.
|
||||
var ErrUnsupported = errors.New("unsupported")
|
||||
|
||||
// Opens a pty and its corresponding tty.
|
||||
func Open() (pty, tty *os.File, err error) {
|
||||
return open()
|
||||
}
|
||||
11
vendor/github.com/kr/pty/ioctl.go
generated
vendored
Normal file
11
vendor/github.com/kr/pty/ioctl.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package pty
|
||||
|
||||
import "syscall"
|
||||
|
||||
func ioctl(fd, cmd, ptr uintptr) error {
|
||||
_, _, e := syscall.Syscall(syscall.SYS_IOCTL, fd, cmd, ptr)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
return nil
|
||||
}
|
||||
39
vendor/github.com/kr/pty/ioctl_bsd.go
generated
vendored
Normal file
39
vendor/github.com/kr/pty/ioctl_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package pty
|
||||
|
||||
// from <sys/ioccom.h>
|
||||
const (
|
||||
_IOC_VOID uintptr = 0x20000000
|
||||
_IOC_OUT uintptr = 0x40000000
|
||||
_IOC_IN uintptr = 0x80000000
|
||||
_IOC_IN_OUT uintptr = _IOC_OUT | _IOC_IN
|
||||
_IOC_DIRMASK = _IOC_VOID | _IOC_OUT | _IOC_IN
|
||||
|
||||
_IOC_PARAM_SHIFT = 13
|
||||
_IOC_PARAM_MASK = (1 << _IOC_PARAM_SHIFT) - 1
|
||||
)
|
||||
|
||||
func _IOC_PARM_LEN(ioctl uintptr) uintptr {
|
||||
return (ioctl >> 16) & _IOC_PARAM_MASK
|
||||
}
|
||||
|
||||
func _IOC(inout uintptr, group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||
return inout | (param_len&_IOC_PARAM_MASK)<<16 | uintptr(group)<<8 | ioctl_num
|
||||
}
|
||||
|
||||
func _IO(group byte, ioctl_num uintptr) uintptr {
|
||||
return _IOC(_IOC_VOID, group, ioctl_num, 0)
|
||||
}
|
||||
|
||||
func _IOR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||
return _IOC(_IOC_OUT, group, ioctl_num, param_len)
|
||||
}
|
||||
|
||||
func _IOW(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||
return _IOC(_IOC_IN, group, ioctl_num, param_len)
|
||||
}
|
||||
|
||||
func _IOWR(group byte, ioctl_num uintptr, param_len uintptr) uintptr {
|
||||
return _IOC(_IOC_IN_OUT, group, ioctl_num, param_len)
|
||||
}
|
||||
19
vendor/github.com/kr/pty/mktypes.bash
generated
vendored
Normal file
19
vendor/github.com/kr/pty/mktypes.bash
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
GOOSARCH="${GOOS}_${GOARCH}"
|
||||
case "$GOOSARCH" in
|
||||
_* | *_ | _)
|
||||
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
GODEFS="go tool cgo -godefs"
|
||||
|
||||
$GODEFS types.go |gofmt > ztypes_$GOARCH.go
|
||||
|
||||
case $GOOS in
|
||||
freebsd)
|
||||
$GODEFS types_$GOOS.go |gofmt > ztypes_$GOOSARCH.go
|
||||
;;
|
||||
esac
|
||||
60
vendor/github.com/kr/pty/pty_darwin.go
generated
vendored
Normal file
60
vendor/github.com/kr/pty/pty_darwin.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package pty
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func open() (pty, tty *os.File, err error) {
|
||||
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sname, err := ptsname(p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = grantpt(p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = unlockpt(p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
t, err := os.OpenFile(sname, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return p, t, nil
|
||||
}
|
||||
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
n := make([]byte, _IOC_PARM_LEN(syscall.TIOCPTYGNAME))
|
||||
|
||||
err := ioctl(f.Fd(), syscall.TIOCPTYGNAME, uintptr(unsafe.Pointer(&n[0])))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i, c := range n {
|
||||
if c == 0 {
|
||||
return string(n[:i]), nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("TIOCPTYGNAME string not NUL-terminated")
|
||||
}
|
||||
|
||||
func grantpt(f *os.File) error {
|
||||
return ioctl(f.Fd(), syscall.TIOCPTYGRANT, 0)
|
||||
}
|
||||
|
||||
func unlockpt(f *os.File) error {
|
||||
return ioctl(f.Fd(), syscall.TIOCPTYUNLK, 0)
|
||||
}
|
||||
73
vendor/github.com/kr/pty/pty_freebsd.go
generated
vendored
Normal file
73
vendor/github.com/kr/pty/pty_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
package pty
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func posix_openpt(oflag int) (fd int, err error) {
|
||||
r0, _, e1 := syscall.Syscall(syscall.SYS_POSIX_OPENPT, uintptr(oflag), 0, 0)
|
||||
fd = int(r0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func open() (pty, tty *os.File, err error) {
|
||||
fd, err := posix_openpt(syscall.O_RDWR | syscall.O_CLOEXEC)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
p := os.NewFile(uintptr(fd), "/dev/pts")
|
||||
sname, err := ptsname(p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
t, err := os.OpenFile("/dev/"+sname, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return p, t, nil
|
||||
}
|
||||
|
||||
func isptmaster(fd uintptr) (bool, error) {
|
||||
err := ioctl(fd, syscall.TIOCPTMASTER, 0)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
emptyFiodgnameArg fiodgnameArg
|
||||
ioctl_FIODGNAME = _IOW('f', 120, unsafe.Sizeof(emptyFiodgnameArg))
|
||||
)
|
||||
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
master, err := isptmaster(f.Fd())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if !master {
|
||||
return "", syscall.EINVAL
|
||||
}
|
||||
|
||||
const n = _C_SPECNAMELEN + 1
|
||||
var (
|
||||
buf = make([]byte, n)
|
||||
arg = fiodgnameArg{Len: n, Buf: (*byte)(unsafe.Pointer(&buf[0]))}
|
||||
)
|
||||
err = ioctl(f.Fd(), ioctl_FIODGNAME, uintptr(unsafe.Pointer(&arg)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i, c := range buf {
|
||||
if c == 0 {
|
||||
return string(buf[:i]), nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("FIODGNAME string not NUL-terminated")
|
||||
}
|
||||
46
vendor/github.com/kr/pty/pty_linux.go
generated
vendored
Normal file
46
vendor/github.com/kr/pty/pty_linux.go
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
package pty
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func open() (pty, tty *os.File, err error) {
|
||||
p, err := os.OpenFile("/dev/ptmx", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sname, err := ptsname(p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
err = unlockpt(p)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
t, err := os.OpenFile(sname, os.O_RDWR|syscall.O_NOCTTY, 0)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return p, t, nil
|
||||
}
|
||||
|
||||
func ptsname(f *os.File) (string, error) {
|
||||
var n _C_uint
|
||||
err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "/dev/pts/" + strconv.Itoa(int(n)), nil
|
||||
}
|
||||
|
||||
func unlockpt(f *os.File) error {
|
||||
var u _C_int
|
||||
// use TIOCSPTLCK with a zero valued arg to clear the slave pty lock
|
||||
return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
|
||||
}
|
||||
11
vendor/github.com/kr/pty/pty_unsupported.go
generated
vendored
Normal file
11
vendor/github.com/kr/pty/pty_unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build !linux,!darwin,!freebsd
|
||||
|
||||
package pty
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
func open() (pty, tty *os.File, err error) {
|
||||
return nil, nil, ErrUnsupported
|
||||
}
|
||||
32
vendor/github.com/kr/pty/run.go
generated
vendored
Normal file
32
vendor/github.com/kr/pty/run.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package pty
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Start assigns a pseudo-terminal tty os.File to c.Stdin, c.Stdout,
|
||||
// and c.Stderr, calls c.Start, and returns the File of the tty's
|
||||
// corresponding pty.
|
||||
func Start(c *exec.Cmd) (pty *os.File, err error) {
|
||||
pty, tty, err := Open()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer tty.Close()
|
||||
c.Stdout = tty
|
||||
c.Stdin = tty
|
||||
c.Stderr = tty
|
||||
if c.SysProcAttr == nil {
|
||||
c.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
c.SysProcAttr.Setctty = true
|
||||
c.SysProcAttr.Setsid = true
|
||||
err = c.Start()
|
||||
if err != nil {
|
||||
pty.Close()
|
||||
return nil, err
|
||||
}
|
||||
return pty, err
|
||||
}
|
||||
10
vendor/github.com/kr/pty/types.go
generated
vendored
Normal file
10
vendor/github.com/kr/pty/types.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// +build ignore
|
||||
|
||||
package pty
|
||||
|
||||
import "C"
|
||||
|
||||
type (
|
||||
_C_int C.int
|
||||
_C_uint C.uint
|
||||
)
|
||||
15
vendor/github.com/kr/pty/types_freebsd.go
generated
vendored
Normal file
15
vendor/github.com/kr/pty/types_freebsd.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// +build ignore
|
||||
|
||||
package pty
|
||||
|
||||
/*
|
||||
#include <sys/param.h>
|
||||
#include <sys/filio.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
_C_SPECNAMELEN = C.SPECNAMELEN /* max length of devicename */
|
||||
)
|
||||
|
||||
type fiodgnameArg C.struct_fiodgname_arg
|
||||
35
vendor/github.com/kr/pty/util.go
generated
vendored
Normal file
35
vendor/github.com/kr/pty/util.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package pty
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Getsize returns the number of rows (lines) and cols (positions
|
||||
// in each line) in terminal t.
|
||||
func Getsize(t *os.File) (rows, cols int, err error) {
|
||||
var ws winsize
|
||||
err = windowrect(&ws, t.Fd())
|
||||
return int(ws.ws_row), int(ws.ws_col), err
|
||||
}
|
||||
|
||||
type winsize struct {
|
||||
ws_row uint16
|
||||
ws_col uint16
|
||||
ws_xpixel uint16
|
||||
ws_ypixel uint16
|
||||
}
|
||||
|
||||
func windowrect(ws *winsize, fd uintptr) error {
|
||||
_, _, errno := syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
fd,
|
||||
syscall.TIOCGWINSZ,
|
||||
uintptr(unsafe.Pointer(ws)),
|
||||
)
|
||||
if errno != 0 {
|
||||
return syscall.Errno(errno)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
9
vendor/github.com/kr/pty/ztypes_386.go
generated
vendored
Normal file
9
vendor/github.com/kr/pty/ztypes_386.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types.go
|
||||
|
||||
package pty
|
||||
|
||||
type (
|
||||
_C_int int32
|
||||
_C_uint uint32
|
||||
)
|
||||
9
vendor/github.com/kr/pty/ztypes_amd64.go
generated
vendored
Normal file
9
vendor/github.com/kr/pty/ztypes_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types.go
|
||||
|
||||
package pty
|
||||
|
||||
type (
|
||||
_C_int int32
|
||||
_C_uint uint32
|
||||
)
|
||||
9
vendor/github.com/kr/pty/ztypes_arm.go
generated
vendored
Normal file
9
vendor/github.com/kr/pty/ztypes_arm.go
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types.go
|
||||
|
||||
package pty
|
||||
|
||||
type (
|
||||
_C_int int32
|
||||
_C_uint uint32
|
||||
)
|
||||
11
vendor/github.com/kr/pty/ztypes_arm64.go
generated
vendored
Normal file
11
vendor/github.com/kr/pty/ztypes_arm64.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types.go
|
||||
|
||||
// +build arm64
|
||||
|
||||
package pty
|
||||
|
||||
type (
|
||||
_C_int int32
|
||||
_C_uint uint32
|
||||
)
|
||||
13
vendor/github.com/kr/pty/ztypes_freebsd_386.go
generated
vendored
Normal file
13
vendor/github.com/kr/pty/ztypes_freebsd_386.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package pty
|
||||
|
||||
const (
|
||||
_C_SPECNAMELEN = 0x3f
|
||||
)
|
||||
|
||||
type fiodgnameArg struct {
|
||||
Len int32
|
||||
Buf *byte
|
||||
}
|
||||
14
vendor/github.com/kr/pty/ztypes_freebsd_amd64.go
generated
vendored
Normal file
14
vendor/github.com/kr/pty/ztypes_freebsd_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package pty
|
||||
|
||||
const (
|
||||
_C_SPECNAMELEN = 0x3f
|
||||
)
|
||||
|
||||
type fiodgnameArg struct {
|
||||
Len int32
|
||||
Pad_cgo_0 [4]byte
|
||||
Buf *byte
|
||||
}
|
||||
13
vendor/github.com/kr/pty/ztypes_freebsd_arm.go
generated
vendored
Normal file
13
vendor/github.com/kr/pty/ztypes_freebsd_arm.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types_freebsd.go
|
||||
|
||||
package pty
|
||||
|
||||
const (
|
||||
_C_SPECNAMELEN = 0x3f
|
||||
)
|
||||
|
||||
type fiodgnameArg struct {
|
||||
Len int32
|
||||
Buf *byte
|
||||
}
|
||||
11
vendor/github.com/kr/pty/ztypes_ppc64.go
generated
vendored
Normal file
11
vendor/github.com/kr/pty/ztypes_ppc64.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build ppc64
|
||||
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types.go
|
||||
|
||||
package pty
|
||||
|
||||
type (
|
||||
_C_int int32
|
||||
_C_uint uint32
|
||||
)
|
||||
11
vendor/github.com/kr/pty/ztypes_ppc64le.go
generated
vendored
Normal file
11
vendor/github.com/kr/pty/ztypes_ppc64le.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build ppc64le
|
||||
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types.go
|
||||
|
||||
package pty
|
||||
|
||||
type (
|
||||
_C_int int32
|
||||
_C_uint uint32
|
||||
)
|
||||
11
vendor/github.com/kr/pty/ztypes_s390x.go
generated
vendored
Normal file
11
vendor/github.com/kr/pty/ztypes_s390x.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// +build s390x
|
||||
|
||||
// Created by cgo -godefs - DO NOT EDIT
|
||||
// cgo -godefs types.go
|
||||
|
||||
package pty
|
||||
|
||||
type (
|
||||
_C_int int32
|
||||
_C_uint uint32
|
||||
)
|
||||
26
vendor/github.com/willdonnelly/passwd/README
generated
vendored
Normal file
26
vendor/github.com/willdonnelly/passwd/README
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
> godoc github.com/willdonnelly/passwd
|
||||
|
||||
PACKAGE
|
||||
|
||||
package passwd
|
||||
import "github.com/willdonnelly/passwd"
|
||||
|
||||
|
||||
FUNCTIONS
|
||||
|
||||
func Parse() (map[string]Entry, error)
|
||||
Parse opens the '/etc/passwd' file and parses it into a map from
|
||||
usernames to Entries
|
||||
|
||||
|
||||
TYPES
|
||||
|
||||
type Entry struct {
|
||||
Pass string
|
||||
Uid string
|
||||
Gid string
|
||||
Gecos string
|
||||
Home string
|
||||
Shell string
|
||||
}
|
||||
An Entry contains all the fields for a specific user
|
||||
71
vendor/github.com/willdonnelly/passwd/passwd.go
generated
vendored
Normal file
71
vendor/github.com/willdonnelly/passwd/passwd.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// Package passwd parsed content and files in form of /etc/passwd
|
||||
package passwd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// An Entry contains all the fields for a specific user
|
||||
type Entry struct {
|
||||
Pass string
|
||||
Uid string
|
||||
Gid string
|
||||
Gecos string
|
||||
Home string
|
||||
Shell string
|
||||
}
|
||||
|
||||
// Parse opens the '/etc/passwd' file and parses it into a map from usernames
|
||||
// to Entries
|
||||
func Parse() (map[string]Entry, error) {
|
||||
return ParseFile("/etc/passwd")
|
||||
}
|
||||
|
||||
// ParseFile opens the file and parses it into a map from usernames to Entries
|
||||
func ParseFile(path string) (map[string]Entry, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
return ParseReader(file)
|
||||
}
|
||||
|
||||
// ParseReader consumes the contents of r and parses it into a map from
|
||||
// usernames to Entries
|
||||
func ParseReader(r io.Reader) (map[string]Entry, error) {
|
||||
lines := bufio.NewReader(r)
|
||||
entries := make(map[string]Entry)
|
||||
for {
|
||||
line, _, err := lines.ReadLine()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
name, entry, err := parseLine(string(copyBytes(line)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entries[name] = entry
|
||||
}
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
func parseLine(line string) (string, Entry, error) {
|
||||
fs := strings.Split(line, ":")
|
||||
if len(fs) != 7 {
|
||||
return "", Entry{}, errors.New("Unexpected number of fields in /etc/passwd")
|
||||
}
|
||||
return fs[0], Entry{fs[1], fs[2], fs[3], fs[4], fs[5], fs[6]}, nil
|
||||
}
|
||||
|
||||
func copyBytes(x []byte) []byte {
|
||||
y := make([]byte, len(x))
|
||||
copy(y, x)
|
||||
return y
|
||||
}
|
||||
14
vendor/manifest
vendored
14
vendor/manifest
vendored
@@ -681,6 +681,12 @@
|
||||
"revision": "77ed1c8a01217656d2080ad51981f6e99adaa177",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/kr/pty",
|
||||
"repository": "https://github.com/kr/pty",
|
||||
"revision": "f7ee69f31298ecbe5d2b349c711e2547a617d398",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/lsegal/gucumber",
|
||||
"repository": "https://github.com/lsegal/gucumber",
|
||||
@@ -871,6 +877,12 @@
|
||||
"branch": "master",
|
||||
"path": "/common"
|
||||
},
|
||||
{
|
||||
"importpath": "github.com/willdonnelly/passwd",
|
||||
"repository": "https://github.com/willdonnelly/passwd",
|
||||
"revision": "7935dab3074ca1d47c8805e0230f8685116b6019",
|
||||
"branch": "master"
|
||||
},
|
||||
{
|
||||
"importpath": "golang.org/x/crypto/curve25519",
|
||||
"repository": "https://go.googlesource.com/crypto",
|
||||
@@ -1199,4 +1211,4 @@
|
||||
"branch": "master"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user