api.go
a11b3139
 package docker
 
 import (
a4f8a249
 	"bufio"
 	"bytes"
8eeff019
 	"code.google.com/p/go.net/websocket"
dd4aab84
 	"encoding/base64"
c0d5d596
 	"encoding/json"
6a55169e
 	"expvar"
b295239d
 	"fmt"
96d1e9bb
 	"github.com/dotcloud/docker/archive"
f37399d2
 	"github.com/dotcloud/docker/auth"
8e7db043
 	"github.com/dotcloud/docker/pkg/systemd"
2e69e172
 	"github.com/dotcloud/docker/utils"
a11b3139
 	"github.com/gorilla/mux"
57cfe72e
 	"io"
a11e6167
 	"io/ioutil"
79e91058
 	"log"
754ed904
 	"mime"
3adf9ce0
 	"net"
a11b3139
 	"net/http"
9f46779d
 	"net/http/pprof"
3adf9ce0
 	"os"
12c9b9b3
 	"os/exec"
999a8d72
 	"regexp"
1aa7f139
 	"strconv"
b295239d
 	"strings"
a11b3139
 )
 
082d1420
 const (
e4cb83c5
 	APIVERSION        = 1.8
082d1420
 	DEFAULTHTTPHOST   = "127.0.0.1"
 	DEFAULTHTTPPORT   = 4243
 	DEFAULTUNIXSOCKET = "/var/run/docker.sock"
 )
faae7220
 
166eba3e
 type HttpApiFunc func(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error
 
57cfe72e
 func hijackServer(w http.ResponseWriter) (io.ReadCloser, io.Writer, error) {
 	conn, _, err := w.(http.Hijacker).Hijack()
04cd20fa
 	if err != nil {
 		return nil, nil, err
 	}
 	// Flush the options to make sure the client sets the raw mode
57cfe72e
 	conn.Write([]byte{})
 	return conn, conn, nil
04cd20fa
 }
 
954ecac3
 //If we don't do this, POST method without Content-type (even with empty body) will fail
 func parseForm(r *http.Request) error {
333bc23f
 	if r == nil {
 		return nil
 	}
954ecac3
 	if err := r.ParseForm(); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
 		return err
 	}
 	return nil
 }
 
0f135ad7
 func parseMultipartForm(r *http.Request) error {
 	if err := r.ParseMultipartForm(4096); err != nil && !strings.HasPrefix(err.Error(), "mime:") {
 		return err
 	}
 	return nil
 }
 
04cd20fa
 func httpError(w http.ResponseWriter, err error) {
b7937e26
 	statusCode := http.StatusInternalServerError
693ff4d2
 	// FIXME: this is brittle and should not be necessary.
 	// If we need to differentiate between different possible error types, we should
 	// create appropriate error types with clearly defined meaning.
 	if strings.Contains(err.Error(), "No such") {
b7937e26
 		statusCode = http.StatusNotFound
0c544357
 	} else if strings.HasPrefix(err.Error(), "Bad parameter") {
b7937e26
 		statusCode = http.StatusBadRequest
67b20f2c
 	} else if strings.HasPrefix(err.Error(), "Conflict") {
b7937e26
 		statusCode = http.StatusConflict
468e4c4b
 	} else if strings.HasPrefix(err.Error(), "Impossible") {
b7937e26
 		statusCode = http.StatusNotAcceptable
90f6bdd6
 	} else if strings.HasPrefix(err.Error(), "Wrong login/password") {
b7937e26
 		statusCode = http.StatusUnauthorized
90f6bdd6
 	} else if strings.Contains(err.Error(), "hasn't been activated") {
b7937e26
 		statusCode = http.StatusForbidden
0e24db3a
 	}
 
ad723bbf
 	if err != nil {
 		utils.Errorf("HTTP Error: statusCode=%d %s", statusCode, err.Error())
0e24db3a
 		http.Error(w, err.Error(), statusCode)
 	}
04cd20fa
 }
 
dd49cc45
 func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
 	b, err := json.Marshal(v)
 
 	if err != nil {
 		return err
 	}
 
f7beba3a
 	w.Header().Set("Content-Type", "application/json")
dd49cc45
 	w.WriteHeader(code)
f7beba3a
 	w.Write(b)
dd49cc45
 
 	return nil
f7beba3a
 }
 
0c544357
 func getBoolParam(value string) (bool, error) {
da199846
 	if value == "" {
0c544357
 		return false, nil
 	}
1581ed52
 	ret, err := strconv.ParseBool(value)
 	if err != nil {
5b3ad002
 		return false, fmt.Errorf("Bad parameter")
da199846
 	}
1581ed52
 	return ret, nil
0c544357
 }
 
754ed904
 func matchesContentType(contentType, expectedType string) bool {
 	mimetype, _, err := mime.ParseMediaType(contentType)
 	if err != nil {
ad723bbf
 		utils.Errorf("Error parsing media type: %s error: %s", contentType, err.Error())
754ed904
 	}
 	return err == nil && mimetype == expectedType
 }
 
3dd1e4d5
 func postAuth(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	authConfig := &auth.AuthConfig{}
 	err := json.NewDecoder(r.Body).Decode(authConfig)
b56b2da5
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
093b85b7
 	status, err := auth.Login(authConfig, srv.HTTPRequestFactory(nil))
3bae188b
 	if err != nil {
 		return err
3dd1e4d5
 	}
b56b2da5
 	if status != "" {
dd49cc45
 		return writeJSON(w, http.StatusOK, &APIAuth{Status: status})
b56b2da5
 	}
0862183c
 	w.WriteHeader(http.StatusNoContent)
7cc08234
 	return nil
b56b2da5
 }
