| // +build daemon |
| |
| package main |
| |
| import ( |
| "fmt" |
| "io" |
| "os" |
| "path/filepath" |
| "time" |
| |
| "github.com/Sirupsen/logrus" |
| apiserver "github.com/docker/docker/api/server" |
| "github.com/docker/docker/autogen/dockerversion" |
| "github.com/docker/docker/daemon" |
| _ "github.com/docker/docker/daemon/execdriver/lxc" |
| _ "github.com/docker/docker/daemon/execdriver/native" |
| "github.com/docker/docker/pkg/homedir" |
| flag "github.com/docker/docker/pkg/mflag" |
| "github.com/docker/docker/pkg/pidfile" |
| "github.com/docker/docker/pkg/signal" |
| "github.com/docker/docker/pkg/system" |
| "github.com/docker/docker/pkg/timeutils" |
| "github.com/docker/docker/registry" |
| "github.com/docker/docker/utils" |
| ) |
| |
| const CanDaemon = true |
| |
| var ( |
| daemonCfg = &daemon.Config{} |
| registryCfg = ®istry.Options{} |
| ) |
| |
| func init() { |
| if daemonCfg.LogConfig.Config == nil { |
| daemonCfg.LogConfig.Config = make(map[string]string) |
| } |
| daemonCfg.InstallFlags() |
| registryCfg.InstallFlags() |
| } |
| |
| func migrateKey() (err error) { |
| // Migrate trust key if exists at ~/.docker/key.json and owned by current user |
| oldPath := filepath.Join(homedir.Get(), ".docker", defaultTrustKeyFile) |
| newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) |
| if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { |
| defer func() { |
| // Ensure old path is removed if no error occurred |
| if err == nil { |
| err = os.Remove(oldPath) |
| } else { |
| logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) |
| } |
| }() |
| |
| if err := os.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil { |
| return fmt.Errorf("Unable to create daemon configuration directory: %s", err) |
| } |
| |
| newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) |
| if err != nil { |
| return fmt.Errorf("error creating key file %q: %s", newPath, err) |
| } |
| defer newFile.Close() |
| |
| oldFile, err := os.Open(oldPath) |
| if err != nil { |
| return fmt.Errorf("error opening key file %q: %s", oldPath, err) |
| } |
| defer oldFile.Close() |
| |
| if _, err := io.Copy(newFile, oldFile); err != nil { |
| return fmt.Errorf("error copying key: %s", err) |
| } |
| |
| logrus.Infof("Migrated key from %s to %s", oldPath, newPath) |
| } |
| |
| return nil |
| } |
| |
| func mainDaemon() { |
| if utils.ExperimentalBuild() { |
| logrus.Warn("Running experimental build") |
| } |
| |
| if flag.NArg() != 0 { |
| flag.Usage() |
| return |
| } |
| |
| logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: timeutils.RFC3339NanoFixed}) |
| |
| var pfile *pidfile.PidFile |
| if daemonCfg.Pidfile != "" { |
| pf, err := pidfile.New(daemonCfg.Pidfile) |
| if err != nil { |
| logrus.Fatalf("Error starting daemon: %v", err) |
| } |
| pfile = pf |
| defer func() { |
| if err := pfile.Remove(); err != nil { |
| logrus.Error(err) |
| } |
| }() |
| } |
| |
| serverConfig := &apiserver.ServerConfig{ |
| Logging: true, |
| EnableCors: daemonCfg.EnableCors, |
| CorsHeaders: daemonCfg.CorsHeaders, |
| Version: dockerversion.VERSION, |
| SocketGroup: daemonCfg.SocketGroup, |
| Tls: *flTls, |
| TlsVerify: *flTlsVerify, |
| TlsCa: *flCa, |
| TlsCert: *flCert, |
| TlsKey: *flKey, |
| } |
| |
| api := apiserver.New(serverConfig) |
| |
| // The serve API routine never exits unless an error occurs |
| // We need to start it as a goroutine and wait on it so |
| // daemon doesn't exit |
| serveAPIWait := make(chan error) |
| go func() { |
| if err := api.ServeApi(flHosts); err != nil { |
| logrus.Errorf("ServeAPI error: %v", err) |
| serveAPIWait <- err |
| return |
| } |
| serveAPIWait <- nil |
| }() |
| |
| if err := migrateKey(); err != nil { |
| logrus.Fatal(err) |
| } |
| daemonCfg.TrustKeyPath = *flTrustKey |
| |
| registryService := registry.NewService(registryCfg) |
| d, err := daemon.NewDaemon(daemonCfg, registryService) |
| if err != nil { |
| if pfile != nil { |
| if err := pfile.Remove(); err != nil { |
| logrus.Error(err) |
| } |
| } |
| logrus.Fatalf("Error starting daemon: %v", err) |
| } |
| |
| logrus.Info("Daemon has completed initialization") |
| |
| logrus.WithFields(logrus.Fields{ |
| "version": dockerversion.VERSION, |
| "commit": dockerversion.GITCOMMIT, |
| "execdriver": d.ExecutionDriver().Name(), |
| "graphdriver": d.GraphDriver().String(), |
| }).Info("Docker daemon") |
| |
| signal.Trap(func() { |
| api.Close() |
| <-serveAPIWait |
| shutdownDaemon(d, 15) |
| if pfile != nil { |
| if err := pfile.Remove(); err != nil { |
| logrus.Error(err) |
| } |
| } |
| }) |
| |
| // after the daemon is done setting up we can tell the api to start |
| // accepting connections with specified daemon |
| api.AcceptConnections(d) |
| |
| // Daemon is fully initialized and handling API traffic |
| // Wait for serve API to complete |
| errAPI := <-serveAPIWait |
| shutdownDaemon(d, 15) |
| if errAPI != nil { |
| if pfile != nil { |
| if err := pfile.Remove(); err != nil { |
| logrus.Error(err) |
| } |
| } |
| logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) |
| } |
| } |
| |
| // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case |
| // d.Shutdown() is waiting too long to kill container or worst it's |
| // blocked there |
| func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { |
| ch := make(chan struct{}) |
| go func() { |
| d.Shutdown() |
| close(ch) |
| }() |
| select { |
| case <-ch: |
| logrus.Debug("Clean shutdown succeded") |
| case <-time.After(timeout * time.Second): |
| logrus.Error("Force shutdown daemon") |
| } |
| } |
| |
| // currentUserIsOwner checks whether the current user is the owner of the given |
| // file. |
| func currentUserIsOwner(f string) bool { |
| if fileInfo, err := system.Stat(f); err == nil && fileInfo != nil { |
| if int(fileInfo.Uid()) == os.Getuid() { |
| return true |
| } |
| } |
| return false |
| } |