~emersion/drmdb

b3c1beadcd00a9215e5f1fd819fa26af13875c18 — Simon Ser 2 years ago df40d73
Implement /submit endpoint
4 files changed, 116 insertions(+), 11 deletions(-)

M cmd/drmdump/main.go
A db.go
M drmtree/drmtree.go
R drmdb.go => server.go
M cmd/drmdump/main.go => cmd/drmdump/main.go +2 -2
@@ 9,8 9,8 @@ import (
	"path/filepath"
	"syscall"

	"git.sr.ht/~emersion/drmdb/treefmt"
	"git.sr.ht/~emersion/drmdb/drmtree"
	"git.sr.ht/~emersion/drmdb/treefmt"
	"git.sr.ht/~emersion/go-drm"
)



@@ 595,7 595,7 @@ func printNode(tf treefmt.Formatter, path string, n *drmtree.Node) {
func main() {
	var (
		outputJSON bool
		input bool
		input      bool
	)
	flag.BoolVar(&outputJSON, "j", false, "Enable JSON output")
	flag.BoolVar(&input, "i", false, "Read JSON data from stdin")

A db.go => db.go +63 -0
@@ 0,0 1,63 @@
package drmdb

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"

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

const dbDir = "db"

func generateKey(n *drmtree.Node) (string, error) {
	if n.Driver == nil || n.Device == nil {
		return "", fmt.Errorf("node is missing driver/device")
	}

	ver := &n.Driver.Version

	var b bytes.Buffer
	b.WriteString(n.Driver.Name)
	b.WriteByte(0)
	fmt.Fprintf(&b, "%v.%v.%v-%v", ver.Major, ver.Minor, ver.Patch, ver.Date)
	b.WriteByte(0)
	switch dev := n.Device.DeviceData.(type) {
	case *drmtree.DevicePCI:
		b.WriteString("pci")
		b.WriteByte(0)
		fmt.Fprintf(&b, "%04X:%04X", dev.Vendor, dev.Device)
	default:
		return "", fmt.Errorf("device bus type %v not supported", n.Device.BusType)
	}

	sum := sha256.Sum256(b.Bytes())
	return hex.EncodeToString(sum[:])[:12], nil
}

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

	p := filepath.Join(dbDir, k+".json")
	f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0666)
	if err != nil {
		if os.IsExist(err) {
			return fmt.Errorf("data has already been submitted")
		}
		return err
	}
	defer f.Close()

	if err := json.NewEncoder(f).Encode(n); err != nil {
		return err
	}

	return f.Close()
}

M drmtree/drmtree.go => drmtree/drmtree.go +11 -9
@@ 142,14 142,14 @@ func parseString() interface{} {
}

var propertyParsers = map[string]func() interface{}{
	"SRC_X": parseUint64,
	"SRC_Y": parseUint64,
	"SRC_W": parseUint64,
	"SRC_H": parseUint64,
	"IN_FORMATS": parseInFormats,
	"MODE_ID": parseModeID,
	"SRC_X":                   parseUint64,
	"SRC_Y":                   parseUint64,
	"SRC_W":                   parseUint64,
	"SRC_H":                   parseUint64,
	"IN_FORMATS":              parseInFormats,
	"MODE_ID":                 parseModeID,
	"WRITEBACK_PIXEL_FORMATS": parseWritebackPixelFormats,
	"PATH": parseString,
	"PATH":                    parseString,
}

func (prop *Property) UnmarshalJSON(b []byte) error {


@@ 157,9 157,9 @@ func (prop *Property) UnmarshalJSON(b []byte) error {

	var rawSpec, rawValue, rawData json.RawMessage
	rawProp := property{
		Spec: &rawSpec,
		Spec:  &rawSpec,
		Value: &rawValue,
		Data: &rawData,
		Data:  &rawData,
	}
	if err := json.Unmarshal(b, &rawProp); err != nil {
		return err


@@ 255,3 255,5 @@ type Node struct {
	CRTCs      []CRTC      `json:"crtcs"`
	Planes     []Plane     `json:"planes"`
}

type NodeMap map[string]*Node

R drmdb.go => server.go +40 -0
@@ 1,10 1,14 @@
package drmdb

import (
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"os"

	"git.sr.ht/~emersion/drmdb/drmtree"
	"github.com/labstack/echo/v4"
)



@@ 16,9 20,20 @@ func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Con
	return t.t.ExecuteTemplate(w, name, data)
}

func badRequest(c echo.Context, msg string, err error) error {
	if err != nil {
		c.Logger().Printf("%v: %v", msg, err)
	}
	return c.String(http.StatusBadRequest, "Error: "+msg+"\n")
}

func New() *echo.Echo {
	e := echo.New()

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

	e.Renderer = &Template{
		t: template.Must(template.ParseGlob("public/*.html")),
	}


@@ 27,6 42,31 @@ func New() *echo.Echo {
		return c.Render(http.StatusOK, "index.html", nil)
	})

	e.POST("/submit", func(c echo.Context) error {
		lr := io.LimitedReader{R: c.Request().Body, N: 512 * 1024}
		var nodes drmtree.NodeMap
		if err := json.NewDecoder(&lr).Decode(&nodes); err != nil {
			return badRequest(c, "invalid JSON", err)
		} else if len(nodes) == 0 {
			return badRequest(c, "data is empty", nil)
		}

		success := false
		for name, n := range nodes {
			if err := store(n); err != nil {
				fmt.Fprintf(c.Response(), "%s: error: %v\n", name, err)
			} else {
				fmt.Fprintf(c.Response(), "%s: data uploaded\n", name)
				success = true
			}
		}

		if success {
			fmt.Fprintf(c.Response(), "Thanks!\n")
		}
		return nil
	})

	e.Static("/assets", "public/assets")

	return e