f37399d2
 
faae7220
 func getVersion(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
de35b346
 	srv.Eng.ServeHTTP(w, r)
 	return nil
b56b2da5
 }
f37399d2
 
faae7220
 func postContainersKill(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
4918769b
 	if err := parseForm(r); err != nil {
 		return err
 	}
fdb3de7b
 	job := srv.Eng.Job("kill", vars["name"])
 	if sig := r.Form.Get("signal"); sig != "" {
 		job.Args = append(job.Args, sig)
 	}
 	if err := job.Run(); err != nil {
7cc08234
 		return err
b56b2da5
 	}
0862183c
 	w.WriteHeader(http.StatusNoContent)
7cc08234
 	return nil
b56b2da5
 }
f37399d2
 
faae7220
 func getContainersExport(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
9656cdf0
 	job := srv.Eng.Job("export", vars["name"])
 	if err := job.Stdout.Add(w); err != nil {
 		return err
 	}
 	if err := job.Run(); err != nil {
8b31d306
 		return err
b56b2da5
 	}
7cc08234
 	return nil
b56b2da5
 }
1e357c69
 
fd224ee5
 func getImagesJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
c7bbe7ca
 
0c544357
 	all, err := getBoolParam(r.Form.Get("all"))
 	if err != nil {
 		return err
 	}
c423a790
 	filter := r.Form.Get("filter")
c7bbe7ca
 
1990c49a
 	outs, err := srv.Images(all, filter)
c423a790
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
dd49cc45
 
d7928b9a
 	if version < 1.7 {
 		outs2 := []APIImagesOld{}
 		for _, ctnr := range outs {
 			outs2 = append(outs2, ctnr.ToLegacy()...)
 		}
 
 		return writeJSON(w, http.StatusOK, outs2)
 	}
5e941f1c
 	return writeJSON(w, http.StatusOK, outs)
c423a790
 }
 
8f647598
 func getImagesViz(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if version > 1.6 {
 		w.WriteHeader(http.StatusNotFound)
 		return fmt.Errorf("This is now implemented in the client.")
 	}
 
 	if err := srv.ImagesViz(w); err != nil {
 		return err
 	}
 	return nil
 }
 
faae7220
 func getInfo(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
51e2c179
 	srv.Eng.ServeHTTP(w, r)
 	return nil
b56b2da5
 }
79e91058
 
b5da8164
 func getEvents(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
f4b41e1a
 	sendEvent := func(wf *utils.WriteFlusher, event *utils.JSONMessage) error {
b5da8164
 		b, err := json.Marshal(event)
 		if err != nil {
8b3519c5
 			return fmt.Errorf("JSON error")
b5da8164
 		}
 		_, err = wf.Write(b)
 		if err != nil {
8b3519c5
 			// On error, evict the listener
ad723bbf
 			utils.Errorf("%s", err)
b5da8164
 			srv.Lock()
2e4d4c9f
 			delete(srv.listeners, r.RemoteAddr)
b5da8164
 			srv.Unlock()
8b3519c5
 			return err
2e4d4c9f
 		}
8b3519c5
 		return nil
2e4d4c9f
 	}
 
 	if err := parseForm(r); err != nil {
 		return err
 	}
 	listener := make(chan utils.JSONMessage)
 	srv.Lock()
 	srv.listeners[r.RemoteAddr] = listener
 	srv.Unlock()
 	since, err := strconv.ParseInt(r.Form.Get("since"), 10, 0)
 	if err != nil {
 		since = 0
 	}
 	w.Header().Set("Content-Type", "application/json")
 	wf := utils.NewWriteFlusher(w)
a963ff5d
 	wf.Flush()
2e4d4c9f
 	if since != 0 {
8b3519c5
 		// If since, send previous events that happened after the timestamp
abfdaca3
 		for _, event := range srv.GetEvents() {
2e4d4c9f
 			if event.Time >= since {
8b3519c5
 				err := sendEvent(wf, &event)
 				if err != nil && err.Error() == "JSON error" {
2e4d4c9f
 					continue
 				}
 				if err != nil {
 					return err
 				}
 			}
 		}
 	}
7c50221d
 	for event := range listener {
8b3519c5
 		err := sendEvent(wf, &event)
 		if err != nil && err.Error() == "JSON error" {
2e4d4c9f
 			continue
 		}
 		if err != nil {
b5da8164
 			return err
 		}
 	}
 	return nil
 }
 
faae7220
 func getImagesHistory(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
 	outs, err := srv.ImageHistory(name)
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
dd49cc45
 
 	return writeJSON(w, http.StatusOK, outs)
b56b2da5
 }
b295239d
 
faae7220
 func getContainersChanges(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
 	changesStr, err := srv.ContainerChanges(name)
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
dd49cc45
 
 	return writeJSON(w, http.StatusOK, changesStr)
b56b2da5
 }
b295239d
 
11e28842
 func getContainersTop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
eb4a0271
 	if version < 1.4 {
 		return fmt.Errorf("top was improved a lot since 1.3, Please upgrade your docker client.")
 	}
2e797196
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
cfec1c3e
 	if err := parseForm(r); err != nil {
 		return err
 	}
