github.com/portworx/docker@v1.12.1/api/client/utils.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "os" 8 gosignal "os/signal" 9 "path/filepath" 10 "runtime" 11 "strings" 12 "time" 13 14 "golang.org/x/net/context" 15 16 "github.com/Sirupsen/logrus" 17 "github.com/docker/docker/pkg/signal" 18 "github.com/docker/docker/pkg/term" 19 "github.com/docker/engine-api/client" 20 "github.com/docker/engine-api/types" 21 ) 22 23 func (cli *DockerCli) resizeTty(ctx context.Context, id string, isExec bool) { 24 height, width := cli.GetTtySize() 25 cli.ResizeTtyTo(ctx, id, height, width, isExec) 26 } 27 28 // ResizeTtyTo resizes tty to specific height and width 29 // TODO: this can be unexported again once all container related commands move to package container 30 func (cli *DockerCli) ResizeTtyTo(ctx context.Context, id string, height, width int, isExec bool) { 31 if height == 0 && width == 0 { 32 return 33 } 34 35 options := types.ResizeOptions{ 36 Height: height, 37 Width: width, 38 } 39 40 var err error 41 if isExec { 42 err = cli.client.ContainerExecResize(ctx, id, options) 43 } else { 44 err = cli.client.ContainerResize(ctx, id, options) 45 } 46 47 if err != nil { 48 logrus.Debugf("Error resize: %s", err) 49 } 50 } 51 52 // getExecExitCode perform an inspect on the exec command. It returns 53 // the running state and the exit code. 54 func (cli *DockerCli) getExecExitCode(ctx context.Context, execID string) (bool, int, error) { 55 resp, err := cli.client.ContainerExecInspect(ctx, execID) 56 if err != nil { 57 // If we can't connect, then the daemon probably died. 58 if err != client.ErrConnectionFailed { 59 return false, -1, err 60 } 61 return false, -1, nil 62 } 63 64 return resp.Running, resp.ExitCode, nil 65 } 66 67 // MonitorTtySize updates the container tty size when the terminal tty changes size 68 func (cli *DockerCli) MonitorTtySize(ctx context.Context, id string, isExec bool) error { 69 cli.resizeTty(ctx, id, isExec) 70 71 if runtime.GOOS == "windows" { 72 go func() { 73 prevH, prevW := cli.GetTtySize() 74 for { 75 time.Sleep(time.Millisecond * 250) 76 h, w := cli.GetTtySize() 77 78 if prevW != w || prevH != h { 79 cli.resizeTty(ctx, id, isExec) 80 } 81 prevH = h 82 prevW = w 83 } 84 }() 85 } else { 86 sigchan := make(chan os.Signal, 1) 87 gosignal.Notify(sigchan, signal.SIGWINCH) 88 go func() { 89 for range sigchan { 90 cli.resizeTty(ctx, id, isExec) 91 } 92 }() 93 } 94 return nil 95 } 96 97 // GetTtySize returns the height and width in characters of the tty 98 func (cli *DockerCli) GetTtySize() (int, int) { 99 if !cli.isTerminalOut { 100 return 0, 0 101 } 102 ws, err := term.GetWinsize(cli.outFd) 103 if err != nil { 104 logrus.Debugf("Error getting size: %s", err) 105 if ws == nil { 106 return 0, 0 107 } 108 } 109 return int(ws.Height), int(ws.Width) 110 } 111 112 // CopyToFile writes the content of the reader to the specified file 113 func CopyToFile(outfile string, r io.Reader) error { 114 tmpFile, err := ioutil.TempFile(filepath.Dir(outfile), ".docker_temp_") 115 if err != nil { 116 return err 117 } 118 119 tmpPath := tmpFile.Name() 120 121 _, err = io.Copy(tmpFile, r) 122 tmpFile.Close() 123 124 if err != nil { 125 os.Remove(tmpPath) 126 return err 127 } 128 129 if err = os.Rename(tmpPath, outfile); err != nil { 130 os.Remove(tmpPath) 131 return err 132 } 133 134 return nil 135 } 136 137 // ForwardAllSignals forwards signals to the container 138 // TODO: this can be unexported again once all container commands are under 139 // api/client/container 140 func (cli *DockerCli) ForwardAllSignals(ctx context.Context, cid string) chan os.Signal { 141 sigc := make(chan os.Signal, 128) 142 signal.CatchAll(sigc) 143 go func() { 144 for s := range sigc { 145 if s == signal.SIGCHLD || s == signal.SIGPIPE { 146 continue 147 } 148 var sig string 149 for sigStr, sigN := range signal.SignalMap { 150 if sigN == s { 151 sig = sigStr 152 break 153 } 154 } 155 if sig == "" { 156 fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s) 157 continue 158 } 159 160 if err := cli.client.ContainerKill(ctx, cid, sig); err != nil { 161 logrus.Debugf("Error sending signal: %s", err) 162 } 163 } 164 }() 165 return sigc 166 } 167 168 // capitalizeFirst capitalizes the first character of string 169 func capitalizeFirst(s string) string { 170 switch l := len(s); l { 171 case 0: 172 return s 173 case 1: 174 return strings.ToLower(s) 175 default: 176 return strings.ToUpper(string(s[0])) + strings.ToLower(s[1:]) 177 } 178 } 179 180 // PrettyPrint outputs arbitrary data for human formatted output by uppercasing the first letter. 181 func PrettyPrint(i interface{}) string { 182 switch t := i.(type) { 183 case nil: 184 return "None" 185 case string: 186 return capitalizeFirst(t) 187 default: 188 return capitalizeFirst(fmt.Sprintf("%s", t)) 189 } 190 }