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