2014-03-28 23:21:55 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-12-14 01:25:28 +00:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2014-03-28 23:21:55 +00:00
|
|
|
"os"
|
|
|
|
gosignal "os/signal"
|
2015-12-14 01:25:28 +00:00
|
|
|
"path/filepath"
|
2015-03-26 22:52:16 +00:00
|
|
|
"runtime"
|
2016-06-14 02:56:23 +00:00
|
|
|
"strings"
|
2015-03-26 22:52:16 +00:00
|
|
|
"time"
|
2014-03-28 23:21:55 +00:00
|
|
|
|
2016-03-16 19:19:22 +00:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2015-03-26 22:22:04 +00:00
|
|
|
"github.com/Sirupsen/logrus"
|
2014-11-13 18:40:22 +00:00
|
|
|
"github.com/docker/docker/pkg/signal"
|
2014-07-24 22:19:50 +00:00
|
|
|
"github.com/docker/docker/pkg/term"
|
2016-01-05 00:05:26 +00:00
|
|
|
"github.com/docker/engine-api/client"
|
|
|
|
"github.com/docker/engine-api/types"
|
2014-03-28 23:21:55 +00:00
|
|
|
)
|
|
|
|
|
2016-05-21 13:57:57 +00:00
|
|
|
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
2016-05-31 23:49:32 +00:00
|
|
|
height, width := cli.GetTtySize()
|
2016-06-05 16:17:39 +00:00
|
|
|
cli.ResizeTtyTo(ctx, id, height, width, isExec)
|
2016-01-22 11:35:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-05 16:17:39 +00:00
|
|
|
// ResizeTtyTo resizes tty to specific height and width
|
|
|
|
// TODO: this can be unexported again once all container related commands move to package container
|
|
|
|
func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) {
|
2014-03-28 23:21:55 +00:00
|
|
|
if height == 0 && width == 0 {
|
|
|
|
return
|
|
|
|
}
|
2014-09-16 06:43:43 +00:00
|
|
|
|
2015-12-06 23:41:57 +00:00
|
|
|
options := types.ResizeOptions{
|
|
|
|
Height: height,
|
|
|
|
Width: width,
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
2015-12-10 03:55:01 +00:00
|
|
|
if isExec {
|
2016-05-21 13:57:57 +00:00
|
|
|
err = cli.client.ContainerExecResize(ctx, id, options)
|
2014-09-16 06:43:43 +00:00
|
|
|
} else {
|
2016-05-21 13:57:57 +00:00
|
|
|
err = cli.client.ContainerResize(ctx, id, options)
|
2014-09-16 06:43:43 +00:00
|
|
|
}
|
|
|
|
|
2015-12-06 23:41:57 +00:00
|
|
|
if err != nil {
|
2015-03-26 22:22:04 +00:00
|
|
|
logrus.Debugf("Error resize: %s", err)
|
2014-03-28 23:21:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 23:50:09 +00:00
|
|
|
// getExecExitCode perform an inspect on the exec command. It returns
|
|
|
|
// the running state and the exit code.
|
2016-05-21 13:57:57 +00:00
|
|
|
func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) {
|
|
|
|
resp, err := cli.client.ContainerExecInspect(ctx, execID)
|
2014-11-17 23:50:09 +00:00
|
|
|
if err != nil {
|
|
|
|
// If we can't connect, then the daemon probably died.
|
2016-01-05 00:05:26 +00:00
|
|
|
if err != client.ErrConnectionFailed {
|
2014-11-17 23:50:09 +00:00
|
|
|
return false, -1, err
|
|
|
|
}
|
|
|
|
return false, -1, nil
|
|
|
|
}
|
|
|
|
|
2015-12-06 05:33:38 +00:00
|
|
|
return resp.Running, resp.ExitCode, nil
|
2014-11-17 23:50:09 +00:00
|
|
|
}
|
|
|
|
|
2016-05-31 23:49:32 +00:00
|
|
|
// MonitorTtySize updates the container tty size when the terminal tty changes size
|
|
|
|
func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error {
|
2016-05-21 13:57:57 +00:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2014-03-28 23:21:55 +00:00
|
|
|
|
2015-03-26 22:52:16 +00:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
go func() {
|
2016-05-31 23:49:32 +00:00
|
|
|
prevH, prevW := cli.GetTtySize()
|
2015-03-26 22:52:16 +00:00
|
|
|
for {
|
|
|
|
time.Sleep(time.Millisecond * 250)
|
2016-05-31 23:49:32 +00:00
|
|
|
h, w := cli.GetTtySize()
|
2015-03-26 22:52:16 +00:00
|
|
|
|
|
|
|
if prevW != w || prevH != h {
|
2016-05-21 13:57:57 +00:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2015-03-26 22:52:16 +00:00
|
|
|
}
|
|
|
|
prevH = h
|
2015-04-06 21:31:42 +00:00
|
|
|
prevW = w
|
2015-03-26 22:52:16 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
} else {
|
|
|
|
sigchan := make(chan os.Signal, 1)
|
|
|
|
gosignal.Notify(sigchan, signal.SIGWINCH)
|
|
|
|
go func() {
|
2015-04-01 22:39:37 +00:00
|
|
|
for range sigchan {
|
2016-05-21 13:57:57 +00:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2015-03-26 22:52:16 +00:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2014-03-28 23:21:55 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-05-31 23:49:32 +00:00
|
|
|
// GetTtySize returns the height and width in characters of the tty
|
|
|
|
func (cli *DockerCli) GetTtySize() (int, int) {
|
2014-09-10 13:35:48 +00:00
|
|
|
if !cli.isTerminalOut {
|
2014-03-28 23:21:55 +00:00
|
|
|
return 0, 0
|
|
|
|
}
|
2014-09-10 13:35:48 +00:00
|
|
|
ws, err := term.GetWinsize(cli.outFd)
|
2014-03-28 23:21:55 +00:00
|
|
|
if err != nil {
|
2015-03-26 22:22:04 +00:00
|
|
|
logrus.Debugf("Error getting size: %s", err)
|
2014-03-28 23:21:55 +00:00
|
|
|
if ws == nil {
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return int(ws.Height), int(ws.Width)
|
|
|
|
}
|
2015-12-14 01:25:28 +00:00
|
|
|
|
2016-06-08 19:58:45 +00:00
|
|
|
// CopyToFile writes the content of the reader to the specified file
|
2016-06-05 14:42:19 +00:00
|
|
|
func CopyToFile(outfile string, r io.Reader) error {
|
2015-12-14 01:25:28 +00:00
|
|
|
tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_")
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
tmpPath := tmpFile.Name()
|
|
|
|
|
|
|
|
_, err = io.Copy(tmpFile, r)
|
|
|
|
tmpFile.Close()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
os.Remove(tmpPath)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err = os.Rename(tmpPath, outfile); err != nil {
|
|
|
|
os.Remove(tmpPath)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2016-02-03 18:55:33 +00:00
|
|
|
|
2016-06-19 16:53:31 +00:00
|
|
|
// ForwardAllSignals forwards signals to the container
|
2016-06-05 15:03:58 +00:00
|
|
|
// TODO: this can be unexported again once all container commands are under
|
|
|
|
// api/client/container
|
|
|
|
func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal {
|
|
|
|
sigc := make(chan os.Signal, 128)
|
|
|
|
signal.CatchAll(sigc)
|
|
|
|
go func() {
|
|
|
|
for s := range sigc {
|
|
|
|
if s == signal.SIGCHLD || s == signal.SIGPIPE {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
var sig string
|
|
|
|
for sigStr, sigN := range signal.SignalMap {
|
|
|
|
if sigN == s {
|
|
|
|
sig = sigStr
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if sig == "" {
|
|
|
|
fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := cli.client.ContainerKill(ctx, cid, sig); err != nil {
|
|
|
|
logrus.Debugf("Error sending signal: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
return sigc
|
|
|
|
}
|
2016-06-14 02:56:23 +00:00
|
|
|
|
|
|
|
// capitalizeFirst capitalizes the first character of string
|
|
|
|
func capitalizeFirst(s string) string {
|
|
|
|
switch l := len(s); l {
|
|
|
|
case 0:
|
|
|
|
return s
|
|
|
|
case 1:
|
|
|
|
return strings.ToLower(s)
|
|
|
|
default:
|
|
|
|
return strings.ToUpper(string(s[0])) + strings.ToLower(s[1:])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// PrettyPrint outputs arbitrary data for human formatted output by uppercasing the first letter.
|
|
|
|
func PrettyPrint(i interface{}) string {
|
|
|
|
switch t := i.(type) {
|
|
|
|
case nil:
|
|
|
|
return "None"
|
|
|
|
case string:
|
|
|
|
return capitalizeFirst(t)
|
|
|
|
default:
|
|
|
|
return capitalizeFirst(fmt.Sprintf("%s", t))
|
|
|
|
}
|
|
|
|
}
|