| package client |
| |
| import ( |
| "fmt" |
| "io" |
| "io/ioutil" |
| "os" |
| gosignal "os/signal" |
| "path/filepath" |
| "runtime" |
| "strings" |
| "time" |
| |
| "golang.org/x/net/context" |
| |
| "github.com/Sirupsen/logrus" |
| "github.com/docker/docker/pkg/signal" |
| "github.com/docker/docker/pkg/term" |
| "github.com/docker/engine-api/client" |
| "github.com/docker/engine-api/types" |
| ) |
| |
| func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) { |
| height, width := cli.GetTtySize() |
| cli.ResizeTtyTo(ctx, id, height, width, isExec) |
| } |
| |
| // 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) { |
| if height == 0 && width == 0 { |
| return |
| } |
| |
| options := types.ResizeOptions{ |
| Height: height, |
| Width: width, |
| } |
| |
| var err error |
| if isExec { |
| err = cli.client.ContainerExecResize(ctx, id, options) |
| } else { |
| err = cli.client.ContainerResize(ctx, id, options) |
| } |
| |
| if err != nil { |
| logrus.Debugf("Error resize: %s", err) |
| } |
| } |
| |
| // getExecExitCode perform an inspect on the exec command. It returns |
| // the running state and the exit code. |
| func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) { |
| resp, err := cli.client.ContainerExecInspect(ctx, execID) |
| if err != nil { |
| // If we can't connect, then the daemon probably died. |
| if err != client.ErrConnectionFailed { |
| return false, -1, err |
| } |
| return false, -1, nil |
| } |
| |
| return resp.Running, resp.ExitCode, nil |
| } |
| |
| // MonitorTtySize updates the container tty size when the terminal tty changes size |
| func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error { |
| cli.resizeTty(ctx, id, isExec) |
| |
| if runtime.GOOS == "windows" { |
| go func() { |
| prevH, prevW := cli.GetTtySize() |
| for { |
| time.Sleep(time.Millisecond * 250) |
| h, w := cli.GetTtySize() |
| |
| if prevW != w || prevH != h { |
| cli.resizeTty(ctx, id, isExec) |
| } |
| prevH = h |
| prevW = w |
| } |
| }() |
| } else { |
| sigchan := make(chan os.Signal, 1) |
| gosignal.Notify(sigchan, signal.SIGWINCH) |
| go func() { |
| for range sigchan { |
| cli.resizeTty(ctx, id, isExec) |
| } |
| }() |
| } |
| return nil |
| } |
| |
| // GetTtySize returns the height and width in characters of the tty |
| func (cli *DockerCli) GetTtySize() (int, int) { |
| if !cli.isTerminalOut { |
| return 0, 0 |
| } |
| ws, err := term.GetWinsize(cli.outFd) |
| if err != nil { |
| logrus.Debugf("Error getting size: %s", err) |
| if ws == nil { |
| return 0, 0 |
| } |
| } |
| return int(ws.Height), int(ws.Width) |
| } |
| |
| // CopyToFile writes the content of the reader to the specified file |
| func CopyToFile(outfile string, r io.Reader) error { |
| 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 |
| } |
| |
| // ForwardAllSignals forwards signals to the container |
| // 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 |
| } |
| |
| // 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)) |
| } |
| } |