5e941f1c
 	procsStr, err := srv.ContainerTop(vars["name"], r.Form.Get("ps_args"))
2e797196
 	if err != nil {
 		return err
 	}
dd49cc45
 	return writeJSON(w, http.StatusOK, procsStr)
2e797196
 }
 
fd224ee5
 func getContainersJSON(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
0c544357
 	all, err := getBoolParam(r.Form.Get("all"))
 	if err != nil {
 		return err
 	}
bd04d7d4
 	size, err := getBoolParam(r.Form.Get("size"))
 	if err != nil {
 		return err
 	}
bc3fa506
 	since := r.Form.Get("since")
 	before := r.Form.Get("before")
60ddcaa1
 	n, err := strconv.Atoi(r.Form.Get("limit"))
b56b2da5
 	if err != nil {
 		n = -1
 	}
b295239d
 
bd04d7d4
 	outs := srv.Containers(all, size, n, since, before)
dd49cc45
 
9ae5054c
 	if version < 1.5 {
 		outs2 := []APIContainersOld{}
 		for _, ctnr := range outs {
5e941f1c
 			outs2 = append(outs2, *ctnr.ToLegacy())
9ae5054c
 		}
 
33e4d736
 		return writeJSON(w, http.StatusOK, outs2)
b56b2da5
 	}
5e941f1c
 	return writeJSON(w, http.StatusOK, outs)
b56b2da5
 }
04cd20fa
 
faae7220
 func postImagesTag(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b295239d
 
e43ff2f6
 	job := srv.Eng.Job("tag", vars["name"], r.Form.Get("repo"), r.Form.Get("tag"))
 	job.Setenv("force", r.Form.Get("force"))
 	if err := job.Run(); err != nil {
7cc08234
 		return err
b56b2da5
 	}
 	w.WriteHeader(http.StatusCreated)
7cc08234
 	return nil
b56b2da5
 }
79512b2a
 
faae7220
 func postCommit(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
5bec9275
 	config := &Config{}
3acfc600
 	if err := json.NewDecoder(r.Body).Decode(config); err != nil && err != io.EOF {
ad723bbf
 		utils.Errorf("%s", err)
b56b2da5
 	}
930ec9f5
 
4b5ceb0f
 	job := srv.Eng.Job("commit", r.Form.Get("container"))
930ec9f5
 	job.Setenv("repo", r.Form.Get("repo"))
 	job.Setenv("tag", r.Form.Get("tag"))
 	job.Setenv("author", r.Form.Get("author"))
 	job.Setenv("comment", r.Form.Get("comment"))
 	job.SetenvJson("config", config)
 
 	var id string
 	job.Stdout.AddString(&id)
 	if err := job.Run(); err != nil {
7cc08234
 		return err
b56b2da5
 	}
dd49cc45
 
 	return writeJSON(w, http.StatusCreated, &APIID{id})
b56b2da5
 }
79512b2a
 
152ebeea
 // Creates an image from Pull or from Import
faae7220
 func postImagesCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
6ce475db
 
b56b2da5
 	src := r.Form.Get("fromSrc")
 	image := r.Form.Get("fromImage")
 	tag := r.Form.Get("tag")
f29e5dc8
 	repo := r.Form.Get("repo")
cf19be44
 
dd4aab84
 	authEncoded := r.Header.Get("X-Registry-Auth")
fcee6056
 	authConfig := &auth.AuthConfig{}
dd4aab84
 	if authEncoded != "" {
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
fcee6056
 			// for a pull it is not an error if no auth was given
d04beb7f
 			// to increase compatibility with the existing api it is defaulting to be empty
fcee6056
 			authConfig = &auth.AuthConfig{}
 		}
 	}
c8c7094b
 	if version > 1.0 {
 		w.Header().Set("Content-Type", "application/json")
 	}
 	sf := utils.NewStreamFormatter(version > 1.0)
b56b2da5
 	if image != "" { //pull
093b85b7
 		metaHeaders := map[string][]string{}
 		for k, v := range r.Header {
 			if strings.HasPrefix(k, "X-Meta-") {
 				metaHeaders[k] = v
 			}
 		}
fcee6056
 		if err := srv.ImagePull(image, tag, w, sf, authConfig, metaHeaders, version > 1.3); err != nil {
c8c7094b
 			if sf.Used() {
5a36efb6
 				w.Write(sf.FormatError(err))
c8c7094b
 				return nil
 			}
f29e5dc8
 			return err
0b6c79b3
 		}
b56b2da5
 	} else { //import
c8c7094b
 		if err := srv.ImageImport(src, repo, tag, r.Body, w, sf); err != nil {
 			if sf.Used() {
5a36efb6
 				w.Write(sf.FormatError(err))
c8c7094b
 				return nil
 			}
f29e5dc8
 			return err
0b6c79b3
 		}
b56b2da5
 	}
7cc08234
 	return nil
b56b2da5
 }
0b6c79b3
 
faae7220
 func getImagesSearch(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
04cd20fa
 
b56b2da5
 	term := r.Form.Get("term")
 	outs, err := srv.ImagesSearch(term)
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
dd49cc45
 
 	return writeJSON(w, http.StatusOK, outs)
b56b2da5
 }
36b968bb
 
faae7220
 func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
36b968bb
 
b56b2da5
 	url := r.Form.Get("url")
 	path := r.Form.Get("path")
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
c8c7094b
 	if version > 1.0 {
 		w.Header().Set("Content-Type", "application/json")
 	}
 	sf := utils.NewStreamFormatter(version > 1.0)
