func (d *Daemon) Init() error { d.shutdownChan = make(chan bool) /* Set the executable path */ absPath, err := os.Readlink("/proc/self/exe") if err != nil { return err } d.execPath = absPath /* Set the LVM environment */ err = os.Setenv("LVM_SUPPRESS_FD_WARNINGS", "1") if err != nil { return err } /* Setup logging if that wasn't done before */ if shared.Log == nil { shared.Log, err = logging.GetLogger("", "", true, true, nil) if err != nil { return err } } if !d.IsMock { shared.Log.Info("LXD is starting", log.Ctx{"path": shared.VarPath("")}) } else { shared.Log.Info("Mock LXD is starting", log.Ctx{"path": shared.VarPath("")}) } /* Detect user namespaces */ runningInUserns = shared.RunningInUserNS() /* Detect AppArmor support */ if aaAvailable && os.Getenv("LXD_SECURITY_APPARMOR") == "false" { aaAvailable = false aaAdmin = false shared.Log.Warn("AppArmor support has been manually disabled") } if aaAvailable && !shared.IsDir("/sys/kernel/security/apparmor") { aaAvailable = false aaAdmin = false shared.Log.Warn("AppArmor support has been disabled because of lack of kernel support") } _, err = exec.LookPath("apparmor_parser") if aaAvailable && err != nil { aaAvailable = false aaAdmin = false shared.Log.Warn("AppArmor support has been disabled because 'apparmor_parser' couldn't be found") } /* Detect AppArmor admin support */ if aaAdmin && !haveMacAdmin() { aaAdmin = false shared.Log.Warn("Per-container AppArmor profiles are disabled because the mac_admin capability is missing.") } if aaAdmin && runningInUserns { aaAdmin = false shared.Log.Warn("Per-container AppArmor profiles are disabled because LXD is running in an unprivileged container.") } /* Detect AppArmor confinment */ if !aaConfined { profile := aaProfile() if profile != "unconfined" && profile != "" { aaConfined = true shared.Log.Warn("Per-container AppArmor profiles are disabled because LXD is already protected by AppArmor.") } } /* Detect CGroup support */ cgCpuController = shared.PathExists("/sys/fs/cgroup/cpu/") if !cgCpuController { shared.Log.Warn("Couldn't find the CGroup CPU controller, CPU time limits will be ignored.") } cgCpusetController = shared.PathExists("/sys/fs/cgroup/cpuset/") if !cgCpusetController { shared.Log.Warn("Couldn't find the CGroup CPUset controller, CPU pinning will be ignored.") } cgMemoryController = shared.PathExists("/sys/fs/cgroup/memory/") if !cgMemoryController { shared.Log.Warn("Couldn't find the CGroup memory controller, memory limits will be ignored.") } cgSwapAccounting = shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes") if !cgSwapAccounting { shared.Log.Warn("CGroup memory swap accounting is disabled, swap limits will be ignored.") } /* Get the list of supported architectures */ var architectures = []int{} uname := syscall.Utsname{} if err := syscall.Uname(&uname); err != nil { return err } architectureName := "" for _, c := range uname.Machine { if c == 0 { break } architectureName += string(byte(c)) } architecture, err := shared.ArchitectureId(architectureName) if err != nil { return err } architectures = append(architectures, architecture) personalities, err := shared.ArchitecturePersonalities(architecture) if err != nil { return err } for _, personality := range personalities { architectures = append(architectures, personality) } d.architectures = architectures /* Set container path */ d.lxcpath = shared.VarPath("containers") /* Make sure all our directories are available */ if err := os.MkdirAll(shared.VarPath("containers"), 0711); err != nil { return err } if err := os.MkdirAll(shared.VarPath("devices"), 0711); err != nil { return err } if err := os.MkdirAll(shared.VarPath("devlxd"), 0755); err != nil { return err } if err := os.MkdirAll(shared.VarPath("images"), 0700); err != nil { return err } if err := os.MkdirAll(shared.VarPath("security"), 0700); err != nil { return err } if err := os.MkdirAll(shared.VarPath("shmounts"), 0711); err != nil { return err } if err := os.MkdirAll(shared.VarPath("snapshots"), 0700); err != nil { return err } /* Detect the filesystem */ d.BackingFs, err = filesystemDetect(d.lxcpath) if err != nil { shared.Log.Error("Error detecting backing fs", log.Ctx{"err": err}) } /* Read the uid/gid allocation */ d.IdmapSet, err = shared.DefaultIdmapSet() if err != nil { shared.Log.Warn("Error reading idmap", log.Ctx{"err": err.Error()}) shared.Log.Warn("Only privileged containers will be able to run") } else { shared.Log.Info("Default uid/gid map:") for _, lxcmap := range d.IdmapSet.ToLxcString() { shared.Log.Info(strings.TrimRight(" - "+lxcmap, "\n")) } } /* Initialize the database */ err = initializeDbObject(d, shared.VarPath("lxd.db")) if err != nil { return err } /* Prune images */ d.pruneChan = make(chan bool) go func() { d.pruneExpiredImages() for { timer := time.NewTimer(24 * time.Hour) timeChan := timer.C select { case <-timeChan: /* run once per day */ d.pruneExpiredImages() case <-d.pruneChan: /* run when image.remote_cache_expiry is changed */ d.pruneExpiredImages() timer.Stop() } } }() /* Setup /dev/lxd */ d.devlxd, err = createAndBindDevLxd() if err != nil { return err } if err := setupSharedMounts(); err != nil { return err } var tlsConfig *tls.Config if !d.IsMock { err = d.SetupStorageDriver() if err != nil { return fmt.Errorf("Failed to setup storage: %s", err) } /* Restart containers */ go func() { containersRestart(d) }() /* Start the scheduler */ go deviceTaskScheduler(d) /* Setup the TLS authentication */ certf, keyf, err := readMyCert() if err != nil { return err } d.certf = certf d.keyf = keyf readSavedClientCAList(d) tlsConfig, err = shared.GetTLSConfig(d.certf, d.keyf) if err != nil { return err } } /* Setup the web server */ d.mux = mux.NewRouter() d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") SyncResponse(true, []string{"/1.0"}).Render(w) }) for _, c := range api10 { d.createCmd("1.0", c) } for _, c := range apiInternal { d.createCmd("internal", c) } d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { shared.Log.Debug("Sending top level 404", log.Ctx{"url": r.URL}) w.Header().Set("Content-Type", "application/json") NotFound.Render(w) }) listeners, err := activation.Listeners(false) if err != nil { return err } var sockets []Socket if len(listeners) > 0 { shared.Log.Info("LXD is socket activated") for _, listener := range listeners { if shared.PathExists(listener.Addr().String()) { sockets = append(sockets, Socket{Socket: listener, CloseOnExit: false}) } else { tlsListener := tls.NewListener(listener, tlsConfig) sockets = append(sockets, Socket{Socket: tlsListener, CloseOnExit: false}) } } } else { shared.Log.Info("LXD isn't socket activated") localSocketPath := shared.VarPath("unix.socket") // If the socket exists, let's try to connect to it and see if there's // a lxd running. if shared.PathExists(localSocketPath) { c, err := lxd.NewClient(&lxd.DefaultConfig, "local") if err != nil { return err } err = c.Finger() if err != nil { shared.Log.Debug("Detected stale unix socket, deleting") // Connecting failed, so let's delete the socket and // listen on it ourselves. err = os.Remove(localSocketPath) if err != nil { return err } } else { return fmt.Errorf("LXD is already running.") } } unixAddr, err := net.ResolveUnixAddr("unix", localSocketPath) if err != nil { return fmt.Errorf("cannot resolve unix socket address: %v", err) } unixl, err := net.ListenUnix("unix", unixAddr) if err != nil { return fmt.Errorf("cannot listen on unix socket: %v", err) } if err := os.Chmod(localSocketPath, 0660); err != nil { return err } var gid int if d.group != "" { gid, err = shared.GroupId(d.group) if err != nil { return err } } else { gid = os.Getgid() } if err := os.Chown(localSocketPath, os.Getuid(), gid); err != nil { return err } sockets = append(sockets, Socket{Socket: unixl, CloseOnExit: true}) } listenAddr, err := d.ConfigValueGet("core.https_address") if err != nil { return err } if listenAddr != "" { _, _, err := net.SplitHostPort(listenAddr) if err != nil { listenAddr = fmt.Sprintf("%s:%s", listenAddr, shared.DefaultPort) } tcpl, err := tls.Listen("tcp", listenAddr, tlsConfig) if err != nil { shared.Log.Error("cannot listen on https socket, skipping...", log.Ctx{"err": err}) } else { sockets = append(sockets, Socket{Socket: tcpl, CloseOnExit: true}) } } if !d.IsMock { d.Sockets = sockets } else { d.Sockets = []Socket{} } d.tomb.Go(func() error { shared.Log.Info("REST API daemon:") for _, socket := range d.Sockets { shared.Log.Info(" - binding socket", log.Ctx{"socket": socket.Socket.Addr()}) current_socket := socket d.tomb.Go(func() error { return http.Serve(current_socket.Socket, d.mux) }) } d.tomb.Go(func() error { server := devLxdServer(d) return server.Serve(d.devlxd) }) return nil }) return nil }
func (d *Daemon) Init() error { /* Initialize some variables */ d.imagesDownloading = map[string]chan bool{} d.readyChan = make(chan bool) d.shutdownChan = make(chan bool) /* Set the executable path */ absPath, err := os.Readlink("/proc/self/exe") if err != nil { return err } d.execPath = absPath /* Set the LVM environment */ err = os.Setenv("LVM_SUPPRESS_FD_WARNINGS", "1") if err != nil { return err } /* Setup logging if that wasn't done before */ if shared.Log == nil { shared.Log, err = logging.GetLogger("", "", true, true, nil) if err != nil { return err } } /* Print welcome message */ if d.MockMode { shared.Log.Info("LXD is starting in mock mode", log.Ctx{"path": shared.VarPath("")}) } else if d.SetupMode { shared.Log.Info("LXD is starting in setup mode", log.Ctx{"path": shared.VarPath("")}) } else { shared.Log.Info("LXD is starting in normal mode", log.Ctx{"path": shared.VarPath("")}) } /* Detect user namespaces */ runningInUserns = shared.RunningInUserNS() /* Detect AppArmor support */ if aaAvailable && os.Getenv("LXD_SECURITY_APPARMOR") == "false" { aaAvailable = false aaAdmin = false shared.Log.Warn("AppArmor support has been manually disabled") } if aaAvailable && !shared.IsDir("/sys/kernel/security/apparmor") { aaAvailable = false aaAdmin = false shared.Log.Warn("AppArmor support has been disabled because of lack of kernel support") } _, err = exec.LookPath("apparmor_parser") if aaAvailable && err != nil { aaAvailable = false aaAdmin = false shared.Log.Warn("AppArmor support has been disabled because 'apparmor_parser' couldn't be found") } /* Detect AppArmor admin support */ if aaAdmin && !haveMacAdmin() { aaAdmin = false shared.Log.Warn("Per-container AppArmor profiles are disabled because the mac_admin capability is missing.") } if aaAdmin && runningInUserns { aaAdmin = false shared.Log.Warn("Per-container AppArmor profiles are disabled because LXD is running in an unprivileged container.") } /* Detect AppArmor confinment */ if !aaConfined { profile := aaProfile() if profile != "unconfined" && profile != "" { aaConfined = true shared.Log.Warn("Per-container AppArmor profiles are disabled because LXD is already protected by AppArmor.") } } /* Detect CGroup support */ cgBlkioController = shared.PathExists("/sys/fs/cgroup/blkio/") if !cgBlkioController { shared.Log.Warn("Couldn't find the CGroup blkio controller, I/O limits will be ignored.") } cgCpuController = shared.PathExists("/sys/fs/cgroup/cpu/") if !cgCpuController { shared.Log.Warn("Couldn't find the CGroup CPU controller, CPU time limits will be ignored.") } cgCpusetController = shared.PathExists("/sys/fs/cgroup/cpuset/") if !cgCpusetController { shared.Log.Warn("Couldn't find the CGroup CPUset controller, CPU pinning will be ignored.") } cgDevicesController = shared.PathExists("/sys/fs/cgroup/devices/") if !cgDevicesController { shared.Log.Warn("Couldn't find the CGroup devices controller, device access control won't work.") } cgMemoryController = shared.PathExists("/sys/fs/cgroup/memory/") if !cgMemoryController { shared.Log.Warn("Couldn't find the CGroup memory controller, memory limits will be ignored.") } cgNetPrioController = shared.PathExists("/sys/fs/cgroup/net_prio/") if !cgNetPrioController { shared.Log.Warn("Couldn't find the CGroup network class controller, network limits will be ignored.") } cgPidsController = shared.PathExists("/sys/fs/cgroup/pids/") if !cgPidsController { shared.Log.Warn("Couldn't find the CGroup pids controller, process limits will be ignored.") } cgSwapAccounting = shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes") if !cgSwapAccounting { shared.Log.Warn("CGroup memory swap accounting is disabled, swap limits will be ignored.") } /* Get the list of supported architectures */ var architectures = []int{} architectureName, err := shared.ArchitectureGetLocal() if err != nil { return err } architecture, err := shared.ArchitectureId(architectureName) if err != nil { return err } architectures = append(architectures, architecture) personalities, err := shared.ArchitecturePersonalities(architecture) if err != nil { return err } for _, personality := range personalities { architectures = append(architectures, personality) } d.architectures = architectures /* Set container path */ d.lxcpath = shared.VarPath("containers") /* Make sure all our directories are available */ if err := os.MkdirAll(shared.VarPath("containers"), 0711); err != nil { return err } if err := os.MkdirAll(shared.VarPath("devices"), 0711); err != nil { return err } if err := os.MkdirAll(shared.VarPath("devlxd"), 0755); err != nil { return err } if err := os.MkdirAll(shared.VarPath("images"), 0700); err != nil { return err } if err := os.MkdirAll(shared.LogPath(), 0700); err != nil { return err } if err := os.MkdirAll(shared.VarPath("security"), 0700); err != nil { return err } if err := os.MkdirAll(shared.VarPath("shmounts"), 0711); err != nil { return err } if err := os.MkdirAll(shared.VarPath("snapshots"), 0700); err != nil { return err } /* Detect the filesystem */ d.BackingFs, err = filesystemDetect(d.lxcpath) if err != nil { shared.Log.Error("Error detecting backing fs", log.Ctx{"err": err}) } /* Read the uid/gid allocation */ d.IdmapSet, err = shared.DefaultIdmapSet() if err != nil { shared.Log.Warn("Error reading idmap", log.Ctx{"err": err.Error()}) shared.Log.Warn("Only privileged containers will be able to run") } else { shared.Log.Info("Default uid/gid map:") for _, lxcmap := range d.IdmapSet.ToLxcString() { shared.Log.Info(strings.TrimRight(" - "+lxcmap, "\n")) } } /* Initialize the database */ err = initializeDbObject(d, shared.VarPath("lxd.db")) if err != nil { return err } /* Setup the storage driver */ if !d.MockMode { err = d.SetupStorageDriver() if err != nil { return fmt.Errorf("Failed to setup storage: %s", err) } } /* Load all config values from the database */ _, err = d.ConfigValuesGet() if err != nil { return err } /* set the initial proxy function based on config values in the DB */ d.updateProxy() /* Setup /dev/lxd */ d.devlxd, err = createAndBindDevLxd() if err != nil { return err } if err := setupSharedMounts(); err != nil { return err } if !d.MockMode { /* Start the scheduler */ go deviceEventListener(d) /* Setup the TLS authentication */ certf, keyf, err := readMyCert() if err != nil { return err } cert, err := tls.LoadX509KeyPair(certf, keyf) if err != nil { return err } tlsConfig := &tls.Config{ InsecureSkipVerify: true, ClientAuth: tls.RequestClientCert, Certificates: []tls.Certificate{cert}, MinVersion: tls.VersionTLS12, MaxVersion: tls.VersionTLS12, CipherSuites: []uint16{ tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, PreferServerCipherSuites: true, } tlsConfig.BuildNameToCertificate() d.tlsConfig = tlsConfig readSavedClientCAList(d) } /* Setup the web server */ d.mux = mux.NewRouter() d.mux.StrictSlash(false) d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") SyncResponse(true, []string{"/1.0"}).Render(w) }) for _, c := range api10 { d.createCmd("1.0", c) } for _, c := range apiInternal { d.createCmd("internal", c) } d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { shared.Log.Debug("Sending top level 404", log.Ctx{"url": r.URL}) w.Header().Set("Content-Type", "application/json") NotFound.Render(w) }) listeners, err := activation.Listeners(false) if err != nil { return err } if len(listeners) > 0 { shared.Log.Info("LXD is socket activated") for _, listener := range listeners { if shared.PathExists(listener.Addr().String()) { d.UnixSocket = &Socket{Socket: listener, CloseOnExit: false} } else { tlsListener := tls.NewListener(listener, d.tlsConfig) d.TCPSocket = &Socket{Socket: tlsListener, CloseOnExit: false} } } } else { shared.Log.Info("LXD isn't socket activated") localSocketPath := shared.VarPath("unix.socket") // If the socket exists, let's try to connect to it and see if there's // a lxd running. if shared.PathExists(localSocketPath) { _, err := lxd.NewClient(&lxd.DefaultConfig, "local") if err != nil { shared.Log.Debug("Detected stale unix socket, deleting") // Connecting failed, so let's delete the socket and // listen on it ourselves. err = os.Remove(localSocketPath) if err != nil { return err } } else { return fmt.Errorf("LXD is already running.") } } unixAddr, err := net.ResolveUnixAddr("unix", localSocketPath) if err != nil { return fmt.Errorf("cannot resolve unix socket address: %v", err) } unixl, err := net.ListenUnix("unix", unixAddr) if err != nil { return fmt.Errorf("cannot listen on unix socket: %v", err) } if err := os.Chmod(localSocketPath, 0660); err != nil { return err } var gid int if d.group != "" { gid, err = shared.GroupId(d.group) if err != nil { return err } } else { gid = os.Getgid() } if err := os.Chown(localSocketPath, os.Getuid(), gid); err != nil { return err } d.UnixSocket = &Socket{Socket: unixl, CloseOnExit: true} } listenAddr, err := d.ConfigValueGet("core.https_address") if err != nil { return err } if listenAddr != "" { _, _, err := net.SplitHostPort(listenAddr) if err != nil { listenAddr = fmt.Sprintf("%s:%s", listenAddr, shared.DefaultPort) } tcpl, err := tls.Listen("tcp", listenAddr, d.tlsConfig) if err != nil { shared.Log.Error("cannot listen on https socket, skipping...", log.Ctx{"err": err}) } else { if d.TCPSocket != nil { shared.Log.Info("Replacing systemd TCP socket by configure one") d.TCPSocket.Socket.Close() } d.TCPSocket = &Socket{Socket: tcpl, CloseOnExit: true} } } d.tomb.Go(func() error { shared.Log.Info("REST API daemon:") if d.UnixSocket != nil { shared.Log.Info(" - binding Unix socket", log.Ctx{"socket": d.UnixSocket.Socket.Addr()}) d.tomb.Go(func() error { return http.Serve(d.UnixSocket.Socket, &lxdHttpServer{d.mux, d}) }) } if d.TCPSocket != nil { shared.Log.Info(" - binding TCP socket", log.Ctx{"socket": d.TCPSocket.Socket.Addr()}) d.tomb.Go(func() error { return http.Serve(d.TCPSocket.Socket, &lxdHttpServer{d.mux, d}) }) } d.tomb.Go(func() error { server := devLxdServer(d) return server.Serve(d.devlxd) }) return nil }) if !d.MockMode && !d.SetupMode { err := d.Ready() if err != nil { return err } } return nil }
// StartDaemon starts the shared daemon with the provided configuration. func StartDaemon() (*Daemon, error) { d := &Daemon{} /* Setup logging */ if shared.Log == nil { shared.SetLogger("", "", true, true) } shared.Log.Info("LXD is starting.") /* Get the list of supported architectures */ var architectures = []int{} uname := syscall.Utsname{} if err := syscall.Uname(&uname); err != nil { return nil, err } architectureName := "" for _, c := range uname.Machine { if c == 0 { break } architectureName += string(byte(c)) } architecture, err := shared.ArchitectureId(architectureName) if err != nil { return nil, err } architectures = append(architectures, architecture) personalities, err := shared.ArchitecturePersonalities(architecture) if err != nil { return nil, err } for _, personality := range personalities { architectures = append(architectures, personality) } d.architectures = architectures /* Create required paths */ d.lxcpath = shared.VarPath("containers") err = os.MkdirAll(d.lxcpath, 0755) if err != nil { return nil, err } // Create default directories if err := os.MkdirAll(shared.VarPath("images"), 0700); err != nil { return nil, err } if err := os.MkdirAll(shared.VarPath("snapshots"), 0700); err != nil { return nil, err } if err := os.MkdirAll(shared.VarPath("devlxd"), 0755); err != nil { return nil, err } /* Detect the filesystem */ d.BackingFs, err = filesystemDetect(d.lxcpath) if err != nil { shared.Log.Error("Error detecting backing fs", log.Ctx{"err": err}) } /* Read the uid/gid allocation */ d.IdmapSet, err = shared.DefaultIdmapSet() if err != nil { shared.Log.Warn("error reading idmap", log.Ctx{"err": err.Error()}) shared.Log.Warn("operations requiring idmap will not be available") } else { shared.Log.Info("Default uid/gid map:") for _, lxcmap := range d.IdmapSet.ToLxcString() { shared.Log.Info(strings.TrimRight(" - "+lxcmap, "\n")) } } /* Initialize the database */ err = initDb(d) if err != nil { return nil, err } /* Setup the TLS authentication */ certf, keyf, err := readMyCert() if err != nil { return nil, err } d.certf = certf d.keyf = keyf readSavedClientCAList(d) tlsConfig, err := shared.GetTLSConfig(d.certf, d.keyf) if err != nil { return nil, err } /* Setup /dev/lxd */ d.devlxd, err = createAndBindDevLxd() if err != nil { return nil, err } if err := setupSharedMounts(); err != nil { return nil, err } /* Restart containers */ containersRestart(d) containersWatch(d) err = d.SetupStorageDriver() if err != nil { return nil, fmt.Errorf("Failed to setup storage: %s", err) } /* Setup the web server */ d.mux = mux.NewRouter() d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") SyncResponse(true, []string{"/1.0"}).Render(w) }) for _, c := range api10 { d.createCmd("1.0", c) } d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { shared.Log.Debug("sending top level 404", log.Ctx{"url": r.URL}) w.Header().Set("Content-Type", "application/json") NotFound.Render(w) }) listeners, err := activation.Listeners(false) if err != nil { return nil, err } var sockets []net.Listener if len(listeners) > 0 { shared.Log.Info("LXD is socket activated.") for _, listener := range listeners { if shared.PathExists(listener.Addr().String()) { sockets = append(sockets, listener) } else { tlsListener := tls.NewListener(listener, tlsConfig) sockets = append(sockets, tlsListener) } } } else { shared.Log.Info("LXD isn't socket activated.") localSocketPath := shared.VarPath("unix.socket") // If the socket exists, let's try to connect to it and see if there's // a lxd running. if shared.PathExists(localSocketPath) { c := &lxd.Config{Remotes: map[string]lxd.RemoteConfig{}} _, err := lxd.NewClient(c, "") if err != nil { shared.Log.Debug("Detected old but dead unix socket, deleting it...") // Connecting failed, so let's delete the socket and // listen on it ourselves. err = os.Remove(localSocketPath) if err != nil { return nil, err } } } unixAddr, err := net.ResolveUnixAddr("unix", localSocketPath) if err != nil { return nil, fmt.Errorf("cannot resolve unix socket address: %v", err) } unixl, err := net.ListenUnix("unix", unixAddr) if err != nil { return nil, fmt.Errorf("cannot listen on unix socket: %v", err) } if err := os.Chmod(localSocketPath, 0660); err != nil { return nil, err } gid, err := shared.GroupId(*group) if err != nil { return nil, err } if err := os.Chown(localSocketPath, os.Getuid(), gid); err != nil { return nil, err } sockets = append(sockets, unixl) } listenAddr, err := d.ConfigValueGet("core.https_address") if err != nil { return nil, err } if listenAddr != "" { _, _, err := net.SplitHostPort(listenAddr) if err != nil { listenAddr = fmt.Sprintf("%s:%s", listenAddr, shared.DefaultPort) } tcpl, err := tls.Listen("tcp", listenAddr, tlsConfig) if err != nil { return nil, fmt.Errorf("cannot listen on https socket: %v", err) } sockets = append(sockets, tcpl) } d.Sockets = sockets d.pruneChan = make(chan bool) go func() { for { expiryStr, err := dbGetImageExpiry(d) var expiry int if err != nil { expiry = 10 } else { expiry, err = strconv.Atoi(expiryStr) if err != nil { expiry = 10 } if expiry <= 0 { expiry = 1 } } timer := time.NewTimer(time.Duration(expiry) * 24 * time.Hour) timeChan := timer.C select { case <-timeChan: d.pruneExpiredImages() case <-d.pruneChan: d.pruneExpiredImages() timer.Stop() } } }() d.tomb.Go(func() error { for _, socket := range d.Sockets { shared.Log.Info(" - binding socket", log.Ctx{"socket": socket.Addr()}) current_socket := socket d.tomb.Go(func() error { return http.Serve(current_socket, d.mux) }) } d.tomb.Go(func() error { server := devLxdServer(d) return server.Serve(d.devlxd) }) return nil }) return d, nil }
func (d *Daemon) Init() error { /* Setup logging */ if shared.Log == nil { shared.SetLogger("", "", true, true) } if !d.IsMock { shared.Log.Info("LXD is starting", log.Ctx{"path": shared.VarPath("")}) } else { shared.Log.Info("Mock LXD is starting", log.Ctx{"path": shared.VarPath("")}) } /* Detect user namespaces */ runningInUserns = shared.RunningInUserNS() /* Detect apparmor support */ if aaEnabled && os.Getenv("LXD_SECURITY_APPARMOR") == "false" { aaEnabled = false shared.Log.Warn("Per-container AppArmor profiles have been manually disabled") } if aaEnabled && !shared.IsDir("/sys/kernel/security/apparmor") { aaEnabled = false shared.Log.Warn("Per-container AppArmor profiles disabled because of lack of kernel support") } if aaEnabled && !haveMacAdmin() { shared.Log.Warn("Per-container AppArmor profiles are disabled because mac_admin capability is missing.") aaEnabled = false } _, err := exec.LookPath("apparmor_parser") if aaEnabled && err != nil { aaEnabled = false shared.Log.Warn("Per-container AppArmor profiles disabled because 'apparmor_parser' couldn't be found") } if aaEnabled && runningInUserns { aaEnabled = false shared.Log.Warn("Per-container AppArmor profiles disabled because LXD is running inside a user namespace") } /* Get the list of supported architectures */ var architectures = []int{} uname := syscall.Utsname{} if err := syscall.Uname(&uname); err != nil { return err } architectureName := "" for _, c := range uname.Machine { if c == 0 { break } architectureName += string(byte(c)) } architecture, err := shared.ArchitectureId(architectureName) if err != nil { return err } architectures = append(architectures, architecture) personalities, err := shared.ArchitecturePersonalities(architecture) if err != nil { return err } for _, personality := range personalities { architectures = append(architectures, personality) } d.architectures = architectures /* Create required paths */ d.lxcpath = shared.VarPath("containers") err = os.MkdirAll(d.lxcpath, 0755) if err != nil { return err } // Create default directories if err := os.MkdirAll(shared.VarPath("images"), 0700); err != nil { return err } if err := os.MkdirAll(shared.VarPath("snapshots"), 0700); err != nil { return err } if err := os.MkdirAll(shared.VarPath("devlxd"), 0755); err != nil { return err } /* Detect the filesystem */ d.BackingFs, err = filesystemDetect(d.lxcpath) if err != nil { shared.Log.Error("Error detecting backing fs", log.Ctx{"err": err}) } /* Read the uid/gid allocation */ d.IdmapSet, err = shared.DefaultIdmapSet() if err != nil { shared.Log.Warn("Error reading idmap", log.Ctx{"err": err.Error()}) shared.Log.Warn("Only privileged containers will be able to run") } else { shared.Log.Info("Default uid/gid map:") for _, lxcmap := range d.IdmapSet.ToLxcString() { shared.Log.Info(strings.TrimRight(" - "+lxcmap, "\n")) } } /* Initialize the database */ err = initializeDbObject(d, shared.VarPath("lxd.db")) if err != nil { return err } /* Prune images */ d.pruneChan = make(chan bool) go func() { for { expiryStr, err := dbImageExpiryGet(d.db) var expiry int if err != nil { expiry = 10 } else { expiry, err = strconv.Atoi(expiryStr) if err != nil { expiry = 10 } if expiry <= 0 { expiry = 1 } } timer := time.NewTimer(time.Duration(expiry) * 24 * time.Hour) timeChan := timer.C select { case <-timeChan: d.pruneExpiredImages() case <-d.pruneChan: d.pruneExpiredImages() timer.Stop() } } }() /* Setup /dev/lxd */ d.devlxd, err = createAndBindDevLxd() if err != nil { return err } if err := setupSharedMounts(); err != nil { return err } var tlsConfig *tls.Config if !d.IsMock { err = d.SetupStorageDriver() if err != nil { return fmt.Errorf("Failed to setup storage: %s", err) } /* Restart containers */ containersRestart(d) containersWatch(d) /* Setup the TLS authentication */ certf, keyf, err := readMyCert() if err != nil { return err } d.certf = certf d.keyf = keyf readSavedClientCAList(d) tlsConfig, err = shared.GetTLSConfig(d.certf, d.keyf) if err != nil { return err } } /* Setup the web server */ d.mux = mux.NewRouter() d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") SyncResponse(true, []string{"/1.0"}).Render(w) }) for _, c := range api10 { d.createCmd("1.0", c) } d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { shared.Log.Debug("Sending top level 404", log.Ctx{"url": r.URL}) w.Header().Set("Content-Type", "application/json") NotFound.Render(w) }) listeners, err := activation.Listeners(false) if err != nil { return err } var sockets []Socket if len(listeners) > 0 { shared.Log.Info("LXD is socket activated") for _, listener := range listeners { if shared.PathExists(listener.Addr().String()) { sockets = append(sockets, Socket{Socket: listener, CloseOnExit: false}) } else { tlsListener := tls.NewListener(listener, tlsConfig) sockets = append(sockets, Socket{Socket: tlsListener, CloseOnExit: false}) } } } else { shared.Log.Info("LXD isn't socket activated") localSocketPath := shared.VarPath("unix.socket") // If the socket exists, let's try to connect to it and see if there's // a lxd running. if shared.PathExists(localSocketPath) { c := &lxd.Config{Remotes: map[string]lxd.RemoteConfig{}} _, err := lxd.NewClient(c, "") if err != nil { shared.Log.Debug("Detected stale unix socket, deleting") // Connecting failed, so let's delete the socket and // listen on it ourselves. err = os.Remove(localSocketPath) if err != nil { return err } } } unixAddr, err := net.ResolveUnixAddr("unix", localSocketPath) if err != nil { return fmt.Errorf("cannot resolve unix socket address: %v", err) } unixl, err := net.ListenUnix("unix", unixAddr) if err != nil { return fmt.Errorf("cannot listen on unix socket: %v", err) } if err := os.Chmod(localSocketPath, 0660); err != nil { return err } gid, err := shared.GroupId(*group) if err != nil { return err } if err := os.Chown(localSocketPath, os.Getuid(), gid); err != nil { return err } sockets = append(sockets, Socket{Socket: unixl, CloseOnExit: true}) } listenAddr, err := d.ConfigValueGet("core.https_address") if err != nil { return err } if listenAddr != "" { _, _, err := net.SplitHostPort(listenAddr) if err != nil { listenAddr = fmt.Sprintf("%s:%s", listenAddr, shared.DefaultPort) } tcpl, err := tls.Listen("tcp", listenAddr, tlsConfig) if err != nil { shared.Log.Error("cannot listen on https socket, skipping...", log.Ctx{"err": err}) } else { sockets = append(sockets, Socket{Socket: tcpl, CloseOnExit: true}) } } if !d.IsMock { d.Sockets = sockets } else { d.Sockets = []Socket{} } d.tomb.Go(func() error { shared.Log.Info("REST API daemon:") for _, socket := range d.Sockets { shared.Log.Info(" - binding socket", log.Ctx{"socket": socket.Socket.Addr()}) current_socket := socket d.tomb.Go(func() error { return http.Serve(current_socket.Socket, d.mux) }) } d.tomb.Go(func() error { server := devLxdServer(d) return server.Serve(d.devlxd) }) return nil }) return nil }