func Test_config_value_set_empty_removes_val(t *testing.T) { d := &Daemon{} err := shared.SetLogger("", "", true, true) if err != nil { t.Error("logging") } err = initializeDbObject(d, ":memory:") defer d.db.Close() if err != nil { t.Error("failed to init db") } if err = d.ConfigValueSet("core.lvm_vg_name", "foo"); err != nil { t.Error("couldn't set value", err) } val, err := d.ConfigValueGet("core.lvm_vg_name") if err != nil { t.Error("Error getting val") } if val != "foo" { t.Error("Expected foo, got ", val) } err = d.ConfigValueSet("core.lvm_vg_name", "") if err != nil { t.Error("error setting to ''") } val, err = d.ConfigValueGet("core.lvm_vg_name") if err != nil { t.Error("Error getting val") } if val != "" { t.Error("Expected '', got ", val) } valMap, err := d.ConfigValuesGet() if err != nil { t.Error("Error getting val") } if key, present := valMap["core.lvm_vg_name"]; present { t.Errorf("un-set key should not be in values map, it is '%v'", key) } }
// This Helper will initialize a test in-memory DB. func createTestDb(t *testing.T) (db *sql.DB) { // Setup logging if main() hasn't been called/when testing if shared.Log == nil { shared.SetLogger("", "", true, true) } var err error db, err = initializeDbObject(":memory:") if err != nil { t.Fatal(err) } _, err = db.Exec(DB_FIXTURES) if err != nil { t.Fatal(err) } return // db is a named output param }
func run() error { if len(os.Args) > 1 { switch os.Args[1] { case "forkstart": return startContainer(os.Args[1:]) case "forkmigrate": return migration.MigrateContainer(os.Args[1:]) /* case "forkputfile" and "forkgetfile" handled specially in copyfile.go */ } } gnuflag.Usage = func() { fmt.Printf("Usage: lxd [options]\n\nOptions:\n") gnuflag.PrintDefaults() } gnuflag.Parse(true) if *help { // The user asked for help via --help, so we shouldn't print to // stderr. gnuflag.SetOut(os.Stdout) gnuflag.Usage() return nil } if *version { fmt.Println(shared.Version) return nil } // Configure logging syslog := "" if *syslogFlag { syslog = "lxd" } err := shared.SetLogger(syslog, *logfile, *verbose, *debug) if err != nil { fmt.Printf("%s", err) return nil } if gnuflag.NArg() != 0 { gnuflag.Usage() return fmt.Errorf("Unknown arguments") } if *cpuProfile != "" { f, err := os.Create(*cpuProfile) if err != nil { fmt.Printf("Error opening cpu profile file: %s\n", err) return nil } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } if *memProfile != "" { go memProfiler() } neededPrograms := []string{"setfacl", "rsync", "tar", "xz"} for _, p := range neededPrograms { _, err := exec.LookPath(p) if err != nil { return err } } if *printGoroutines > 0 { go func() { for { time.Sleep(time.Duration(*printGoroutines) * time.Second) shared.PrintStack() } }() } d, err := startDaemon() if err != nil { if d != nil && d.db != nil { d.db.Close() } return err } var ret error var wg sync.WaitGroup wg.Add(1) go func() { ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGPWR) sig := <-ch shared.Log.Info( fmt.Sprintf("Received '%s signal', shutting down containers.", sig)) ret = d.Stop() containersShutdown(d) wg.Done() }() go func() { ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGINT) signal.Notify(ch, syscall.SIGQUIT) signal.Notify(ch, syscall.SIGTERM) sig := <-ch shared.Log.Info(fmt.Sprintf("Received '%s signal', exiting.\n", sig)) ret = d.Stop() wg.Done() }() wg.Wait() return ret }
func run() error { gettext.BindTextdomain("lxd", "", nil) gettext.Textdomain("lxd") verbose := gnuflag.Bool("verbose", false, gettext.Gettext("Enables verbose mode.")) debug := gnuflag.Bool("debug", false, gettext.Gettext("Enables debug mode.")) forceLocal := gnuflag.Bool("force-local", false, gettext.Gettext("Enables debug mode.")) gnuflag.StringVar(&lxd.ConfigDir, "config", lxd.ConfigDir, gettext.Gettext("Alternate config directory.")) if len(os.Args) >= 3 && os.Args[1] == "config" && os.Args[2] == "profile" { fmt.Fprintf(os.Stderr, "`lxc config profile` is deprecated, please use `lxc profile`\n") os.Args = append(os.Args[:1], os.Args[2:]...) } if len(os.Args) >= 2 && (os.Args[1] == "-h" || os.Args[1] == "--help") { os.Args[1] = "help" } if len(os.Args) >= 2 && (os.Args[1] == "--all") { os.Args[1] = "help" os.Args = append(os.Args, "--all") } if len(os.Args) == 2 && os.Args[1] == "--version" { os.Args[1] = "version" } if len(os.Args) < 2 { commands["help"].run(nil, nil) os.Exit(1) } name := os.Args[1] cmd, ok := commands[name] if !ok { fmt.Fprintf(os.Stderr, gettext.Gettext("error: unknown command: %s\n"), name) commands["help"].run(nil, nil) os.Exit(1) } cmd.flags() gnuflag.Usage = func() { fmt.Fprintf(os.Stderr, gettext.Gettext("Usage: %s\n\nOptions:\n\n"), strings.TrimSpace(cmd.usage())) gnuflag.PrintDefaults() } os.Args = os.Args[1:] gnuflag.Parse(true) shared.SetLogger("", "", *verbose, *debug) var config *lxd.Config var err error if *forceLocal { config = &lxd.DefaultConfig } else { config, err = lxd.LoadConfig() if err != nil { return err } } certf := lxd.ConfigPath("client.crt") keyf := lxd.ConfigPath("client.key") if !*forceLocal && os.Args[0] != "help" && os.Args[0] != "version" && (!shared.PathExists(certf) || !shared.PathExists(keyf)) { fmt.Fprintf(os.Stderr, gettext.Gettext("Generating a client certificate. This may take a minute...\n")) err = shared.FindOrGenCert(certf, keyf) if err != nil { return err } fmt.Fprintf(os.Stderr, gettext.Gettext("If this is your first run, you will need to import images using the 'lxd-images' script.\n")) fmt.Fprintf(os.Stderr, gettext.Gettext("For example: 'lxd-images import ubuntu --alias ubuntu'.\n")) } err = cmd.run(config, gnuflag.Args()) if err == errArgs { fmt.Fprintf(os.Stderr, gettext.Gettext("error: %v\n%s"), err, cmd.usage()) os.Exit(1) } return err }
func run() error { gnuflag.Usage = func() { fmt.Printf("Usage: lxd [command] [options]\n\nOptions:\n") gnuflag.PrintDefaults() fmt.Printf("\nCommands:\n") fmt.Printf(" shutdown\n") fmt.Printf(" Perform a clean shutdown of LXD and all running containers\n") fmt.Printf(" activateifneeded\n") fmt.Printf(" Check if LXD should be started (at boot) and if so, spawn it through socket activation\n") fmt.Printf("\nInternal commands (don't call directly):\n") fmt.Printf(" forkgetfile\n") fmt.Printf(" Grab a file from a running container\n") fmt.Printf(" forkputfile\n") fmt.Printf(" Pushes a file to a running container\n") fmt.Printf(" forkstart\n") fmt.Printf(" Start a container\n") fmt.Printf(" forkmigrate\n") fmt.Printf(" Restore a container after migration\n") } gnuflag.Parse(true) if *help { // The user asked for help via --help, so we shouldn't print to // stderr. gnuflag.SetOut(os.Stdout) gnuflag.Usage() return nil } if *version { fmt.Println(shared.Version) return nil } if len(shared.VarPath("unix.sock")) > 107 { return fmt.Errorf("LXD_DIR is too long, must be < %d", 107-len("unix.sock")) } // Configure logging syslog := "" if *syslogFlag { syslog = "lxd" } err := shared.SetLogger(syslog, *logfile, *verbose, *debug) if err != nil { fmt.Printf("%s", err) return nil } // Process sub-commands if len(os.Args) > 1 { // "forkputfile" and "forkgetfile" are handled specially in copyfile.go switch os.Args[1] { case "forkstart": return startContainer(os.Args[1:]) case "forkmigrate": return MigrateContainer(os.Args[1:]) case "shutdown": return cleanShutdown() case "activateifneeded": return activateIfNeeded() } } if gnuflag.NArg() != 0 { gnuflag.Usage() return fmt.Errorf("Unknown arguments") } if *cpuProfile != "" { f, err := os.Create(*cpuProfile) if err != nil { fmt.Printf("Error opening cpu profile file: %s\n", err) return nil } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } if *memProfile != "" { go memProfiler() } neededPrograms := []string{"setfacl", "rsync", "tar", "xz"} for _, p := range neededPrograms { _, err := exec.LookPath(p) if err != nil { return err } } if *printGoroutines > 0 { go func() { for { time.Sleep(time.Duration(*printGoroutines) * time.Second) shared.PrintStack() } }() } d, err := startDaemon() if err != nil { if d != nil && d.db != nil { d.db.Close() } return err } var ret error var wg sync.WaitGroup wg.Add(1) go func() { ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGPWR) sig := <-ch shared.Log.Info( fmt.Sprintf("Received '%s signal', shutting down containers.", sig)) containersShutdown(d) ret = d.Stop() wg.Done() }() go func() { ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGINT) signal.Notify(ch, syscall.SIGQUIT) signal.Notify(ch, syscall.SIGTERM) sig := <-ch shared.Log.Info(fmt.Sprintf("Received '%s signal', exiting.\n", sig)) ret = d.Stop() wg.Done() }() wg.Wait() return ret }
// 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 }
func run() error { verbose := gnuflag.Bool("verbose", false, i18n.G("Enables verbose mode.")) debug := gnuflag.Bool("debug", false, i18n.G("Enables debug mode.")) forceLocal := gnuflag.Bool("force-local", false, i18n.G("Force using the local unix socket.")) configDir := os.Getenv("LXD_CONF") if configDir != "" { lxd.ConfigDir = configDir } if len(os.Args) >= 3 && os.Args[1] == "config" && os.Args[2] == "profile" { fmt.Fprintf(os.Stderr, i18n.G("`lxc config profile` is deprecated, please use `lxc profile`")+"\n") os.Args = append(os.Args[:1], os.Args[2:]...) } if len(os.Args) >= 2 && (os.Args[1] == "-h" || os.Args[1] == "--help") { os.Args[1] = "help" } if len(os.Args) >= 2 && (os.Args[1] == "--all") { os.Args[1] = "help" os.Args = append(os.Args, "--all") } if len(os.Args) == 2 && os.Args[1] == "--version" { os.Args[1] = "version" } if len(os.Args) < 2 { commands["help"].run(nil, nil) os.Exit(1) } var config *lxd.Config var err error if *forceLocal { config = &lxd.DefaultConfig } else { config, err = lxd.LoadConfig() if err != nil { return err } // One time migration from old config if config.DefaultRemote == "" { _, ok := config.Remotes["local"] if !ok { config.Remotes["local"] = lxd.LocalRemote } config.DefaultRemote = "local" lxd.SaveConfig(config) } } // This is quite impolite, but it seems gnuflag needs us to shift our // own exename out of the arguments before parsing them. However, this // is useful for execIfAlias, which wants to know exactly the command // line we received, and in some cases is called before this shift, and // in others after. So, let's save the original args. origArgs := os.Args name := os.Args[1] cmd, ok := commands[name] if !ok { execIfAliases(config, origArgs) fmt.Fprintf(os.Stderr, i18n.G("error: unknown command: %s")+"\n", name) commands["help"].run(nil, nil) os.Exit(1) } cmd.flags() gnuflag.Usage = func() { fmt.Fprintf(os.Stderr, i18n.G("Usage: %s")+"\n\n"+i18n.G("Options:")+"\n\n", strings.TrimSpace(cmd.usage())) gnuflag.PrintDefaults() } os.Args = os.Args[1:] gnuflag.Parse(true) shared.SetLogger("", "", *verbose, *debug, nil) certf := lxd.ConfigPath("client.crt") keyf := lxd.ConfigPath("client.key") if !*forceLocal && os.Args[0] != "help" && os.Args[0] != "version" && (!shared.PathExists(certf) || !shared.PathExists(keyf)) { fmt.Fprintf(os.Stderr, i18n.G("Generating a client certificate. This may take a minute...")+"\n") err = shared.FindOrGenCert(certf, keyf) if err != nil { return err } fmt.Fprintf(os.Stderr, i18n.G("If this is your first run, you will need to import images using the 'lxd-images' script.")+"\n") fmt.Fprintf(os.Stderr, i18n.G("For example: 'lxd-images import ubuntu --alias ubuntu'.")+"\n") } err = cmd.run(config, gnuflag.Args()) if err == errArgs { /* If we got an error about invalid arguments, let's try to * expand this as an alias */ execIfAliases(config, origArgs) fmt.Fprintf(os.Stderr, i18n.G("error: %v")+"\n%s\n", err, cmd.usage()) os.Exit(1) } return err }
func run() error { // Our massive custom usage gnuflag.Usage = func() { fmt.Printf("Usage: lxd [command] [options]\n") fmt.Printf("\nCommands:\n") fmt.Printf(" activateifneeded\n") fmt.Printf(" Check if LXD should be started (at boot) and if so, spawns it through socket activation\n") fmt.Printf(" daemon [--group=lxd] (default command)\n") fmt.Printf(" Start the main LXD daemon\n") fmt.Printf(" init [--auto] [--network-address=IP] [--network-port=8443] [--storage-backend=dir]\n") fmt.Printf(" [--storage-create-device=DEVICE] [--storage-create-loop=SIZE] [--storage-pool=POOL]\n") fmt.Printf(" [--trust-password=]\n") fmt.Printf(" Setup storage and networking\n") fmt.Printf(" shutdown [--timeout=60]\n") fmt.Printf(" Perform a clean shutdown of LXD and all running containers\n") fmt.Printf(" waitready [--timeout=15]\n") fmt.Printf(" Wait until LXD is ready to handle requests\n") fmt.Printf("\n\nCommon options:\n") fmt.Printf(" --debug\n") fmt.Printf(" Enable debug mode\n") fmt.Printf(" --help\n") fmt.Printf(" Print this help message\n") fmt.Printf(" --logfile FILE\n") fmt.Printf(" Logfile to log to (e.g., /var/log/lxd/lxd.log)\n") fmt.Printf(" --syslog\n") fmt.Printf(" Enable syslog logging\n") fmt.Printf(" --verbose\n") fmt.Printf(" Enable verbose mode\n") fmt.Printf(" --version\n") fmt.Printf(" Print LXD's version number and exit\n") fmt.Printf("\nDaemon options:\n") fmt.Printf(" --group GROUP\n") fmt.Printf(" Group which owns the shared socket\n") fmt.Printf("\nDaemon debug options:\n") fmt.Printf(" --cpuprofile FILE\n") fmt.Printf(" Enable cpu profiling into the specified file\n") fmt.Printf(" --memprofile FILE\n") fmt.Printf(" Enable memory profiling into the specified file\n") fmt.Printf(" --print-goroutines-every SECONDS\n") fmt.Printf(" For debugging, print a complete stack trace every n seconds\n") fmt.Printf("\nInit options:\n") fmt.Printf(" --auto\n") fmt.Printf(" Automatic (non-interactive) mode\n") fmt.Printf(" --network-address ADDRESS\n") fmt.Printf(" Address to bind LXD to (default: none)\n") fmt.Printf(" --network-port PORT\n") fmt.Printf(" Port to bind LXD to (default: 8443)\n") fmt.Printf(" --storage-backend NAME\n") fmt.Printf(" Storage backend to use (zfs or dir, default: dir)\n") fmt.Printf(" --storage-create-device DEVICE\n") fmt.Printf(" Setup device based storage using DEVICE\n") fmt.Printf(" --storage-create-loop SIZE\n") fmt.Printf(" Setup loop based storage with SIZE in GB\n") fmt.Printf(" --storage-pool NAME\n") fmt.Printf(" Storage pool to use or create\n") fmt.Printf(" --trust-password PASSWORD\n") fmt.Printf(" Password required to add new clients\n") fmt.Printf("\nShutdown options:\n") fmt.Printf(" --timeout SECONDS\n") fmt.Printf(" How long to wait before failing\n") fmt.Printf("\nWaitready options:\n") fmt.Printf(" --timeout SECONDS\n") fmt.Printf(" How long to wait before failing\n") fmt.Printf("\n\nInternal commands (don't call these directly):\n") fmt.Printf(" forkgetfile\n") fmt.Printf(" Grab a file from a running container\n") fmt.Printf(" forkmigrate\n") fmt.Printf(" Restore a container after migration\n") fmt.Printf(" forkputfile\n") fmt.Printf(" Push a file to a running container\n") fmt.Printf(" forkstart\n") fmt.Printf(" Start a container\n") } // Parse the arguments gnuflag.Parse(true) // Set the global variables debug = *argDebug verbose = *argVerbose if *argHelp { // The user asked for help via --help, so we shouldn't print to // stderr. gnuflag.SetOut(os.Stdout) gnuflag.Usage() return nil } // Deal with --version right here if *argVersion { fmt.Println(shared.Version) return nil } if len(shared.VarPath("unix.sock")) > 107 { return fmt.Errorf("LXD_DIR is too long, must be < %d", 107-len("unix.sock")) } // Configure logging syslog := "" if *argSyslog { syslog = "lxd" } handler := eventsHandler{} err := shared.SetLogger(syslog, *argLogfile, *argVerbose, *argDebug, handler) if err != nil { fmt.Printf("%s", err) return nil } // Process sub-commands if len(os.Args) > 1 { // "forkputfile" and "forkgetfile" are handled specially in copyfile.go switch os.Args[1] { case "activateifneeded": return activateIfNeeded() case "daemon": return daemon() case "forkmigrate": return MigrateContainer(os.Args[1:]) case "forkstart": return startContainer(os.Args[1:]) case "init": return setupLXD() case "shutdown": return cleanShutdown() case "waitready": return waitReady() } } // Fail if some other command is passed if gnuflag.NArg() > 0 { gnuflag.Usage() return fmt.Errorf("Unknown arguments") } return daemon() }
// StartDaemon starts the shared daemon with the provided configuration. func StartDaemon(listenAddr string) (*Daemon, error) { d := &Daemon{} // Setup logging if main() hasn't been called/when testing if shared.Log == nil { shared.SetLogger("", "", true, true) } d.lxcpath = shared.VarPath("lxc") err := os.MkdirAll(shared.VarPath("/"), 0755) if err != nil { return nil, err } err = os.MkdirAll(d.lxcpath, 0755) if err != nil { return nil, err } d.BackingFs, err = shared.GetFilesystem(d.lxcpath) if err != nil { shared.Log.Error("Error detecting backing fs", log.Ctx{"err": err}) } certf, keyf, err := readMyCert() if err != nil { return nil, err } d.certf = certf d.keyf = keyf err = initDb(d) if err != nil { return nil, err } readSavedClientCAList(d) 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) }) 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.Debug("Default uid/gid map:") for _, lxcmap := range d.IdmapSet.ToLxcString() { shared.Log.Debug(strings.TrimRight(" - "+lxcmap, "\n")) } } tlsConfig, err := shared.GetTLSConfig(d.certf, d.keyf) if err != nil { return nil, err } listeners, err := activation.Listeners(false) if err != nil { return nil, err } var localSockets []net.Listener var remoteSockets []net.Listener if len(listeners) > 0 { shared.Log.Debug("LXD is socket activated.") for _, listener := range listeners { if shared.PathExists(listener.Addr().String()) { localSockets = append(localSockets, listener) } else { tlsListener := tls.NewListener(listener, tlsConfig) remoteSockets = append(remoteSockets, tlsListener) } } } else { shared.Log.Debug("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 } localSockets = append(localSockets, unixl) if listenAddr != "" { tcpl, err := tls.Listen("tcp", listenAddr, tlsConfig) if err != nil { return nil, fmt.Errorf("cannot listen on unix socket: %v", err) } remoteSockets = append(remoteSockets, tcpl) } } d.localSockets = localSockets d.remoteSockets = remoteSockets d.devlxd, err = createAndBindDevLxd() if err != nil { return nil, err } containersRestart(d) containersWatch(d) d.tomb.Go(func() error { for _, socket := range d.localSockets { shared.Log.Debug(" - binding local socket", log.Ctx{"socket": socket.Addr()}) d.tomb.Go(func() error { return http.Serve(socket, d.mux) }) } for _, socket := range d.remoteSockets { shared.Log.Debug(" - binding remote socket", log.Ctx{"socket": socket.Addr()}) d.tomb.Go(func() error { return http.Serve(socket, d.mux) }) } d.tomb.Go(func() error { server := devLxdServer(d) return server.Serve(d.devlxd) }) return nil }) return d, nil }
func run() error { gnuflag.Usage = func() { fmt.Printf("Usage: lxd [command] [options]\n\nOptions:\n") gnuflag.PrintDefaults() fmt.Printf("\nCommands:\n") fmt.Printf(" shutdown\n") fmt.Printf(" Perform a clean shutdown of LXD and all running containers\n") fmt.Printf(" activateifneeded\n") fmt.Printf(" Check if LXD should be started (at boot) and if so, spawn it through socket activation\n") fmt.Printf("\nInternal commands (don't call directly):\n") fmt.Printf(" forkgetfile\n") fmt.Printf(" Grab a file from a running container\n") fmt.Printf(" forkputfile\n") fmt.Printf(" Pushes a file to a running container\n") fmt.Printf(" forkstart\n") fmt.Printf(" Start a container\n") fmt.Printf(" forkmigrate\n") fmt.Printf(" Restore a container after migration\n") } gnuflag.Parse(true) if *help { // The user asked for help via --help, so we shouldn't print to // stderr. gnuflag.SetOut(os.Stdout) gnuflag.Usage() return nil } if *version { fmt.Println(shared.Version) return nil } // Configure logging syslog := "" if *syslogFlag { syslog = "lxd" } err := shared.SetLogger(syslog, *logfile, *verbose, *debug) if err != nil { fmt.Printf("%s", err) return nil } // Process sub-commands if len(os.Args) > 1 { // "forkputfile" and "forkgetfile" are handled specially in copyfile.go switch os.Args[1] { case "forkstart": return startContainer(os.Args[1:]) case "forkmigrate": return MigrateContainer(os.Args[1:]) case "shutdown": return cleanShutdown() case "activateifneeded": return activateIfNeeded() } } if gnuflag.NArg() != 0 { gnuflag.Usage() return fmt.Errorf("Unknown arguments") } if *cpuProfile != "" { f, err := os.Create(*cpuProfile) if err != nil { fmt.Printf("Error opening cpu profile file: %s\n", err) return nil } pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } if *memProfile != "" { go memProfiler() } neededPrograms := []string{"setfacl", "rsync", "tar", "xz"} for _, p := range neededPrograms { _, err := exec.LookPath(p) if err != nil { return err } } _, err = exec.LookPath("apparmor_parser") if err == nil && shared.IsDir("/sys/kernel/security/apparmor") { aaEnabled = true } else { shared.Log.Warn("apparmor_parser binary not found or apparmor " + "fs not mounted. AppArmor disabled.") } if aaEnabled && os.Getenv("LXD_SECURITY_APPARMOR") == "false" { aaEnabled = false shared.Log.Warn("per-container apparmor profiles have been manually disabled") } runningInUserns = shared.RunningInUserNS() if aaEnabled && runningInUserns { aaEnabled = false shared.Log.Warn("per-container apparmor profiles disabled because we are in a user namespace") } /* Can we create devices? */ checkCanMknod() if *printGoroutines > 0 { go func() { for { time.Sleep(time.Duration(*printGoroutines) * time.Second) shared.PrintStack() } }() } d, err := startDaemon() if err != nil { if d != nil && d.db != nil { d.db.Close() } return err } var ret error var wg sync.WaitGroup wg.Add(1) go func() { ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGPWR) sig := <-ch shared.Log.Info( fmt.Sprintf("Received '%s signal', shutting down containers.", sig)) containersShutdown(d) ret = d.Stop() wg.Done() }() go func() { ch := make(chan os.Signal) signal.Notify(ch, syscall.SIGINT) signal.Notify(ch, syscall.SIGQUIT) signal.Notify(ch, syscall.SIGTERM) sig := <-ch shared.Log.Info(fmt.Sprintf("Received '%s signal', exiting.\n", sig)) ret = d.Stop() wg.Done() }() wg.Wait() return ret }