5957dd90
 	err := srv.ImageInsert(name, url, path, w, sf)
0f135ad7
 	if err != nil {
8cc19765
 		if sf.Used() {
 			w.Write(sf.FormatError(err))
 			return nil
 		}
 		return err
b56b2da5
 	}
dd49cc45
 
5957dd90
 	return nil
b56b2da5
 }
59a6316f
 
faae7220
 func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
093b85b7
 	metaHeaders := map[string][]string{}
 	for k, v := range r.Header {
 		if strings.HasPrefix(k, "X-Meta-") {
 			metaHeaders[k] = v
 		}
 	}
2f4de386
 	if err := parseForm(r); err != nil {
 		return err
 	}
da3bb9a7
 	authConfig := &auth.AuthConfig{}
 
dd4aab84
 	authEncoded := r.Header.Get("X-Registry-Auth")
 	if authEncoded != "" {
 		// the new format is to handle the authConfig as a header
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
 			// to increase compatibility to existing api it is defaulting to be empty
da3bb9a7
 			authConfig = &auth.AuthConfig{}
 		}
 	} else {
d04beb7f
 		// the old format is supported for compatibility if there was no authConfig header
da3bb9a7
 		if err := json.NewDecoder(r.Body).Decode(authConfig); err != nil {
 			return err
 		}
 
 	}
2f4de386
 
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
 	name := vars["name"]
c8c7094b
 	if version > 1.0 {
 		w.Header().Set("Content-Type", "application/json")
 	}
 	sf := utils.NewStreamFormatter(version > 1.0)
093b85b7
 	if err := srv.ImagePush(name, w, sf, authConfig, metaHeaders); err != nil {
c8c7094b
 		if sf.Used() {
5a36efb6
 			w.Write(sf.FormatError(err))
c8c7094b
 			return nil
 		}
2f4de386
 		return err
 	}
7cc08234
 	return nil
b56b2da5
 }
10c0e990
 
7eaa59f6
 func getImagesGet(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	name := vars["name"]
1211065c
 	if version > 1.0 {
 		w.Header().Set("Content-Type", "application/x-tar")
 	}
fd7ab143
 	return srv.ImageExport(name, w)
7eaa59f6
 }
 
 func postImagesLoad(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
fd7ab143
 	return srv.ImageLoad(r.Body)
7eaa59f6
 }
 
faae7220
 func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
0d292440
 	if err := parseForm(r); err != nil {
 		return nil
 	}
a3f6054f
 	out := &APIRun{}
e5f8ab61
 	job := srv.Eng.Job("create", r.Form.Get("name"))
 	if err := job.DecodeEnv(r.Body); err != nil {
7cc08234
 		return err
b56b2da5
 	}
3e9575e2
 	resolvConf, err := utils.GetResolvConf()
 	if err != nil {
 		return err
 	}
e5f8ab61
 	if !job.GetenvBool("NetworkDisabled") && len(job.Getenv("Dns")) == 0 && len(srv.runtime.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) {
7f118519
 		out.Warnings = append(out.Warnings, fmt.Sprintf("Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns))
e5f8ab61
 		job.SetenvList("Dns", defaultDns)
a3f6054f
 	}
e5f8ab61
 	// Read container ID from the first line of stdout
a4f8a249
 	job.Stdout.AddString(&out.ID)
e5f8ab61
 	// Read warnings from stderr
a4f8a249
 	warnings := &bytes.Buffer{}
 	job.Stderr.Add(warnings)
e5f8ab61
 	if err := job.Run(); err != nil {
7cc08234
 		return err
b56b2da5
 	}
a4f8a249
 	// Parse warnings from stderr
 	scanner := bufio.NewScanner(warnings)
 	for scanner.Scan() {
 		out.Warnings = append(out.Warnings, scanner.Text())
 	}
e5f8ab61
 	if job.GetenvInt("Memory") > 0 && !srv.runtime.capabilities.MemoryLimit {
152ebeea
 		log.Println("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.")
b56b2da5
 		out.Warnings = append(out.Warnings, "Your kernel does not support memory limit capabilities. Limitation discarded.")
 	}
e5f8ab61
 	if job.GetenvInt("Memory") > 0 && !srv.runtime.capabilities.SwapLimit {
152ebeea
 		log.Println("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.")
b56b2da5
 		out.Warnings = append(out.Warnings, "Your kernel does not support memory swap capabilities. Limitation discarded.")
 	}
a3f6054f
 
e5f8ab61
 	if !job.GetenvBool("NetworkDisabled") && srv.runtime.capabilities.IPv4ForwardingDisabled {
10190be5
 		log.Println("Warning: IPv4 forwarding is disabled.")
 		out.Warnings = append(out.Warnings, "IPv4 forwarding is disabled.")
 	}
 
f4ba0d42
 	return writeJSON(w, http.StatusCreated, out)
b56b2da5
 }
10c0e990
 
faae7220
 func postContainersRestart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
 	t, err := strconv.Atoi(r.Form.Get("t"))
 	if err != nil || t < 0 {
 		t = 10
 	}
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
 	if err := srv.ContainerRestart(name, t); err != nil {
7cc08234
 		return err
b56b2da5
 	}
0862183c
 	w.WriteHeader(http.StatusNoContent)
7cc08234
 	return nil
b56b2da5
 }
