func containerDeleteSnapshots(d *Daemon, cname string) error { prefix := fmt.Sprintf("%s/", cname) length := len(prefix) q := "SELECT name, id FROM containers WHERE type=? AND SUBSTR(name,1,?)=?" var id int var sname string inargs := []interface{}{cTypeSnapshot, length, prefix} outfmt := []interface{}{sname, id} results, err := dbQueryScan(d.db, q, inargs, outfmt) if err != nil { return err } var ids []int backingFs, err := shared.GetFilesystem(shared.VarPath("lxc", cname)) if err != nil && !os.IsNotExist(err) { shared.Debugf("Error cleaning up snapshots: %s\n", err) return err } for _, r := range results { sname = r[0].(string) id = r[1].(int) ids = append(ids, id) cdir := shared.VarPath("lxc", cname, "snapshots", sname) if backingFs == "btrfs" { exec.Command("btrfs", "subvolume", "delete", cdir).Run() } os.RemoveAll(cdir) } for _, id := range ids { _, err = dbExec(d.db, "DELETE FROM containers WHERE id=?", id) if err != nil { return err } } return nil }
func removeContainerPath(d *Daemon, name string) error { cpath := shared.VarPath("lxc", name) backingFs, err := shared.GetFilesystem(cpath) if err != nil { if os.IsNotExist(err) { return nil } shared.Debugf("Error cleaning up %s: %s\n", cpath, err) return err } vgname, vgnameIsSet, err := getServerConfigValue(d, "core.lvm_vg_name") if err != nil { return fmt.Errorf("Error checking server config: %v", err) } if vgnameIsSet { err = shared.LVMRemoveLV(vgname, name) if err != nil { return fmt.Errorf("failed to remove deleted container LV: %v", err) } } else if backingFs == "btrfs" && btrfsIsSubvolume(cpath) { if err := btrfsDeleteSubvol(cpath); err != nil { return err } } err = os.RemoveAll(cpath) if err != nil { shared.Debugf("Error cleaning up %s: %s\n", cpath, err) return err } return nil }
func createFromImage(d *Daemon, req *containerPostReq) Response { var hash string var err error var run func() shared.OperationResult backingFs, err := shared.GetFilesystem(d.lxcpath) if err != nil { return InternalError(err) } if req.Source.Alias != "" { if req.Source.Mode == "pull" && req.Source.Server != "" { hash, err = remoteGetImageFingerprint(d, req.Source.Server, req.Source.Alias) if err != nil { return InternalError(err) } } else { hash, err = dbAliasGet(d.db, req.Source.Alias) if err != nil { return InternalError(err) } } } else if req.Source.Fingerprint != "" { hash = req.Source.Fingerprint } else { return BadRequest(fmt.Errorf("must specify one of alias or fingerprint for init from image")) } if req.Source.Server != "" { err := ensureLocalImage(d, req.Source.Server, hash, req.Source.Secret) if err != nil { return InternalError(err) } } imgInfo, err := dbImageGet(d.db, hash, false) if err != nil { return SmartError(err) } hash = imgInfo.Fingerprint dpath := shared.VarPath("lxc", req.Name) if shared.PathExists(dpath) { return InternalError(fmt.Errorf("Container exists")) } name := req.Name args := DbCreateContainerArgs{ d: d, name: name, ctype: cTypeRegular, config: req.Config, profiles: req.Profiles, ephem: req.Ephemeral, baseImage: hash, architecture: imgInfo.Architecture, } _, err = dbCreateContainer(args) if err != nil { removeContainerPath(d, name) return SmartError(err) } c, err := newLxdContainer(name, d) if err != nil { removeContainer(d, name) return SmartError(err) } vgname, vgnameIsSet, err := getServerConfigValue(d, "core.lvm_vg_name") if err != nil { return InternalError(fmt.Errorf("Error checking server config: %v", err)) } if vgnameIsSet && shared.PathExists(fmt.Sprintf("%s.lv", shared.VarPath("images", hash))) { run = shared.OperationWrap(func() error { lvpath, err := shared.LVMCreateSnapshotLV(name, hash, vgname) if err != nil { return fmt.Errorf("Error creating snapshot of source LV '%s/%s': %s", vgname, hash, err) } destPath := shared.VarPath("lxc", name) err = os.MkdirAll(destPath, 0700) if err != nil { return fmt.Errorf("Error creating container directory: %v", err) } if !c.isPrivileged() { output, err := exec.Command("mount", "-o", "discard", lvpath, destPath).CombinedOutput() if err != nil { return fmt.Errorf("Error mounting snapshot LV: %v\noutput:'%s'", err, output) } if err = shiftRootfs(c, c.name, d); err != nil { return fmt.Errorf("Error in shiftRootfs: %v", err) } cpath := shared.VarPath("lxc", c.name) output, err = exec.Command("umount", cpath).CombinedOutput() if err != nil { return fmt.Errorf("Error unmounting '%s' after shiftRootfs: %v", cpath, err) } } return nil }) } else if backingFs == "btrfs" && shared.PathExists(fmt.Sprintf("%s.btrfs", shared.VarPath("images", hash))) { run = shared.OperationWrap(func() error { if _, err := btrfsCopyImage(hash, name, d); err != nil { return err } if !c.isPrivileged() { err = shiftRootfs(c, name, d) if err != nil { return err } } err = templateApply(c, "create") if err != nil { return err } return nil }) } else { rootfsPath := fmt.Sprintf("%s/rootfs", dpath) err = os.MkdirAll(rootfsPath, 0700) if err != nil { return InternalError(fmt.Errorf("Error creating rootfs directory")) } run = shared.OperationWrap(func() error { if err := extractImage(hash, name, d); err != nil { return err } if !c.isPrivileged() { err = shiftRootfs(c, name, d) if err != nil { return err } } err = templateApply(c, "create") if err != nil { return err } return nil }) } resources := make(map[string][]string) resources["containers"] = []string{req.Name} return &asyncResponse{run: run, resources: resources} }
// StartDaemon starts the shared daemon with the provided configuration. func StartDaemon(listenAddr string) (*Daemon, error) { d := &Daemon{} 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.Debugf("Error detecting backing fs: %s\n", 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.Debugf("sending top level 404: %s", r.URL) w.Header().Set("Content-Type", "application/json") NotFound.Render(w) }) d.IdmapSet, err = shared.DefaultIdmapSet() if err != nil { shared.Logf("error reading idmap: %s", err.Error()) shared.Logf("operations requiring idmap will not be available") } else { shared.Debugf("Default uid/gid map:") for _, lxcmap := range d.IdmapSet.ToLxcString() { shared.Debugf(" - " + lxcmap) } } 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.Debugf("LXD is socket activated.\n") for _, listener := range listeners { if _, err := os.Stat(listener.Addr().String()); err == nil { localSockets = append(localSockets, listener) } else { tlsListener := tls.NewListener(listener, tlsConfig) remoteSockets = append(remoteSockets, tlsListener) } } } else { shared.Debugf("LXD isn't socket activated.\n") 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 _, err := os.Stat(localSocketPath); err == nil { c := &lxd.Config{Remotes: map[string]lxd.RemoteConfig{}} _, err := lxd.NewClient(c, "") if err != nil { shared.Debugf("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.Debugf(" - binding local socket: %s\n", socket.Addr()) d.tomb.Go(func() error { return http.Serve(socket, d.mux) }) } for _, socket := range d.remoteSockets { shared.Debugf(" - binding remote socket: %s\n", 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 api10Get(d *Daemon, r *http.Request) Response { body := shared.Jmap{"api_compat": shared.APICompat} if d.isTrustedClient(r) { body["auth"] = "trusted" uname := syscall.Utsname{} if err := syscall.Uname(&uname); err != nil { return InternalError(err) } backing_fs, err := shared.GetFilesystem(d.lxcpath) if err != nil { return InternalError(err) } env := shared.Jmap{ "lxc_version": lxc.Version(), "lxd_version": shared.Version, "driver": "lxc", "backing_fs": backing_fs} /* * Based on: https://groups.google.com/forum/#!topic/golang-nuts/Jel8Bb-YwX8 * there is really no better way to do this, which is * unfortunate. Also, we ditch the more accepted CharsToString * version in that thread, since it doesn't seem as portable, * viz. github issue #206. */ kernelVersion := "" for _, c := range uname.Release { if c == 0 { break } kernelVersion += string(byte(c)) } env["kernel_version"] = kernelVersion body["environment"] = env serverConfig, err := getServerConfig(d) if err != nil { return InternalError(err) } config := shared.Jmap{} for key, value := range serverConfig { if key == "core.trust_password" { config[key] = true } else { config[key] = value } } body["config"] = config } else { body["auth"] = "untrusted" } return SyncResponse(true, body) }