github.com/a4a881d4/docker@v1.9.0-rc2/api/client/start.go (about) 1 package client 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io" 7 "net/url" 8 "os" 9 10 "github.com/Sirupsen/logrus" 11 "github.com/docker/docker/api/types" 12 Cli "github.com/docker/docker/cli" 13 flag "github.com/docker/docker/pkg/mflag" 14 "github.com/docker/docker/pkg/promise" 15 "github.com/docker/docker/pkg/signal" 16 ) 17 18 func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal { 19 sigc := make(chan os.Signal, 128) 20 signal.CatchAll(sigc) 21 go func() { 22 for s := range sigc { 23 if s == signal.SIGCHLD { 24 continue 25 } 26 var sig string 27 for sigStr, sigN := range signal.SignalMap { 28 if sigN == s { 29 sig = sigStr 30 break 31 } 32 } 33 if sig == "" { 34 fmt.Fprintf(cli.err, "Unsupported signal: %v. Discarding.\n", s) 35 continue 36 } 37 if _, _, err := readBody(cli.call("POST", fmt.Sprintf("/containers/%s/kill?signal=%s", cid, sig), nil, nil)); err != nil { 38 logrus.Debugf("Error sending signal: %s", err) 39 } 40 } 41 }() 42 return sigc 43 } 44 45 // CmdStart starts one or more containers. 46 // 47 // Usage: docker start [OPTIONS] CONTAINER [CONTAINER...] 48 func (cli *DockerCli) CmdStart(args ...string) error { 49 cmd := Cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, Cli.DockerCommands["start"].Description, true) 50 attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals") 51 openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN") 52 cmd.Require(flag.Min, 1) 53 54 cmd.ParseFlags(args, true) 55 56 var ( 57 cErr chan error 58 tty bool 59 ) 60 61 if *attach || *openStdin { 62 if cmd.NArg() > 1 { 63 return fmt.Errorf("You cannot start and attach multiple containers at once.") 64 } 65 66 serverResp, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil) 67 if err != nil { 68 return err 69 } 70 71 defer serverResp.body.Close() 72 73 var c types.ContainerJSON 74 if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil { 75 return err 76 } 77 78 tty = c.Config.Tty 79 80 if !tty { 81 sigc := cli.forwardAllSignals(cmd.Arg(0)) 82 defer signal.StopCatch(sigc) 83 } 84 85 var in io.ReadCloser 86 87 v := url.Values{} 88 v.Set("stream", "1") 89 90 if *openStdin && c.Config.OpenStdin { 91 v.Set("stdin", "1") 92 in = cli.in 93 } 94 95 v.Set("stdout", "1") 96 v.Set("stderr", "1") 97 98 hijacked := make(chan io.Closer) 99 // Block the return until the chan gets closed 100 defer func() { 101 logrus.Debugf("CmdStart() returned, defer waiting for hijack to finish.") 102 if _, ok := <-hijacked; ok { 103 fmt.Fprintln(cli.err, "Hijack did not finish (chan still open)") 104 } 105 cli.in.Close() 106 }() 107 cErr = promise.Go(func() error { 108 return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil) 109 }) 110 111 // Acknowledge the hijack before starting 112 select { 113 case closer := <-hijacked: 114 // Make sure that the hijack gets closed when returning (results 115 // in closing the hijack chan and freeing server's goroutines) 116 if closer != nil { 117 defer closer.Close() 118 } 119 case err := <-cErr: 120 if err != nil { 121 return err 122 } 123 } 124 } 125 126 var encounteredError error 127 var errNames []string 128 for _, name := range cmd.Args() { 129 _, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, nil)) 130 if err != nil { 131 if !*attach && !*openStdin { 132 // attach and openStdin is false means it could be starting multiple containers 133 // when a container start failed, show the error message and start next 134 fmt.Fprintf(cli.err, "%s\n", err) 135 errNames = append(errNames, name) 136 } else { 137 encounteredError = err 138 } 139 } else { 140 if !*attach && !*openStdin { 141 fmt.Fprintf(cli.out, "%s\n", name) 142 } 143 } 144 } 145 146 if len(errNames) > 0 { 147 encounteredError = fmt.Errorf("Error: failed to start containers: %v", errNames) 148 } 149 if encounteredError != nil { 150 return encounteredError 151 } 152 153 if *openStdin || *attach { 154 if tty && cli.isTerminalOut { 155 if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil { 156 fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err) 157 } 158 } 159 if attchErr := <-cErr; attchErr != nil { 160 return attchErr 161 } 162 _, status, err := getExitCode(cli, cmd.Arg(0)) 163 if err != nil { 164 return err 165 } 166 if status != 0 { 167 return Cli.StatusError{StatusCode: status} 168 } 169 } 170 return nil 171 }