f37399d2
 
faae7220
 func deleteContainers(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
1cbdaeba
 
0c544357
 	removeVolume, err := getBoolParam(r.Form.Get("v"))
 	if err != nil {
 		return err
 	}
1cbdaeba
 	removeLink, err := getBoolParam(r.Form.Get("link"))
 	if err != nil {
 		return err
 	}
ab96da8e
 
1cbdaeba
 	if err := srv.ContainerDestroy(name, removeVolume, removeLink); err != nil {
7cc08234
 		return err
b56b2da5
 	}
0862183c
 	w.WriteHeader(http.StatusNoContent)
7cc08234
 	return nil
b56b2da5
 }
6ce475db
 
faae7220
 func deleteImages(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
67b20f2c
 	if err := parseForm(r); err != nil {
 		return err
 	}
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
c46382ba
 	imgs, err := srv.ImageDelete(name, version > 1.1)
9060b5c2
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
9060b5c2
 	if imgs != nil {
54da339b
 		if len(imgs) != 0 {
dd49cc45
 			return writeJSON(w, http.StatusOK, imgs)
9060b5c2
 		}
5e941f1c
 		return fmt.Errorf("Conflict, %s wasn't deleted", name)
9060b5c2
 	}
5e941f1c
 	w.WriteHeader(http.StatusNoContent)
7cc08234
 	return nil
b56b2da5
 }
b295239d
 
faae7220
 func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
958b4a87
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
 	name := vars["name"]
 	job := srv.Eng.Job("start", name)
4fdf11b2
 	// allow a nil body for backwards compatibility
 	if r.Body != nil {
754ed904
 		if matchesContentType(r.Header.Get("Content-Type"), "application/json") {
958b4a87
 			if err := job.DecodeEnv(r.Body); err != nil {
d8d33e8b
 				return err
 			}
4fdf11b2
 		}
 	}
958b4a87
 	if err := job.Run(); err != nil {
7cc08234
 		return err
b56b2da5
 	}
0862183c
 	w.WriteHeader(http.StatusNoContent)
7cc08234
 	return nil
b56b2da5
 }
b295239d
 
faae7220
 func postContainersStop(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
dbe1915f
 	job := srv.Eng.Job("stop", vars["name"])
6ba456ff
 	job.Setenv("t", r.Form.Get("t"))
dbe1915f
 	if err := job.Run(); err != nil {
7cc08234
 		return err
b56b2da5
 	}
0862183c
 	w.WriteHeader(http.StatusNoContent)
7cc08234
 	return nil
b56b2da5
 }
b295239d
 
faae7220
 func postContainersWait(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
3569d080
 	job := srv.Eng.Job("wait", vars["name"])
 	var statusStr string
 	job.Stdout.AddString(&statusStr)
 	if err := job.Run(); err != nil {
 		return err
 	}
 	// Parse a 16-bit encoded integer to map typical unix exit status.
 	status, err := strconv.ParseInt(statusStr, 10, 16)
b56b2da5
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
3569d080
 	return writeJSON(w, http.StatusOK, &APIWait{StatusCode: int(status)})
b56b2da5
 }
04cd20fa
 
70d2123e
 func postContainersResize(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if err := parseForm(r); err != nil {
 		return err
 	}
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
73e8a39f
 	if err := srv.Eng.Job("resize", vars["name"], r.Form.Get("h"), r.Form.Get("w")).Run(); err != nil {
70d2123e
 		return err
 	}
 	return nil
 }
 
faae7220
 func postContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
954ecac3
 	if err := parseForm(r); err != nil {
7cc08234
 		return err
b56b2da5
 	}
0c544357
 	logs, err := getBoolParam(r.Form.Get("logs"))
 	if err != nil {
 		return err
 	}
 	stream, err := getBoolParam(r.Form.Get("stream"))
 	if err != nil {
 		return err
 	}
 	stdin, err := getBoolParam(r.Form.Get("stdin"))
 	if err != nil {
 		return err
 	}
 	stdout, err := getBoolParam(r.Form.Get("stdout"))
 	if err != nil {
 		return err
 	}
 	stderr, err := getBoolParam(r.Form.Get("stderr"))
 	if err != nil {
 		return err
 	}
 
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
 
e854b7b2
 	c, err := srv.ContainerInspect(name)
 	if err != nil {
e5fa4a49
 		return err
 	}
 
e854b7b2
 	inStream, outStream, err := hijackServer(w)
b56b2da5
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
5190f7f3
 	defer func() {
e854b7b2
 		if tcpc, ok := inStream.(*net.TCPConn); ok {
5190f7f3
 			tcpc.CloseWrite()
 		} else {
e854b7b2
 			inStream.Close()
5190f7f3
 		}
 	}()
 	defer func() {
e854b7b2
 		if tcpc, ok := outStream.(*net.TCPConn); ok {
5190f7f3
 			tcpc.CloseWrite()
e854b7b2
 		} else if closer, ok := outStream.(io.Closer); ok {
5190f7f3
 			closer.Close()
 		}
 	}()
a11b3139
 
e854b7b2
 	var errStream io.Writer
 
 	fmt.Fprintf(outStream, "HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n")
 
082d1420
 	if !c.Config.Tty && version >= 1.6 {
e854b7b2
 		errStream = utils.NewStdWriter(outStream, utils.Stderr)
 		outStream = utils.NewStdWriter(outStream, utils.Stdout)
 	} else {
 		errStream = outStream
 	}
 
 	if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, inStream, outStream, errStream); err != nil {
 		fmt.Fprintf(outStream, "Error: %s\n", err)
b56b2da5
 	}
