github.com/lmars/docker@v1.6.0-rc2/api/server/server.go (about)

     1  package server
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  
     7  	"encoding/base64"
     8  	"encoding/json"
     9  	"expvar"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"net"
    14  	"net/http"
    15  	"net/http/pprof"
    16  	"os"
    17  	"strconv"
    18  	"strings"
    19  
    20  	"crypto/tls"
    21  	"crypto/x509"
    22  
    23  	"code.google.com/p/go.net/websocket"
    24  	"github.com/docker/libcontainer/user"
    25  	"github.com/gorilla/mux"
    26  
    27  	log "github.com/Sirupsen/logrus"
    28  	"github.com/docker/docker/api"
    29  	"github.com/docker/docker/api/types"
    30  	"github.com/docker/docker/daemon/networkdriver/portallocator"
    31  	"github.com/docker/docker/engine"
    32  	"github.com/docker/docker/pkg/listenbuffer"
    33  	"github.com/docker/docker/pkg/parsers"
    34  	"github.com/docker/docker/pkg/stdcopy"
    35  	"github.com/docker/docker/pkg/version"
    36  	"github.com/docker/docker/registry"
    37  	"github.com/docker/docker/utils"
    38  )
    39  
    40  var (
    41  	activationLock chan struct{}
    42  )
    43  
    44  type HttpServer struct {
    45  	srv *http.Server
    46  	l   net.Listener
    47  }
    48  
    49  func (s *HttpServer) Serve() error {
    50  	return s.srv.Serve(s.l)
    51  }
    52  func (s *HttpServer) Close() error {
    53  	return s.l.Close()
    54  }
    55  
    56  type HttpApiFunc func(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error
    57  
    58  func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
    59  	conn, _, err := w.(http.Hijacker).Hijack()
    60  	if err != nil {
    61  		return nil, nil, err
    62  	}
    63  	// Flush the options to make sure the client sets the raw mode
    64  	conn.Write([]byte{})
    65  	return conn, conn, nil
    66  }
    67  
    68  func closeStreams(streams ...interface{}) {
    69  	for _, stream := range streams {
    70  		if tcpc, ok := stream.(interface {
    71  			CloseWrite() error
    72  		}); ok {
    73  			tcpc.CloseWrite()
    74  		} else if closer, ok := stream.(io.Closer); ok {
    75  			closer.Close()
    76  		}
    77  	}
    78  }
    79  
    80  // Check to make sure request's Content-Type is application/json
    81  func checkForJson(r *http.Request) error {
    82  	ct := r.Header.Get("Content-Type")
    83  
    84  	// No Content-Type header is ok as long as there's no Body
    85  	if ct == "" {
    86  		if r.Body == nil || r.ContentLength == 0 {
    87  			return nil
    88  		}
    89  	}
    90  
    91  	// Otherwise it better be json
    92  	if api.MatchesContentType(ct, "application/json") {
    93  		return nil
    94  	}
    95  	return fmt.Errorf("Content-Type specified (%s) must be 'application/json'", ct)
    96  }
    97  
    98  //If we don't do this, POST method without Content-type (even with empty body) will fail
    99  func parseForm(r *http.Request) error {
   100  	if r == nil {
   101  		return nil
   102  	}
   103  	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
   104  		return err
   105  	}
   106  	return nil
   107  }
   108  
   109  func parseMultipartForm(r *http.Request) error {
   110  	if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
   111  		return err
   112  	}
   113  	return nil
   114  }
   115  
   116  func httpError(w http.ResponseWriter, err error) {
   117  	statusCode := http.StatusInternalServerError
   118  	// FIXME: this is brittle and should not be necessary.
   119  	// If we need to differentiate between different possible error types, we should
   120  	// create appropriate error types with clearly defined meaning.
   121  	errStr := strings.ToLower(err.Error())
   122  	if strings.Contains(errStr, "no such") {
   123  		statusCode = http.StatusNotFound
   124  	} else if strings.Contains(errStr, "bad parameter") {
   125  		statusCode = http.StatusBadRequest
   126  	} else if strings.Contains(errStr, "conflict") {
   127  		statusCode = http.StatusConflict
   128  	} else if strings.Contains(errStr, "impossible") {
   129  		statusCode = http.StatusNotAcceptable
   130  	} else if strings.Contains(errStr, "wrong login/password") {
   131  		statusCode = http.StatusUnauthorized
   132  	} else if strings.Contains(errStr, "hasn't been activated") {
   133  		statusCode = http.StatusForbidden
   134  	}
   135  
   136  	if err != nil {
   137  		log.Errorf("HTTP Error: statusCode=%d %v", statusCode, err)
   138  		http.Error(w, err.Error(), statusCode)
   139  	}
   140  }
   141  
   142  // writeJSONEnv writes the engine.Env values to the http response stream as a
   143  // json encoded body.
   144  func writeJSONEnv(w http.ResponseWriter, code int, v engine.Env) error {
   145  	w.Header().Set("Content-Type", "application/json")
   146  	w.WriteHeader(code)
   147  	return v.Encode(w)
   148  }
   149  
   150  // writeJSON writes the value v to the http response stream as json with standard
   151  // json encoding.
   152  func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
   153  	w.Header().Set("Content-Type", "application/json")
   154  	w.WriteHeader(code)
   155  	return json.NewEncoder(w).Encode(v)
   156  }
   157  
   158  func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) {
   159  	w.Header().Set("Content-Type", "application/json")
   160  	if flush {
   161  		job.Stdout.Add(utils.NewWriteFlusher(w))
   162  	} else {
   163  		job.Stdout.Add(w)
   164  	}
   165  }
   166  
   167  func getBoolParam(value string) (bool, error) {
   168  	if value == "" {
   169  		return false, nil
   170  	}
   171  	ret, err := strconv.ParseBool(value)
   172  	if err != nil {
   173  		return false, fmt.Errorf("Bad parameter")
   174  	}
   175  	return ret, nil
   176  }
   177  
   178  func postAuth(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   179  	var (
   180  		authConfig, err = ioutil.ReadAll(r.Body)
   181  		job             = eng.Job("auth")
   182  		stdoutBuffer    = bytes.NewBuffer(nil)
   183  	)
   184  	if err != nil {
   185  		return err
   186  	}
   187  	job.Setenv("authConfig", string(authConfig))
   188  	job.Stdout.Add(stdoutBuffer)
   189  	if err = job.Run(); err != nil {
   190  		return err
   191  	}
   192  	if status := engine.Tail(stdoutBuffer, 1); status != "" {
   193  		var env engine.Env
   194  		env.Set("Status", status)
   195  		return writeJSONEnv(w, http.StatusOK, env)
   196  	}
   197  	w.WriteHeader(http.StatusNoContent)
   198  	return nil
   199  }
   200  
   201  func getVersion(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   202  	w.Header().Set("Content-Type", "application/json")
   203  	eng.ServeHTTP(w, r)
   204  	return nil
   205  }
   206  
   207  func postContainersKill(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   208  	if vars == nil {
   209  		return fmt.Errorf("Missing parameter")
   210  	}
   211  	if err := parseForm(r); err != nil {
   212  		return err
   213  	}
   214  	job := eng.Job("kill", vars["name"])
   215  	if sig := r.Form.Get("signal"); sig != "" {
   216  		job.Args = append(job.Args, sig)
   217  	}
   218  	if err := job.Run(); err != nil {
   219  		return err
   220  	}
   221  	w.WriteHeader(http.StatusNoContent)
   222  	return nil
   223  }
   224  
   225  func postContainersPause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   226  	if vars == nil {
   227  		return fmt.Errorf("Missing parameter")
   228  	}
   229  	if err := parseForm(r); err != nil {
   230  		return err
   231  	}
   232  	job := eng.Job("pause", vars["name"])
   233  	if err := job.Run(); err != nil {
   234  		return err
   235  	}
   236  	w.WriteHeader(http.StatusNoContent)
   237  	return nil
   238  }
   239  
   240  func postContainersUnpause(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   241  	if vars == nil {
   242  		return fmt.Errorf("Missing parameter")
   243  	}
   244  	if err := parseForm(r); err != nil {
   245  		return err
   246  	}
   247  	job := eng.Job("unpause", vars["name"])
   248  	if err := job.Run(); err != nil {
   249  		return err
   250  	}
   251  	w.WriteHeader(http.StatusNoContent)
   252  	return nil
   253  }
   254  
   255  func getContainersExport(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   256  	if vars == nil {
   257  		return fmt.Errorf("Missing parameter")
   258  	}
   259  	job := eng.Job("export", vars["name"])
   260  	job.Stdout.Add(w)
   261  	if err := job.Run(); err != nil {
   262  		return err
   263  	}
   264  	return nil
   265  }
   266  
   267  func getImagesJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   268  	if err := parseForm(r); err != nil {
   269  		return err
   270  	}
   271  
   272  	var (
   273  		err  error
   274  		outs *engine.Table
   275  		job  = eng.Job("images")
   276  	)
   277  
   278  	job.Setenv("filters", r.Form.Get("filters"))
   279  	// FIXME this parameter could just be a match filter
   280  	job.Setenv("filter", r.Form.Get("filter"))
   281  	job.Setenv("all", r.Form.Get("all"))
   282  
   283  	if version.GreaterThanOrEqualTo("1.7") {
   284  		streamJSON(job, w, false)
   285  	} else if outs, err = job.Stdout.AddListTable(); err != nil {
   286  		return err
   287  	}
   288  
   289  	if err := job.Run(); err != nil {
   290  		return err
   291  	}
   292  
   293  	if version.LessThan("1.7") && outs != nil { // Convert to legacy format
   294  		outsLegacy := engine.NewTable("Created", 0)
   295  		for _, out := range outs.Data {
   296  			for _, repoTag := range out.GetList("RepoTags") {
   297  				repo, tag := parsers.ParseRepositoryTag(repoTag)
   298  				outLegacy := &engine.Env{}
   299  				outLegacy.Set("Repository", repo)
   300  				outLegacy.SetJson("Tag", tag)
   301  				outLegacy.Set("Id", out.Get("Id"))
   302  				outLegacy.SetInt64("Created", out.GetInt64("Created"))
   303  				outLegacy.SetInt64("Size", out.GetInt64("Size"))
   304  				outLegacy.SetInt64("VirtualSize", out.GetInt64("VirtualSize"))
   305  				outsLegacy.Add(outLegacy)
   306  			}
   307  		}
   308  		w.Header().Set("Content-Type", "application/json")
   309  		if _, err := outsLegacy.WriteListTo(w); err != nil {
   310  			return err
   311  		}
   312  	}
   313  	return nil
   314  }
   315  
   316  func getImagesViz(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   317  	if version.GreaterThan("1.6") {
   318  		w.WriteHeader(http.StatusNotFound)
   319  		return fmt.Errorf("This is now implemented in the client.")
   320  	}
   321  	eng.ServeHTTP(w, r)
   322  	return nil
   323  }
   324  
   325  func getInfo(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   326  	w.Header().Set("Content-Type", "application/json")
   327  	eng.ServeHTTP(w, r)
   328  	return nil
   329  }
   330  
   331  func getEvents(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   332  	if err := parseForm(r); err != nil {
   333  		return err
   334  	}
   335  
   336  	var job = eng.Job("events")
   337  	streamJSON(job, w, true)
   338  	job.Setenv("since", r.Form.Get("since"))
   339  	job.Setenv("until", r.Form.Get("until"))
   340  	job.Setenv("filters", r.Form.Get("filters"))
   341  	return job.Run()
   342  }
   343  
   344  func getImagesHistory(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   345  	if vars == nil {
   346  		return fmt.Errorf("Missing parameter")
   347  	}
   348  
   349  	var job = eng.Job("history", vars["name"])
   350  	streamJSON(job, w, false)
   351  
   352  	if err := job.Run(); err != nil {
   353  		return err
   354  	}
   355  	return nil
   356  }
   357  
   358  func getContainersChanges(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   359  	if vars == nil {
   360  		return fmt.Errorf("Missing parameter")
   361  	}
   362  	var job = eng.Job("container_changes", vars["name"])
   363  	streamJSON(job, w, false)
   364  
   365  	return job.Run()
   366  }
   367  
   368  func getContainersTop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   369  	if version.LessThan("1.4") {
   370  		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
   371  	}
   372  	if vars == nil {
   373  		return fmt.Errorf("Missing parameter")
   374  	}
   375  	if err := parseForm(r); err != nil {
   376  		return err
   377  	}
   378  
   379  	job := eng.Job("top", vars["name"], r.Form.Get("ps_args"))
   380  	streamJSON(job, w, false)
   381  	return job.Run()
   382  }
   383  
   384  func getContainersJSON(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   385  	if err := parseForm(r); err != nil {
   386  		return err
   387  	}
   388  	var (
   389  		err  error
   390  		outs *engine.Table
   391  		job  = eng.Job("containers")
   392  	)
   393  
   394  	job.Setenv("all", r.Form.Get("all"))
   395  	job.Setenv("size", r.Form.Get("size"))
   396  	job.Setenv("since", r.Form.Get("since"))
   397  	job.Setenv("before", r.Form.Get("before"))
   398  	job.Setenv("limit", r.Form.Get("limit"))
   399  	job.Setenv("filters", r.Form.Get("filters"))
   400  
   401  	if version.GreaterThanOrEqualTo("1.5") {
   402  		streamJSON(job, w, false)
   403  	} else if outs, err = job.Stdout.AddTable(); err != nil {
   404  		return err
   405  	}
   406  	if err = job.Run(); err != nil {
   407  		return err
   408  	}
   409  	if version.LessThan("1.5") { // Convert to legacy format
   410  		for _, out := range outs.Data {
   411  			ports := engine.NewTable("", 0)
   412  			ports.ReadListFrom([]byte(out.Get("Ports")))
   413  			out.Set("Ports", api.DisplayablePorts(ports))
   414  		}
   415  		w.Header().Set("Content-Type", "application/json")
   416  		if _, err = outs.WriteListTo(w); err != nil {
   417  			return err
   418  		}
   419  	}
   420  	return nil
   421  }
   422  
   423  func getContainersStats(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   424  	if err := parseForm(r); err != nil {
   425  		return err
   426  	}
   427  	if vars == nil {
   428  		return fmt.Errorf("Missing parameter")
   429  	}
   430  	name := vars["name"]
   431  	job := eng.Job("container_stats", name)
   432  	streamJSON(job, w, true)
   433  	return job.Run()
   434  }
   435  
   436  func getContainersLogs(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   437  	if err := parseForm(r); err != nil {
   438  		return err
   439  	}
   440  	if vars == nil {
   441  		return fmt.Errorf("Missing parameter")
   442  	}
   443  
   444  	var (
   445  		inspectJob = eng.Job("container_inspect", vars["name"])
   446  		logsJob    = eng.Job("logs", vars["name"])
   447  		c, err     = inspectJob.Stdout.AddEnv()
   448  	)
   449  	if err != nil {
   450  		return err
   451  	}
   452  	logsJob.Setenv("follow", r.Form.Get("follow"))
   453  	logsJob.Setenv("tail", r.Form.Get("tail"))
   454  	logsJob.Setenv("stdout", r.Form.Get("stdout"))
   455  	logsJob.Setenv("stderr", r.Form.Get("stderr"))
   456  	logsJob.Setenv("timestamps", r.Form.Get("timestamps"))
   457  	// Validate args here, because we can't return not StatusOK after job.Run() call
   458  	stdout, stderr := logsJob.GetenvBool("stdout"), logsJob.GetenvBool("stderr")
   459  	if !(stdout || stderr) {
   460  		return fmt.Errorf("Bad parameters: you must choose at least one stream")
   461  	}
   462  	if err = inspectJob.Run(); err != nil {
   463  		return err
   464  	}
   465  
   466  	var outStream, errStream io.Writer
   467  	outStream = utils.NewWriteFlusher(w)
   468  
   469  	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
   470  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
   471  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
   472  	} else {
   473  		errStream = outStream
   474  	}
   475  
   476  	logsJob.Stdout.Add(outStream)
   477  	logsJob.Stderr.Set(errStream)
   478  	if err := logsJob.Run(); err != nil {
   479  		fmt.Fprintf(outStream, "Error running logs job: %s\n", err)
   480  	}
   481  	return nil
   482  }
   483  
   484  func postImagesTag(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   485  	if err := parseForm(r); err != nil {
   486  		return err
   487  	}
   488  	if vars == nil {
   489  		return fmt.Errorf("Missing parameter")
   490  	}
   491  
   492  	job := eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
   493  	job.Setenv("force", r.Form.Get("force"))
   494  	if err := job.Run(); err != nil {
   495  		return err
   496  	}
   497  	w.WriteHeader(http.StatusCreated)
   498  	return nil
   499  }
   500  
   501  func postCommit(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   502  	if err := parseForm(r); err != nil {
   503  		return err
   504  	}
   505  	var (
   506  		config       engine.Env
   507  		env          engine.Env
   508  		job          = eng.Job("commit", r.Form.Get("container"))
   509  		stdoutBuffer = bytes.NewBuffer(nil)
   510  	)
   511  
   512  	if err := checkForJson(r); err != nil {
   513  		return err
   514  	}
   515  
   516  	if err := config.Decode(r.Body); err != nil {
   517  		log.Errorf("%s", err)
   518  	}
   519  
   520  	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
   521  		job.Setenv("pause", "1")
   522  	} else {
   523  		job.Setenv("pause", r.FormValue("pause"))
   524  	}
   525  
   526  	job.Setenv("repo", r.Form.Get("repo"))
   527  	job.Setenv("tag", r.Form.Get("tag"))
   528  	job.Setenv("author", r.Form.Get("author"))
   529  	job.Setenv("comment", r.Form.Get("comment"))
   530  	job.SetenvList("changes", r.Form["changes"])
   531  	job.SetenvSubEnv("config", &config)
   532  
   533  	job.Stdout.Add(stdoutBuffer)
   534  	if err := job.Run(); err != nil {
   535  		return err
   536  	}
   537  	env.Set("Id", engine.Tail(stdoutBuffer, 1))
   538  	return writeJSONEnv(w, http.StatusCreated, env)
   539  }
   540  
   541  // Creates an image from Pull or from Import
   542  func postImagesCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   543  	if err := parseForm(r); err != nil {
   544  		return err
   545  	}
   546  
   547  	var (
   548  		image = r.Form.Get("fromImage")
   549  		repo  = r.Form.Get("repo")
   550  		tag   = r.Form.Get("tag")
   551  		job   *engine.Job
   552  	)
   553  	authEncoded := r.Header.Get("X-Registry-Auth")
   554  	authConfig := &registry.AuthConfig{}
   555  	if authEncoded != "" {
   556  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   557  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   558  			// for a pull it is not an error if no auth was given
   559  			// to increase compatibility with the existing api it is defaulting to be empty
   560  			authConfig = &registry.AuthConfig{}
   561  		}
   562  	}
   563  	if image != "" { //pull
   564  		if tag == "" {
   565  			image, tag = parsers.ParseRepositoryTag(image)
   566  		}
   567  		metaHeaders := map[string][]string{}
   568  		for k, v := range r.Header {
   569  			if strings.HasPrefix(k, "X-Meta-") {
   570  				metaHeaders[k] = v
   571  			}
   572  		}
   573  		job = eng.Job("pull", image, tag)
   574  		job.SetenvBool("parallel", version.GreaterThan("1.3"))
   575  		job.SetenvJson("metaHeaders", metaHeaders)
   576  		job.SetenvJson("authConfig", authConfig)
   577  	} else { //import
   578  		if tag == "" {
   579  			repo, tag = parsers.ParseRepositoryTag(repo)
   580  		}
   581  		job = eng.Job("import", r.Form.Get("fromSrc"), repo, tag)
   582  		job.Stdin.Add(r.Body)
   583  		job.SetenvList("changes", r.Form["changes"])
   584  	}
   585  
   586  	if version.GreaterThan("1.0") {
   587  		job.SetenvBool("json", true)
   588  		streamJSON(job, w, true)
   589  	} else {
   590  		job.Stdout.Add(utils.NewWriteFlusher(w))
   591  	}
   592  	if err := job.Run(); err != nil {
   593  		if !job.Stdout.Used() {
   594  			return err
   595  		}
   596  		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
   597  		w.Write(sf.FormatError(err))
   598  	}
   599  
   600  	return nil
   601  }
   602  
   603  func getImagesSearch(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   604  	if err := parseForm(r); err != nil {
   605  		return err
   606  	}
   607  	var (
   608  		authEncoded = r.Header.Get("X-Registry-Auth")
   609  		authConfig  = &registry.AuthConfig{}
   610  		metaHeaders = map[string][]string{}
   611  	)
   612  
   613  	if authEncoded != "" {
   614  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   615  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   616  			// for a search it is not an error if no auth was given
   617  			// to increase compatibility with the existing api it is defaulting to be empty
   618  			authConfig = &registry.AuthConfig{}
   619  		}
   620  	}
   621  	for k, v := range r.Header {
   622  		if strings.HasPrefix(k, "X-Meta-") {
   623  			metaHeaders[k] = v
   624  		}
   625  	}
   626  
   627  	var job = eng.Job("search", r.Form.Get("term"))
   628  	job.SetenvJson("metaHeaders", metaHeaders)
   629  	job.SetenvJson("authConfig", authConfig)
   630  	streamJSON(job, w, false)
   631  
   632  	return job.Run()
   633  }
   634  
   635  func postImagesPush(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   636  	if vars == nil {
   637  		return fmt.Errorf("Missing parameter")
   638  	}
   639  
   640  	metaHeaders := map[string][]string{}
   641  	for k, v := range r.Header {
   642  		if strings.HasPrefix(k, "X-Meta-") {
   643  			metaHeaders[k] = v
   644  		}
   645  	}
   646  	if err := parseForm(r); err != nil {
   647  		return err
   648  	}
   649  	authConfig := &registry.AuthConfig{}
   650  
   651  	authEncoded := r.Header.Get("X-Registry-Auth")
   652  	if authEncoded != "" {
   653  		// the new format is to handle the authConfig as a header
   654  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
   655  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
   656  			// to increase compatibility to existing api it is defaulting to be empty
   657  			authConfig = &registry.AuthConfig{}
   658  		}
   659  	} else {
   660  		// the old format is supported for compatibility if there was no authConfig header
   661  		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
   662  			return err
   663  		}
   664  	}
   665  
   666  	job := eng.Job("push", vars["name"])
   667  	job.SetenvJson("metaHeaders", metaHeaders)
   668  	job.SetenvJson("authConfig", authConfig)
   669  	job.Setenv("tag", r.Form.Get("tag"))
   670  	if version.GreaterThan("1.0") {
   671  		job.SetenvBool("json", true)
   672  		streamJSON(job, w, true)
   673  	} else {
   674  		job.Stdout.Add(utils.NewWriteFlusher(w))
   675  	}
   676  
   677  	if err := job.Run(); err != nil {
   678  		if !job.Stdout.Used() {
   679  			return err
   680  		}
   681  		sf := utils.NewStreamFormatter(version.GreaterThan("1.0"))
   682  		w.Write(sf.FormatError(err))
   683  	}
   684  	return nil
   685  }
   686  
   687  func getImagesGet(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   688  	if vars == nil {
   689  		return fmt.Errorf("Missing parameter")
   690  	}
   691  	if err := parseForm(r); err != nil {
   692  		return err
   693  	}
   694  	if version.GreaterThan("1.0") {
   695  		w.Header().Set("Content-Type", "application/x-tar")
   696  	}
   697  	var job *engine.Job
   698  	if name, ok := vars["name"]; ok {
   699  		job = eng.Job("image_export", name)
   700  	} else {
   701  		job = eng.Job("image_export", r.Form["names"]...)
   702  	}
   703  	job.Stdout.Add(w)
   704  	return job.Run()
   705  }
   706  
   707  func postImagesLoad(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   708  	job := eng.Job("load")
   709  	job.Stdin.Add(r.Body)
   710  	return job.Run()
   711  }
   712  
   713  func postContainersCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   714  	if err := parseForm(r); err != nil {
   715  		return nil
   716  	}
   717  	if err := checkForJson(r); err != nil {
   718  		return err
   719  	}
   720  	var (
   721  		job          = eng.Job("create", r.Form.Get("name"))
   722  		outWarnings  []string
   723  		stdoutBuffer = bytes.NewBuffer(nil)
   724  		warnings     = bytes.NewBuffer(nil)
   725  	)
   726  
   727  	if err := job.DecodeEnv(r.Body); err != nil {
   728  		return err
   729  	}
   730  	// Read container ID from the first line of stdout
   731  	job.Stdout.Add(stdoutBuffer)
   732  	// Read warnings from stderr
   733  	job.Stderr.Add(warnings)
   734  	if err := job.Run(); err != nil {
   735  		return err
   736  	}
   737  	// Parse warnings from stderr
   738  	scanner := bufio.NewScanner(warnings)
   739  	for scanner.Scan() {
   740  		outWarnings = append(outWarnings, scanner.Text())
   741  	}
   742  	return writeJSON(w, http.StatusCreated, &types.ContainerCreateResponse{
   743  		ID:       engine.Tail(stdoutBuffer, 1),
   744  		Warnings: outWarnings,
   745  	})
   746  }
   747  
   748  func postContainersRestart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   749  	if err := parseForm(r); err != nil {
   750  		return err
   751  	}
   752  	if vars == nil {
   753  		return fmt.Errorf("Missing parameter")
   754  	}
   755  	job := eng.Job("restart", vars["name"])
   756  	job.Setenv("t", r.Form.Get("t"))
   757  	if err := job.Run(); err != nil {
   758  		return err
   759  	}
   760  	w.WriteHeader(http.StatusNoContent)
   761  	return nil
   762  }
   763  
   764  func postContainerRename(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   765  	if err := parseForm(r); err != nil {
   766  		return err
   767  	}
   768  	if vars == nil {
   769  		return fmt.Errorf("Missing parameter")
   770  	}
   771  
   772  	newName := r.URL.Query().Get("name")
   773  	job := eng.Job("container_rename", vars["name"], newName)
   774  	job.Setenv("t", r.Form.Get("t"))
   775  	if err := job.Run(); err != nil {
   776  		return err
   777  	}
   778  	w.WriteHeader(http.StatusNoContent)
   779  	return nil
   780  }
   781  
   782  func deleteContainers(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   783  	if err := parseForm(r); err != nil {
   784  		return err
   785  	}
   786  	if vars == nil {
   787  		return fmt.Errorf("Missing parameter")
   788  	}
   789  	job := eng.Job("rm", vars["name"])
   790  
   791  	job.Setenv("forceRemove", r.Form.Get("force"))
   792  
   793  	job.Setenv("removeVolume", r.Form.Get("v"))
   794  	job.Setenv("removeLink", r.Form.Get("link"))
   795  	if err := job.Run(); err != nil {
   796  		return err
   797  	}
   798  	w.WriteHeader(http.StatusNoContent)
   799  	return nil
   800  }
   801  
   802  func deleteImages(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   803  	if err := parseForm(r); err != nil {
   804  		return err
   805  	}
   806  	if vars == nil {
   807  		return fmt.Errorf("Missing parameter")
   808  	}
   809  	var job = eng.Job("image_delete", vars["name"])
   810  	streamJSON(job, w, false)
   811  	job.Setenv("force", r.Form.Get("force"))
   812  	job.Setenv("noprune", r.Form.Get("noprune"))
   813  
   814  	return job.Run()
   815  }
   816  
   817  func postContainersStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   818  	if vars == nil {
   819  		return fmt.Errorf("Missing parameter")
   820  	}
   821  	var (
   822  		name = vars["name"]
   823  		job  = eng.Job("start", name)
   824  	)
   825  
   826  	// If contentLength is -1, we can assumed chunked encoding
   827  	// or more technically that the length is unknown
   828  	// http://golang.org/src/pkg/net/http/request.go#L139
   829  	// net/http otherwise seems to swallow any headers related to chunked encoding
   830  	// including r.TransferEncoding
   831  	// allow a nil body for backwards compatibility
   832  	if r.Body != nil && (r.ContentLength > 0 || r.ContentLength == -1) {
   833  		if err := checkForJson(r); err != nil {
   834  			return err
   835  		}
   836  
   837  		if err := job.DecodeEnv(r.Body); err != nil {
   838  			return err
   839  		}
   840  	}
   841  
   842  	if err := job.Run(); err != nil {
   843  		if err.Error() == "Container already started" {
   844  			w.WriteHeader(http.StatusNotModified)
   845  			return nil
   846  		}
   847  		return err
   848  	}
   849  	w.WriteHeader(http.StatusNoContent)
   850  	return nil
   851  }
   852  
   853  func postContainersStop(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   854  	if err := parseForm(r); err != nil {
   855  		return err
   856  	}
   857  	if vars == nil {
   858  		return fmt.Errorf("Missing parameter")
   859  	}
   860  	job := eng.Job("stop", vars["name"])
   861  	job.Setenv("t", r.Form.Get("t"))
   862  	if err := job.Run(); err != nil {
   863  		if err.Error() == "Container already stopped" {
   864  			w.WriteHeader(http.StatusNotModified)
   865  			return nil
   866  		}
   867  		return err
   868  	}
   869  	w.WriteHeader(http.StatusNoContent)
   870  	return nil
   871  }
   872  
   873  func postContainersWait(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   874  	if vars == nil {
   875  		return fmt.Errorf("Missing parameter")
   876  	}
   877  	var (
   878  		env          engine.Env
   879  		stdoutBuffer = bytes.NewBuffer(nil)
   880  		job          = eng.Job("wait", vars["name"])
   881  	)
   882  	job.Stdout.Add(stdoutBuffer)
   883  	if err := job.Run(); err != nil {
   884  		return err
   885  	}
   886  
   887  	env.Set("StatusCode", engine.Tail(stdoutBuffer, 1))
   888  	return writeJSONEnv(w, http.StatusOK, env)
   889  }
   890  
   891  func postContainersResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   892  	if err := parseForm(r); err != nil {
   893  		return err
   894  	}
   895  	if vars == nil {
   896  		return fmt.Errorf("Missing parameter")
   897  	}
   898  	if err := eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
   899  		return err
   900  	}
   901  	return nil
   902  }
   903  
   904  func postContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   905  	if err := parseForm(r); err != nil {
   906  		return err
   907  	}
   908  	if vars == nil {
   909  		return fmt.Errorf("Missing parameter")
   910  	}
   911  
   912  	var (
   913  		job    = eng.Job("container_inspect", vars["name"])
   914  		c, err = job.Stdout.AddEnv()
   915  	)
   916  	if err != nil {
   917  		return err
   918  	}
   919  	if err = job.Run(); err != nil {
   920  		return err
   921  	}
   922  
   923  	inStream, outStream, err := hijackServer(w)
   924  	if err != nil {
   925  		return err
   926  	}
   927  	defer closeStreams(inStream, outStream)
   928  
   929  	var errStream io.Writer
   930  
   931  	if _, ok := r.Header["Upgrade"]; ok {
   932  		fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
   933  	} else {
   934  		fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
   935  	}
   936  
   937  	if c.GetSubEnv("Config") != nil && !c.GetSubEnv("Config").GetBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
   938  		errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
   939  		outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
   940  	} else {
   941  		errStream = outStream
   942  	}
   943  
   944  	job = eng.Job("attach", vars["name"])
   945  	job.Setenv("logs", r.Form.Get("logs"))
   946  	job.Setenv("stream", r.Form.Get("stream"))
   947  	job.Setenv("stdin", r.Form.Get("stdin"))
   948  	job.Setenv("stdout", r.Form.Get("stdout"))
   949  	job.Setenv("stderr", r.Form.Get("stderr"))
   950  	job.Stdin.Add(inStream)
   951  	job.Stdout.Add(outStream)
   952  	job.Stderr.Set(errStream)
   953  	if err := job.Run(); err != nil {
   954  		fmt.Fprintf(outStream, "Error attaching: %s\n", err)
   955  
   956  	}
   957  	return nil
   958  }
   959  
   960  func wsContainersAttach(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   961  	if err := parseForm(r); err != nil {
   962  		return err
   963  	}
   964  	if vars == nil {
   965  		return fmt.Errorf("Missing parameter")
   966  	}
   967  
   968  	if err := eng.Job("container_inspect", vars["name"]).Run(); err != nil {
   969  		return err
   970  	}
   971  
   972  	h := websocket.Handler(func(ws *websocket.Conn) {
   973  		defer ws.Close()
   974  		job := eng.Job("attach", vars["name"])
   975  		job.Setenv("logs", r.Form.Get("logs"))
   976  		job.Setenv("stream", r.Form.Get("stream"))
   977  		job.Setenv("stdin", r.Form.Get("stdin"))
   978  		job.Setenv("stdout", r.Form.Get("stdout"))
   979  		job.Setenv("stderr", r.Form.Get("stderr"))
   980  		job.Stdin.Add(ws)
   981  		job.Stdout.Add(ws)
   982  		job.Stderr.Set(ws)
   983  		if err := job.Run(); err != nil {
   984  			log.Errorf("Error attaching websocket: %s", err)
   985  		}
   986  	})
   987  	h.ServeHTTP(w, r)
   988  
   989  	return nil
   990  }
   991  
   992  func getContainersByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
   993  	if vars == nil {
   994  		return fmt.Errorf("Missing parameter")
   995  	}
   996  	var job = eng.Job("container_inspect", vars["name"])
   997  	if version.LessThan("1.12") {
   998  		job.SetenvBool("raw", true)
   999  	}
  1000  	streamJSON(job, w, false)
  1001  	return job.Run()
  1002  }
  1003  
  1004  func getExecByID(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1005  	if vars == nil {
  1006  		return fmt.Errorf("Missing parameter 'id'")
  1007  	}
  1008  	var job = eng.Job("execInspect", vars["id"])
  1009  	streamJSON(job, w, false)
  1010  	return job.Run()
  1011  }
  1012  
  1013  func getImagesByName(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1014  	if vars == nil {
  1015  		return fmt.Errorf("Missing parameter")
  1016  	}
  1017  	var job = eng.Job("image_inspect", vars["name"])
  1018  	if version.LessThan("1.12") {
  1019  		job.SetenvBool("raw", true)
  1020  	}
  1021  	streamJSON(job, w, false)
  1022  	return job.Run()
  1023  }
  1024  
  1025  func postBuild(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1026  	if version.LessThan("1.3") {
  1027  		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
  1028  	}
  1029  	var (
  1030  		authEncoded       = r.Header.Get("X-Registry-Auth")
  1031  		authConfig        = &registry.AuthConfig{}
  1032  		configFileEncoded = r.Header.Get("X-Registry-Config")
  1033  		configFile        = &registry.ConfigFile{}
  1034  		job               = eng.Job("build")
  1035  	)
  1036  
  1037  	// This block can be removed when API versions prior to 1.9 are deprecated.
  1038  	// Both headers will be parsed and sent along to the daemon, but if a non-empty
  1039  	// ConfigFile is present, any value provided as an AuthConfig directly will
  1040  	// be overridden. See BuildFile::CmdFrom for details.
  1041  	if version.LessThan("1.9") && authEncoded != "" {
  1042  		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
  1043  		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
  1044  			// for a pull it is not an error if no auth was given
  1045  			// to increase compatibility with the existing api it is defaulting to be empty
  1046  			authConfig = &registry.AuthConfig{}
  1047  		}
  1048  	}
  1049  
  1050  	if configFileEncoded != "" {
  1051  		configFileJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(configFileEncoded))
  1052  		if err := json.NewDecoder(configFileJson).Decode(configFile); err != nil {
  1053  			// for a pull it is not an error if no auth was given
  1054  			// to increase compatibility with the existing api it is defaulting to be empty
  1055  			configFile = &registry.ConfigFile{}
  1056  		}
  1057  	}
  1058  
  1059  	if version.GreaterThanOrEqualTo("1.8") {
  1060  		job.SetenvBool("json", true)
  1061  		streamJSON(job, w, true)
  1062  	} else {
  1063  		job.Stdout.Add(utils.NewWriteFlusher(w))
  1064  	}
  1065  
  1066  	if r.FormValue("forcerm") == "1" && version.GreaterThanOrEqualTo("1.12") {
  1067  		job.Setenv("rm", "1")
  1068  	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
  1069  		job.Setenv("rm", "1")
  1070  	} else {
  1071  		job.Setenv("rm", r.FormValue("rm"))
  1072  	}
  1073  	if r.FormValue("pull") == "1" && version.GreaterThanOrEqualTo("1.16") {
  1074  		job.Setenv("pull", "1")
  1075  	}
  1076  	job.Stdin.Add(r.Body)
  1077  	job.Setenv("remote", r.FormValue("remote"))
  1078  	job.Setenv("dockerfile", r.FormValue("dockerfile"))
  1079  	job.Setenv("t", r.FormValue("t"))
  1080  	job.Setenv("q", r.FormValue("q"))
  1081  	job.Setenv("nocache", r.FormValue("nocache"))
  1082  	job.Setenv("forcerm", r.FormValue("forcerm"))
  1083  	job.SetenvJson("authConfig", authConfig)
  1084  	job.SetenvJson("configFile", configFile)
  1085  	job.Setenv("memswap", r.FormValue("memswap"))
  1086  	job.Setenv("memory", r.FormValue("memory"))
  1087  	job.Setenv("cpusetcpus", r.FormValue("cpusetcpus"))
  1088  	job.Setenv("cpushares", r.FormValue("cpushares"))
  1089  
  1090  	// Job cancellation. Note: not all job types support this.
  1091  	if closeNotifier, ok := w.(http.CloseNotifier); ok {
  1092  		finished := make(chan struct{})
  1093  		defer close(finished)
  1094  		go func() {
  1095  			select {
  1096  			case <-finished:
  1097  			case <-closeNotifier.CloseNotify():
  1098  				log.Infof("Client disconnected, cancelling job: %v", job)
  1099  				job.Cancel()
  1100  			}
  1101  		}()
  1102  	}
  1103  
  1104  	if err := job.Run(); err != nil {
  1105  		if !job.Stdout.Used() {
  1106  			return err
  1107  		}
  1108  		sf := utils.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
  1109  		w.Write(sf.FormatError(err))
  1110  	}
  1111  	return nil
  1112  }
  1113  
  1114  func postContainersCopy(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1115  	if vars == nil {
  1116  		return fmt.Errorf("Missing parameter")
  1117  	}
  1118  
  1119  	var copyData engine.Env
  1120  
  1121  	if err := checkForJson(r); err != nil {
  1122  		return err
  1123  	}
  1124  
  1125  	if err := copyData.Decode(r.Body); err != nil {
  1126  		return err
  1127  	}
  1128  
  1129  	if copyData.Get("Resource") == "" {
  1130  		return fmt.Errorf("Path cannot be empty")
  1131  	}
  1132  
  1133  	origResource := copyData.Get("Resource")
  1134  
  1135  	if copyData.Get("Resource")[0] == '/' {
  1136  		copyData.Set("Resource", copyData.Get("Resource")[1:])
  1137  	}
  1138  
  1139  	job := eng.Job("container_copy", vars["name"], copyData.Get("Resource"))
  1140  	job.Stdout.Add(w)
  1141  	w.Header().Set("Content-Type", "application/x-tar")
  1142  	if err := job.Run(); err != nil {
  1143  		log.Errorf("%v", err)
  1144  		if strings.Contains(strings.ToLower(err.Error()), "no such id") {
  1145  			w.WriteHeader(http.StatusNotFound)
  1146  		} else if strings.Contains(err.Error(), "no such file or directory") {
  1147  			return fmt.Errorf("Could not find the file %s in container %s", origResource, vars["name"])
  1148  		}
  1149  	}
  1150  	return nil
  1151  }
  1152  
  1153  func postContainerExecCreate(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1154  	if err := parseForm(r); err != nil {
  1155  		return nil
  1156  	}
  1157  	var (
  1158  		out          engine.Env
  1159  		name         = vars["name"]
  1160  		job          = eng.Job("execCreate", name)
  1161  		stdoutBuffer = bytes.NewBuffer(nil)
  1162  	)
  1163  
  1164  	if err := job.DecodeEnv(r.Body); err != nil {
  1165  		return err
  1166  	}
  1167  
  1168  	job.Stdout.Add(stdoutBuffer)
  1169  	// Register an instance of Exec in container.
  1170  	if err := job.Run(); err != nil {
  1171  		fmt.Fprintf(os.Stderr, "Error setting up exec command in container %s: %s\n", name, err)
  1172  		return err
  1173  	}
  1174  	// Return the ID
  1175  	out.Set("Id", engine.Tail(stdoutBuffer, 1))
  1176  
  1177  	return writeJSONEnv(w, http.StatusCreated, out)
  1178  }
  1179  
  1180  // TODO(vishh): Refactor the code to avoid having to specify stream config as part of both create and start.
  1181  func postContainerExecStart(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1182  	if err := parseForm(r); err != nil {
  1183  		return nil
  1184  	}
  1185  	var (
  1186  		name             = vars["name"]
  1187  		job              = eng.Job("execStart", name)
  1188  		errOut io.Writer = os.Stderr
  1189  	)
  1190  
  1191  	if err := job.DecodeEnv(r.Body); err != nil {
  1192  		return err
  1193  	}
  1194  	if !job.GetenvBool("Detach") {
  1195  		// Setting up the streaming http interface.
  1196  		inStream, outStream, err := hijackServer(w)
  1197  		if err != nil {
  1198  			return err
  1199  		}
  1200  		defer closeStreams(inStream, outStream)
  1201  
  1202  		var errStream io.Writer
  1203  
  1204  		if _, ok := r.Header["Upgrade"]; ok {
  1205  			fmt.Fprintf(outStream, "HTTP/1.1 101 UPGRADED\r\nContent-Type: application/vnd.docker.raw-stream\r\nConnection: Upgrade\r\nUpgrade: tcp\r\n\r\n")
  1206  		} else {
  1207  			fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
  1208  		}
  1209  
  1210  		if !job.GetenvBool("Tty") && version.GreaterThanOrEqualTo("1.6") {
  1211  			errStream = stdcopy.NewStdWriter(outStream, stdcopy.Stderr)
  1212  			outStream = stdcopy.NewStdWriter(outStream, stdcopy.Stdout)
  1213  		} else {
  1214  			errStream = outStream
  1215  		}
  1216  		job.Stdin.Add(inStream)
  1217  		job.Stdout.Add(outStream)
  1218  		job.Stderr.Set(errStream)
  1219  		errOut = outStream
  1220  	}
  1221  	// Now run the user process in container.
  1222  	job.SetCloseIO(false)
  1223  	if err := job.Run(); err != nil {
  1224  		fmt.Fprintf(errOut, "Error starting exec command in container %s: %s\n", name, err)
  1225  		return err
  1226  	}
  1227  	w.WriteHeader(http.StatusNoContent)
  1228  
  1229  	return nil
  1230  }
  1231  
  1232  func postContainerExecResize(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1233  	if err := parseForm(r); err != nil {
  1234  		return err
  1235  	}
  1236  	if vars == nil {
  1237  		return fmt.Errorf("Missing parameter")
  1238  	}
  1239  	if err := eng.Job("execResize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
  1240  		return err
  1241  	}
  1242  	return nil
  1243  }
  1244  
  1245  func optionsHandler(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1246  	w.WriteHeader(http.StatusOK)
  1247  	return nil
  1248  }
  1249  func writeCorsHeaders(w http.ResponseWriter, r *http.Request, corsHeaders string) {
  1250  	log.Debugf("CORS header is enabled and set to: %s", corsHeaders)
  1251  	w.Header().Add("Access-Control-Allow-Origin", corsHeaders)
  1252  	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
  1253  	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
  1254  }
  1255  
  1256  func ping(eng *engine.Engine, version version.Version, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
  1257  	_, err := w.Write([]byte{'O', 'K'})
  1258  	return err
  1259  }
  1260  
  1261  func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, corsHeaders string, dockerVersion version.Version) http.HandlerFunc {
  1262  	return func(w http.ResponseWriter, r *http.Request) {
  1263  		// log the request
  1264  		log.Debugf("Calling %s %s", localMethod, localRoute)
  1265  
  1266  		if logging {
  1267  			log.Infof("%s %s", r.Method, r.RequestURI)
  1268  		}
  1269  
  1270  		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
  1271  			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
  1272  			if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) {
  1273  				log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion)
  1274  			}
  1275  		}
  1276  		version := version.Version(mux.Vars(r)["version"])
  1277  		if version == "" {
  1278  			version = api.APIVERSION
  1279  		}
  1280  		if corsHeaders != "" {
  1281  			writeCorsHeaders(w, r, corsHeaders)
  1282  		}
  1283  
  1284  		if version.GreaterThan(api.APIVERSION) {
  1285  			http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound)
  1286  			return
  1287  		}
  1288  
  1289  		if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil {
  1290  			log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err)
  1291  			httpError(w, err)
  1292  		}
  1293  	}
  1294  }
  1295  
  1296  // Replicated from expvar.go as not public.
  1297  func expvarHandler(w http.ResponseWriter, r *http.Request) {
  1298  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
  1299  	fmt.Fprintf(w, "{\n")
  1300  	first := true
  1301  	expvar.Do(func(kv expvar.KeyValue) {
  1302  		if !first {
  1303  			fmt.Fprintf(w, ",\n")
  1304  		}
  1305  		first = false
  1306  		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
  1307  	})
  1308  	fmt.Fprintf(w, "\n}\n")
  1309  }
  1310  
  1311  func AttachProfiler(router *mux.Router) {
  1312  	router.HandleFunc("/debug/vars", expvarHandler)
  1313  	router.HandleFunc("/debug/pprof/", pprof.Index)
  1314  	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
  1315  	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
  1316  	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
  1317  	router.HandleFunc("/debug/pprof/block", pprof.Handler("block").ServeHTTP)
  1318  	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
  1319  	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
  1320  	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
  1321  }
  1322  
  1323  // we keep enableCors just for legacy usage, need to be removed in the future
  1324  func createRouter(eng *engine.Engine, logging, enableCors bool, corsHeaders string, dockerVersion string) *mux.Router {
  1325  	r := mux.NewRouter()
  1326  	if os.Getenv("DEBUG") != "" {
  1327  		AttachProfiler(r)
  1328  	}
  1329  	m := map[string]map[string]HttpApiFunc{
  1330  		"GET": {
  1331  			"/_ping":                          ping,
  1332  			"/events":                         getEvents,
  1333  			"/info":                           getInfo,
  1334  			"/version":                        getVersion,
  1335  			"/images/json":                    getImagesJSON,
  1336  			"/images/viz":                     getImagesViz,
  1337  			"/images/search":                  getImagesSearch,
  1338  			"/images/get":                     getImagesGet,
  1339  			"/images/{name:.*}/get":           getImagesGet,
  1340  			"/images/{name:.*}/history":       getImagesHistory,
  1341  			"/images/{name:.*}/json":          getImagesByName,
  1342  			"/containers/ps":                  getContainersJSON,
  1343  			"/containers/json":                getContainersJSON,
  1344  			"/containers/{name:.*}/export":    getContainersExport,
  1345  			"/containers/{name:.*}/changes":   getContainersChanges,
  1346  			"/containers/{name:.*}/json":      getContainersByName,
  1347  			"/containers/{name:.*}/top":       getContainersTop,
  1348  			"/containers/{name:.*}/logs":      getContainersLogs,
  1349  			"/containers/{name:.*}/stats":     getContainersStats,
  1350  			"/containers/{name:.*}/attach/ws": wsContainersAttach,
  1351  			"/exec/{id:.*}/json":              getExecByID,
  1352  		},
  1353  		"POST": {
  1354  			"/auth":                         postAuth,
  1355  			"/commit":                       postCommit,
  1356  			"/build":                        postBuild,
  1357  			"/images/create":                postImagesCreate,
  1358  			"/images/load":                  postImagesLoad,
  1359  			"/images/{name:.*}/push":        postImagesPush,
  1360  			"/images/{name:.*}/tag":         postImagesTag,
  1361  			"/containers/create":            postContainersCreate,
  1362  			"/containers/{name:.*}/kill":    postContainersKill,
  1363  			"/containers/{name:.*}/pause":   postContainersPause,
  1364  			"/containers/{name:.*}/unpause": postContainersUnpause,
  1365  			"/containers/{name:.*}/restart": postContainersRestart,
  1366  			"/containers/{name:.*}/start":   postContainersStart,
  1367  			"/containers/{name:.*}/stop":    postContainersStop,
  1368  			"/containers/{name:.*}/wait":    postContainersWait,
  1369  			"/containers/{name:.*}/resize":  postContainersResize,
  1370  			"/containers/{name:.*}/attach":  postContainersAttach,
  1371  			"/containers/{name:.*}/copy":    postContainersCopy,
  1372  			"/containers/{name:.*}/exec":    postContainerExecCreate,
  1373  			"/exec/{name:.*}/start":         postContainerExecStart,
  1374  			"/exec/{name:.*}/resize":        postContainerExecResize,
  1375  			"/containers/{name:.*}/rename":  postContainerRename,
  1376  		},
  1377  		"DELETE": {
  1378  			"/containers/{name:.*}": deleteContainers,
  1379  			"/images/{name:.*}":     deleteImages,
  1380  		},
  1381  		"OPTIONS": {
  1382  			"": optionsHandler,
  1383  		},
  1384  	}
  1385  
  1386  	// If "api-cors-header" is not given, but "api-enable-cors" is true, we set cors to "*"
  1387  	// otherwise, all head values will be passed to HTTP handler
  1388  	if corsHeaders == "" && enableCors {
  1389  		corsHeaders = "*"
  1390  	}
  1391  
  1392  	for method, routes := range m {
  1393  		for route, fct := range routes {
  1394  			log.Debugf("Registering %s, %s", method, route)
  1395  			// NOTE: scope issue, make sure the variables are local and won't be changed
  1396  			localRoute := route
  1397  			localFct := fct
  1398  			localMethod := method
  1399  
  1400  			// build the handler function
  1401  			f := makeHttpHandler(eng, logging, localMethod, localRoute, localFct, corsHeaders, version.Version(dockerVersion))
  1402  
  1403  			// add the new route
  1404  			if localRoute == "" {
  1405  				r.Methods(localMethod).HandlerFunc(f)
  1406  			} else {
  1407  				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
  1408  				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
  1409  			}
  1410  		}
  1411  	}
  1412  
  1413  	return r
  1414  }
  1415  
  1416  // ServeRequest processes a single http request to the docker remote api.
  1417  // FIXME: refactor this to be part of Server and not require re-creating a new
  1418  // router each time. This requires first moving ListenAndServe into Server.
  1419  func ServeRequest(eng *engine.Engine, apiversion version.Version, w http.ResponseWriter, req *http.Request) {
  1420  	router := createRouter(eng, false, true, "", "")
  1421  	// Insert APIVERSION into the request as a convenience
  1422  	req.URL.Path = fmt.Sprintf("/v%s%s", apiversion, req.URL.Path)
  1423  	router.ServeHTTP(w, req)
  1424  }
  1425  
  1426  func lookupGidByName(nameOrGid string) (int, error) {
  1427  	groupFile, err := user.GetGroupPath()
  1428  	if err != nil {
  1429  		return -1, err
  1430  	}
  1431  	groups, err := user.ParseGroupFileFilter(groupFile, func(g user.Group) bool {
  1432  		return g.Name == nameOrGid || strconv.Itoa(g.Gid) == nameOrGid
  1433  	})
  1434  	if err != nil {
  1435  		return -1, err
  1436  	}
  1437  	if groups != nil && len(groups) > 0 {
  1438  		return groups[0].Gid, nil
  1439  	}
  1440  	gid, err := strconv.Atoi(nameOrGid)
  1441  	if err == nil {
  1442  		log.Warnf("Could not find GID %d", gid)
  1443  		return gid, nil
  1444  	}
  1445  	return -1, fmt.Errorf("Group %s not found", nameOrGid)
  1446  }
  1447  
  1448  func setupTls(cert, key, ca string, l net.Listener) (net.Listener, error) {
  1449  	tlsCert, err := tls.LoadX509KeyPair(cert, key)
  1450  	if err != nil {
  1451  		if os.IsNotExist(err) {
  1452  			return nil, fmt.Errorf("Could not load X509 key pair (%s, %s): %v", cert, key, err)
  1453  		}
  1454  		return nil, fmt.Errorf("Error reading X509 key pair (%s, %s): %q. Make sure the key is encrypted.",
  1455  			cert, key, err)
  1456  	}
  1457  	tlsConfig := &tls.Config{
  1458  		NextProtos:   []string{"http/1.1"},
  1459  		Certificates: []tls.Certificate{tlsCert},
  1460  		// Avoid fallback on insecure SSL protocols
  1461  		MinVersion: tls.VersionTLS10,
  1462  	}
  1463  
  1464  	if ca != "" {
  1465  		certPool := x509.NewCertPool()
  1466  		file, err := ioutil.ReadFile(ca)
  1467  		if err != nil {
  1468  			return nil, fmt.Errorf("Could not read CA certificate: %v", err)
  1469  		}
  1470  		certPool.AppendCertsFromPEM(file)
  1471  		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
  1472  		tlsConfig.ClientCAs = certPool
  1473  	}
  1474  
  1475  	return tls.NewListener(l, tlsConfig), nil
  1476  }
  1477  
  1478  func newListener(proto, addr string, bufferRequests bool) (net.Listener, error) {
  1479  	if bufferRequests {
  1480  		return listenbuffer.NewListenBuffer(proto, addr, activationLock)
  1481  	}
  1482  
  1483  	return net.Listen(proto, addr)
  1484  }
  1485  
  1486  func changeGroup(addr string, nameOrGid string) error {
  1487  	gid, err := lookupGidByName(nameOrGid)
  1488  	if err != nil {
  1489  		return err
  1490  	}
  1491  
  1492  	log.Debugf("%s group found. gid: %d", nameOrGid, gid)
  1493  	return os.Chown(addr, 0, gid)
  1494  }
  1495  
  1496  func setSocketGroup(addr, group string) error {
  1497  	if group == "" {
  1498  		return nil
  1499  	}
  1500  
  1501  	if err := changeGroup(addr, group); err != nil {
  1502  		if group != "docker" {
  1503  			return err
  1504  		}
  1505  		log.Debugf("Warning: could not chgrp %s to docker: %v", addr, err)
  1506  	}
  1507  
  1508  	return nil
  1509  }
  1510  
  1511  func allocateDaemonPort(addr string) error {
  1512  	host, port, err := net.SplitHostPort(addr)
  1513  	if err != nil {
  1514  		return err
  1515  	}
  1516  
  1517  	intPort, err := strconv.Atoi(port)
  1518  	if err != nil {
  1519  		return err
  1520  	}
  1521  
  1522  	var hostIPs []net.IP
  1523  	if parsedIP := net.ParseIP(host); parsedIP != nil {
  1524  		hostIPs = append(hostIPs, parsedIP)
  1525  	} else if hostIPs, err = net.LookupIP(host); err != nil {
  1526  		return fmt.Errorf("failed to lookup %s address in host specification", host)
  1527  	}
  1528  
  1529  	for _, hostIP := range hostIPs {
  1530  		if _, err := portallocator.RequestPort(hostIP, "tcp", intPort); err != nil {
  1531  			return fmt.Errorf("failed to allocate daemon listening port %d (err: %v)", intPort, err)
  1532  		}
  1533  	}
  1534  	return nil
  1535  }
  1536  
  1537  func setupTcpHttp(addr string, job *engine.Job) (*HttpServer, error) {
  1538  	if !job.GetenvBool("TlsVerify") {
  1539  		log.Infof("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
  1540  	}
  1541  
  1542  	r := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("CorsHeaders"), job.Getenv("Version"))
  1543  
  1544  	l, err := newListener("tcp", addr, job.GetenvBool("BufferRequests"))
  1545  	if err != nil {
  1546  		return nil, err
  1547  	}
  1548  
  1549  	if err := allocateDaemonPort(addr); err != nil {
  1550  		return nil, err
  1551  	}
  1552  
  1553  	if job.GetenvBool("Tls") || job.GetenvBool("TlsVerify") {
  1554  		var tlsCa string
  1555  		if job.GetenvBool("TlsVerify") {
  1556  			tlsCa = job.Getenv("TlsCa")
  1557  		}
  1558  		l, err = setupTls(job.Getenv("TlsCert"), job.Getenv("TlsKey"), tlsCa, l)
  1559  		if err != nil {
  1560  			return nil, err
  1561  		}
  1562  	}
  1563  	return &HttpServer{&http.Server{Addr: addr, Handler: r}, l}, nil
  1564  }
  1565  
  1566  type Server interface {
  1567  	Serve() error
  1568  	Close() error
  1569  }
  1570  
  1571  // ServeApi loops through all of the protocols sent in to docker and spawns
  1572  // off a go routine to setup a serving http.Server for each.
  1573  func ServeApi(job *engine.Job) engine.Status {
  1574  	if len(job.Args) == 0 {
  1575  		return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
  1576  	}
  1577  	var (
  1578  		protoAddrs = job.Args
  1579  		chErrors   = make(chan error, len(protoAddrs))
  1580  	)
  1581  	activationLock = make(chan struct{})
  1582  
  1583  	for _, protoAddr := range protoAddrs {
  1584  		protoAddrParts := strings.SplitN(protoAddr, "://", 2)
  1585  		if len(protoAddrParts) != 2 {
  1586  			return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name)
  1587  		}
  1588  		go func() {
  1589  			log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1])
  1590  			srv, err := NewServer(protoAddrParts[0], protoAddrParts[1], job)
  1591  			if err != nil {
  1592  				chErrors <- err
  1593  				return
  1594  			}
  1595  			job.Eng.OnShutdown(func() {
  1596  				if err := srv.Close(); err != nil {
  1597  					log.Error(err)
  1598  				}
  1599  			})
  1600  			if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
  1601  				err = nil
  1602  			}
  1603  			chErrors <- err
  1604  		}()
  1605  	}
  1606  
  1607  	for i := 0; i < len(protoAddrs); i++ {
  1608  		err := <-chErrors
  1609  		if err != nil {
  1610  			return job.Error(err)
  1611  		}
  1612  	}
  1613  
  1614  	return engine.StatusOK
  1615  }