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