7cc08234
 	return nil
b56b2da5
 }
a4bcf7e1
 
166eba3e
 func wsContainersAttach(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
8eeff019
 
 	if err := parseForm(r); err != nil {
 		return err
 	}
 	logs, err := getBoolParam(r.Form.Get("logs"))
 	if err != nil {
 		return err
 	}
 	stream, err := getBoolParam(r.Form.Get("stream"))
 	if err != nil {
 		return err
 	}
 	stdin, err := getBoolParam(r.Form.Get("stdin"))
 	if err != nil {
 		return err
 	}
 	stdout, err := getBoolParam(r.Form.Get("stdout"))
 	if err != nil {
 		return err
 	}
 	stderr, err := getBoolParam(r.Form.Get("stderr"))
 	if err != nil {
 		return err
 	}
 
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
 	name := vars["name"]
 
 	if _, err := srv.ContainerInspect(name); err != nil {
 		return err
 	}
 
166eba3e
 	h := websocket.Handler(func(ws *websocket.Conn) {
 		defer ws.Close()
8eeff019
 
e854b7b2
 		if err := srv.ContainerAttach(name, logs, stream, stdin, stdout, stderr, ws, ws, ws); err != nil {
ad723bbf
 			utils.Errorf("Error: %s", err)
166eba3e
 		}
 	})
 	h.ServeHTTP(w, r)
8eeff019
 
 	return nil
 }
 
faae7220
 func getContainersByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
a4bcf7e1
 
b56b2da5
 	container, err := srv.ContainerInspect(name)
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
dd49cc45
 
5ec2fea6
 	_, err = srv.ImageInspect(name)
 	if err == nil {
 		return fmt.Errorf("Conflict between containers and images")
 	}
07324a37
 
c4c90e9c
 	container.readHostConfig()
 	c := APIContainer{container, container.hostConfig}
 
 	return writeJSON(w, http.StatusOK, c)
b56b2da5
 }
a4bcf7e1
 
faae7220
 func getImagesByName(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
ff67da9c
 	if vars == nil {
7cc08234
 		return fmt.Errorf("Missing parameter")
ff67da9c
 	}
b56b2da5
 	name := vars["name"]
1e357c69
 
b56b2da5
 	image, err := srv.ImageInspect(name)
 	if err != nil {
7cc08234
 		return err
b56b2da5
 	}
dd49cc45
 
5ec2fea6
 	_, err = srv.ContainerInspect(name)
 	if err == nil {
 		return fmt.Errorf("Conflict between containers and images")
c2a14bb1
 	}
dd49cc45
 
71d46eaf
 	return writeJSON(w, http.StatusOK, image)
0f312113
 }
 
a4879901
 func postBuild(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
90dde9be
 	if version < 1.3 {
 		return fmt.Errorf("Multipart upload for build is no longer supported. Please upgrade your docker client.")
0f135ad7
 	}
8291f00a
 	var (
 		remoteURL         = r.FormValue("remote")
 		repoName          = r.FormValue("t")
 		rawSuppressOutput = r.FormValue("q")
 		rawNoCache        = r.FormValue("nocache")
 		rawRm             = r.FormValue("rm")
 		authEncoded       = r.Header.Get("X-Registry-Auth")
 		authConfig        = &auth.AuthConfig{}
 		tag               string
 	)
 	repoName, tag = utils.ParseRepositoryTag(repoName)
228091c7
 	if authEncoded != "" {
 		authJson := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
 		if err := json.NewDecoder(authJson).Decode(authConfig); err != nil {
 			// for a pull it is not an error if no auth was given
 			// to increase compatibility with the existing api it is defaulting to be empty
 			authConfig = &auth.AuthConfig{}
 		}
 	}
0f135ad7
 
352991bd
 	var context io.Reader
0f135ad7
 
a11e6167
 	if remoteURL == "" {
352991bd
 		context = r.Body
 	} else if utils.IsGIT(remoteURL) {
 		if !strings.HasPrefix(remoteURL, "git://") {
 			remoteURL = "https://" + remoteURL
a11e6167
 		}
352991bd
 		root, err := ioutil.TempDir("", "docker-build-git")
a11e6167
 		if err != nil {
d42c10aa
 			return err
 		}
352991bd
 		defer os.RemoveAll(root)
0f135ad7
 
352991bd
 		if output, err := exec.Command("git", "clone", remoteURL, root).CombinedOutput(); err != nil {
 			return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
 		}
0f135ad7
 
96d1e9bb
 		c, err := archive.Tar(root, archive.Bzip2)
352991bd
 		if err != nil {
 			return err
 		}
 		context = c
 	} else if utils.IsURL(remoteURL) {
12180948
 		f, err := utils.Download(remoteURL)
352991bd
 		if err != nil {
 			return err
d42c10aa
 		}
352991bd
 		defer f.Body.Close()
 		dockerFile, err := ioutil.ReadAll(f.Body)
 		if err != nil {
 			return err
 		}
359a6f49
 		c, err := MkBuildContext(string(dockerFile), nil)
352991bd
 		if err != nil {
 			return err
 		}
 		context = c
0f135ad7
 	}
474191dd
 
 	suppressOutput, err := getBoolParam(rawSuppressOutput)
 	if err != nil {
 		return err
 	}
3a123bc4
 	noCache, err := getBoolParam(rawNoCache)
 	if err != nil {
 		return err
 	}
b7a3fc68
 	rm, err := getBoolParam(rawRm)
 	if err != nil {
 		return err
 	}
474191dd
 
de4429f7
 	if version >= 1.8 {
b04c6466
 		w.Header().Set("Content-Type", "application/json")
 	}
de4429f7
 	sf := utils.NewStreamFormatter(version >= 1.8)
 	b := NewBuildFile(srv,
 		&StdoutFormater{
 			Writer:          utils.NewWriteFlusher(w),
 			StreamFormatter: sf,
 		},
 		&StderrFormater{
 			Writer:          utils.NewWriteFlusher(w),
 			StreamFormatter: sf,
 		},
228091c7
 		!suppressOutput, !noCache, rm, utils.NewWriteFlusher(w), sf, authConfig)
d9bce2de
 	id, err := b.Build(context)
 	if err != nil {
b04c6466
 		if sf.Used() {
 			w.Write(sf.FormatError(err))
 			return nil
 		}
5bd0437e
 		return fmt.Errorf("Error build: %s", err)
d9bce2de
 	}
 	if repoName != "" {
a11e6167
 		srv.runtime.repositories.Set(repoName, tag, id, false)
0f135ad7
 	}
 	return nil
 }
 
