// 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 { var err error shared.Log, err = logging.GetLogger("", "", true, true, nil) if err != nil { t.Fatal(err) } } var err error d := &Daemon{MockMode: true} err = initializeDbObject(d, ":memory:") db = d.db 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 (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 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.")) noAlias := gnuflag.Bool("no-alias", false, i18n.G("Ignore aliases when determining what command to run.")) configDir := "$HOME/.config/lxc" if os.Getenv("LXD_CONF") != "" { configDir = os.Getenv("LXD_CONF") } configPath = os.ExpandEnv(path.Join(configDir, "config.yml")) 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(configPath) if err != nil { return err } } // 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] /* at this point we haven't parsed the args, so we have to look for * --no-alias by hand. */ if !shared.StringInSlice("--no-alias", origArgs) { execIfAliases(config, origArgs) } cmd, ok := commands[name] if !ok { commands["help"].run(nil, nil) fmt.Fprintf(os.Stderr, "\n"+i18n.G("error: unknown command: %s")+"\n", name) 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.Log, err = logging.GetLogger("", "", *verbose, *debug, nil) if err != nil { return err } certf := config.ConfigPath("client.crt") keyf := config.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 } } 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 */ if !*noAlias { execIfAliases(config, origArgs) } fmt.Fprintf(os.Stderr, "%s\n\n"+i18n.G("error: %v")+"\n", cmd.usage(), err) 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(" ready\n") fmt.Printf(" Tells LXD that any setup-mode configuration has been done and that it can start containers.\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("\nInit options for non-interactive mode (--auto):\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(" forkexec\n") fmt.Printf(" Execute a command in a container\n") fmt.Printf(" forkgetnet\n") fmt.Printf(" Get container network information\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") fmt.Printf(" callhook\n") fmt.Printf(" Call a container hook\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{} var err error shared.Log, err = logging.GetLogger(syslog, *argLogfile, *argVerbose, *argDebug, handler) if err != nil { fmt.Printf("%s", err) return nil } // Process sub-commands if len(os.Args) > 1 { // "forkputfile", "forkgetfile", "forkmount" and "forkumount" are handled specially in nsexec.go // "forkgetnet" is partially handled in nsexec.go (setns) switch os.Args[1] { // Main commands case "activateifneeded": return cmdActivateIfNeeded() case "daemon": return cmdDaemon() case "callhook": return cmdCallHook(os.Args[1:]) case "init": return cmdInit() case "ready": return cmdReady() case "shutdown": return cmdShutdown() case "waitready": return cmdWaitReady() // Internal commands case "forkgetnet": return printnet() case "forkmigrate": return MigrateContainer(os.Args[1:]) case "forkstart": return startContainer(os.Args[1:]) case "forkexec": ret, err := execContainer(os.Args[1:]) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err) } os.Exit(ret) } } // Fail if some other command is passed if gnuflag.NArg() > 0 { gnuflag.Usage() return fmt.Errorf("Unknown arguments") } return cmdDaemon() }
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.")) noAlias := gnuflag.Bool("no-alias", false, i18n.G("Ignore aliases when determining what command to run.")) configDir := "$HOME/.config/lxc" if os.Getenv("LXD_CONF") != "" { configDir = os.Getenv("LXD_CONF") } configPath = os.ExpandEnv(path.Join(configDir, "config.yml")) 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 && os.Args[1] == "--man" { os.Args[1] = "manpage" } 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(configPath) if err != nil { return err } } // 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] /* at this point we haven't parsed the args, so we have to look for * --no-alias by hand. */ if !shared.StringInSlice("--no-alias", origArgs) { execIfAliases(config, origArgs) } cmd, ok := commands[name] if !ok { commands["help"].run(nil, nil) fmt.Fprintf(os.Stderr, "\n"+i18n.G("error: unknown command: %s")+"\n", name) 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.Log, err = logging.GetLogger("", "", *verbose, *debug, nil) if err != nil { return err } // If the user is running a command that may attempt to connect to the local daemon // and this is the first time the client has been run by the user, then check to see // if LXD has been properly configured. Don't display the message if the var path // does not exist (LXD not installed), as the user may be targeting a remote daemon. if os.Args[0] != "help" && os.Args[0] != "version" && shared.PathExists(shared.VarPath("")) && !shared.PathExists(config.ConfigDir) { // Create the config dir so that we don't get in here again for this user. err = os.MkdirAll(config.ConfigDir, 0750) if err != nil { return err } fmt.Fprintf(os.Stderr, i18n.G("If this is your first time using LXD, you should also run: sudo lxd init")+"\n") fmt.Fprintf(os.Stderr, i18n.G("To start your first container, try: lxc launch ubuntu:16.04")+"\n\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 */ if !*noAlias { execIfAliases(config, origArgs) } fmt.Fprintf(os.Stderr, "%s\n\n"+i18n.G("error: %v")+"\n", cmd.usage(), err) os.Exit(1) } return err }
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 }
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.Log, err = logging.GetLogger("", "", *verbose, *debug, nil) 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, 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 }