~emersion/drmdb

afa09d62c88148514522c9254c46ed41cdc1eb69 — Simon Ser 7 months ago 1863c38
Move database functions to a separate package
4 files changed, 128 insertions(+), 112 deletions(-)

R db.go => database/db.go
M server.go
M snapshot.go
A walk.go
R db.go => database/db.go +15 -11
@@ 1,4 1,4 @@
package drmdb
package database

import (
	"bytes"


@@ 14,9 14,9 @@ import (
	"git.sr.ht/~emersion/drmdb/drmtree"
)

const dbDir = "db"
const Dir = "db"

var errStop = fmt.Errorf("drmdb: stop walking")
var ErrStop = fmt.Errorf("drmdb: stop walking")

func generateKey(n *drmtree.Node) (string, error) {
	if n.Driver == nil || n.Device == nil {


@@ 56,13 56,17 @@ func generateKey(n *drmtree.Node) (string, error) {
	return hex.EncodeToString(sum[:])[:12], nil
}

func store(n *drmtree.Node) (string, error) {
func Init() error {
	return os.MkdirAll(Dir, 0755)
}

func Store(n *drmtree.Node) (string, error) {
	k, err := generateKey(n)
	if err != nil {
		return "", err
	}

	p := filepath.Join(dbDir, k+".json")
	p := filepath.Join(Dir, k+".json")
	f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
	if err != nil {
		if os.IsExist(err) {


@@ 79,8 83,8 @@ func store(n *drmtree.Node) (string, error) {
	return k, f.Close()
}

func load(k string) (*drmtree.Node, error) {
	p := filepath.Join(dbDir, k + ".json")
func Load(k string) (*drmtree.Node, error) {
	p := filepath.Join(Dir, k + ".json")
	f, err := os.Open(p)
	if err != nil {
		return nil, err


@@ 95,8 99,8 @@ func load(k string) (*drmtree.Node, error) {
	return &n, f.Close()
}

func walk(fn func(k string, n *drmtree.Node) error) error {
	files, err := ioutil.ReadDir(dbDir)
func Walk(fn func(k string, n *drmtree.Node) error) error {
	files, err := ioutil.ReadDir(Dir)
	if err != nil {
		return err
	}


@@ 107,12 111,12 @@ func walk(fn func(k string, n *drmtree.Node) error) error {
		}
		k := strings.TrimSuffix(fi.Name(), ".json")

		n, err := load(k)
		n, err := Load(k)
		if err != nil {
			return err
		}

		if err := fn(k, n); err == errStop {
		if err := fn(k, n); err == ErrStop {
			return nil
		} else if err != nil {
			return err

M server.go => server.go +8 -99
@@ 10,12 10,12 @@ import (
	"strconv"
	"strings"

	"git.sr.ht/~emersion/drmdb/database"
	"git.sr.ht/~emersion/drmdb/drmtree"
	"git.sr.ht/~emersion/drmdb/treefmt"
	"git.sr.ht/~emersion/go-drm"
	"git.sr.ht/~emersion/go-hwids"
	"github.com/labstack/echo/v4"
	"github.com/mcuadros/go-version"
)

func badRequest(c echo.Context, msg string, err error) error {


@@ 25,97 25,6 @@ func badRequest(c echo.Context, msg string, err error) error {
	return c.String(http.StatusBadRequest, "Error: "+msg+"\n")
}

func walkProps(props drmtree.PropertyMap, obj drm.AnyID, f func(drm.AnyID, string, *drmtree.Property) error) error {
	for name, prop := range props {
		if err := f(obj, name, &prop); err != nil {
			return err
		}
	}
	return nil
}

func walkNodeProps(n *drmtree.Node, f func(drm.AnyID, string, *drmtree.Property) error) error {
	for _, conn := range n.Connectors {
		if err := walkProps(conn.Properties, conn.ID, f); err != nil {
			return err
		}
	}
	for _, crtc := range n.CRTCs {
		if err := walkProps(crtc.Properties, crtc.ID, f); err != nil {
			return err
		}
	}
	for _, plane := range n.Planes {
		if err := walkProps(plane.Properties, plane.ID, f); err != nil {
			return err
		}
	}
	return nil
}

func driverLess(a *drmtree.Driver, b *drmtree.Driver) bool {
	if a.Version.Less(&b.Version) {
		return true
	}
	// Linux is the upstream
	if b.Kernel.SysName == "Linux" && a.Kernel.SysName != "Linux" {
		return true
	}
	return version.Compare(a.Kernel.Release, b.Kernel.Release, "<")
}

type walkLatestField int

const (
	walkLatestDriver walkLatestField = iota
	walkLatestDevice
)

func walkLatest(f walkLatestField, fn func(k string, n *drmtree.Node) error) error {
	type node struct {
		k string
		n *drmtree.Node
	}

	latest := make(map[string]node)
	err := walk(func(k string, n *drmtree.Node) error {
		var latestKey string
		switch f {
		case walkLatestDriver:
			latestKey = n.Driver.Name
		case walkLatestDevice:
			latestKey = n.Device.BusID()
		}
		if latestKey == "" {
			return nil
		}

		other, ok := latest[latestKey]
		if ok && driverLess(n.Driver, other.n.Driver) {
			return nil
		}
		latest[latestKey] = node{k, n}
		return nil
	})
	if err != nil {
		return err
	}

	keys := make([]string, 0, len(latest))
	for k := range latest {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		n := latest[k]
		if err := fn(n.k, n.n); err != nil {
			return err
		}
	}
	return nil
}

func loadPCIIDs() (vendors map[uint16]string, devices map[uint32]string, err error) {
	vendors = make(map[uint16]string)
	devices = make(map[uint32]string)


@@ 149,8 58,8 @@ func loadPCIIDs() (vendors map[uint16]string, devices map[uint32]string, err err
func New() *echo.Echo {
	e := echo.New()

	if err := os.MkdirAll(dbDir, 0755); err != nil {
		e.Logger.Fatal("Failed to create DB dir:", err)
	if err := database.Init(); err != nil {
		e.Logger.Fatal("Failed to initialize database:", err)
	}

	var err error


@@ 181,7 90,7 @@ func New() *echo.Echo {

		success := false
		for name, n := range nodes {
			if key, err := store(n); err != nil {
			if key, err := database.Store(n); err != nil {
				fmt.Fprintf(c.Response(), "%s: error: %v\n", name, err)
			} else {
				u := "https://" + c.Request().Host + "/devices/" + key


@@ 272,7 181,7 @@ func New() *echo.Echo {
			raw = true
		}

		n, err := load(key)
		n, err := database.Load(key)
		if err != nil {
			if os.IsNotExist(err) {
				return c.String(http.StatusNotFound, "no such device")


@@ 284,7 193,7 @@ func New() *echo.Echo {
			return c.JSON(http.StatusOK, n)
		} else {
			var altDevices []altDeviceData
			err := walk(func(k string, alt *drmtree.Node) error {
			err := database.Walk(func(k string, alt *drmtree.Node) error {
				if k == key {
					return nil
				}


@@ 363,7 272,7 @@ func New() *echo.Echo {

		drivers := make(map[string]struct{})
		props := make(map[string]propertyData)
		err := walk(func(k string, n *drmtree.Node) error {
		err := database.Walk(func(k string, n *drmtree.Node) error {
			drv := n.Driver.Name
			drivers[drv] = struct{}{}



@@ 402,7 311,7 @@ func New() *echo.Echo {
		var property drmtree.Property

		drivers := make(map[string]bool)
		err := walk(func(k string, n *drmtree.Node) error {
		err := database.Walk(func(k string, n *drmtree.Node) error {
			drivers[n.Driver.Name] = false

			return walkNodeProps(n, func(obj drm.AnyID, name string, p *drmtree.Property) error {

M snapshot.go => snapshot.go +4 -2
@@ 7,6 7,8 @@ import (
	"io/ioutil"
	"os"
	"path/filepath"

	"git.sr.ht/~emersion/drmdb/database"
)

const licenseFile = "LICENSE"


@@ 49,13 51,13 @@ func writeSnapshot(w io.Writer) error {
		return err
	}

	files, err := ioutil.ReadDir(dbDir)
	files, err := ioutil.ReadDir(database.Dir)
	if err != nil {
		return err
	}

	for _, fi := range files {
		p := filepath.Join(dbDir, fi.Name())
		p := filepath.Join(database.Dir, fi.Name())
		if err := writeSnapshotFile(tw, p, fi); err != nil {
			return err
		}

A walk.go => walk.go +101 -0
@@ 0,0 1,101 @@
package drmdb

import (
	"sort"

	"git.sr.ht/~emersion/drmdb/database"
	"git.sr.ht/~emersion/drmdb/drmtree"
	"git.sr.ht/~emersion/go-drm"
	"github.com/mcuadros/go-version"
)

func walkProps(props drmtree.PropertyMap, obj drm.AnyID, f func(drm.AnyID, string, *drmtree.Property) error) error {
	for name, prop := range props {
		if err := f(obj, name, &prop); err != nil {
			return err
		}
	}
	return nil
}

func walkNodeProps(n *drmtree.Node, f func(drm.AnyID, string, *drmtree.Property) error) error {
	for _, conn := range n.Connectors {
		if err := walkProps(conn.Properties, conn.ID, f); err != nil {
			return err
		}
	}
	for _, crtc := range n.CRTCs {
		if err := walkProps(crtc.Properties, crtc.ID, f); err != nil {
			return err
		}
	}
	for _, plane := range n.Planes {
		if err := walkProps(plane.Properties, plane.ID, f); err != nil {
			return err
		}
	}
	return nil
}

func driverLess(a *drmtree.Driver, b *drmtree.Driver) bool {
	if a.Version.Less(&b.Version) {
		return true
	}
	// Linux is the upstream
	if b.Kernel.SysName == "Linux" && a.Kernel.SysName != "Linux" {
		return true
	}
	return version.Compare(a.Kernel.Release, b.Kernel.Release, "<")
}

type walkLatestField int

const (
	walkLatestDriver walkLatestField = iota
	walkLatestDevice
)

func walkLatest(f walkLatestField, fn func(k string, n *drmtree.Node) error) error {
	type node struct {
		k string
		n *drmtree.Node
	}

	latest := make(map[string]node)
	err := database.Walk(func(k string, n *drmtree.Node) error {
		var latestKey string
		switch f {
		case walkLatestDriver:
			latestKey = n.Driver.Name
		case walkLatestDevice:
			latestKey = n.Device.BusID()
		}
		if latestKey == "" {
			return nil
		}

		other, ok := latest[latestKey]
		if ok && driverLess(n.Driver, other.n.Driver) {
			return nil
		}
		latest[latestKey] = node{k, n}
		return nil
	})
	if err != nil {
		return err
	}

	keys := make([]string, 0, len(latest))
	for k := range latest {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		n := latest[k]
		if err := fn(n.k, n.n); err != nil {
			return err
		}
	}
	return nil
}