5b8cfbe1
 func postContainersCopy(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	if vars == nil {
 		return fmt.Errorf("Missing parameter")
 	}
 	name := vars["name"]
 
 	copyData := &APICopy{}
d94b1860
 	contentType := r.Header.Get("Content-Type")
 	if contentType == "application/json" {
5b8cfbe1
 		if err := json.NewDecoder(r.Body).Decode(copyData); err != nil {
 			return err
 		}
 	} else {
d94b1860
 		return fmt.Errorf("Content-Type not supported: %s", contentType)
5b8cfbe1
 	}
 
 	if copyData.Resource == "" {
27159ce6
 		return fmt.Errorf("Path cannot be empty")
5b8cfbe1
 	}
 	if copyData.Resource[0] == '/' {
d94b1860
 		copyData.Resource = copyData.Resource[1:]
5b8cfbe1
 	}
 
 	if err := srv.ContainerCopy(name, copyData.Resource, w); err != nil {
ad723bbf
 		utils.Errorf("%s", err.Error())
5b8cfbe1
 		return err
 	}
 	return nil
 }
 
dd53c457
 func optionsHandler(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
 	w.WriteHeader(http.StatusOK)
 	return nil
 }
6d5bdff3
 func writeCorsHeaders(w http.ResponseWriter, r *http.Request) {
 	w.Header().Add("Access-Control-Allow-Origin", "*")
 	w.Header().Add("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
393e873d
 	w.Header().Add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, OPTIONS")
6d5bdff3
 }
 
166eba3e
 func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 		// log the request
 		utils.Debugf("Calling %s %s", localMethod, localRoute)
 
 		if logging {
 			log.Println(r.Method, r.RequestURI)
 		}
 
 		if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") {
 			userAgent := strings.Split(r.Header.Get("User-Agent"), "/")
 			if len(userAgent) == 2 && userAgent[1] != VERSION {
 				utils.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], VERSION)
 			}
 		}
 		version, err := strconv.ParseFloat(mux.Vars(r)["version"], 64)
 		if err != nil {
 			version = APIVERSION
 		}
1cbdaeba
 		if srv.runtime.config.EnableCors {
166eba3e
 			writeCorsHeaders(w, r)
 		}
 
 		if version == 0 || version > APIVERSION {
 			w.WriteHeader(http.StatusNotFound)
 			return
 		}
 
 		if err := handlerFunc(srv, version, w, r, mux.Vars(r)); err != nil {
ad723bbf
 			utils.Errorf("Error: %s", err)
166eba3e
 			httpError(w, err)
 		}
 	}
 }
 
6a55169e
 // Replicated from expvar.go as not public.
 func expvarHandler(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json; charset=utf-8")
 	fmt.Fprintf(w, "{\n")
 	first := true
 	expvar.Do(func(kv expvar.KeyValue) {
 		if !first {
 			fmt.Fprintf(w, ",\n")
 		}
 		first = false
 		fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
 	})
 	fmt.Fprintf(w, "\n}\n")
 }
 
9f46779d
 func AttachProfiler(router *mux.Router) {
6a55169e
 	router.HandleFunc("/debug/vars", expvarHandler)
9f46779d
 	router.HandleFunc("/debug/pprof/", pprof.Index)
 	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
 	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
 	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
 	router.HandleFunc("/debug/pprof/heap", pprof.Handler("heap").ServeHTTP)
 	router.HandleFunc("/debug/pprof/goroutine", pprof.Handler("goroutine").ServeHTTP)
 	router.HandleFunc("/debug/pprof/threadcreate", pprof.Handler("threadcreate").ServeHTTP)
 }
 
