2014-03-28 19:21:55 -04:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2015-12-13 20:25:28 -05:00
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
2014-03-28 19:21:55 -04:00
|
|
|
"os"
|
|
|
|
gosignal "os/signal"
|
2015-12-13 20:25:28 -05:00
|
|
|
"path/filepath"
|
2015-03-26 18:52:16 -04:00
|
|
|
"runtime"
|
2016-06-13 22:56:23 -04:00
|
|
|
"strings"
|
2015-03-26 18:52:16 -04:00
|
|
|
"time"
|
2014-03-28 19:21:55 -04:00
|
|
|
|
2016-03-16 15:19:22 -04:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2015-03-26 18:22:04 -04:00
|
|
|
"github.com/Sirupsen/logrus"
|
2014-11-13 13:40:22 -05:00
|
|
|
"github.com/docker/docker/pkg/signal"
|
2014-07-24 18:19:50 -04:00
|
|
|
"github.com/docker/docker/pkg/term"
|
2016-01-04 19:05:26 -05:00
|
|
|
"github.com/docker/engine-api/client"
|
|
|
|
"github.com/docker/engine-api/types"
|
2014-03-28 19:21:55 -04:00
|
|
|
)
|
|
|
|
|
2016-05-21 09:57:57 -04:00
|
|
|
func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) {
|
2016-05-31 19:49:32 -04:00
|
|
|
height, width := cli.GetTtySize()
|
2016-06-05 12:17:39 -04:00
|
|
|
cli.ResizeTtyTo(ctx, id, height, width, isExec)
|
2016-01-22 06:35:55 -05:00
|
|
|
}
|
|
|
|
|
2016-06-05 12:17:39 -04: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 19:21:55 -04:00
|
|
|
if height == 0 && width == 0 {
|
|
|
|
return
|
|
|
|
}
|
2014-09-16 02:43:43 -04:00
|
|
|
|
2015-12-06 18:41:57 -05:00
|
|
|
options := types.ResizeOptions{
|
|
|
|
Height: height,
|
|
|
|
Width: width,
|
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
2015-12-09 22:55:01 -05:00
|
|
|
if isExec {
|
2016-05-21 09:57:57 -04:00
|
|
|
err = cli.client.ContainerExecResize(ctx, id, options)
|
2014-09-16 02:43:43 -04:00
|
|
|
} else {
|
2016-05-21 09:57:57 -04:00
|
|
|
err = cli.client.ContainerResize(ctx, id, options)
|
2014-09-16 02:43:43 -04:00
|
|
|
}
|
|
|
|
|
2015-12-06 18:41:57 -05:00
|
|
|
if err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Error resize: %s", err)
|
2014-03-28 19:21:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-20 09:27:56 -04:00
|
|
|
// GetExecExitCode perform an inspect on the exec command. It returns
|
2014-11-17 18:50:09 -05:00
|
|
|
// the running state and the exit code.
|
2016-06-20 09:27:56 -04:00
|
|
|
func (cli *DockerCli) GetExecExitCode(ctx context.Context, execID string) (bool, int, error) {
|
2016-05-21 09:57:57 -04:00
|
|
|
resp, err := cli.client.ContainerExecInspect(ctx, execID)
|
2014-11-17 18:50:09 -05:00
|
|
|
if err != nil {
|
|
|
|
// If we can't connect, then the daemon probably died.
|
2016-01-04 19:05:26 -05:00
|
|
|
if err != client.ErrConnectionFailed {
|
2014-11-17 18:50:09 -05:00
|
|
|
return false, -1, err
|
|
|
|
}
|
|
|
|
return false, -1, nil
|
|
|
|
}
|
|
|
|
|
2015-12-06 00:33:38 -05:00
|
|
|
return resp.Running, resp.ExitCode, nil
|
2014-11-17 18:50:09 -05:00
|
|
|
}
|
|
|
|
|
2016-05-31 19:49:32 -04: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 09:57:57 -04:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2014-03-28 19:21:55 -04:00
|
|
|
|
2015-03-26 18:52:16 -04:00
|
|
|
if runtime.GOOS == "windows" {
|
|
|
|
go func() {
|
2016-05-31 19:49:32 -04:00
|
|
|
prevH, prevW := cli.GetTtySize()
|
2015-03-26 18:52:16 -04:00
|
|
|
for {
|
|
|
|
time.Sleep(time.Millisecond * 250)
|
2016-05-31 19:49:32 -04:00
|
|
|
h, w := cli.GetTtySize()
|
2015-03-26 18:52:16 -04:00
|
|
|
|
|
|
|
if prevW != w || prevH != h {
|
2016-05-21 09:57:57 -04:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2015-03-26 18:52:16 -04:00
|
|
|
}
|
|
|
|
prevH = h
|
2015-04-06 17:31:42 -04:00
|
|
|
prevW = w
|
2015-03-26 18:52:16 -04:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
} else {
|
|
|
|
sigchan := make(chan os.Signal, 1)
|
|
|
|
gosignal.Notify(sigchan, signal.SIGWINCH)
|
|
|
|
go func() {
|
2015-04-01 18:39:37 -04:00
|
|
|
for range sigchan {
|
2016-05-21 09:57:57 -04:00
|
|
|
cli.resizeTty(ctx, id, isExec)
|
2015-03-26 18:52:16 -04:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
2014-03-28 19:21:55 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-05-31 19:49:32 -04:00
|
|
|
// GetTtySize returns the height and width in characters of the tty
|
|
|
|
func (cli *DockerCli) GetTtySize() (int, int) {
|
2014-09-10 09:35:48 -04:00
|
|
|
if !cli.isTerminalOut {
|
2014-03-28 19:21:55 -04:00
|
|
|
return 0, 0
|
|
|
|
}
|
2014-09-10 09:35:48 -04:00
|
|
|
ws, err := term.GetWinsize(cli.outFd)
|
2014-03-28 19:21:55 -04:00
|
|
|
if err != nil {
|
2015-03-26 18:22:04 -04:00
|
|
|
logrus.Debugf("Error getting size: %s", err)
|
2014-03-28 19:21:55 -04:00
|
|
|
if ws == nil {
|
|
|
|
return 0, 0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return int(ws.Height), int(ws.Width)
|
|
|
|
}
|
2015-12-13 20:25:28 -05:00
|
|
|
|
2016-06-08 15:58:45 -04:00
|
|
|
// CopyToFile writes the content of the reader to the specified file
|
2016-06-05 10:42:19 -04:00
|
|
|
func CopyToFile(outfile string, r io.Reader) error {
|
2015-12-13 20:25:28 -05: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 13:55:33 -05:00
|
|
|
|
2016-06-19 12:53:31 -04:00
|
|
|
// ForwardAllSignals forwards signals to the container
|
2016-06-05 11:03:58 -04: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-13 22:56:23 -04: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))
|
|
|
|
}
|
|
|
|
}
|