瀏覽代碼

Merge pull request #13771 from tiborvass/daemon-cli

New `docker daemon` command
Jessie Frazelle 10 年之前
父節點
當前提交
7674f21686
共有 86 個文件被更改,包括 1118 次插入701 次删除
  1. 4 2
      api/client/attach.go
  2. 4 3
      api/client/build.go
  3. 77 165
      api/client/cli.go
  4. 0 12
      api/client/client.go
  5. 2 1
      api/client/commit.go
  6. 2 1
      api/client/cp.go
  7. 2 1
      api/client/create.go
  8. 2 1
      api/client/diff.go
  9. 2 1
      api/client/events.go
  10. 4 3
      api/client/exec.go
  11. 2 1
      api/client/export.go
  12. 0 34
      api/client/help.go
  13. 2 1
      api/client/history.go
  14. 2 1
      api/client/images.go
  15. 2 1
      api/client/import.go
  16. 2 1
      api/client/info.go
  17. 11 3
      api/client/inspect.go
  18. 2 1
      api/client/kill.go
  19. 2 1
      api/client/load.go
  20. 2 1
      api/client/login.go
  21. 2 1
      api/client/logout.go
  22. 2 1
      api/client/logs.go
  23. 2 1
      api/client/pause.go
  24. 2 1
      api/client/port.go
  25. 2 1
      api/client/ps.go
  26. 2 1
      api/client/pull.go
  27. 2 1
      api/client/push.go
  28. 2 1
      api/client/rename.go
  29. 2 1
      api/client/restart.go
  30. 2 1
      api/client/rm.go
  31. 2 1
      api/client/rmi.go
  32. 3 2
      api/client/run.go
  33. 2 1
      api/client/save.go
  34. 2 1
      api/client/search.go
  35. 3 2
      api/client/start.go
  36. 2 1
      api/client/stats.go
  37. 2 1
      api/client/stop.go
  38. 2 1
      api/client/tag.go
  39. 2 1
      api/client/top.go
  40. 2 1
      api/client/unpause.go
  41. 4 3
      api/client/version.go
  42. 2 1
      api/client/wait.go
  43. 200 0
      cli/cli.go
  44. 12 0
      cli/client.go
  45. 20 0
      cli/common.go
  46. 17 17
      daemon/config.go
  47. 3 3
      daemon/config_experimental.go
  48. 20 19
      daemon/config_linux.go
  49. 3 1
      daemon/config_stub.go
  50. 2 2
      daemon/config_windows.go
  51. 22 5
      docker/client.go
  52. 101 0
      docker/common.go
  53. 135 29
      docker/daemon.go
  54. 12 0
      docker/daemon_none.go
  55. 33 102
      docker/docker.go
  56. 46 124
      docker/flags.go
  57. 0 14
      docker/log.go
  58. 2 2
      docs/articles/basics.md
  59. 4 5
      docs/articles/configuring.md
  60. 1 1
      docs/articles/https.md
  61. 2 2
      docs/articles/networking.md
  62. 2 2
      docs/articles/registry_mirror.md
  63. 1 1
      docs/articles/systemd.md
  64. 1 1
      docs/installation/binaries.md
  65. 1 1
      docs/installation/ubuntulinux.md
  66. 2 2
      docs/project/set-up-dev-env.md
  67. 1 1
      docs/project/test-and-docs.md
  68. 1 1
      docs/reference/api/docker_remote_api_v1.20.md
  69. 3 0
      docs/reference/commandline/cli.md
  70. 28 31
      docs/reference/commandline/daemon.md
  71. 1 1
      docs/userguide/labels-custom-metadata.md
  72. 1 1
      integration-cli/docker_cli_build_test.go
  73. 103 15
      integration-cli/docker_cli_daemon_test.go
  74. 25 13
      integration-cli/docker_cli_help_test.go
  75. 11 0
      integration-cli/docker_cli_run_test.go
  76. 24 15
      integration-cli/docker_utils.go
  77. 7 0
      opts/hosts_unix.go
  78. 7 0
      opts/hosts_windows.go
  79. 14 11
      opts/opts.go
  80. 1 1
      opts/opts_test.go
  81. 8 5
      opts/ulimit.go
  82. 1 1
      opts/ulimit_test.go
  83. 51 1
      pkg/mflag/flag.go
  84. 11 4
      pkg/tlsconfig/config.go
  85. 3 3
      registry/config.go
  86. 1 3
      runconfig/parse.go

+ 4 - 2
api/client/attach.go

@@ -8,6 +8,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/signal"
 )