0a28628c
 func createRouter(srv *Server, logging bool) (*mux.Router, error) {
b56b2da5
 	r := mux.NewRouter()
9f46779d
 	if os.Getenv("DEBUG") != "" {
 		AttachProfiler(r)
 	}
d639f61e
 	m := map[string]map[string]HttpApiFunc{
b56b2da5
 		"GET": {
e2ca600f
 			"/events":                         getEvents,
166eba3e
 			"/info":                           getInfo,
e2ca600f
 			"/version":                        getVersion,
166eba3e
 			"/images/json":                    getImagesJSON,
8f647598
 			"/images/viz":                     getImagesViz,
166eba3e
 			"/images/search":                  getImagesSearch,
7eaa59f6
 			"/images/{name:.*}/get":           getImagesGet,
166eba3e
 			"/images/{name:.*}/history":       getImagesHistory,
 			"/images/{name:.*}/json":          getImagesByName,
 			"/containers/ps":                  getContainersJSON,
 			"/containers/json":                getContainersJSON,
 			"/containers/{name:.*}/export":    getContainersExport,
 			"/containers/{name:.*}/changes":   getContainersChanges,
 			"/containers/{name:.*}/json":      getContainersByName,
e2ca600f
 			"/containers/{name:.*}/top":       getContainersTop,
166eba3e
 			"/containers/{name:.*}/attach/ws": wsContainersAttach,
b56b2da5
 		},
 		"POST": {
152ebeea
 			"/auth":                         postAuth,
b56b2da5
 			"/commit":                       postCommit,
0f135ad7
 			"/build":                        postBuild,
152ebeea
 			"/images/create":                postImagesCreate,
1941c791
 			"/images/{name:.*}/insert":      postImagesInsert,
7eaa59f6
 			"/images/load":                  postImagesLoad,
1941c791
 			"/images/{name:.*}/push":        postImagesPush,
152ebeea
 			"/images/{name:.*}/tag":         postImagesTag,
 			"/containers/create":            postContainersCreate,
 			"/containers/{name:.*}/kill":    postContainersKill,
b56b2da5
 			"/containers/{name:.*}/restart": postContainersRestart,
 			"/containers/{name:.*}/start":   postContainersStart,
 			"/containers/{name:.*}/stop":    postContainersStop,
 			"/containers/{name:.*}/wait":    postContainersWait,
70d2123e
 			"/containers/{name:.*}/resize":  postContainersResize,
b56b2da5
 			"/containers/{name:.*}/attach":  postContainersAttach,
5b8cfbe1
 			"/containers/{name:.*}/copy":    postContainersCopy,
b56b2da5
 		},
 		"DELETE": {
 			"/containers/{name:.*}": deleteContainers,
 			"/images/{name:.*}":     deleteImages,
 		},
dd53c457
 		"OPTIONS": {
 			"": optionsHandler,
 		},
b56b2da5
 	}
 
 	for method, routes := range m {
 		for route, fct := range routes {
2e69e172
 			utils.Debugf("Registering %s, %s", method, route)
b56b2da5
 			// NOTE: scope issue, make sure the variables are local and won't be changed
 			localRoute := route
 			localFct := fct
166eba3e
 			localMethod := method
b419699a
 
166eba3e
 			// build the handler function
 			f := makeHttpHandler(srv, logging, localMethod, localRoute, localFct)
dd53c457
 
166eba3e
 			// add the new route
dd53c457
 			if localRoute == "" {
 				r.Methods(localMethod).HandlerFunc(f)
 			} else {
 				r.Path("/v{version:[0-9.]+}" + localRoute).Methods(localMethod).HandlerFunc(f)
 				r.Path(localRoute).Methods(localMethod).HandlerFunc(f)
 			}
131c6ab3
 		}
b56b2da5
 	}
8eeff019
 
0a28628c
 	return r, nil
 }
 
359a6f49
 // ServeRequest processes a single http request to the docker remote api.
 // FIXME: refactor this to be part of Server and not require re-creating a new
 // router each time. This requires first moving ListenAndServe into Server.
 func ServeRequest(srv *Server, apiversion float64, w http.ResponseWriter, req *http.Request) error {
 	router, err := createRouter(srv, false)
 	if err != nil {
 		return err
 	}
 	// Insert APIVERSION into the request as a convenience
 	req.URL.Path = fmt.Sprintf("/v%g%s", apiversion, req.URL.Path)
 	router.ServeHTTP(w, req)
 	return nil
 }
 
3adf9ce0
 func ListenAndServe(proto, addr string, srv *Server, logging bool) error {
0a28628c
 	r, err := createRouter(srv, logging)
 	if err != nil {
 		return err
 	}
3adf9ce0
 	l, e := net.Listen(proto, addr)
 	if e != nil {
 		return e
 	}
 	if proto == "unix" {
999a8d72
 		if err := os.Chmod(addr, 0660); err != nil {
 			return err
 		}
 
 		groups, err := ioutil.ReadFile("/etc/group")
 		if err != nil {
 			return err
 		}
 		re := regexp.MustCompile("(^|\n)docker:.*?:([0-9]+)")
 		if gidMatch := re.FindStringSubmatch(string(groups)); gidMatch != nil {
 			gid, err := strconv.Atoi(gidMatch[2])
 			if err != nil {
 				return err
 			}
 			utils.Debugf("docker group found. gid: %d", gid)
 			if err := os.Chown(addr, 0, gid); err != nil {
 				return err
 			}
 		}
3adf9ce0
 	}
 	httpSrv := http.Server{Addr: addr, Handler: r}
97088ebe
 
 	log.Printf("Listening for HTTP on %s (%s)\n", addr, proto)
 	// Tell the init daemon we are accepting requests
 	go systemd.SdNotify("READY=1")
3adf9ce0
 	return httpSrv.Serve(l)
a11b3139
 }