daemon.go 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. // +build daemon
  2. package main
  3. import (
  4. "crypto/tls"
  5. "fmt"
  6. "io"
  7. "os"
  8. "path/filepath"
  9. "strings"
  10. "time"
  11. "github.com/Sirupsen/logrus"
  12. "github.com/docker/distribution/uuid"
  13. apiserver "github.com/docker/docker/api/server"
  14. "github.com/docker/docker/autogen/dockerversion"
  15. "github.com/docker/docker/cli"
  16. "github.com/docker/docker/cliconfig"
  17. "github.com/docker/docker/daemon"
  18. "github.com/docker/docker/daemon/logger"
  19. "github.com/docker/docker/opts"
  20. flag "github.com/docker/docker/pkg/mflag"
  21. "github.com/docker/docker/pkg/pidfile"
  22. "github.com/docker/docker/pkg/signal"
  23. "github.com/docker/docker/pkg/system"
  24. "github.com/docker/docker/pkg/timeutils"
  25. "github.com/docker/docker/pkg/tlsconfig"
  26. "github.com/docker/docker/registry"
  27. "github.com/docker/docker/utils"
  28. )
  29. const daemonUsage = " docker daemon [ --help | ... ]\n"
  30. var (
  31. flDaemon = flag.Bool([]string{"#d", "#-daemon"}, false, "Enable daemon mode (deprecated; use docker daemon)")
  32. daemonCli cli.Handler = NewDaemonCli()
  33. )
  34. // TODO: remove once `-d` is retired
  35. func handleGlobalDaemonFlag() {
  36. // This block makes sure that if the deprecated daemon flag `--daemon` is absent,
  37. // then all daemon-specific flags are absent as well.
  38. if !*flDaemon && daemonFlags != nil {
  39. flag.CommandLine.Visit(func(fl *flag.Flag) {
  40. for _, name := range fl.Names {
  41. name := strings.TrimPrefix(name, "#")
  42. if daemonFlags.Lookup(name) != nil {
  43. // daemon flag was NOT specified, but daemon-specific flags were
  44. // so let's error out
  45. fmt.Fprintf(os.Stderr, "docker: the daemon flag '-%s' must follow the 'docker daemon' command.\n", name)
  46. os.Exit(1)
  47. }
  48. }
  49. })
  50. }
  51. if *flDaemon {
  52. daemonCli.(*DaemonCli).CmdDaemon(flag.Args()...)
  53. os.Exit(0)
  54. }
  55. }
  56. func presentInHelp(usage string) string { return usage }
  57. func absentFromHelp(string) string { return "" }
  58. // NewDaemonCli returns a pre-configured daemon CLI
  59. func NewDaemonCli() *DaemonCli {
  60. daemonFlags = cli.Subcmd("daemon", nil, "Enable daemon mode", true)
  61. // TODO(tiborvass): remove InstallFlags?
  62. daemonConfig := new(daemon.Config)
  63. daemonConfig.LogConfig.Config = make(map[string]string)
  64. daemonConfig.ClusterOpts = make(map[string]string)
  65. daemonConfig.InstallFlags(daemonFlags, presentInHelp)
  66. daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp)
  67. registryOptions := new(registry.Options)
  68. registryOptions.InstallFlags(daemonFlags, presentInHelp)
  69. registryOptions.InstallFlags(flag.CommandLine, absentFromHelp)
  70. daemonFlags.Require(flag.Exact, 0)
  71. return &DaemonCli{
  72. Config: daemonConfig,
  73. registryOptions: registryOptions,
  74. }
  75. }
  76. func migrateKey() (err error) {
  77. // Migrate trust key if exists at ~/.docker/key.json and owned by current user
  78. oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile)
  79. newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile)
  80. if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) {
  81. defer func() {
  82. // Ensure old path is removed if no error occurred
  83. if err == nil {
  84. err = os.Remove(oldPath)
  85. } else {
  86. logrus.Warnf("Key migration failed, key file not removed at %s", oldPath)
  87. os.Remove(newPath)
  88. }
  89. }()
  90. if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil {
  91. return fmt.Errorf("Unable to create daemon configuration directory: %s", err)
  92. }
  93. newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
  94. if err != nil {
  95. return fmt.Errorf("error creating key file %q: %s", newPath, err)
  96. }
  97. defer newFile.Close()
  98. oldFile, err := os.Open(oldPath)
  99. if err != nil {
  100. return fmt.Errorf("error opening key file %q: %s", oldPath, err)
  101. }
  102. defer oldFile.Close()
  103. if _, err := io.Copy(newFile, oldFile); err != nil {
  104. return fmt.Errorf("error copying key: %s", err)
  105. }
  106. logrus.Infof("Migrated key from %s to %s", oldPath, newPath)
  107. }
  108. return nil
  109. }
  110. // DaemonCli represents the daemon CLI.
  111. type DaemonCli struct {
  112. *daemon.Config
  113. registryOptions *registry.Options
  114. }
  115. func getGlobalFlag() (globalFlag *flag.Flag) {
  116. defer func() {
  117. if x := recover(); x != nil {
  118. switch f := x.(type) {
  119. case *flag.Flag:
  120. globalFlag = f
  121. default:
  122. panic(x)
  123. }
  124. }
  125. }()
  126. visitor := func(f *flag.Flag) { panic(f) }
  127. commonFlags.FlagSet.Visit(visitor)
  128. clientFlags.FlagSet.Visit(visitor)
  129. return
  130. }
  131. // CmdDaemon is the daemon command, called the raw arguments after `docker daemon`.
  132. func (cli *DaemonCli) CmdDaemon(args ...string) error {
  133. // warn from uuid package when running the daemon
  134. uuid.Loggerf = logrus.Warnf
  135. if *flDaemon {
  136. // allow legacy forms `docker -D -d` and `docker -d -D`
  137. logrus.Warn("please use 'docker daemon' instead.")
  138. } else if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() {
  139. // deny `docker -D daemon`
  140. illegalFlag := getGlobalFlag()
  141. fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0])
  142. os.Exit(1)
  143. } else {
  144. // allow new form `docker daemon -D`
  145. flag.Merge(daemonFlags, commonFlags.FlagSet)
  146. }
  147. daemonFlags.ParseFlags(args, true)
  148. commonFlags.PostParse()
  149. if commonFlags.TrustKey == "" {
  150. commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile)
  151. }
  152. if utils.ExperimentalBuild() {
  153. logrus.Warn("Running experimental build")
  154. }
  155. logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed})
  156. if err := setDefaultUmask(); err != nil {
  157. logrus.Fatalf("Failed to set umask: %v", err)
  158. }
  159. if len(cli.LogConfig.Config) > 0 {
  160. if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil {
  161. logrus.Fatalf("Failed to set log opts: %v", err)
  162. }
  163. }
  164. var pfile *pidfile.PIDFile
  165. if cli.Pidfile != "" {
  166. pf, err := pidfile.New(cli.Pidfile)
  167. if err != nil {
  168. logrus.Fatalf("Error starting daemon: %v", err)
  169. }
  170. pfile = pf
  171. defer func() {
  172. if err := pfile.Remove(); err != nil {
  173. logrus.Error(err)
  174. }
  175. }()
  176. }
  177. serverConfig := &apiserver.Config{
  178. Logging: true,
  179. Version: dockerversion.VERSION,
  180. }
  181. serverConfig = setPlatformServerConfig(serverConfig, cli.Config)
  182. defaultHost := opts.DefaultHost
  183. if commonFlags.TLSOptions != nil {
  184. if !commonFlags.TLSOptions.InsecureSkipVerify {
  185. // server requires and verifies client's certificate
  186. commonFlags.TLSOptions.ClientAuth = tls.RequireAndVerifyClientCert
  187. }
  188. tlsConfig, err := tlsconfig.Server(*commonFlags.TLSOptions)
  189. if err != nil {
  190. logrus.Fatal(err)
  191. }
  192. serverConfig.TLSConfig = tlsConfig
  193. defaultHost = opts.DefaultTLSHost
  194. }
  195. if len(commonFlags.Hosts) == 0 {
  196. commonFlags.Hosts = make([]string, 1)
  197. }
  198. for i := 0; i < len(commonFlags.Hosts); i++ {
  199. var err error
  200. if commonFlags.Hosts[i], err = opts.ParseHost(defaultHost, commonFlags.Hosts[i]); err != nil {
  201. logrus.Fatalf("error parsing -H %s : %v", commonFlags.Hosts[i], err)
  202. }
  203. }
  204. for _, protoAddr := range commonFlags.Hosts {
  205. protoAddrParts := strings.SplitN(protoAddr, "://", 2)
  206. if len(protoAddrParts) != 2 {
  207. logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr)
  208. }
  209. serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]})
  210. }
  211. api, err := apiserver.New(serverConfig)
  212. if err != nil {
  213. logrus.Fatal(err)
  214. }
  215. // The serve API routine never exits unless an error occurs
  216. // We need to start it as a goroutine and wait on it so
  217. // daemon doesn't exit
  218. // All servers must be protected with some mechanism (systemd socket, listenbuffer)
  219. // which prevents real handling of request until routes will be set.
  220. serveAPIWait := make(chan error)
  221. go func() {
  222. if err := api.ServeAPI(); err != nil {
  223. logrus.Errorf("ServeAPI error: %v", err)
  224. serveAPIWait <- err
  225. return
  226. }
  227. serveAPIWait <- nil
  228. }()
  229. if err := migrateKey(); err != nil {
  230. logrus.Fatal(err)
  231. }
  232. cli.TrustKeyPath = commonFlags.TrustKey
  233. registryService := registry.NewService(cli.registryOptions)
  234. d, err := daemon.NewDaemon(cli.Config, registryService)
  235. if err != nil {
  236. if pfile != nil {
  237. if err := pfile.Remove(); err != nil {
  238. logrus.Error(err)
  239. }
  240. }
  241. logrus.Fatalf("Error starting daemon: %v", err)
  242. }
  243. logrus.Info("Daemon has completed initialization")
  244. logrus.WithFields(logrus.Fields{
  245. "version": dockerversion.VERSION,
  246. "commit": dockerversion.GITCOMMIT,
  247. "execdriver": d.ExecutionDriver().Name(),
  248. "graphdriver": d.GraphDriver().String(),
  249. }).Info("Docker daemon")
  250. api.InitRouters(d)
  251. signal.Trap(func() {
  252. api.Close()
  253. <-serveAPIWait
  254. shutdownDaemon(d, 15)
  255. if pfile != nil {
  256. if err := pfile.Remove(); err != nil {
  257. logrus.Error(err)
  258. }
  259. }
  260. })
  261. // after the daemon is done setting up we can tell the api to start
  262. // accepting connections with specified daemon
  263. notifySystem()
  264. api.AcceptConnections()
  265. // Daemon is fully initialized and handling API traffic
  266. // Wait for serve API to complete
  267. errAPI := <-serveAPIWait
  268. shutdownDaemon(d, 15)
  269. if errAPI != nil {
  270. if pfile != nil {
  271. if err := pfile.Remove(); err != nil {
  272. logrus.Error(err)
  273. }
  274. }
  275. logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
  276. }
  277. return nil
  278. }
  279. // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case
  280. // d.Shutdown() is waiting too long to kill container or worst it's
  281. // blocked there
  282. func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) {
  283. ch := make(chan struct{})
  284. go func() {
  285. d.Shutdown()
  286. close(ch)
  287. }()
  288. select {
  289. case <-ch:
  290. logrus.Debug("Clean shutdown succeeded")
  291. case <-time.After(timeout * time.Second):
  292. logrus.Error("Force shutdown daemon")
  293. }
  294. }