@@ -16,9 +17,10 @@ import (
 //
 // Usage: docker attach [OPTIONS] CONTAINER
 func (cli *DockerCli) CmdAttach(args ...string) error {
-	cmd := cli.Subcmd("attach", []string{"CONTAINER"}, "Attach to a running container", true)
+	cmd := Cli.Subcmd("attach", []string{"CONTAINER"}, "Attach to a running container", true)
 	noStdin := cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN")
 	proxy := cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process")
+
 	cmd.Require(flag.Exact, 1)
 
 	cmd.ParseFlags(args, true)
@@ -75,7 +77,7 @@ func (cli *DockerCli) CmdAttach(args ...string) error {
 		return err
 	}
 	if status != 0 {
-		return StatusError{StatusCode: status}
+		return Cli.StatusError{StatusCode: status}
 	}
 
 	return nil

+ 4 - 3
api/client/build.go

@@ -18,6 +18,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/api"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/graph/tags"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/archive"
@@ -46,7 +47,7 @@ const (
 //
 // Usage: docker build [OPTIONS] PATH | URL | -
 func (cli *DockerCli) CmdBuild(args ...string) error {
-	cmd := cli.Subcmd("build", []string{"PATH | URL | -"}, "Build a new image from the source code at PATH", true)
+	cmd := Cli.Subcmd("build", []string{"PATH | URL | -"}, "Build a new image from the source code at PATH", true)
 	tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) for the image")
 	suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers")
 	noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image")
@@ -64,7 +65,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 	flCgroupParent := cmd.String([]string{"-cgroup-parent"}, "", "Optional parent cgroup for the container")
 
 	ulimits := make(map[string]*ulimit.Ulimit)
-	flUlimits := opts.NewUlimitOpt(ulimits)
+	flUlimits := opts.NewUlimitOpt(&ulimits)
 	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
 
 	cmd.Require(flag.Exact, 1)
@@ -325,7 +326,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
 		if jerr.Code == 0 {
 			jerr.Code = 1
 		}
-		return StatusError{Status: jerr.Message, StatusCode: jerr.Code}
+		return Cli.StatusError{Status: jerr.Message, StatusCode: jerr.Code}
 	}
 	return err
 }

+ 77 - 165
api/client/cli.go

@@ -2,25 +2,28 @@ package client
 
 import (
 	"crypto/tls"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
 	"net/http"
 	"net/url"
-	"reflect"
+	"os"
 	"strings"
-	"text/template"
 
+	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cliconfig"
-	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/sockets"
 	"github.com/docker/docker/pkg/term"
+	"github.com/docker/docker/pkg/tlsconfig"
 )
 
 // DockerCli represents the docker command line client.
 // Instances of the client can be returned from NewDockerCli.
 type DockerCli struct {
+	// initializing closure
+	init func() error
+
 	// proto holds the client protocol i.e. unix.
 	proto string
 	// addr holds the client address.
@@ -55,116 +58,11 @@ type DockerCli struct {
 	transport *http.Transport
 }
 
-var funcMap = template.FuncMap{
-	"json": func(v interface{}) string {
-		a, _ := json.Marshal(v)
-		return string(a)
-	},
-}
-
-func (cli *DockerCli) Out() io.Writer {
-	return cli.out
-}
-
-func (cli *DockerCli) Err() io.Writer {
-	return cli.err
-}
-
-func (cli *DockerCli) getMethod(args ...string) (func(...string) error, bool) {
-	camelArgs := make([]string, len(args))
-	for i, s := range args {
-		if len(s) == 0 {
-			return nil, false
-		}
-		camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
-	}
-	methodName := "Cmd" + strings.Join(camelArgs, "")
-	method := reflect.ValueOf(cli).MethodByName(methodName)
-	if !method.IsValid() {
-		return nil, false
-	}
-	return method.Interface().(func(...string) error), true
-}
-
-// Cmd executes the specified command.
-func (cli *DockerCli) Cmd(args ...string) error {
-	if len(args) > 1 {
-		method, exists := cli.getMethod(args[:2]...)
-		if exists {
-			return method(args[2:]...)
-		}
-	}
-	if len(args) > 0 {
-		method, exists := cli.getMethod(args[0])
-		if !exists {
-			return fmt.Errorf("docker: '%s' is not a docker command.\nSee 'docker --help'.", args[0])
-		}
-		return method(args[1:]...)
+func (cli *DockerCli) Initialize() error {
+	if cli.init == nil {
+		return nil
 	}
-	return cli.CmdHelp()
-}
-
-// Subcmd is a subcommand of the main "docker" command.
-// A subcommand represents an action that can be performed
-// from the Docker command line client.
-//
-// Multiple subcommand synopses may be provided with one 'Usage' line being
-// printed for each in the following way:
-//
-//	Usage:	docker <subcmd-name> [OPTIONS] <synopsis 0>
-// 		docker <subcmd-name> [OPTIONS] <synopsis 1>
-// 		...
-//
-// If no undeprecated flags are added to the returned FlagSet, "[OPTIONS]" will
-// not be included on the usage synopsis lines. If no synopses are given, only
-// one usage synopsis line will be printed with nothing following the
-// "[OPTIONS]" section
-//
-// To see all available subcommands, run "docker --help".
-func (cli *DockerCli) Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet {
-	var errorHandling flag.ErrorHandling
-	if exitOnError {
-		errorHandling = flag.ExitOnError
-	} else {
-		errorHandling = flag.ContinueOnError
-	}
-
-	flags := flag.NewFlagSet(name, errorHandling)
-
-	flags.Usage = func() {
-		flags.ShortUsage()
-		flags.PrintDefaults()
-	}
-
-	flags.ShortUsage = func() {
-		options := ""
-		if flags.FlagCountUndeprecated() > 0 {
-			options = " [OPTIONS]"
-		}
-
-		if len(synopses) == 0 {
-			synopses = []string{""}
-		}
-
-		// Allow for multiple command usage synopses.
-		for i, synopsis := range synopses {
-			lead := "\t"
-			if i == 0 {
-				// First line needs the word 'Usage'.
-				lead = "Usage:\t"
-			}
-
-			if synopsis != "" {
-				synopsis = " " + synopsis
-			}
-
-			fmt.Fprintf(flags.Out(), "\n%sdocker %s%s%s", lead, name, options, synopsis)
-		}
-
-		fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
-	}
-
-	return flags
+	return cli.init()
 }
 
 // CheckTtyInput checks if we are trying to attach to a container tty
@@ -187,64 +85,78 @@ func (cli *DockerCli) PsFormat() string {
 // The key file, protocol (i.e. unix) and address are passed in as strings, along with the tls.Config. If the tls.Config
 // is set the client scheme will be set to https.
 // The client will be given a 32-second timeout (see https://github.com/docker/docker/pull/8035).
-func NewDockerCli(in io.ReadCloser, out, err io.Writer, keyFile string, proto, addr string, tlsConfig *tls.Config) *DockerCli {
-	var (
-		inFd          uintptr
-		outFd         uintptr
-		isTerminalIn  = false
-		isTerminalOut = false
-		scheme        = "http"
-		basePath      = ""
-	)
-
-	if tlsConfig != nil {
-		scheme = "https"
-	}
-	if in != nil {
-		inFd, isTerminalIn = term.GetFdInfo(in)
+func NewDockerCli(in io.ReadCloser, out, err io.Writer, clientFlags *cli.ClientFlags) *DockerCli {
+	cli := &DockerCli{
+		in:      in,
+		out:     out,
+		err:     err,
+		keyFile: clientFlags.Common.TrustKey,
 	}
 
-	if out != nil {
-		outFd, isTerminalOut = term.GetFdInfo(out)
-	}
+	cli.init = func() error {
+		clientFlags.PostParse()
 
-	if err == nil {
-		err = out
-	}
+		hosts := clientFlags.Common.Hosts
 
-	// The transport is created here for reuse during the client session.
-	tr := &http.Transport{
-		TLSClientConfig: tlsConfig,
-	}
-	sockets.ConfigureTCPTransport(tr, proto, addr)
+		switch len(hosts) {
+		case 0:
+			defaultHost := os.Getenv("DOCKER_HOST")
+			if defaultHost == "" {
+				defaultHost = opts.DefaultHost
+			}
+			defaultHost, err := opts.ValidateHost(defaultHost)
+			if err != nil {
+				return err
+			}
+			hosts = []string{defaultHost}
+		case 1:
+			// only accept one host to talk to
+		default:
+			return errors.New("Please specify only one -H")
+		}
 
-	configFile, e := cliconfig.Load(cliconfig.ConfigDir())
-	if e != nil {
-		fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e)
-	}
+		protoAddrParts := strings.SplitN(hosts[0], "://", 2)
+		cli.proto, cli.addr = protoAddrParts[0], protoAddrParts[1]
 
-	if proto == "tcp" {
-		// error is checked in pkg/parsers already
-		parsed, _ := url.Parse("tcp://" + addr)
-		addr = parsed.Host
-		basePath = parsed.Path
-	}
+		if cli.proto == "tcp" {
+			// error is checked in pkg/parsers already
+			parsed, _ := url.Parse("tcp://" + cli.addr)
+			cli.addr = parsed.Host
+			cli.basePath = parsed.Path
+		}
 
-	return &DockerCli{
-		proto:         proto,
-		addr:          addr,
-		basePath:      basePath,
-		configFile:    configFile,
-		in:            in,
-		out:           out,
-		err:           err,
-		keyFile:       keyFile,
-		inFd:          inFd,
-		outFd:         outFd,
-		isTerminalIn:  isTerminalIn,
-		isTerminalOut: isTerminalOut,
-		tlsConfig:     tlsConfig,
-		scheme:        scheme,
-		transport:     tr,
+		if clientFlags.Common.TLSOptions != nil {
+			cli.scheme = "https"
+			var e error
+			cli.tlsConfig, e = tlsconfig.Client(*clientFlags.Common.TLSOptions)
+			if e != nil {
+				return e
+			}
+		} else {
+			cli.scheme = "http"
+		}
+
+		if cli.in != nil {
+			cli.inFd, cli.isTerminalIn = term.GetFdInfo(cli.in)
+		}
+		if cli.out != nil {
+			cli.outFd, cli.isTerminalOut = term.GetFdInfo(cli.out)
+		}
+
+		// The transport is created here for reuse during the client session.
+		cli.transport = &http.Transport{
+			TLSClientConfig: cli.tlsConfig,
+		}
+		sockets.ConfigureTCPTransport(cli.transport, cli.proto, cli.addr)
+
+		configFile, e := cliconfig.Load(cliconfig.ConfigDir())
+		if e != nil {
+			fmt.Fprintf(cli.err, "WARNING: Error loading config file:%v\n", e)
+		}
+		cli.configFile = configFile
+
+		return nil
 	}
+
+	return cli
 }

+ 0 - 12
api/client/client.go

@@ -3,15 +3,3 @@
 // Run "docker help SUBCOMMAND" or "docker SUBCOMMAND --help" to see more information on any Docker subcommand, including the full list of options supported for the subcommand.
 // See https://docs.docker.com/installation/ for instructions on installing Docker.
 package client
-
-import "fmt"
-
-// An StatusError reports an unsuccessful exit by a command.
-type StatusError struct {
-	Status     string
-	StatusCode int
-}
-
-func (e StatusError) Error() string {
-	return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
-}

+ 2 - 1
api/client/commit.go

@@ -6,6 +6,7 @@ import (
 	"net/url"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
@@ -17,7 +18,7 @@ import (
 //
 // Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
 func (cli *DockerCli) CmdCommit(args ...string) error {
-	cmd := cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, "Create a new image from a container's changes", true)
+	cmd := Cli.Subcmd("commit", []string{"CONTAINER [REPOSITORY[:TAG]]"}, "Create a new image from a container's changes", true)
 	flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit")
 	flComment := cmd.String([]string{"m", "-message"}, "", "Commit message")
 	flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith <hannibal@a-team.com>\")")

+ 2 - 1
api/client/cp.go

@@ -12,6 +12,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/archive"
 	flag "github.com/docker/docker/pkg/mflag"
 )
@@ -37,7 +38,7 @@ const (
 // 	docker cp CONTAINER:PATH LOCALPATH|-
 // 	docker cp LOCALPATH|- CONTAINER:PATH
 func (cli *DockerCli) CmdCp(args ...string) error {
-	cmd := cli.Subcmd(
+	cmd := Cli.Subcmd(
 		"cp",
 		[]string{"CONTAINER:PATH LOCALPATH|-", "LOCALPATH|- CONTAINER:PATH"},
 		strings.Join([]string{

+ 2 - 1
api/client/create.go

@@ -10,6 +10,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/graph/tags"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/registry"
@@ -137,7 +138,7 @@ func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runc
 //
 // Usage: docker create [OPTIONS] IMAGE [COMMAND] [ARG...]
 func (cli *DockerCli) CmdCreate(args ...string) error {
-	cmd := cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true)
+	cmd := Cli.Subcmd("create", []string{"IMAGE [COMMAND] [ARG...]"}, "Create a new container", true)
 
 	// These are flags not stored in Config/HostConfig
 	var (

+ 2 - 1
api/client/diff.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/archive"
 	flag "github.com/docker/docker/pkg/mflag"
 )
@@ -17,7 +18,7 @@ import (
 //
 // Usage: docker diff CONTAINER
 func (cli *DockerCli) CmdDiff(args ...string) error {
-	cmd := cli.Subcmd("diff", []string{"CONTAINER"}, "Inspect changes on a container's filesystem", true)
+	cmd := Cli.Subcmd("diff", []string{"CONTAINER"}, "Inspect changes on a container's filesystem", true)
 	cmd.Require(flag.Exact, 1)
 
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/events.go

@@ -4,6 +4,7 @@ import (
 	"net/url"
 	"time"
 
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers/filters"
@@ -14,7 +15,7 @@ import (
 //
 // Usage: docker events [OPTIONS]
 func (cli *DockerCli) CmdEvents(args ...string) error {
-	cmd := cli.Subcmd("events", nil, "Get real time events from the server", true)
+	cmd := Cli.Subcmd("events", nil, "Get real time events from the server", true)
 	since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp")
 	until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp")
 	flFilter := opts.NewListOpts(nil)

+ 4 - 3
api/client/exec.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/runconfig"
 )
@@ -15,12 +16,12 @@ import (
 //
 // Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
 func (cli *DockerCli) CmdExec(args ...string) error {
-	cmd := cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, "Run a command in a running container", true)
+	cmd := Cli.Subcmd("exec", []string{"CONTAINER COMMAND [ARG...]"}, "Run a command in a running container", true)
 
 	execConfig, err := runconfig.ParseExec(cmd, args)
 	// just in case the ParseExec does not exit
 	if execConfig.Container == "" || err != nil {
-		return StatusError{StatusCode: 1}
+		return Cli.StatusError{StatusCode: 1}
 	}
 
 	serverResp, err := cli.call("POST", "/containers/"+execConfig.Container+"/exec", execConfig, nil)
@@ -126,7 +127,7 @@ func (cli *DockerCli) CmdExec(args ...string) error {
 	}
 
 	if status != 0 {
-		return StatusError{StatusCode: status}
+		return Cli.StatusError{StatusCode: status}
 	}
 
 	return nil

+ 2 - 1
api/client/export.go

@@ -5,6 +5,7 @@ import (
 	"io"
 	"os"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -14,7 +15,7 @@ import (
 //
 // Usage: docker export [OPTIONS] CONTAINER
 func (cli *DockerCli) CmdExport(args ...string) error {
-	cmd := cli.Subcmd("export", []string{"CONTAINER"}, "Export the contents of a container's filesystem as a tar archive", true)
+	cmd := Cli.Subcmd("export", []string{"CONTAINER"}, "Export the contents of a container's filesystem as a tar archive", true)
 	outfile := cmd.String([]string{"o", "-output"}, "", "Write to a file, instead of STDOUT")
 	cmd.Require(flag.Exact, 1)
 

+ 0 - 34
api/client/help.go

@@ -1,34 +0,0 @@
-package client
-
-import (
-	"fmt"
-
-	flag "github.com/docker/docker/pkg/mflag"
-)
-
-// CmdHelp displays information on a Docker command.
-//
-// If more than one command is specified, information is only shown for the first command.
-//
-// Usage: docker help COMMAND or docker COMMAND --help
-func (cli *DockerCli) CmdHelp(args ...string) error {
-	if len(args) > 1 {
-		method, exists := cli.getMethod(args[:2]...)
-		if exists {
-			method("--help")
-			return nil
-		}
-	}
-	if len(args) > 0 {
-		method, exists := cli.getMethod(args[0])
-		if !exists {
-			return fmt.Errorf("docker: '%s' is not a docker command. See 'docker --help'.", args[0])
-		}
-		method("--help")
-		return nil
-	}
-
-	flag.Usage()
-
-	return nil
-}

+ 2 - 1
api/client/history.go

@@ -7,6 +7,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringutils"
@@ -17,7 +18,7 @@ import (
 //
 // Usage: docker history [OPTIONS] IMAGE
 func (cli *DockerCli) CmdHistory(args ...string) error {
-	cmd := cli.Subcmd("history", []string{"IMAGE"}, "Show the history of an image", true)
+	cmd := Cli.Subcmd("history", []string{"IMAGE"}, "Show the history of an image", true)
 	human := cmd.Bool([]string{"H", "-human"}, true, "Print sizes and dates in human readable format")
 	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")

+ 2 - 1
api/client/images.go

@@ -8,6 +8,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
@@ -21,7 +22,7 @@ import (
 //
 // Usage: docker images [OPTIONS] [REPOSITORY]
 func (cli *DockerCli) CmdImages(args ...string) error {
-	cmd := cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true)
+	cmd := Cli.Subcmd("images", []string{"[REPOSITORY]"}, "List images", true)
 	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs")
 	all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (default hides intermediate images)")
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")

+ 2 - 1
api/client/import.go

@@ -6,6 +6,7 @@ import (
 	"net/url"
 	"os"
 
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
@@ -19,7 +20,7 @@ import (
 //
 // Usage: docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
 func (cli *DockerCli) CmdImport(args ...string) error {
-	cmd := cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
+	cmd := Cli.Subcmd("import", []string{"file|URL|- [REPOSITORY[:TAG]]"}, "Create an empty filesystem image and import the contents of the\ntarball (.tar, .tar.gz, .tgz, .bzip, .tar.xz, .txz) into it, then\noptionally tag it.", true)
 	flChanges := opts.NewListOpts(nil)
 	cmd.Var(&flChanges, []string{"c", "-change"}, "Apply Dockerfile instruction to the created image")
 	cmd.Require(flag.Min, 1)

+ 2 - 1
api/client/info.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/ioutils"
 	flag "github.com/docker/docker/pkg/mflag"
@@ -15,7 +16,7 @@ import (
 //
 // Usage: docker info
 func (cli *DockerCli) CmdInfo(args ...string) error {
-	cmd := cli.Subcmd("info", nil, "Display system-wide information", true)
+	cmd := Cli.Subcmd("info", nil, "Display system-wide information", true)
 	cmd.Require(flag.Exact, 0)
 
 	cmd.ParseFlags(args, true)

+ 11 - 3
api/client/inspect.go

@@ -9,14 +9,22 @@ import (
 	"text/template"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
+var funcMap = template.FuncMap{
+	"json": func(v interface{}) string {
+		a, _ := json.Marshal(v)
+		return string(a)
+	},
+}
+
 // CmdInspect displays low-level information on one or more containers or images.
 //
 // Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
 func (cli *DockerCli) CmdInspect(args ...string) error {
-	cmd := cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true)
+	cmd := Cli.Subcmd("inspect", []string{"CONTAINER|IMAGE [CONTAINER|IMAGE...]"}, "Return low-level information on a container or image", true)
 	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
 	inspectType := cmd.String([]string{"-type"}, "", "Return JSON for specified type, (e.g image or container)")
 	cmd.Require(flag.Min, 1)
@@ -29,7 +37,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 
 	if *tmplStr != "" {
 		if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
-			return StatusError{StatusCode: 64,
+			return Cli.StatusError{StatusCode: 64,
 				Status: "Template parsing error: " + err.Error()}
 		}
 	}
@@ -143,7 +151,7 @@ func (cli *DockerCli) CmdInspect(args ...string) error {
 	}
 
 	if status != 0 {
-		return StatusError{StatusCode: status}
+		return Cli.StatusError{StatusCode: status}
 	}
 	return nil
 }

+ 2 - 1
api/client/kill.go

@@ -3,6 +3,7 @@ package client
 import (
 	"fmt"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -10,7 +11,7 @@ import (
 //
 // Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdKill(args ...string) error {
-	cmd := cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, "Kill a running container using SIGKILL or a specified signal", true)
+	cmd := Cli.Subcmd("kill", []string{"CONTAINER [CONTAINER...]"}, "Kill a running container using SIGKILL or a specified signal", true)
 	signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container")
 	cmd.Require(flag.Min, 1)
 

+ 2 - 1
api/client/load.go

@@ -4,6 +4,7 @@ import (
 	"io"
 	"os"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -13,7 +14,7 @@ import (
 //
 // Usage: docker load [OPTIONS]
 func (cli *DockerCli) CmdLoad(args ...string) error {
-	cmd := cli.Subcmd("load", nil, "Load an image from a tar archive or STDIN", true)
+	cmd := Cli.Subcmd("load", nil, "Load an image from a tar archive or STDIN", true)
 	infile := cmd.String([]string{"i", "-input"}, "", "Read from a tar archive file, instead of STDIN")
 	cmd.Require(flag.Exact, 0)
 

+ 2 - 1
api/client/login.go

@@ -9,6 +9,7 @@ import (
 	"strings"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/cliconfig"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/term"
@@ -21,7 +22,7 @@ import (
 //
 // Usage: docker login SERVER
 func (cli *DockerCli) CmdLogin(args ...string) error {
-	cmd := cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
+	cmd := Cli.Subcmd("login", []string{"[SERVER]"}, "Register or log in to a Docker registry server, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
 	cmd.Require(flag.Max, 1)
 
 	var username, password, email string

+ 2 - 1
api/client/logout.go

@@ -3,6 +3,7 @@ package client
 import (
 	"fmt"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/registry"
 )
@@ -13,7 +14,7 @@ import (
 //
 // Usage: docker logout [SERVER]
 func (cli *DockerCli) CmdLogout(args ...string) error {
-	cmd := cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
+	cmd := Cli.Subcmd("logout", []string{"[SERVER]"}, "Log out from a Docker registry, if no server is\nspecified \""+registry.INDEXSERVER+"\" is the default.", true)
 	cmd.Require(flag.Max, 1)
 
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/logs.go

@@ -7,6 +7,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/timeutils"
 )
@@ -15,7 +16,7 @@ import (
 //
 // docker logs [OPTIONS] CONTAINER
 func (cli *DockerCli) CmdLogs(args ...string) error {
-	cmd := cli.Subcmd("logs", []string{"CONTAINER"}, "Fetch the logs of a container", true)
+	cmd := Cli.Subcmd("logs", []string{"CONTAINER"}, "Fetch the logs of a container", true)
 	follow := cmd.Bool([]string{"f", "-follow"}, false, "Follow log output")
 	since := cmd.String([]string{"-since"}, "", "Show logs since timestamp")
 	times := cmd.Bool([]string{"t", "-timestamps"}, false, "Show timestamps")

+ 2 - 1
api/client/pause.go

@@ -3,6 +3,7 @@ package client
 import (
 	"fmt"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -10,7 +11,7 @@ import (
 //
 // Usage: docker pause CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdPause(args ...string) error {
-	cmd := cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, "Pause all processes within a container", true)
+	cmd := Cli.Subcmd("pause", []string{"CONTAINER [CONTAINER...]"}, "Pause all processes within a container", true)
 	cmd.Require(flag.Min, 1)
 
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/port.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"strings"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/nat"
 )
@@ -14,7 +15,7 @@ import (
 //
 // Usage: docker port CONTAINER [PRIVATE_PORT[/PROTO]]
 func (cli *DockerCli) CmdPort(args ...string) error {
-	cmd := cli.Subcmd("port", []string{"CONTAINER [PRIVATE_PORT[/PROTO]]"}, "List port mappings for the CONTAINER, or lookup the public-facing port that\nis NAT-ed to the PRIVATE_PORT", true)
+	cmd := Cli.Subcmd("port", []string{"CONTAINER [PRIVATE_PORT[/PROTO]]"}, "List port mappings for the CONTAINER, or lookup the public-facing port that\nis NAT-ed to the PRIVATE_PORT", true)
 	cmd.Require(flag.Min, 1)
 
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/ps.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/docker/docker/api/client/ps"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers/filters"
@@ -22,7 +23,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		psFilterArgs = filters.Args{}
 		v            = url.Values{}
 
-		cmd      = cli.Subcmd("ps", nil, "List containers", true)
+		cmd      = Cli.Subcmd("ps", nil, "List containers", true)
 		quiet    = cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
 		size     = cmd.Bool([]string{"s", "-size"}, false, "Display total file sizes")
 		all      = cmd.Bool([]string{"a", "-all"}, false, "Show all containers (default shows just running)")

+ 2 - 1
api/client/pull.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"net/url"
 
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/graph/tags"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
@@ -15,7 +16,7 @@ import (
 //
 // Usage: docker pull [OPTIONS] IMAGENAME[:TAG|@DIGEST]
 func (cli *DockerCli) CmdPull(args ...string) error {
-	cmd := cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, "Pull an image or a repository from a registry", true)
+	cmd := Cli.Subcmd("pull", []string{"NAME[:TAG|@DIGEST]"}, "Pull an image or a repository from a registry", true)
 	allTags := cmd.Bool([]string{"a", "-all-tags"}, false, "Download all tagged images in the repository")
 	cmd.Require(flag.Exact, 1)
 

+ 2 - 1
api/client/push.go

@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"net/url"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/registry"
@@ -13,7 +14,7 @@ import (
 //
 // Usage: docker push NAME[:TAG]
 func (cli *DockerCli) CmdPush(args ...string) error {
-	cmd := cli.Subcmd("push", []string{"NAME[:TAG]"}, "Push an image or a repository to a registry", true)
+	cmd := Cli.Subcmd("push", []string{"NAME[:TAG]"}, "Push an image or a repository to a registry", true)
 	cmd.Require(flag.Exact, 1)
 
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/rename.go

@@ -3,6 +3,7 @@ package client
 import (
 	"fmt"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -10,7 +11,7 @@ import (
 //
 // Usage: docker rename OLD_NAME NEW_NAME
 func (cli *DockerCli) CmdRename(args ...string) error {
-	cmd := cli.Subcmd("rename", []string{"OLD_NAME NEW_NAME"}, "Rename a container", true)
+	cmd := Cli.Subcmd("rename", []string{"OLD_NAME NEW_NAME"}, "Rename a container", true)
 	cmd.Require(flag.Exact, 2)
 
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/restart.go

@@ -5,6 +5,7 @@ import (
 	"net/url"
 	"strconv"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -12,7 +13,7 @@ import (
 //
 // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdRestart(args ...string) error {
-	cmd := cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
+	cmd := Cli.Subcmd("restart", []string{"CONTAINER [CONTAINER...]"}, "Restart a running container", true)
 	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container")
 	cmd.Require(flag.Min, 1)
 

+ 2 - 1
api/client/rm.go

@@ -5,6 +5,7 @@ import (
 	"net/url"
 	"strings"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -12,7 +13,7 @@ import (
 //
 // Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdRm(args ...string) error {
-	cmd := cli.Subcmd("rm", []string{"CONTAINER [CONTAINER...]"}, "Remove one or more containers", true)
+	cmd := Cli.Subcmd("rm", []string{"CONTAINER [CONTAINER...]"}, "Remove one or more containers", true)
 	v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container")
 	link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link")
 	force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)")

+ 2 - 1
api/client/rmi.go

@@ -6,6 +6,7 @@ import (
 	"net/url"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -13,7 +14,7 @@ import (
 //
 // Usage: docker rmi [OPTIONS] IMAGE [IMAGE...]
 func (cli *DockerCli) CmdRmi(args ...string) error {
-	cmd := cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, "Remove one or more images", true)
+	cmd := Cli.Subcmd("rmi", []string{"IMAGE [IMAGE...]"}, "Remove one or more images", true)
 	force := cmd.Bool([]string{"f", "-force"}, false, "Force removal of the image")
 	noprune := cmd.Bool([]string{"-no-prune"}, false, "Do not delete untagged parents")
 	cmd.Require(flag.Min, 1)

+ 3 - 2
api/client/run.go

@@ -8,6 +8,7 @@ import (
 	"runtime"
 
 	"github.com/Sirupsen/logrus"
+	Cli "github.com/docker/docker/cli"
 	"github.com/docker/docker/opts"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/signal"
@@ -39,7 +40,7 @@ func (cid *cidFile) Write(id string) error {
 //
 // Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
 func (cli *DockerCli) CmdRun(args ...string) error {
-	cmd := cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, "Run a command in a new container", true)
+	cmd := Cli.Subcmd("run", []string{"IMAGE [COMMAND] [ARG...]"}, "Run a command in a new container", true)
 
 	// These are flags not stored in Config/HostConfig
 	var (
@@ -249,7 +250,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
 		}
 	}
 	if status != 0 {
-		return StatusError{StatusCode: status}
+		return Cli.StatusError{StatusCode: status}
 	}
 	return nil
 }

+ 2 - 1
api/client/save.go

@@ -6,6 +6,7 @@ import (
 	"net/url"
 	"os"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -15,7 +16,7 @@ import (
 //
 // Usage: docker save [OPTIONS] IMAGE [IMAGE...]
 func (cli *DockerCli) CmdSave(args ...string) error {
-	cmd := cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
+	cmd := Cli.Subcmd("save", []string{"IMAGE [IMAGE...]"}, "Save an image(s) to a tar archive (streamed to STDOUT by default)", true)
 	outfile := cmd.String([]string{"o", "-output"}, "", "Write to an file, instead of STDOUT")
 	cmd.Require(flag.Min, 1)
 

+ 2 - 1
api/client/search.go

@@ -8,6 +8,7 @@ import (
 	"strings"
 	"text/tabwriter"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/pkg/stringutils"
@@ -25,7 +26,7 @@ func (r ByStars) Less(i, j int) bool { return r[i].StarCount < r[j].StarCount }
 //
 // Usage: docker search [OPTIONS] TERM
 func (cli *DockerCli) CmdSearch(args ...string) error {
-	cmd := cli.Subcmd("search", []string{"TERM"}, "Search the Docker Hub for images", true)
+	cmd := Cli.Subcmd("search", []string{"TERM"}, "Search the Docker Hub for images", true)
 	noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output")
 	trusted := cmd.Bool([]string{"#t", "#trusted", "#-trusted"}, false, "Only show trusted builds")
 	automated := cmd.Bool([]string{"-automated"}, false, "Only show automated builds")

+ 3 - 2
api/client/start.go

@@ -9,6 +9,7 @@ import (
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/promise"
 	"github.com/docker/docker/pkg/signal"
@@ -44,7 +45,7 @@ func (cli *DockerCli) forwardAllSignals(cid string) chan os.Signal {
 //
 // Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdStart(args ...string) error {
-	cmd := cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, "Start one or more stopped containers", true)
+	cmd := Cli.Subcmd("start", []string{"CONTAINER [CONTAINER...]"}, "Start one or more stopped containers", true)
 	attach := cmd.Bool([]string{"a", "-attach"}, false, "Attach STDOUT/STDERR and forward signals")
 	openStdin := cmd.Bool([]string{"i", "-interactive"}, false, "Attach container's STDIN")
 	cmd.Require(flag.Min, 1)
@@ -162,7 +163,7 @@ func (cli *DockerCli) CmdStart(args ...string) error {
 			return err
 		}
 		if status != 0 {
-			return StatusError{StatusCode: status}
+			return Cli.StatusError{StatusCode: status}
 		}
 	}
 	return nil

+ 2 - 1
api/client/stats.go

@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/units"
 )
@@ -124,7 +125,7 @@ func (s *containerStats) Display(w io.Writer) error {
 //
 // Usage: docker stats CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdStats(args ...string) error {
-	cmd := cli.Subcmd("stats", []string{"CONTAINER [CONTAINER...]"}, "Display a live stream of one or more containers' resource usage statistics", true)
+	cmd := Cli.Subcmd("stats", []string{"CONTAINER [CONTAINER...]"}, "Display a live stream of one or more containers' resource usage statistics", true)
 	noStream := cmd.Bool([]string{"-no-stream"}, false, "Disable streaming stats and only pull the first result")
 	cmd.Require(flag.Min, 1)
 

+ 2 - 1
api/client/stop.go

@@ -5,6 +5,7 @@ import (
 	"net/url"
 	"strconv"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -14,7 +15,7 @@ import (
 //
 // Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdStop(args ...string) error {
-	cmd := cli.Subcmd("stop", []string{"CONTAINER [CONTAINER...]"}, "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true)
+	cmd := Cli.Subcmd("stop", []string{"CONTAINER [CONTAINER...]"}, "Stop a running container by sending SIGTERM and then SIGKILL after a\ngrace period", true)
 	nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing it")
 	cmd.Require(flag.Min, 1)
 

+ 2 - 1
api/client/tag.go

@@ -3,6 +3,7 @@ package client
 import (
 	"net/url"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/parsers"
 	"github.com/docker/docker/registry"
@@ -12,7 +13,7 @@ import (
 //
 // Usage: docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
 func (cli *DockerCli) CmdTag(args ...string) error {
-	cmd := cli.Subcmd("tag", []string{"IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]"}, "Tag an image into a repository", true)
+	cmd := Cli.Subcmd("tag", []string{"IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]"}, "Tag an image into a repository", true)
 	force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force")
 	cmd.Require(flag.Exact, 2)
 

+ 2 - 1
api/client/top.go

@@ -8,6 +8,7 @@ import (
 	"text/tabwriter"
 
 	"github.com/docker/docker/api/types"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -15,7 +16,7 @@ import (
 //
 // Usage: docker top CONTAINER
 func (cli *DockerCli) CmdTop(args ...string) error {
-	cmd := cli.Subcmd("top", []string{"CONTAINER [ps OPTIONS]"}, "Display the running processes of a container", true)
+	cmd := Cli.Subcmd("top", []string{"CONTAINER [ps OPTIONS]"}, "Display the running processes of a container", true)
 	cmd.Require(flag.Min, 1)
 
 	cmd.ParseFlags(args, true)

+ 2 - 1
api/client/unpause.go

@@ -3,6 +3,7 @@ package client
 import (
 	"fmt"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -10,7 +11,7 @@ import (
 //
 // Usage: docker unpause CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdUnpause(args ...string) error {
-	cmd := cli.Subcmd("unpause", []string{"CONTAINER [CONTAINER...]"}, "Unpause all processes within a container", true)
+	cmd := Cli.Subcmd("unpause", []string{"CONTAINER [CONTAINER...]"}, "Unpause all processes within a container", true)
 	cmd.Require(flag.Min, 1)
 
 	cmd.ParseFlags(args, true)

+ 4 - 3
api/client/version.go

@@ -8,6 +8,7 @@ import (
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/autogen/dockerversion"
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/utils"
 )
@@ -42,7 +43,7 @@ type VersionData struct {
 //
 // Usage: docker version
 func (cli *DockerCli) CmdVersion(args ...string) (err error) {
-	cmd := cli.Subcmd("version", nil, "Show the Docker version information.", true)
+	cmd := Cli.Subcmd("version", nil, "Show the Docker version information.", true)
 	tmplStr := cmd.String([]string{"f", "#format", "-format"}, "", "Format the output using the given go template")
 	cmd.Require(flag.Exact, 0)
 
@@ -53,7 +54,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
 
 	var tmpl *template.Template
 	if tmpl, err = template.New("").Funcs(funcMap).Parse(*tmplStr); err != nil {
-		return StatusError{StatusCode: 64,
+		return Cli.StatusError{StatusCode: 64,
 			Status: "Template parsing error: " + err.Error()}
 	}
 
@@ -85,7 +86,7 @@ func (cli *DockerCli) CmdVersion(args ...string) (err error) {
 	defer serverResp.body.Close()
 
 	if err = json.NewDecoder(serverResp.body).Decode(&vd.Server); err != nil {
-		return StatusError{StatusCode: 1,
+		return Cli.StatusError{StatusCode: 1,
 			Status: "Error reading remote version: " + err.Error()}
 	}
 

+ 2 - 1
api/client/wait.go

@@ -3,6 +3,7 @@ package client
 import (
 	"fmt"
 
+	Cli "github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 )
 
@@ -12,7 +13,7 @@ import (
 //
 // Usage: docker wait CONTAINER [CONTAINER...]
 func (cli *DockerCli) CmdWait(args ...string) error {
-	cmd := cli.Subcmd("wait", []string{"CONTAINER [CONTAINER...]"}, "Block until a container stops, then print its exit code.", true)
+	cmd := Cli.Subcmd("wait", []string{"CONTAINER [CONTAINER...]"}, "Block until a container stops, then print its exit code.", true)
 	cmd.Require(flag.Min, 1)
 
 	cmd.ParseFlags(args, true)

+ 200 - 0
cli/cli.go

@@ -0,0 +1,200 @@
+package cli
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+	"strings"
+
+	flag "github.com/docker/docker/pkg/mflag"
+)
+
+// Cli represents a command line interface.
+type Cli struct {
+	Stderr   io.Writer
+	handlers []Handler
+	Usage    func()
+}
+
+// Handler holds the different commands Cli will call
+// It should have methods with names starting with `Cmd` like:
+// 	func (h myHandler) CmdFoo(args ...string) error
+type Handler interface{}
+
+// Initializer can be optionally implemented by a Handler to
+// initialize before each call to one of its commands.
+type Initializer interface {
+	Initialize() error
+}
+
+// New instantiates a ready-to-use Cli.
+func New(handlers ...Handler) *Cli {
+	// make the generic Cli object the first cli handler
+	// in order to handle `docker help` appropriately
+	cli := new(Cli)
+	cli.handlers = append([]Handler{cli}, handlers...)
+	return cli
+}
+
+// initErr is an error returned upon initialization of a handler implementing Initializer.
+type initErr struct{ error }
+
+func (err initErr) Error() string {
+	return err.Error()
+}
+
+func (cli *Cli) command(args ...string) (func(...string) error, error) {
+	for _, c := range cli.handlers {
+		if c == nil {
+			continue
+		}
+		camelArgs := make([]string, len(args))
+		for i, s := range args {
+			if len(s) == 0 {
+				return nil, errors.New("empty command")
+			}
+			camelArgs[i] = strings.ToUpper(s[:1]) + strings.ToLower(s[1:])
+		}
+		methodName := "Cmd" + strings.Join(camelArgs, "")
+		method := reflect.ValueOf(c).MethodByName(methodName)
+		if method.IsValid() {
+			if c, ok := c.(Initializer); ok {
+				if err := c.Initialize(); err != nil {
+					return nil, initErr{err}
+				}
+			}
+			return method.Interface().(func(...string) error), nil
+		}
+	}
+	return nil, errors.New("command not found")
+}
+
+// Run executes the specified command.
+func (cli *Cli) Run(args ...string) error {
+	if len(args) > 1 {
+		command, err := cli.command(args[:2]...)
+		switch err := err.(type) {
+		case nil:
+			return command(args[2:]...)
+		case initErr:
+			return err.error
+		}
+	}
+	if len(args) > 0 {
+		command, err := cli.command(args[0])
+		switch err := err.(type) {
+		case nil:
+			return command(args[1:]...)
+		case initErr:
+			return err.error
+		}
+		cli.noSuchCommand(args[0])
+	}
+	return cli.CmdHelp()
+}
+
+func (cli *Cli) noSuchCommand(command string) {
+	if cli.Stderr == nil {
+		cli.Stderr = os.Stderr
+	}
+	fmt.Fprintf(cli.Stderr, "docker: '%s' is not a docker command.\nSee 'docker --help'.\n", command)
+	os.Exit(1)
+}
+
+// CmdHelp displays information on a Docker command.
+//
+// If more than one command is specified, information is only shown for the first command.
+//
+// Usage: docker help COMMAND or docker COMMAND --help
+func (cli *Cli) CmdHelp(args ...string) error {
+	if len(args) > 1 {
+		command, err := cli.command(args[:2]...)
+		switch err := err.(type) {
+		case nil:
+			command("--help")
+			return nil
+		case initErr:
+			return err.error
+		}
+	}
+	if len(args) > 0 {
+		command, err := cli.command(args[0])
+		switch err := err.(type) {
+		case nil:
+			command("--help")
+			return nil
+		case initErr:
+			return err.error
+		}
+		cli.noSuchCommand(args[0])
+	}
+
+	if cli.Usage == nil {
+		flag.Usage()
+	} else {
+		cli.Usage()
+	}
+
+	return nil
+}
+
+// Subcmd is a subcommand of the main "docker" command.
+// A subcommand represents an action that can be performed
+// from the Docker command line client.
+//
+// To see all available subcommands, run "docker --help".
+func Subcmd(name string, synopses []string, description string, exitOnError bool) *flag.FlagSet {
+	var errorHandling flag.ErrorHandling
+	if exitOnError {
+		errorHandling = flag.ExitOnError
+	} else {
+		errorHandling = flag.ContinueOnError
+	}
+	flags := flag.NewFlagSet(name, errorHandling)
+	flags.Usage = func() {
+		flags.ShortUsage()
+		flags.PrintDefaults()
+	}
+
+	flags.ShortUsage = func() {
+		options := ""
+		if flags.FlagCountUndeprecated() > 0 {
+			options = " [OPTIONS]"
+		}
+
+		if len(synopses) == 0 {
+			synopses = []string{""}
+		}
+
+		// Allow for multiple command usage synopses.
+		for i, synopsis := range synopses {
+			lead := "\t"
+			if i == 0 {
+				// First line needs the word 'Usage'.
+				lead = "Usage:\t"
+			}
+
+			if synopsis != "" {
+				synopsis = " " + synopsis
+			}
+
+			fmt.Fprintf(flags.Out(), "\n%sdocker %s%s%s", lead, name, options, synopsis)
+		}
+
+		fmt.Fprintf(flags.Out(), "\n\n%s\n", description)
+	}
+
+	return flags
+}
+
+// An StatusError reports an unsuccessful exit by a command.
+type StatusError struct {
+	Status     string
+	StatusCode int
+}
+
+func (e StatusError) Error() string {
+	return fmt.Sprintf("Status: %s, Code: %d", e.Status, e.StatusCode)
+}

+ 12 - 0
cli/client.go

@@ -0,0 +1,12 @@
+package cli
+
+import flag "github.com/docker/docker/pkg/mflag"
+
+// ClientFlags represents flags for the docker client.
+type ClientFlags struct {
+	FlagSet   *flag.FlagSet
+	Common    *CommonFlags
+	PostParse func()
+
+	ConfigDir string
+}

+ 20 - 0
cli/common.go

@@ -0,0 +1,20 @@
+package cli
+
+import (
+	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/pkg/tlsconfig"
+)
+
+// CommonFlags represents flags that are common to both the client and the daemon.
+type CommonFlags struct {
+	FlagSet   *flag.FlagSet
+	PostParse func()
+
+	Debug      bool
+	Hosts      []string
+	LogLevel   string
+	TLS        bool
+	TLSVerify  bool
+	TLSOptions *tlsconfig.Options
+	TrustKey   string
+}

+ 17 - 17
daemon/config.go

@@ -41,22 +41,22 @@ type CommonConfig struct {
 // the current process.
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // from the command-line.
-func (config *Config) InstallCommonFlags() {
-	opts.ListVar(&config.GraphOptions, []string{"-storage-opt"}, "Set storage driver options")
-	opts.ListVar(&config.ExecOptions, []string{"-exec-opt"}, "Set exec driver options")
-	flag.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, defaultPidFile, "Path to use for daemon PID file")
-	flag.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, "Root of the Docker runtime")
-	flag.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", "Root of the Docker execdriver")
-	flag.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, "--restart on the daemon has been deprecated in favor of --restart policies on docker run")
-	flag.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", "Storage driver to use")
-	flag.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, "Exec driver to use")
-	flag.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, "Set the containers network MTU")
-	flag.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, "Enable CORS headers in the remote API, this is deprecated by --api-cors-header")
-	flag.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", "Set CORS headers in the remote API")
+func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+	cmd.Var(opts.NewListOptsRef(&config.GraphOptions, nil), []string{"-storage-opt"}, usageFn("Set storage driver options"))
+	cmd.Var(opts.NewListOptsRef(&config.ExecOptions, nil), []string{"-exec-opt"}, usageFn("Set exec driver options"))
+	cmd.StringVar(&config.Pidfile, []string{"p", "-pidfile"}, defaultPidFile, usageFn("Path to use for daemon PID file"))
+	cmd.StringVar(&config.Root, []string{"g", "-graph"}, defaultGraph, usageFn("Root of the Docker runtime"))
+	cmd.StringVar(&config.ExecRoot, []string{"-exec-root"}, "/var/run/docker", usageFn("Root of the Docker execdriver"))
+	cmd.BoolVar(&config.AutoRestart, []string{"#r", "#-restart"}, true, usageFn("--restart on the daemon has been deprecated in favor of --restart policies on docker run"))
+	cmd.StringVar(&config.GraphDriver, []string{"s", "-storage-driver"}, "", usageFn("Storage driver to use"))
+	cmd.StringVar(&config.ExecDriver, []string{"e", "-exec-driver"}, defaultExec, usageFn("Exec driver to use"))
+	cmd.IntVar(&config.Mtu, []string{"#mtu", "-mtu"}, 0, usageFn("Set the containers network MTU"))
+	cmd.BoolVar(&config.EnableCors, []string{"#api-enable-cors", "#-api-enable-cors"}, false, usageFn("Enable CORS headers in the remote API, this is deprecated by --api-cors-header"))
+	cmd.StringVar(&config.CorsHeaders, []string{"-api-cors-header"}, "", usageFn("Set CORS headers in the remote API"))
 	// FIXME: why the inconsistency between "hosts" and "sockets"?
-	opts.IPListVar(&config.Dns, []string{"#dns", "-dns"}, "DNS server to use")
-	opts.DNSSearchListVar(&config.DnsSearch, []string{"-dns-search"}, "DNS search domains to use")
-	opts.LabelListVar(&config.Labels, []string{"-label"}, "Set key=value labels to the daemon")
-	flag.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", "Default driver for container logs")
-	opts.LogOptsVar(config.LogConfig.Config, []string{"-log-opt"}, "Set log driver options")
+	cmd.Var(opts.NewListOptsRef(&config.Dns, opts.ValidateIPAddress), []string{"#dns", "-dns"}, usageFn("DNS server to use"))
+	cmd.Var(opts.NewListOptsRef(&config.DnsSearch, opts.ValidateDNSSearch), []string{"-dns-search"}, usageFn("DNS search domains to use"))
+	cmd.Var(opts.NewListOptsRef(&config.Labels, opts.ValidateLabel), []string{"-label"}, usageFn("Set key=value labels to the daemon"))
+	cmd.StringVar(&config.LogConfig.Type, []string{"-log-driver"}, "json-file", usageFn("Default driver for container logs"))
+	cmd.Var(opts.NewMapOpts(config.LogConfig.Config, nil), []string{"-log-opt"}, usageFn("Set log driver options"))
 }

+ 3 - 3
daemon/config_experimental.go

@@ -4,7 +4,7 @@ package daemon
 
 import flag "github.com/docker/docker/pkg/mflag"
 
-func (config *Config) attachExperimentalFlags() {
-	flag.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", "Set default network")
-	flag.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", "Set KV Store configuration")
+func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
+	cmd.StringVar(&config.DefaultNetwork, []string{"-default-network"}, "", usageFn("Set default network"))
+	cmd.StringVar(&config.NetworkKVStore, []string{"-kv-store"}, "", usageFn("Set KV Store configuration"))
 }

+ 20 - 19
daemon/config_linux.go

@@ -49,27 +49,28 @@ type bridgeConfig struct {
 // the current process.
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // from the command-line.
-func (config *Config) InstallFlags() {
+func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags()
+	config.InstallCommonFlags(cmd, usageFn)
 
 	// Then platform-specific install flags
-	flag.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, "Enable selinux support")
-	flag.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", "Group for the unix socket")
+	cmd.BoolVar(&config.EnableSelinuxSupport, []string{"-selinux-enabled"}, false, usageFn("Enable selinux support"))
+	cmd.StringVar(&config.SocketGroup, []string{"G", "-group"}, "docker", usageFn("Group for the unix socket"))
 	config.Ulimits = make(map[string]*ulimit.Ulimit)
-	opts.UlimitMapVar(config.Ulimits, []string{"-default-ulimit"}, "Set default ulimits for containers")
-	flag.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, "Enable addition of iptables rules")
-	flag.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, "Enable net.ipv4.ip_forward")
-	flag.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, "Enable IP masquerading")
-	flag.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, "Enable IPv6 networking")
-	flag.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", "Specify network bridge IP")
-	flag.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", "Attach containers to a network bridge")
-	flag.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", "IPv4 subnet for fixed IPs")
-	flag.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", "IPv6 subnet for fixed IPs")
-	opts.IPVar(&config.Bridge.DefaultGatewayIPv4, []string{"-default-gateway"}, "", "Container default gateway IPv4 address")
-	opts.IPVar(&config.Bridge.DefaultGatewayIPv6, []string{"-default-gateway-v6"}, "", "Container default gateway IPv6 address")
-	flag.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, "Enable inter-container communication")
-	opts.IPVar(&config.Bridge.DefaultIP, []string{"#ip", "-ip"}, "0.0.0.0", "Default IP when binding container ports")
-	flag.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, "Use userland proxy for loopback traffic")
-	config.attachExperimentalFlags()
+	cmd.Var(opts.NewUlimitOpt(&config.Ulimits), []string{"-default-ulimit"}, usageFn("Set default ulimits for containers"))
+	cmd.BoolVar(&config.Bridge.EnableIPTables, []string{"#iptables", "-iptables"}, true, usageFn("Enable addition of iptables rules"))
+	cmd.BoolVar(&config.Bridge.EnableIPForward, []string{"#ip-forward", "-ip-forward"}, true, usageFn("Enable net.ipv4.ip_forward"))
+	cmd.BoolVar(&config.Bridge.EnableIPMasq, []string{"-ip-masq"}, true, usageFn("Enable IP masquerading"))
+	cmd.BoolVar(&config.Bridge.EnableIPv6, []string{"-ipv6"}, false, usageFn("Enable IPv6 networking"))
+	cmd.StringVar(&config.Bridge.IP, []string{"#bip", "-bip"}, "", usageFn("Specify network bridge IP"))
+	cmd.StringVar(&config.Bridge.Iface, []string{"b", "-bridge"}, "", usageFn("Attach containers to a network bridge"))
+	cmd.StringVar(&config.Bridge.FixedCIDR, []string{"-fixed-cidr"}, "", usageFn("IPv4 subnet for fixed IPs"))
+	cmd.StringVar(&config.Bridge.FixedCIDRv6, []string{"-fixed-cidr-v6"}, "", usageFn("IPv6 subnet for fixed IPs"))
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultGatewayIPv4, ""), []string{"-default-gateway"}, usageFn("Container default gateway IPv4 address"))
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultGatewayIPv6, ""), []string{"-default-gateway-v6"}, usageFn("Container default gateway IPv6 address"))
+	cmd.BoolVar(&config.Bridge.InterContainerCommunication, []string{"#icc", "-icc"}, true, usageFn("Enable inter-container communication"))
+	cmd.Var(opts.NewIpOpt(&config.Bridge.DefaultIP, "0.0.0.0"), []string{"#ip", "-ip"}, usageFn("Default IP when binding container ports"))
+	cmd.BoolVar(&config.Bridge.EnableUserlandProxy, []string{"-userland-proxy"}, true, usageFn("Use userland proxy for loopback traffic"))
+
+	config.attachExperimentalFlags(cmd, usageFn)
 }

+ 3 - 1
daemon/config_stub.go

@@ -2,5 +2,7 @@
 
 package daemon
 
-func (config *Config) attachExperimentalFlags() {
+import flag "github.com/docker/docker/pkg/mflag"
+
+func (config *Config) attachExperimentalFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 }

+ 2 - 2
daemon/config_windows.go

@@ -32,9 +32,9 @@ type Config struct {
 // the current process.
 // Subsequent calls to `flag.Parse` will populate config with values parsed
 // from the command-line.
-func (config *Config) InstallFlags() {
+func (config *Config) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 	// First handle install flags which are consistent cross-platform
-	config.InstallCommonFlags()
+	config.InstallCommonFlags(cmd, usageFn)
 
 	// Then platform-specific install flags.
 	flag.StringVar(&config.Bridge.VirtualSwitchName, []string{"b", "-bridge"}, "", "Attach containers to a virtual switch")

+ 22 - 5
docker/client.go

@@ -1,11 +1,28 @@
-// +build !daemon
-
 package main
 
 import (
-	"log" // see gh#8745, client needs to use go log pkg
+	"path/filepath"
+
+	"github.com/docker/docker/cli"
+	"github.com/docker/docker/cliconfig"
+	flag "github.com/docker/docker/pkg/mflag"
 )
 
-func mainDaemon() {
-	log.Fatal("This is a client-only binary - running the Docker daemon is not supported.")
+var clientFlags = &cli.ClientFlags{FlagSet: new(flag.FlagSet), Common: commonFlags}
+
+func init() {
+	client := clientFlags.FlagSet
+	client.StringVar(&clientFlags.ConfigDir, []string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")
+
+	clientFlags.PostParse = func() {
+		clientFlags.Common.PostParse()
+
+		if clientFlags.ConfigDir != "" {
+			cliconfig.SetConfigDir(clientFlags.ConfigDir)
+		}
+
+		if clientFlags.Common.TrustKey == "" {
+			clientFlags.Common.TrustKey = filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile)
+		}
+	}
 }

+ 101 - 0
docker/common.go

@@ -0,0 +1,101 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/cli"
+	"github.com/docker/docker/cliconfig"
+	"github.com/docker/docker/opts"
+	flag "github.com/docker/docker/pkg/mflag"
+	"github.com/docker/docker/pkg/tlsconfig"
+)
+
+const (
+	defaultTrustKeyFile = "key.json"
+	defaultCaFile       = "ca.pem"
+	defaultKeyFile      = "key.pem"
+	defaultCertFile     = "cert.pem"
+)
+
+var (
+	daemonFlags *flag.FlagSet
+	commonFlags = &cli.CommonFlags{FlagSet: new(flag.FlagSet)}
+
+	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
+	dockerTLSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
+)
+
+func init() {
+	if dockerCertPath == "" {
+		dockerCertPath = cliconfig.ConfigDir()
+	}
+
+	commonFlags.PostParse = postParseCommon
+
+	cmd := commonFlags.FlagSet
+
+	cmd.BoolVar(&commonFlags.Debug, []string{"D", "-debug"}, false, "Enable debug mode")
+	cmd.StringVar(&commonFlags.LogLevel, []string{"l", "-log-level"}, "info", "Set the logging level")
+	cmd.BoolVar(&commonFlags.TLS, []string{"-tls"}, false, "Use TLS; implied by --tlsverify")
+	cmd.BoolVar(&commonFlags.TLSVerify, []string{"-tlsverify"}, dockerTLSVerify, "Use TLS and verify the remote")
+
+	// TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file")
+
+	var tlsOptions tlsconfig.Options
+	commonFlags.TLSOptions = &tlsOptions
+	cmd.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA")
+	cmd.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
+	cmd.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")
+
+	cmd.Var(opts.NewListOptsRef(&commonFlags.Hosts, opts.ValidateHost), []string{"H", "-host"}, "Daemon socket(s) to connect to")
+}
+
+func postParseCommon() {
+	cmd := commonFlags.FlagSet
+
+	if commonFlags.LogLevel != "" {
+		lvl, err := logrus.ParseLevel(commonFlags.LogLevel)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", commonFlags.LogLevel)
+			os.Exit(1)
+		}
+		logrus.SetLevel(lvl)
+	} else {
+		logrus.SetLevel(logrus.InfoLevel)
+	}
+
+	if commonFlags.Debug {
+		os.Setenv("DEBUG", "1")
+		logrus.SetLevel(logrus.DebugLevel)
+	}
+
+	// Regardless of whether the user sets it to true or false, if they
+	// specify --tlsverify at all then we need to turn on tls
+	// TLSVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well
+	if cmd.IsSet("-tlsverify") || commonFlags.TLSVerify {
+		commonFlags.TLS = true
+	}
+
+	if !commonFlags.TLS {
+		commonFlags.TLSOptions = nil
+	} else {
+		tlsOptions := commonFlags.TLSOptions
+		tlsOptions.InsecureSkipVerify = !commonFlags.TLSVerify
+
+		// Reset CertFile and KeyFile to empty string if the user did not specify
+		// the respective flags and the respective default files were not found.
+		if !cmd.IsSet("-tlscert") {
+			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
+				tlsOptions.CertFile = ""
+			}
+		}
+		if !cmd.IsSet("-tlskey") {
+			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
+				tlsOptions.KeyFile = ""
+			}
+		}
+	}
+}

+ 135 - 29
docker/daemon.go

@@ -8,14 +8,18 @@ import (
 	"io"
 	"os"
 	"path/filepath"
+	"runtime"
+	"strings"
 	"time"
 
 	"github.com/Sirupsen/logrus"
 	apiserver "github.com/docker/docker/api/server"
 	"github.com/docker/docker/autogen/dockerversion"
+	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cliconfig"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/logger"
+	"github.com/docker/docker/opts"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/pidfile"
 	"github.com/docker/docker/pkg/signal"
@@ -26,17 +30,63 @@ import (
 	"github.com/docker/docker/utils"
 )
 
+const daemonUsage = "       docker daemon [ --help | ... ]\n"
+
 var (
-	daemonCfg   = &daemon.Config{}
-	registryCfg = &registry.Options{}
+	flDaemon              = flag.Bool([]string{"#d", "#-daemon"}, false, "Enable daemon mode (deprecated; use docker daemon)")
+	daemonCli cli.Handler = NewDaemonCli()
 )
 
-func init() {
-	if daemonCfg.LogConfig.Config == nil {
-		daemonCfg.LogConfig.Config = make(map[string]string)
+// TODO: remove once `-d` is retired
+func handleGlobalDaemonFlag() {
+	// This block makes sure that if the deprecated daemon flag `--daemon` is absent,
+	// then all daemon-specific flags are absent as well.
+	if !*flDaemon && daemonFlags != nil {
+		flag.CommandLine.Visit(func(fl *flag.Flag) {
+			for _, name := range fl.Names {
+				name := strings.TrimPrefix(name, "#")
+				if daemonFlags.Lookup(name) != nil {
+					// daemon flag was NOT specified, but daemon-specific flags were
+					// so let's error out
+					fmt.Fprintf(os.Stderr, "docker: the daemon flag '-%s' must follow the 'docker daemon' command.\n", name)
+					os.Exit(1)
+				}
+			}
+		})
+	}
+
+	if *flDaemon {
+		if *flHelp {
+			// We do not show the help output here, instead, we tell the user about the new daemon command,
+			// because the help output is so long they would not see the warning anyway.
+			fmt.Fprintln(os.Stderr, "Please use 'docker daemon --help' instead.")
+			os.Exit(0)
+		}
+		daemonCli.(*DaemonCli).CmdDaemon(flag.Args()...)
+		os.Exit(0)
+	}
+}
+
+func presentInHelp(usage string) string { return usage }
+func absentFromHelp(string) string      { return "" }
+
+// NewDaemonCli returns a pre-configured daemon CLI
+func NewDaemonCli() *DaemonCli {
+	daemonFlags = cli.Subcmd("daemon", nil, "Enable daemon mode", true)
+
+	// TODO(tiborvass): remove InstallFlags?
+	daemonConfig := new(daemon.Config)
+	daemonConfig.InstallFlags(daemonFlags, presentInHelp)
+	daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
+	registryOptions := new(registry.Options)
+	registryOptions.InstallFlags(daemonFlags, presentInHelp)
+	registryOptions.InstallFlags(flag.CommandLine, absentFromHelp)
+	daemonFlags.Require(flag.Exact, 0)
+
+	return &DaemonCli{
+		Config:          daemonConfig,
+		registryOptions: registryOptions,
 	}
-	daemonCfg.InstallFlags()
-	registryCfg.InstallFlags()
 }
 
 func migrateKey() (err error) {
@@ -79,14 +129,56 @@ func migrateKey() (err error) {
 	return nil
 }
 
-func mainDaemon() {
-	if utils.ExperimentalBuild() {
-		logrus.Warn("Running experimental build")
+// DaemonCli represents the daemon CLI.
+type DaemonCli struct {
+	*daemon.Config
+	registryOptions *registry.Options
+}
+
+func getGlobalFlag() (globalFlag *flag.Flag) {
+	defer func() {
+		if x := recover(); x != nil {
+			switch f := x.(type) {
+			case *flag.Flag:
+				globalFlag = f
+			default:
+				panic(x)
+			}
+		}
+	}()
+	visitor := func(f *flag.Flag) { panic(f) }
+	commonFlags.FlagSet.Visit(visitor)
+	clientFlags.FlagSet.Visit(visitor)
+	return
+}
+
+// CmdDaemon is the daemon command, called the raw arguments after `docker daemon`.
+func (cli *DaemonCli) CmdDaemon(args ...string) error {
+	if *flDaemon {
+		// allow legacy forms `docker -D -d` and `docker -d -D`
+		logrus.Warn("please use 'docker daemon' instead.")
+	} else if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() {
+		// deny `docker -D daemon`
+		illegalFlag := getGlobalFlag()
+		fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0])
+		os.Exit(1)
+	} else {
+		// allow new form `docker daemon -D`
+		flag.Merge(daemonFlags, commonFlags.FlagSet)
+	}
+
+	daemonFlags.ParseFlags(args, true)
+	commonFlags.PostParse()
+
+	if len(commonFlags.Hosts) == 0 {
+		commonFlags.Hosts = []string{opts.DefaultHost}
+	}
+	if commonFlags.TrustKey == "" {
+		commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile)
 	}
 
-	if flag.NArg() != 0 {
-		flag.Usage()
-		return
+	if utils.ExperimentalBuild() {
+		logrus.Warn("Running experimental build")
 	}
 
 	logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed})
@@ -95,15 +187,15 @@ func mainDaemon() {
 		logrus.Fatalf("Failed to set umask: %v", err)
 	}
 
-	if len(daemonCfg.LogConfig.Config) > 0 {
-		if err := logger.ValidateLogOpts(daemonCfg.LogConfig.Type, daemonCfg.LogConfig.Config); err != nil {
+	if len(cli.LogConfig.Config) > 0 {
+		if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil {
 			logrus.Fatalf("Failed to set log opts: %v", err)
 		}
 	}
 
 	var pfile *pidfile.PidFile
-	if daemonCfg.Pidfile != "" {
-		pf, err := pidfile.New(daemonCfg.Pidfile)
+	if cli.Pidfile != "" {
+		pf, err := pidfile.New(cli.Pidfile)
 		if err != nil {
 			logrus.Fatalf("Error starting daemon: %v", err)
 		}
@@ -115,21 +207,26 @@ func mainDaemon() {
 		}()
 	}
 
+	if cli.LogConfig.Config == nil {
+		cli.LogConfig.Config = make(map[string]string)
+	}
+
 	serverConfig := &apiserver.ServerConfig{
 		Logging:     true,
-		EnableCors:  daemonCfg.EnableCors,
-		CorsHeaders: daemonCfg.CorsHeaders,
+		EnableCors:  cli.EnableCors,
+		CorsHeaders: cli.CorsHeaders,
 		Version:     dockerversion.VERSION,
 	}
-	serverConfig = setPlatformServerConfig(serverConfig, daemonCfg)
+	serverConfig = setPlatformServerConfig(serverConfig, cli.Config)
 
-	if *flTLS {
-		if *flTLSVerify {
-			tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert
+	if commonFlags.TLSOptions != nil {
+		if !commonFlags.TLSOptions.InsecureSkipVerify {
+			// server requires and verifies client's certificate
+			commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert
 		}
-		tlsConfig, err := tlsconfig.Server(tlsOptions)
+		tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions)
 		if err != nil {
-			logrus.Fatal(err)
+			logrus.Fatalf("foobar: %v", err)
 		}
 		serverConfig.TLSConfig = tlsConfig
 	}
@@ -141,7 +238,7 @@ func mainDaemon() {
 	// daemon doesn't exit
 	serveAPIWait := make(chan error)
 	go func() {
-		if err := api.ServeApi(flHosts); err != nil {
+		if err := api.ServeApi(commonFlags.Hosts); err != nil {
 			logrus.Errorf("ServeAPI error: %v", err)
 			serveAPIWait <- err
 			return
@@ -152,10 +249,10 @@ func mainDaemon() {
 	if err := migrateKey(); err != nil {
 		logrus.Fatal(err)
 	}
-	daemonCfg.TrustKeyPath = *flTrustKey
+	cli.TrustKeyPath = commonFlags.TrustKey
 
-	registryService := registry.NewService(registryCfg)
-	d, err := daemon.NewDaemon(daemonCfg, registryService)
+	registryService := registry.NewService(cli.registryOptions)
+	d, err := daemon.NewDaemon(cli.Config, registryService)
 	if err != nil {
 		if pfile != nil {
 			if err := pfile.Remove(); err != nil {
@@ -201,6 +298,7 @@ func mainDaemon() {
 		}
 		logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
 	}
+	return nil
 }
 
 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
@@ -219,3 +317,11 @@ func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) {
 		logrus.Error("Force shutdown daemon")
 	}
 }
+
+func getDaemonConfDir() string {
+	// TODO: update for Windows daemon
+	if runtime.GOOS == "windows" {
+		return cliconfig.ConfigDir()
+	}
+	return "/etc/docker"
+}

+ 12 - 0
docker/daemon_none.go

@@ -0,0 +1,12 @@
+// +build !daemon
+
+package main
+
+import "github.com/docker/docker/cli"
+
+const daemonUsage = ""
+
+var daemonCli cli.Handler
+
+// TODO: remove once `-d` is retired
+func handleGlobalDaemonFlag() {}

+ 33 - 102
docker/docker.go

@@ -1,31 +1,20 @@
 package main
 
 import (
-	"crypto/tls"
 	"fmt"
 	"os"
-	"runtime"
-	"strings"
+	"sort"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/client"
 	"github.com/docker/docker/autogen/dockerversion"
-	"github.com/docker/docker/cliconfig"
-	"github.com/docker/docker/opts"
+	"github.com/docker/docker/cli"
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/docker/pkg/term"
-	"github.com/docker/docker/pkg/tlsconfig"
 	"github.com/docker/docker/utils"
 )
 
-const (
-	defaultTrustKeyFile = "key.json"
-	defaultCaFile       = "ca.pem"
-	defaultKeyFile      = "key.pem"
-	defaultCertFile     = "cert.pem"
-)
-
 func main() {
 	if reexec.Init() {
 		return
@@ -34,116 +23,58 @@ func main() {
 	// Set terminal emulation based on platform as required.
 	stdin, stdout, stderr := term.StdStreams()
 
-	initLogging(stderr)
+	logrus.SetOutput(stderr)
 
-	flag.Parse()
-	// FIXME: validate daemon flags here
+	flag.Merge(flag.CommandLine, clientFlags.FlagSet, commonFlags.FlagSet)
 
-	if *flVersion {
-		showVersion()
-		return
-	}
+	flag.Usage = func() {
+		fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n"+daemonUsage+"       docker [ -h | --help | -v | --version ]\n\n")
+		fmt.Fprint(os.Stdout, "A self-sufficient runtime for containers.\n\nOptions:\n")
 
-	if *flConfigDir != "" {
-		cliconfig.SetConfigDir(*flConfigDir)
-	}
+		flag.CommandLine.SetOutput(os.Stdout)
+		flag.PrintDefaults()
 
-	if *flLogLevel != "" {
-		lvl, err := logrus.ParseLevel(*flLogLevel)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "Unable to parse logging level: %s\n", *flLogLevel)
-			os.Exit(1)
-		}
-		setLogLevel(lvl)
-	} else {
-		setLogLevel(logrus.InfoLevel)
-	}
+		help := "\nCommands:\n"
 
-	if *flDebug {
-		os.Setenv("DEBUG", "1")
-		setLogLevel(logrus.DebugLevel)
-	}
+		// TODO(tiborvass): no need to sort if we ensure dockerCommands is sorted
+		sort.Sort(byName(dockerCommands))
 
-	if len(flHosts) == 0 {
-		defaultHost := os.Getenv("DOCKER_HOST")
-		if defaultHost == "" || *flDaemon {
-			if runtime.GOOS != "windows" {
-				// If we do not have a host, default to unix socket
-				defaultHost = fmt.Sprintf("unix://%s", opts.DefaultUnixSocket)
-			} else {
-				// If we do not have a host, default to TCP socket on Windows
-				defaultHost = fmt.Sprintf("tcp://%s:%d", opts.DefaultHTTPHost, opts.DefaultHTTPPort)
-			}
-		}
-		defaultHost, err := opts.ValidateHost(defaultHost)
-		if err != nil {
-			if *flDaemon {
-				logrus.Fatal(err)
-			} else {
-				fmt.Fprint(os.Stderr, err)
-			}
-			os.Exit(1)
+		for _, cmd := range dockerCommands {
+			help += fmt.Sprintf("    %-10.10s%s\n", cmd.name, cmd.description)
 		}
-		flHosts = append(flHosts, defaultHost)
-	}
-
-	setDefaultConfFlag(flTrustKey, defaultTrustKeyFile)
 
-	// Regardless of whether the user sets it to true or false, if they
-	// specify --tlsverify at all then we need to turn on tls
-	// *flTlsVerify can be true even if not set due to DOCKER_TLS_VERIFY env var, so we need to check that here as well
-	if flag.IsSet("-tlsverify") || *flTLSVerify {
-		*flTLS = true
+		help += "\nRun 'docker COMMAND --help' for more information on a command."
+		fmt.Fprintf(os.Stdout, "%s\n", help)
 	}
 
-	if *flDaemon {
-		if *flHelp {
-			flag.Usage()
-			return
-		}
-		mainDaemon()
+	flag.Parse()
+
+	if *flVersion {
+		showVersion()
 		return
 	}
 
-	// From here on, we assume we're a client, not a server.
+	clientCli := client.NewDockerCli(stdin, stdout, stderr, clientFlags)
+	// TODO: remove once `-d` is retired
+	handleGlobalDaemonFlag()
 
-	if len(flHosts) > 1 {
-		fmt.Fprintf(os.Stderr, "Please specify only one -H")
-		os.Exit(0)
-	}
-	protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
-
-	var tlsConfig *tls.Config
-	if *flTLS {
-		tlsOptions.InsecureSkipVerify = !*flTLSVerify
-		if !flag.IsSet("-tlscert") {
-			if _, err := os.Stat(tlsOptions.CertFile); os.IsNotExist(err) {
-				tlsOptions.CertFile = ""
-			}
-		}
-		if !flag.IsSet("-tlskey") {
-			if _, err := os.Stat(tlsOptions.KeyFile); os.IsNotExist(err) {
-				tlsOptions.KeyFile = ""
-			}
-		}
-		var err error
-		tlsConfig, err = tlsconfig.Client(tlsOptions)
-		if err != nil {
-			fmt.Fprintln(stderr, err)
-			os.Exit(1)
-		}
+	if *flHelp {
+		// if global flag --help is present, regardless of what other options and commands there are,
+		// just print the usage.
+		flag.Usage()
+		return
 	}
-	cli := client.NewDockerCli(stdin, stdout, stderr, *flTrustKey, protoAddrParts[0], protoAddrParts[1], tlsConfig)
 
-	if err := cli.Cmd(flag.Args()...); err != nil {
-		if sterr, ok := err.(client.StatusError); ok {
+	c := cli.New(clientCli, daemonCli)
+	if err := c.Run(flag.Args()...); err != nil {
+		if sterr, ok := err.(cli.StatusError); ok {
 			if sterr.Status != "" {
-				fmt.Fprintln(cli.Err(), sterr.Status)
+				fmt.Fprintln(os.Stderr, sterr.Status)
 				os.Exit(1)
 			}
 			os.Exit(sterr.StatusCode)
 		}
-		fmt.Fprintln(cli.Err(), err)
+		fmt.Fprintln(os.Stderr, err)
 		os.Exit(1)
 	}
 }

+ 46 - 124
docker/flags.go

@@ -1,16 +1,10 @@
 package main
 
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"runtime"
-	"sort"
+import flag "github.com/docker/docker/pkg/mflag"
 
-	"github.com/docker/docker/cliconfig"
-	"github.com/docker/docker/opts"
-	flag "github.com/docker/docker/pkg/mflag"
-	"github.com/docker/docker/pkg/tlsconfig"
+var (
+	flHelp    = flag.Bool([]string{"h", "-help"}, false, "Print usage")
+	flVersion = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
 )
 
 type command struct {
@@ -24,118 +18,46 @@ func (a byName) Len() int           { return len(a) }
 func (a byName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 func (a byName) Less(i, j int) bool { return a[i].name < a[j].name }
 
-var (
-	dockerCertPath  = os.Getenv("DOCKER_CERT_PATH")
-	dockerTlSVerify = os.Getenv("DOCKER_TLS_VERIFY") != ""
-
-	dockerCommands = []command{
-		{"attach", "Attach to a running container"},
-		{"build", "Build an image from a Dockerfile"},
-		{"commit", "Create a new image from a container's changes"},
-		{"cp", "Copy files/folders from a container to a HOSTDIR or to STDOUT"},
-		{"create", "Create a new container"},
-		{"diff", "Inspect changes on a container's filesystem"},
-		{"events", "Get real time events from the server"},
-		{"exec", "Run a command in a running container"},
-		{"export", "Export a container's filesystem as a tar archive"},
-		{"history", "Show the history of an image"},
-		{"images", "List images"},
-		{"import", "Import the contents from a tarball to create a filesystem image"},
-		{"info", "Display system-wide information"},
-		{"inspect", "Return low-level information on a container or image"},
-		{"kill", "Kill a running container"},
-		{"load", "Load an image from a tar archive or STDIN"},
-		{"login", "Register or log in to a Docker registry"},
-		{"logout", "Log out from a Docker registry"},
-		{"logs", "Fetch the logs of a container"},
-		{"port", "List port mappings or a specific mapping for the CONTAINER"},
-		{"pause", "Pause all processes within a container"},
-		{"ps", "List containers"},
-		{"pull", "Pull an image or a repository from a registry"},
-		{"push", "Push an image or a repository to a registry"},
-		{"rename", "Rename a container"},
-		{"restart", "Restart a running container"},
-		{"rm", "Remove one or more containers"},
-		{"rmi", "Remove one or more images"},
-		{"run", "Run a command in a new container"},
-		{"save", "Save an image(s) to a tar archive"},
-		{"search", "Search the Docker Hub for images"},
-		{"start", "Start one or more stopped containers"},
-		{"stats", "Display a live stream of container(s) resource usage statistics"},
-		{"stop", "Stop a running container"},
-		{"tag", "Tag an image into a repository"},
-		{"top", "Display the running processes of a container"},
-		{"unpause", "Unpause all processes within a container"},
-		{"version", "Show the Docker version information"},
-		{"wait", "Block until a container stops, then print its exit code"},
-	}
-)
-
-func init() {
-	if dockerCertPath == "" {
-		dockerCertPath = cliconfig.ConfigDir()
-	}
-}
-
-func getDaemonConfDir() string {
-	// TODO: update for Windows daemon
-	if runtime.GOOS == "windows" {
-		return cliconfig.ConfigDir()
-	}
-	return "/etc/docker"
-}
-
-var (
-	flConfigDir = flag.String([]string{"-config"}, cliconfig.ConfigDir(), "Location of client config files")
-	flVersion   = flag.Bool([]string{"v", "-version"}, false, "Print version information and quit")
-	flDaemon    = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
-	flDebug     = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
-	flLogLevel  = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level")
-	flTLS       = flag.Bool([]string{"-tls"}, false, "Use TLS; implied by --tlsverify")
-	flHelp      = flag.Bool([]string{"h", "-help"}, false, "Print usage")
-	flTLSVerify = flag.Bool([]string{"-tlsverify"}, dockerTlSVerify, "Use TLS and verify the remote")
-
-	// these are initialized in init() below since their default values depend on dockerCertPath which isn't fully initialized until init() runs
-	tlsOptions tlsconfig.Options
-	flTrustKey *string
-	flHosts    []string
-)
-
-func setDefaultConfFlag(flag *string, def string) {
-	if *flag == "" {
-		if *flDaemon {
-			*flag = filepath.Join(getDaemonConfDir(), def)
-		} else {
-			*flag = filepath.Join(cliconfig.ConfigDir(), def)
-		}
-	}
-}
-
-func init() {
-	var placeholderTrustKey string
-	// TODO use flag flag.String([]string{"i", "-identity"}, "", "Path to libtrust key file")
-	flTrustKey = &placeholderTrustKey
-
-	flag.StringVar(&tlsOptions.CAFile, []string{"-tlscacert"}, filepath.Join(dockerCertPath, defaultCaFile), "Trust certs signed only by this CA")
-	flag.StringVar(&tlsOptions.CertFile, []string{"-tlscert"}, filepath.Join(dockerCertPath, defaultCertFile), "Path to TLS certificate file")
-	flag.StringVar(&tlsOptions.KeyFile, []string{"-tlskey"}, filepath.Join(dockerCertPath, defaultKeyFile), "Path to TLS key file")
-	opts.HostListVar(&flHosts, []string{"H", "-host"}, "Daemon socket(s) to connect to")
-
-	flag.Usage = func() {
-		fmt.Fprint(os.Stdout, "Usage: docker [OPTIONS] COMMAND [arg...]\n\nA self-sufficient runtime for containers.\n\nOptions:\n")
-
-		flag.CommandLine.SetOutput(os.Stdout)
-		flag.PrintDefaults()
-
-		help := "\nCommands:\n"
-
-		sort.Sort(byName(dockerCommands))
-
-		for _, cmd := range dockerCommands {
-			help += fmt.Sprintf("    %-10.10s%s\n", cmd.name, cmd.description)
-		}
-
-		help += "\nRun 'docker COMMAND --help' for more information on a command."
-		fmt.Fprintf(os.Stdout, "%s\n", help)
-	}
+// TODO(tiborvass): do not show 'daemon' on client-only binaries
+// and deduplicate description in dockerCommands and cli subcommands
+var dockerCommands = []command{
+	{"attach", "Attach to a running container"},
+	{"build", "Build an image from a Dockerfile"},
+	{"commit", "Create a new image from a container's changes"},
+	{"cp", "Copy files/folders from a container to a HOSTDIR or to STDOUT"},
+	{"create", "Create a new container"},
+	{"diff", "Inspect changes on a container's filesystem"},
+	{"events", "Get real time events from the server"},
+	{"exec", "Run a command in a running container"},
+	{"export", "Export a container's filesystem as a tar archive"},
+	{"history", "Show the history of an image"},
+	{"images", "List images"},
+	{"import", "Import the contents from a tarball to create a filesystem image"},
+	{"info", "Display system-wide information"},
+	{"inspect", "Return low-level information on a container or image"},
+	{"kill", "Kill a running container"},
+	{"load", "Load an image from a tar archive or STDIN"},
+	{"login", "Register or log in to a Docker registry"},
+	{"logout", "Log out from a Docker registry"},
+	{"logs", "Fetch the logs of a container"},
+	{"port", "List port mappings or a specific mapping for the CONTAINER"},
+	{"pause", "Pause all processes within a container"},
+	{"ps", "List containers"},
+	{"pull", "Pull an image or a repository from a registry"},
+	{"push", "Push an image or a repository to a registry"},
+	{"rename", "Rename a container"},
+	{"restart", "Restart a running container"},
+	{"rm", "Remove one or more containers"},
+	{"rmi", "Remove one or more images"},
+	{"run", "Run a command in a new container"},
+	{"save", "Save an image(s) to a tar archive"},
+	{"search", "Search the Docker Hub for images"},
+	{"start", "Start one or more stopped containers"},
+	{"stats", "Display a live stream of container(s) resource usage statistics"},
+	{"stop", "Stop a running container"},
+	{"tag", "Tag an image into a repository"},
+	{"top", "Display the running processes of a container"},
+	{"unpause", "Unpause all processes within a container"},
+	{"version", "Show the Docker version information"},
+	{"wait", "Block until a container stops, then print its exit code"},
 }

+ 0 - 14
docker/log.go

@@ -1,14 +0,0 @@
-package main
-
-import (
-	"github.com/Sirupsen/logrus"
-	"io"
-)
-
-func setLogLevel(lvl logrus.Level) {
-	logrus.SetLevel(lvl)
-}
-
-func initLogging(stderr io.Writer) {
-	logrus.SetOutput(stderr)
-}

+ 2 - 2
docs/articles/basics.md

@@ -103,7 +103,7 @@ when no `-H` was passed in.
 
 Run Docker in daemon mode:
 
-    $ sudo <path to>/docker -H 0.0.0.0:5555 -d &
+    $ sudo <path to>/docker daemon -H 0.0.0.0:5555 &
 
 Download an `ubuntu` image:
 
@@ -113,7 +113,7 @@ You can use multiple `-H`, for example, if you want to listen on both
 TCP and a Unix socket
 
     # Run docker in daemon mode
-    $ sudo <path to>/docker -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock -d &
+    $ sudo <path to>/docker daemon -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock &
     # Download an ubuntu image, use default Unix socket
     $ docker pull ubuntu
     # OR use the TCP port

+ 4 - 5
docs/articles/configuring.md

@@ -24,7 +24,7 @@ or `systemd` to manage the `docker` daemon's start and stop.
 The `docker` daemon can be run directly using the `-d` option. By default it listens on
 the Unix socket `unix:///var/run/docker.sock`
 
-    $ docker -d
+    $ docker daemon
 
     INFO[0000] +job init_networkdriver()
     INFO[0000] +job serveapi(unix:///var/run/docker.sock)
@@ -34,10 +34,9 @@ the Unix socket `unix:///var/run/docker.sock`
 
 ### Configuring the docker daemon directly
 
-If you're running the `docker` daemon directly by running `docker -d` instead
+If you're running the `docker` daemon directly by running `docker daemon` instead
 of using a process manager, you can append the configuration options to the `docker` run
-command directly. Just like the `-d` option, other options can be passed to the `docker`
-daemon to configure it.
+command directly. Other options can be passed to the `docker` daemon to configure it.
 
 Some of the daemon's options are:
 
@@ -50,7 +49,7 @@ Some of the daemon's options are:
 
 Here is a an example of running the `docker` daemon with configuration options:
 
-    $ docker -d -D --tls=true --tlscert=/var/docker/server.pem --tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376
+    $ docker daemon -D --tls=true --tlscert=/var/docker/server.pem --tlskey=/var/docker/serverkey.pem -H tcp://192.168.59.3:2376
 
 These options :
 

+ 1 - 1
docs/articles/https.md

@@ -136,7 +136,7 @@ prevent accidental damage:
 Now you can make the Docker daemon only accept connections from clients
 providing a certificate trusted by our CA:
 
-    $ docker -d --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
+    $ docker daemon --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
       -H=0.0.0.0:2376
 
 To be able to connect to Docker and validate its certificate, you now

+ 2 - 2
docs/articles/networking.md

@@ -503,7 +503,7 @@ To assign globally routable IPv6 addresses to your containers you have to
 specify an IPv6 subnet to pick the addresses from. Set the IPv6 subnet via the
 `--fixed-cidr-v6` parameter when starting Docker daemon:
 
-    docker -d --ipv6 --fixed-cidr-v6="2001:db8:1::/64"
+    docker daemon --ipv6 --fixed-cidr-v6="2001:db8:1::/64"
 
 The subnet for Docker containers should at least have a size of `/80`. This way
 an IPv6 address can end with the container's MAC address and you prevent NDP
@@ -589,7 +589,7 @@ Let's split up the configurable address range into two subnets
 `2001:db8::c000/125` and `2001:db8::c008/125`. The first one can be used by the
 host itself, the latter by Docker:
 
-    docker -d --ipv6 --fixed-cidr-v6 2001:db8::c008/125
+    docker daemon --ipv6 --fixed-cidr-v6 2001:db8::c008/125
 
 You notice the Docker subnet is within the subnet managed by your router that
 is connected to `eth0`. This means all devices (containers) with the addresses

+ 2 - 2
docs/articles/registry_mirror.md

@@ -36,11 +36,11 @@ There are two steps to set up and use a local registry mirror.
 You will need to pass the `--registry-mirror` option to your Docker daemon on
 startup:
 
-    docker --registry-mirror=http://<my-docker-mirror-host> -d
+    docker daemon --registry-mirror=http://<my-docker-mirror-host>
 
 For example, if your mirror is serving on `http://10.0.0.2:5000`, you would run:
 
-    docker --registry-mirror=http://10.0.0.2:5000 -d
+    docker daemon --registry-mirror=http://10.0.0.2:5000
 
 **NOTE:**
 Depending on your local host setup, you may be able to add the

+ 1 - 1
docs/articles/systemd.md

@@ -70,7 +70,7 @@ In this example, we'll assume that your `docker.service` file looks something li
     [Service]
     Type=notify
     EnvironmentFile=-/etc/sysconfig/docker
-    ExecStart=/usr/bin/docker -d -H fd:// $OPTIONS
+    ExecStart=/usr/bin/docker daemon -H fd:// $OPTIONS
     LimitNOFILE=1048576
     LimitNPROC=1048576
 

+ 1 - 1
docs/installation/binaries.md

@@ -174,7 +174,7 @@ For example:
 ## Run the Docker daemon
 
     # start the docker in daemon mode from the directory you unpacked
-    $ sudo ./docker -d &
+    $ sudo ./docker daemon &
 
 ## Giving non-root access
 

+ 1 - 1
docs/installation/ubuntulinux.md

@@ -185,7 +185,7 @@ To create the `docker` group and add your user:
 
 	If this fails with a message similar to this:
 
-		Cannot connect to the Docker daemon. Is 'docker -d' running on this host?
+		Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?
 
 	Check that the `DOCKER_HOST` environment variable is not set for your shell.
 	If it is, unset it.

+ 2 - 2
docs/project/set-up-dev-env.md

@@ -285,7 +285,7 @@ with the `make.sh` script.
 
 8. Start a `docker` daemon running inside your container.
 
-        root@5f8630b873fe:/go/src/github.com/docker/docker#  docker -dD
+        root@5f8630b873fe:/go/src/github.com/docker/docker#  docker daemon -D
 
     The `-dD` flag starts the daemon in debug mode. You'll find this useful
     when debugging your code.
@@ -411,7 +411,7 @@ onto the `/go` directory inside the container.
 
     * copy the binary inside the development container using
       `cp bundles/1.5.0-dev/binary/docker /usr/bin`
-    * start `docker -dD` to launch the Docker daemon inside the container
+    * start `docker daemon -D` to launch the Docker daemon inside the container
     * run `docker ps` on local host to get the development container's name
     * connect to your running container `docker exec -it container_name bash`
     * use the `docker run hello-world` command to create and run a container 

+ 1 - 1
docs/project/test-and-docs.md

@@ -120,7 +120,7 @@ Run the entire test suite on your current repository:
         PASS
         coverage: 70.8% of statements
         ---> Making bundle: test-docker-py (in bundles/1.5.0-dev/test-docker-py)
-        +++ exec docker --daemon --debug --host unix:///go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.sock --storage-driver vfs --exec-driver native --pidfile /go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.pid
+        +++ exec docker daemon --debug --host unix:///go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.sock --storage-driver vfs --exec-driver native --pidfile /go/src/github.com/docker/docker/bundles/1.5.0-dev/test-docker-py/docker.pid
         .................................................................
         ----------------------------------------------------------------------
         Ran 65 tests in 89.266s

+ 1 - 1
docs/reference/api/docker_remote_api_v1.20.md

@@ -2269,4 +2269,4 @@ To set cross origin requests to the remote api please give values to
 `--api-cors-header` when running Docker in daemon mode. Set * (asterisk) allows all,
 default or blank means CORS disabled
 
-    $ docker -d -H="192.168.1.9:2375" --api-cors-header="http://foo.bar"
+    $ docker daemon -H="192.168.1.9:2375" --api-cors-header="http://foo.bar"

+ 3 - 0
docs/reference/commandline/cli.md

@@ -19,6 +19,9 @@ or execute `docker help`:
 
     $ docker
       Usage: docker [OPTIONS] COMMAND [arg...]
+             docker daemon [ --help | ... ]
+             docker [ -h | --help | -v | --version ]
+
         -H, --host=[]: The socket(s) to bind to in daemon mode, specified using one or more tcp://host:port, unix:///path/to/socket, fd://* or fd://socketfd.
 
       A self-sufficient runtime for Linux containers.

+ 28 - 31
docs/reference/commandline/daemon.md

@@ -10,7 +10,7 @@ parent = "smn_cli"
 
 # daemon
 
-    Usage: docker [OPTIONS] COMMAND [arg...]
+    Usage: docker daemon [OPTIONS]
 
     A self-sufficient runtime for linux containers.
 
@@ -18,9 +18,7 @@ parent = "smn_cli"
       --api-cors-header=""                   Set CORS headers in the remote API
       -b, --bridge=""                        Attach containers to a network bridge
       --bip=""                               Specify network bridge IP
-      --config=~/.docker                     Location of client config files
       -D, --debug=false                      Enable debug mode
-      -d, --daemon=false                     Enable daemon mode
       --default-gateway=""                   Container default gateway IPv4 address
       --default-gateway-v6=""                Container default gateway IPv6 address
       --dns=[]                               DNS server to use
@@ -58,15 +56,14 @@ parent = "smn_cli"
       --tlskey="~/.docker/key.pem"           Path to TLS key file
       --tlsverify=false                      Use TLS and verify the remote
       --userland-proxy=true                  Use userland proxy for loopback traffic
-      -v, --version=false                    Print version information and quit
 
 Options with [] may be specified multiple times.
 
 The Docker daemon is the persistent process that manages containers. Docker
 uses the same binary for both the daemon and client. To run the daemon you
-provide the `-d` flag.
+type `docker daemon`.
 
-To run the daemon with debug output, use `docker -d -D`.
+To run the daemon with debug output, use `docker daemon -D`.
 
 ## Daemon socket option
 
@@ -94,8 +91,8 @@ communication with the daemon.
 
 On Systemd based systems, you can communicate with the daemon via
 [Systemd socket activation](http://0pointer.de/blog/projects/socket-activation.html),
-use `docker -d -H fd://`. Using `fd://` will work perfectly for most setups but
-you can also specify individual sockets: `docker -d -H fd://3`. If the
+use `docker daemon -H fd://`. Using `fd://` will work perfectly for most setups but
+you can also specify individual sockets: `docker daemon -H fd://3`. If the
 specified socket activated files aren't found, then Docker will exit. You can
 find examples of using Systemd socket activation with Docker and Systemd in the
 [Docker source tree](https://github.com/docker/docker/tree/master/contrib/init/systemd/).
@@ -104,7 +101,7 @@ You can configure the Docker daemon to listen to multiple sockets at the same
 time using multiple `-H` options:
 
     # listen using the default unix socket, and on 2 specific IP addresses on this host.
-    docker -d -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2
+    docker daemon -H unix:///var/run/docker.sock -H tcp://192.168.59.106 -H tcp://10.10.10.2
 
 The Docker client will honor the `DOCKER_HOST` environment variable to set the
 `-H` flag for the client.
@@ -152,16 +149,16 @@ article explains how to tune your existing setup without the use of options.
 
 The `btrfs` driver is very fast for `docker build` - but like `devicemapper`
 does not share executable memory between devices. Use
-`docker -d -s btrfs -g /mnt/btrfs_partition`.
+`docker daemon -s btrfs -g /mnt/btrfs_partition`.
 
 The `zfs` driver is probably not fast as `btrfs` but has a longer track record
 on stability. Thanks to `Single Copy ARC` shared blocks between clones will be
-cached only once. Use `docker -d -s zfs`. To select a different zfs filesystem
+cached only once. Use `docker daemon -s zfs`. To select a different zfs filesystem
 set `zfs.fsname` option as described in [Storage driver options](#storage-driver-options).
 
 The `overlay` is a very fast union filesystem. It is now merged in the main
 Linux kernel as of [3.18.0](https://lkml.org/lkml/2014/10/26/137). Call
-`docker -d -s overlay` to use it.
+`docker daemon -s overlay` to use it.
 
 > **Note:**
 > As promising as `overlay` is, the feature is still quite young and should not
@@ -196,7 +193,7 @@ options for `zfs` start with `zfs`.
 
      Example use:
 
-        docker -d --storage-opt dm.thinpooldev=/dev/mapper/thin-pool
+        docker daemon --storage-opt dm.thinpooldev=/dev/mapper/thin-pool
 
  *  `dm.basesize`
 
@@ -216,7 +213,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.basesize=20G
+        $ docker daemon --storage-opt dm.basesize=20G
 
  *  `dm.loopdatasize`
 
@@ -229,7 +226,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.loopdatasize=200G
+        $ docker daemon --storage-opt dm.loopdatasize=200G
 
  *  `dm.loopmetadatasize`
 
@@ -242,7 +239,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.loopmetadatasize=4G
+        $ docker daemon --storage-opt dm.loopmetadatasize=4G
 
  *  `dm.fs`
 
@@ -251,7 +248,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.fs=xfs
+        $ docker daemon --storage-opt dm.fs=xfs
 
  *  `dm.mkfsarg`
 
@@ -259,7 +256,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt "dm.mkfsarg=-O ^has_journal"
+        $ docker daemon --storage-opt "dm.mkfsarg=-O ^has_journal"
 
  *  `dm.mountopt`
 
@@ -267,7 +264,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.mountopt=nodiscard
+        $ docker daemon --storage-opt dm.mountopt=nodiscard
 
  *  `dm.datadev`
 
@@ -281,7 +278,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
+        $ docker daemon --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
 
  *  `dm.metadatadev`
 
@@ -299,7 +296,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
+        $ docker daemon --storage-opt dm.datadev=/dev/sdb1 --storage-opt dm.metadatadev=/dev/sdc1
 
  *  `dm.blocksize`
 
@@ -308,7 +305,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.blocksize=512K
+        $ docker daemon --storage-opt dm.blocksize=512K
 
  *  `dm.blkdiscard`
 
@@ -322,7 +319,7 @@ options for `zfs` start with `zfs`.
 
     Example use:
 
-        $ docker -d --storage-opt dm.blkdiscard=false
+        $ docker daemon --storage-opt dm.blkdiscard=false
 
  *  `dm.override_udev_sync_check`
 
@@ -348,7 +345,7 @@ options for `zfs` start with `zfs`.
     To allow the `docker` daemon to start, regardless of `udev` sync not being
     supported, set `dm.override_udev_sync_check` to true:
 
-        $ docker -d --storage-opt dm.override_udev_sync_check=true
+        $ docker daemon --storage-opt dm.override_udev_sync_check=true
 
     When this value is `true`, the  `devicemapper` continues and simply warns
     you the errors are happening.
@@ -373,7 +370,7 @@ Currently supported options of `zfs`:
 
     Example use:
 
-        $ docker -d -s zfs --storage-opt zfs.fsname=zroot/docker
+        $ docker daemon -s zfs --storage-opt zfs.fsname=zroot/docker
 
 ## Docker execdriver option
 
@@ -397,17 +394,17 @@ it is not available, the system uses `cgroupfs`. By default, if no option is
 specified, the execdriver first tries `systemd` and falls back to `cgroupfs`.
 This example sets the execdriver to `cgroupfs`:
 
-    $ sudo docker -d --exec-opt native.cgroupdriver=cgroupfs
+    $ sudo docker daemon --exec-opt native.cgroupdriver=cgroupfs
 
 Setting this option applies to all containers the daemon launches.
 
 ## Daemon DNS options
 
 To set the DNS server for all Docker containers, use
-`docker -d --dns 8.8.8.8`.
+`docker daemon --dns 8.8.8.8`.
 
 To set the DNS search domain for all Docker containers, use
-`docker -d --dns-search example.com`.
+`docker daemon --dns-search example.com`.
 
 ## Insecure registries
 
@@ -456,7 +453,7 @@ need to be added to your Docker host's configuration:
 1. Install the `ca-certificates` package for your distribution
 2. Ask your network admin for the proxy's CA certificate and append them to
    `/etc/pki/tls/certs/ca-bundle.crt`
-3. Then start your Docker daemon with `HTTPS_PROXY=http://username:password@proxy:port/ docker -d`.
+3. Then start your Docker daemon with `HTTPS_PROXY=http://username:password@proxy:port/ docker daemon`.
    The `username:` and `password@` are optional - and are only needed if your
    proxy is set up to require authentication.
 
@@ -486,9 +483,9 @@ Docker supports softlinks for the Docker data directory (`/var/lib/docker`) and
 for `/var/lib/docker/tmp`. The `DOCKER_TMPDIR` and the data directory can be
 set like this:
 
-    DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
+    DOCKER_TMPDIR=/mnt/disk2/tmp /usr/local/bin/docker daemon -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
     # or
     export DOCKER_TMPDIR=/mnt/disk2/tmp
-    /usr/local/bin/docker -d -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
+    /usr/local/bin/docker daemon -D -g /var/lib/docker -H unix:// > /var/lib/boot2docker/docker.log 2>&1
 
 

+ 1 - 1
docs/userguide/labels-custom-metadata.md

@@ -164,7 +164,7 @@ List all images with `vendor` `ACME`:
 ## Daemon labels
 
 
-    docker -d \
+    docker daemon \
       --dns 8.8.8.8 \
       --dns 8.8.4.4 \
       -H unix:///var/run/docker.sock \

+ 1 - 1
integration-cli/docker_cli_build_test.go

@@ -79,7 +79,7 @@ func (s *DockerSuite) TestBuildShCmdJSONEntrypoint(c *check.C) {
 	out, _ := dockerCmd(c, "run", "--rm", name)
 
 	if strings.TrimSpace(out) != "/bin/sh -c echo test" {
-		c.Fatal("CMD did not contain /bin/sh -c")
+		c.Fatalf("CMD did not contain /bin/sh -c : %s", out)
 	}
 
 }

+ 103 - 15
integration-cli/docker_cli_daemon_test.go

@@ -324,6 +324,100 @@ func (s *DockerDaemonSuite) TestDaemonLogLevelWrong(c *check.C) {
 	c.Assert(s.d.Start("--log-level=bogus"), check.NotNil, check.Commentf("Daemon shouldn't start with wrong log level"))
 }
 
+func (s *DockerSuite) TestDaemonStartWithBackwardCompatibility(c *check.C) {
+
+	var validCommandArgs = [][]string{
+		{"--selinux-enabled", "-l", "info"},
+		{"--insecure-registry", "daemon"},
+	}
+
+	var invalidCommandArgs = [][]string{
+		{"--selinux-enabled", "--storage-opt"},
+		{"-D", "-b"},
+		{"--config", "/tmp"},
+	}
+
+	for _, args := range validCommandArgs {
+		d := NewDaemon(c)
+		d.Command = "--daemon"
+		if err := d.Start(args...); err != nil {
+			c.Fatalf("Daemon should have started successfully with --daemon %v: %v", args, err)
+		}
+		d.Stop()
+	}
+
+	for _, args := range invalidCommandArgs {
+		d := NewDaemon(c)
+		if err := d.Start(args...); err == nil {
+			d.Stop()
+			c.Fatalf("Daemon should have failed to start with %v", args)
+		}
+	}
+}
+
+func (s *DockerSuite) TestDaemonStartWithDaemonCommand(c *check.C) {
+
+	type kind int
+
+	const (
+		common kind = iota
+		daemon
+	)
+
+	var flags = []map[kind][]string{
+		{common: {"-l", "info"}, daemon: {"--selinux-enabled"}},
+		{common: {"-D"}, daemon: {"--selinux-enabled", "-r"}},
+		{common: {"-D"}, daemon: {"--restart"}},
+		{common: {"--debug"}, daemon: {"--log-driver=json-file", "--log-opt=max-size=1k"}},
+	}
+
+	var invalidGlobalFlags = [][]string{
+		//Invalid because you cannot pass daemon flags as global flags.
+		{"--selinux-enabled", "-l", "info"},
+		{"-D", "-r"},
+		{"--config", "/tmp"},
+	}
+
+	// `docker daemon -l info --selinux-enabled`
+	// should NOT error out
+	for _, f := range flags {
+		d := NewDaemon(c)
+		args := append(f[common], f[daemon]...)
+		if err := d.Start(args...); err != nil {
+			c.Fatalf("Daemon should have started successfully with %v: %v", args, err)
+		}
+		d.Stop()
+	}
+
+	// `docker -l info daemon --selinux-enabled`
+	// should error out
+	for _, f := range flags {
+		d := NewDaemon(c)
+		d.GlobalFlags = f[common]
+		if err := d.Start(f[daemon]...); err == nil {
+			d.Stop()
+			c.Fatalf("Daemon should have failed to start with docker %v daemon %v", d.GlobalFlags, f[daemon])
+		}
+	}
+
+	for _, f := range invalidGlobalFlags {
+		cmd := exec.Command(dockerBinary, append(f, "daemon")...)
+		errch := make(chan error)
+		var err error
+		go func() {
+			errch <- cmd.Run()
+		}()
+		select {
+		case <-time.After(time.Second):
+			cmd.Process.Kill()
+		case err = <-errch:
+		}
+		if err == nil {
+			c.Fatalf("Daemon should have failed to start with docker %v daemon", f)
+		}
+	}
+}
+
 func (s *DockerDaemonSuite) TestDaemonLogLevelDebug(c *check.C) {
 	if err := s.d.Start("--log-level=debug"); err != nil {
 		c.Fatal(err)
@@ -382,7 +476,7 @@ func (s *DockerDaemonSuite) TestDaemonAllocatesListeningPort(c *check.C) {
 		{"localhost", "127.0.0.1", "1235"},
 	}
 
-	cmdArgs := []string{}
+	cmdArgs := make([]string, 0, len(listeningPorts)*2)
 	for _, hostDirective := range listeningPorts {
 		cmdArgs = append(cmdArgs, "--host", fmt.Sprintf("tcp://%s:%s", hostDirective[0], hostDirective[2]))
 	}
@@ -798,8 +892,7 @@ func (s *DockerDaemonSuite) TestDaemonLinksIpTablesRulesWhenLinkAndUnlink(c *che
 	c.Assert(err, check.IsNil, check.Commentf(out))
 	defer deleteInterface(c, bridgeName)
 
-	args := []string{"--bridge", bridgeName, "--icc=false"}
-	err = s.d.StartWithBusybox(args...)
+	err = s.d.StartWithBusybox("--bridge", bridgeName, "--icc=false")
 	c.Assert(err, check.IsNil)
 	defer s.d.Restart()
 
@@ -1210,7 +1303,7 @@ func (s *DockerDaemonSuite) TestDaemonRestartKillWait(c *check.C) {
 // TestHttpsInfo connects via two-way authenticated HTTPS to the info endpoint
 func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) {
 	const (
-		testDaemonHTTPSAddr = "localhost:4271"
+		testDaemonHTTPSAddr = "tcp://localhost:4271"
 	)
 
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem",
@@ -1218,9 +1311,7 @@ func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) {
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 	}
 
-	//force tcp protocol
-	host := fmt.Sprintf("tcp://%s", testDaemonHTTPSAddr)
-	daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-cert.pem", "--tlskey", "fixtures/https/client-key.pem"}
+	daemonArgs := []string{"--host", testDaemonHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-cert.pem", "--tlskey", "fixtures/https/client-key.pem"}
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	if err != nil {
 		c.Fatalf("Error Occurred: %s and output: %s", err, out)
@@ -1232,16 +1323,15 @@ func (s *DockerDaemonSuite) TestHttpsInfo(c *check.C) {
 func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) {
 	const (
 		errBadCertificate   = "remote error: bad certificate"
-		testDaemonHTTPSAddr = "localhost:4271"
+		testDaemonHTTPSAddr = "tcp://localhost:4271"
 	)
+
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-cert.pem",
 		"--tlskey", "fixtures/https/server-key.pem", "-H", testDaemonHTTPSAddr); err != nil {
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 	}
 
-	//force tcp protocol
-	host := fmt.Sprintf("tcp://%s", testDaemonHTTPSAddr)
-	daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
+	daemonArgs := []string{"--host", testDaemonHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	if err == nil || !strings.Contains(out, errBadCertificate) {
 		c.Fatalf("Expected err: %s, got instead: %s and output: %s", errBadCertificate, err, out)
@@ -1253,16 +1343,14 @@ func (s *DockerDaemonSuite) TestHttpsInfoRogueCert(c *check.C) {
 func (s *DockerDaemonSuite) TestHttpsInfoRogueServerCert(c *check.C) {
 	const (
 		errCaUnknown             = "x509: certificate signed by unknown authority"
-		testDaemonRogueHTTPSAddr = "localhost:4272"
+		testDaemonRogueHTTPSAddr = "tcp://localhost:4272"
 	)
 	if err := s.d.Start("--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/server-rogue-cert.pem",
 		"--tlskey", "fixtures/https/server-rogue-key.pem", "-H", testDaemonRogueHTTPSAddr); err != nil {
 		c.Fatalf("Could not start daemon with busybox: %v", err)
 	}
 
-	//force tcp protocol
-	host := fmt.Sprintf("tcp://%s", testDaemonRogueHTTPSAddr)
-	daemonArgs := []string{"--host", host, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
+	daemonArgs := []string{"--host", testDaemonRogueHTTPSAddr, "--tlsverify", "--tlscacert", "fixtures/https/ca.pem", "--tlscert", "fixtures/https/client-rogue-cert.pem", "--tlskey", "fixtures/https/client-rogue-key.pem"}
 	out, err := s.d.CmdWithArgs(daemonArgs, "info")
 	if err == nil || !strings.Contains(out, errCaUnknown) {
 		c.Fatalf("Expected err: %s, got instead: %s and output: %s", errCaUnknown, err, out)

+ 25 - 13
integration-cli/docker_cli_help_test.go

@@ -89,10 +89,18 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 			c.Fatalf("Missing 'Commands:' in:\n%s", out)
 		}
 
-		// Grab all chars starting at "Commands:"
-		// Skip first line, its "Commands:"
 		cmds := []string{}
-		for _, cmd := range strings.Split(out[i:], "\n")[1:] {
+		// Grab all chars starting at "Commands:"
+		helpOut := strings.Split(out[i:], "\n")
+		// First line is just "Commands:"
+		if isLocalDaemon {
+			// Replace first line with "daemon" command since it's not part of the list of commands.
+			helpOut[0] = " daemon"
+		} else {
+			// Skip first line
+			helpOut = helpOut[1:]
+		}
+		for _, cmd := range helpOut {
 			var stderr string
 
 			// Stop on blank line or non-idented line
@@ -192,9 +200,10 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 				// lead to incorrect test result (like false negative).
 				// Whatever the reason, skip trying to run w/o args and
 				// jump to trying with a bogus arg.
-				skipNoArgs := map[string]string{
-					"events": "",
-					"load":   "",
+				skipNoArgs := map[string]struct{}{
+					"daemon": {},
+					"events": {},
+					"load":   {},
 				}
 
 				ec = 0
@@ -230,6 +239,9 @@ func (s *DockerSuite) TestHelpTextVerify(c *check.C) {
 		}
 
 		expected := 39
+		if isLocalDaemon {
+			expected++ // for the daemon command
+		}
 		if len(cmds) != expected {
 			c.Fatalf("Wrong # of cmds(%d), it should be: %d\nThe list:\n%q",
 				len(cmds), expected, cmds)
@@ -246,7 +258,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd := exec.Command(dockerBinary)
 	stdout, stderr, ec, err := runCommandWithStdoutStderr(cmd)
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
-		c.Fatalf("Bad results from 'docker'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	// Be really pick
 	if strings.HasSuffix(stdout, "\n\n") {
@@ -257,7 +269,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "help")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
-		c.Fatalf("Bad results from 'docker help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	// Be really pick
 	if strings.HasSuffix(stdout, "\n\n") {
@@ -268,7 +280,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "--help")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
-		c.Fatalf("Bad results from 'docker --help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker --help'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	// Be really pick
 	if strings.HasSuffix(stdout, "\n\n") {
@@ -280,7 +292,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "inspect", "busybox")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) == 0 || len(stderr) != 0 || ec != 0 || err != nil {
-		c.Fatalf("Bad results from 'docker inspect busybox'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker inspect busybox'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	// Be really pick
 	if strings.HasSuffix(stdout, "\n\n") {
@@ -292,7 +304,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "rm")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
-		c.Fatalf("Bad results from 'docker rm'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker rm'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	// Should not contain full help text but should contain info about
 	// # of args and Usage line
@@ -305,7 +317,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "rm", "NoSuchContainer")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
-		c.Fatalf("Bad results from 'docker rm NoSuchContainer'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker rm NoSuchContainer'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	// Be really picky
 	if strings.HasSuffix(stderr, "\n\n") {
@@ -316,7 +328,7 @@ func (s *DockerSuite) TestHelpExitCodesHelpOutput(c *check.C) {
 	cmd = exec.Command(dockerBinary, "BadCmd")
 	stdout, stderr, ec, err = runCommandWithStdoutStderr(cmd)
 	if len(stdout) != 0 || len(stderr) == 0 || ec == 0 || err == nil {
-		c.Fatalf("Bad results from 'docker BadCmd'\nec:%d\nstdout:%s\nstderr:%s\nerr:%q", ec, stdout, stderr, err)
+		c.Fatalf("Bad results from 'docker BadCmd'\nec:%d\nstdout:%s\nstderr:%s\nerr:%v", ec, stdout, stderr, err)
 	}
 	if stderr != "docker: 'BadCmd' is not a docker command.\nSee 'docker --help'.\n" {
 		c.Fatalf("Unexcepted output for 'docker badCmd'\nstderr:%s", stderr)

+ 11 - 0
integration-cli/docker_cli_run_test.go

@@ -217,6 +217,17 @@ func (s *DockerSuite) TestRunLinksContainerWithContainerId(c *check.C) {
 	}
 }
 
+// Issue 9677.
+func (s *DockerSuite) TestRunWithDaemonFlags(c *check.C) {
+	out, _, err := dockerCmdWithError(c, "--selinux-enabled", "run", "-i", "-t", "busybox", "true")
+	if err != nil {
+		if !strings.Contains(out, "must follow the 'docker daemon' command") && // daemon
+			!strings.Contains(out, "flag provided but not defined: --selinux-enabled") { // no daemon (client-only)
+			c.Fatal(err, out)
+		}
+	}
+}
+
 // Regression test for #4979
 func (s *DockerSuite) TestRunWithVolumesFromExited(c *check.C) {
 	out, exitCode := dockerCmd(c, "run", "--name", "test-data", "--volume", "/some/dir", "busybox", "touch", "/some/dir/file")

+ 24 - 15
integration-cli/docker_utils.go

@@ -29,6 +29,12 @@ import (
 
 // Daemon represents a Docker daemon for the testing framework.
 type Daemon struct {
+	// Defaults to "daemon"
+	// Useful to set to --daemon or -d for checking backwards compatability
+	Command     string
+	GlobalFlags []string
+
+	id             string
 	c              *check.C
 	logFile        *os.File
 	folder         string
@@ -59,7 +65,8 @@ func NewDaemon(c *check.C) *Daemon {
 		c.Fatal("Please set the DEST environment variable")
 	}
 
-	dir := filepath.Join(dest, fmt.Sprintf("d%d", time.Now().UnixNano()%100000000))
+	id := fmt.Sprintf("d%d", time.Now().UnixNano()%100000000)
+	dir := filepath.Join(dest, id)
 	daemonFolder, err := filepath.Abs(dir)
 	if err != nil {
 		c.Fatalf("Could not make %q an absolute path: %v", dir, err)
@@ -77,6 +84,8 @@ func NewDaemon(c *check.C) *Daemon {
 	}
 
 	return &Daemon{
+		Command:       "daemon",
+		id:            id,
 		c:             c,
 		folder:        daemonFolder,
 		storageDriver: os.Getenv("DOCKER_GRAPHDRIVER"),
@@ -90,22 +99,22 @@ func NewDaemon(c *check.C) *Daemon {
 func (d *Daemon) Start(arg ...string) error {
 	dockerBinary, err := exec.LookPath(dockerBinary)
 	if err != nil {
-		d.c.Fatalf("could not find docker binary in $PATH: %v", err)
+		d.c.Fatalf("[%s] could not find docker binary in $PATH: %v", d.id, err)
 	}
 
-	args := []string{
+	args := append(d.GlobalFlags,
+		d.Command,
 		"--host", d.sock(),
-		"--daemon",
 		"--graph", fmt.Sprintf("%s/graph", d.folder),
 		"--pidfile", fmt.Sprintf("%s/docker.pid", d.folder),
 		fmt.Sprintf("--userland-proxy=%t", d.userlandProxy),
-	}
+	)
 
 	// If we don't explicitly set the log-level or debug flag(-D) then
 	// turn on debug mode
 	foundIt := false
 	for _, a := range arg {
-		if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") {
+		if strings.Contains(a, "--log-level") || strings.Contains(a, "-D") || strings.Contains(a, "--debug") {
 			foundIt = true
 		}
 	}
@@ -125,21 +134,21 @@ func (d *Daemon) Start(arg ...string) error {
 
 	d.logFile, err = os.OpenFile(filepath.Join(d.folder, "docker.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600)
 	if err != nil {
-		d.c.Fatalf("Could not create %s/docker.log: %v", d.folder, err)
+		d.c.Fatalf("[%s] Could not create %s/docker.log: %v", d.id, d.folder, err)
 	}
 
 	d.cmd.Stdout = d.logFile
 	d.cmd.Stderr = d.logFile
 
 	if err := d.cmd.Start(); err != nil {
-		return fmt.Errorf("could not start daemon container: %v", err)
+		return fmt.Errorf("[%s] could not start daemon container: %v", d.id, err)
 	}
 
 	wait := make(chan error)
 
 	go func() {
 		wait <- d.cmd.Wait()
-		d.c.Log("exiting daemon")
+		d.c.Logf("[%s] exiting daemon", d.id)
 		close(wait)
 	}()
 
@@ -149,14 +158,14 @@ func (d *Daemon) Start(arg ...string) error {
 	// make sure daemon is ready to receive requests
 	startTime := time.Now().Unix()
 	for {
-		d.c.Log("waiting for daemon to start")
+		d.c.Logf("[%s] waiting for daemon to start", d.id)
 		if time.Now().Unix()-startTime > 5 {
 			// After 5 seconds, give up
-			return errors.New("Daemon exited and never started")
+			return fmt.Errorf("[%s] Daemon exited and never started", d.id)
 		}
 		select {
 		case <-time.After(2 * time.Second):
-			return errors.New("timeout: daemon does not respond")
+			return fmt.Errorf("[%s] timeout: daemon does not respond", d.id)
 		case <-tick:
 			c, err := net.Dial("unix", filepath.Join(d.folder, "docker.sock"))
 			if err != nil {
@@ -168,7 +177,7 @@ func (d *Daemon) Start(arg ...string) error {
 
 			req, err := http.NewRequest("GET", "/_ping", nil)
 			if err != nil {
-				d.c.Fatalf("could not create new request: %v", err)
+				d.c.Fatalf("[%s] could not create new request: %v", d.id, err)
 			}
 
 			resp, err := client.Do(req)
@@ -176,10 +185,10 @@ func (d *Daemon) Start(arg ...string) error {
 				continue
 			}
 			if resp.StatusCode != http.StatusOK {
-				d.c.Logf("received status != 200 OK: %s", resp.Status)
+				d.c.Logf("[%s] received status != 200 OK: %s", d.id, resp.Status)
 			}
 
-			d.c.Log("daemon started")
+			d.c.Logf("[%s] daemon started", d.id)
 			return nil
 		}
 	}

+ 7 - 0
opts/hosts_unix.go

@@ -0,0 +1,7 @@
+// +build !windows
+
+package opts
+
+import "fmt"
+
+var DefaultHost = fmt.Sprintf("unix://%s", DefaultUnixSocket)

+ 7 - 0
opts/hosts_windows.go

@@ -0,0 +1,7 @@
+// +build windows
+
+package opts
+
+import "fmt"
+
+var DefaultHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort)

+ 14 - 11
opts/opts.go

@@ -32,37 +32,37 @@ var (
 // ListVar Defines a flag with the specified names and usage, and put the value
 // list into ListOpts that will hold the values.
 func ListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, nil), names, usage)
+	flag.Var(NewListOptsRef(values, nil), names, usage)
 }
 
 // MapVar Defines a flag with the specified names and usage, and put the value
 // map into MapOpt that will hold the values (key,value).
 func MapVar(values map[string]string, names []string, usage string) {
-	flag.Var(newMapOpt(values, nil), names, usage)
+	flag.Var(NewMapOpts(values, nil), names, usage)
 }
 
 // LogOptsVar Defines a flag with the specified names and usage for --log-opts,
 // and put the value map into MapOpt that will hold the values (key,value).
 func LogOptsVar(values map[string]string, names []string, usage string) {
-	flag.Var(newMapOpt(values, nil), names, usage)
+	flag.Var(NewMapOpts(values, nil), names, usage)
 }
 
 // HostListVar Defines a flag with the specified names and usage and put the
 // value into a ListOpts that will hold the values, validating the Host format.
 func HostListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, ValidateHost), names, usage)
+	flag.Var(NewListOptsRef(values, ValidateHost), names, usage)
 }
 
 // IPListVar Defines a flag with the specified names and usage and put the
 // value into a ListOpts that will hold the values, validating the IP format.
 func IPListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, ValidateIPAddress), names, usage)
+	flag.Var(NewListOptsRef(values, ValidateIPAddress), names, usage)
 }
 
 // DNSSearchListVar Defines a flag with the specified names and usage and put the
 // value into a ListOpts that will hold the values, validating the DNS search format.
 func DNSSearchListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, ValidateDNSSearch), names, usage)
+	flag.Var(NewListOptsRef(values, ValidateDNSSearch), names, usage)
 }
 
 // IPVar Defines a flag with the specified names and usage for IP and will use
@@ -74,12 +74,12 @@ func IPVar(value *net.IP, names []string, defaultValue, usage string) {
 // LabelListVar Defines a flag with the specified names and usage and put the
 // value into a ListOpts that will hold the values, validating the label format.
 func LabelListVar(values *[]string, names []string, usage string) {
-	flag.Var(newListOptsRef(values, ValidateLabel), names, usage)
+	flag.Var(NewListOptsRef(values, ValidateLabel), names, usage)
 }
 
 // UlimitMapVar Defines a flag with the specified names and usage for --ulimit,
 // and put the value map into a UlimitOpt that will hold the values.
-func UlimitMapVar(values map[string]*ulimit.Ulimit, names []string, usage string) {
+func UlimitMapVar(values *map[string]*ulimit.Ulimit, names []string, usage string) {
 	flag.Var(NewUlimitOpt(values), names, usage)
 }
 
@@ -92,10 +92,10 @@ type ListOpts struct {
 // NewListOpts Create a new ListOpts with the specified validator.
 func NewListOpts(validator ValidatorFctType) ListOpts {
 	var values []string
-	return *newListOptsRef(&values, validator)
+	return *NewListOptsRef(&values, validator)
 }
 
-func newListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
+func NewListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
 	return &ListOpts{
 		values:    values,
 		validator: validator,
@@ -191,7 +191,10 @@ func (opts *MapOpts) String() string {
 	return fmt.Sprintf("%v", map[string]string((opts.values)))
 }
 
-func newMapOpt(values map[string]string, validator ValidatorFctType) *MapOpts {
+func NewMapOpts(values map[string]string, validator ValidatorFctType) *MapOpts {
+	if values == nil {
+		values = make(map[string]string)
+	}
 	return &MapOpts{
 		values:    values,
 		validator: validator,

+ 1 - 1
opts/opts_test.go

@@ -32,7 +32,7 @@ func TestValidateIPAddress(t *testing.T) {
 
 func TestMapOpts(t *testing.T) {
 	tmpMap := make(map[string]string)
-	o := newMapOpt(tmpMap, logOptsValidator)
+	o := NewMapOpts(tmpMap, logOptsValidator)
 	o.Set("max-size=1")
 	if o.String() != "map[max-size:1]" {
 		t.Errorf("%s != [map[max-size:1]", o.String())

+ 8 - 5
opts/ulimit.go

@@ -7,10 +7,13 @@ import (
 )
 
 type UlimitOpt struct {
-	values map[string]*ulimit.Ulimit
+	values *map[string]*ulimit.Ulimit
 }
 
-func NewUlimitOpt(ref map[string]*ulimit.Ulimit) *UlimitOpt {
+func NewUlimitOpt(ref *map[string]*ulimit.Ulimit) *UlimitOpt {
+	if ref == nil {
+		ref = &map[string]*ulimit.Ulimit{}
+	}
 	return &UlimitOpt{ref}
 }
 
@@ -20,14 +23,14 @@ func (o *UlimitOpt) Set(val string) error {
 		return err
 	}
 
-	o.values[l.Name] = l
+	(*o.values)[l.Name] = l
 
 	return nil
 }
 
 func (o *UlimitOpt) String() string {
 	var out []string
-	for _, v := range o.values {
+	for _, v := range *o.values {
 		out = append(out, v.String())
 	}
 
@@ -36,7 +39,7 @@ func (o *UlimitOpt) String() string {
 
 func (o *UlimitOpt) GetList() []*ulimit.Ulimit {
 	var ulimits []*ulimit.Ulimit
-	for _, v := range o.values {
+	for _, v := range *o.values {
 		ulimits = append(ulimits, v)
 	}
 

+ 1 - 1
opts/ulimit_test.go

@@ -11,7 +11,7 @@ func TestUlimitOpt(t *testing.T) {
 		"nofile": {"nofile", 1024, 512},
 	}
 
-	ulimitOpt := NewUlimitOpt(ulimitMap)
+	ulimitOpt := NewUlimitOpt(&ulimitMap)
 
 	expected := "[nofile=512:1024]"
 	if ulimitOpt.String() != expected {

+ 51 - 1
pkg/mflag/flag.go

@@ -526,7 +526,7 @@ func (f *FlagSet) PrintDefaults() {
 				names = append(names, name)
 			}
 		}
-		if len(names) > 0 {
+		if len(names) > 0 && len(flag.Usage) > 0 {
 			val := flag.DefValue
 
 			if home != "" && strings.HasPrefix(val, home) {
@@ -1143,3 +1143,53 @@ func (f *FlagSet) Init(name string, errorHandling ErrorHandling) {
 	f.name = name
 	f.errorHandling = errorHandling
 }
+
+type mergeVal struct {
+	Value
+	key  string
+	fset *FlagSet
+}
+
+func (v mergeVal) Set(s string) error {
+	return v.fset.Set(v.key, s)
+}
+
+func (v mergeVal) IsBoolFlag() bool {
+	if b, ok := v.Value.(boolFlag); ok {
+		return b.IsBoolFlag()
+	}
+	return false
+}
+
+func Merge(dest *FlagSet, flagsets ...*FlagSet) error {
+	for _, fset := range flagsets {
+		for k, f := range fset.formal {
+			if _, ok := dest.formal[k]; ok {
+				var err error
+				if fset.name == "" {
+					err = fmt.Errorf("flag redefined: %s", k)
+				} else {
+					err = fmt.Errorf("%s flag redefined: %s", fset.name, k)
+				}
+				fmt.Fprintln(fset.Out(), err.Error())
+				// Happens only if flags are declared with identical names
+				switch dest.errorHandling {
+				case ContinueOnError:
+					return err
+				case ExitOnError:
+					os.Exit(2)
+				case PanicOnError:
+					panic(err)
+				}
+			}
+			newF := *f
+			newF.Value = mergeVal{f.Value, k, fset}
+			dest.formal[k] = &newF
+		}
+	}
+	return nil
+}
+
+func (f *FlagSet) IsEmpty() bool {
+	return len(f.actual) == 0
+}

+ 11 - 4
pkg/tlsconfig/config.go

@@ -17,11 +17,18 @@ import (
 
 // Options represents the information needed to create client and server TLS configurations.
 type Options struct {
+	CAFile string
+
+	// If either CertFile or KeyFile is empty, Client() will not load them
+	// preventing the client from authenticating to the server.
+	// However, Server() requires them and will error out if they are empty.
+	CertFile string
+	KeyFile  string
+
+	// client-only option
 	InsecureSkipVerify bool
-	ClientAuth         tls.ClientAuthType
-	CAFile             string
-	CertFile           string
-	KeyFile            string
+	// server-only option
+	ClientAuth tls.ClientAuthType
 }
 
 // Extra (server-side) accepted CBC cipher suites - will phase out in the future

+ 3 - 3
registry/config.go

@@ -43,11 +43,11 @@ var (
 
 // InstallFlags adds command-line options to the top-level flag parser for
 // the current process.
-func (options *Options) InstallFlags() {
+func (options *Options) InstallFlags(cmd *flag.FlagSet, usageFn func(string) string) {
 	options.Mirrors = opts.NewListOpts(ValidateMirror)
-	flag.Var(&options.Mirrors, []string{"-registry-mirror"}, "Preferred Docker registry mirror")
+	cmd.Var(&options.Mirrors, []string{"-registry-mirror"}, usageFn("Preferred Docker registry mirror"))
 	options.InsecureRegistries = opts.NewListOpts(ValidateIndexName)
-	flag.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, "Enable insecure registry communication")
+	cmd.Var(&options.InsecureRegistries, []string{"-insecure-registry"}, usageFn("Enable insecure registry communication"))
 }
 
 type netIPNet net.IPNet

+ 1 - 3
runconfig/parse.go

@@ -9,7 +9,6 @@ import (
 	flag "github.com/docker/docker/pkg/mflag"
 	"github.com/docker/docker/pkg/nat"
 	"github.com/docker/docker/pkg/parsers"
-	"github.com/docker/docker/pkg/ulimit"
 	"github.com/docker/docker/pkg/units"
 )
 
@@ -48,8 +47,7 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 		flLabels  = opts.NewListOpts(opts.ValidateEnv)
 		flDevices = opts.NewListOpts(opts.ValidateDevice)
 
-		ulimits   = make(map[string]*ulimit.Ulimit)
-		flUlimits = opts.NewUlimitOpt(ulimits)
+		flUlimits = opts.NewUlimitOpt(nil)
 
 		flPublish     = opts.NewListOpts(nil)
 		flExpose      = opts.NewListOpts(nil)