shithub: snarflog

ref: 3aadede9a41a1a7c4928631b9681a67c1c6ac2a3
dir: /main.go/

View raw version
package main

import (
	"fmt"
	"os"
	"io"
	"sync"
	"time"

	"golang.org/x/sys/plan9"
	"github.com/knusbaum/go9p/fs"
	"github.com/knusbaum/go9p"
	"github.com/knusbaum/go9p/proto"
)

func (f *snarfile) Close(fid uint64) error {
	f.mu.Lock()
	defer f.mu.Unlock()
	defer f.BaseFile.Close(fid)
	if data, ok := f.writemap[fid]; ok {
		realwrite, err := os.OpenFile("/mnt/snarflog/rsnarf", os.O_WRONLY, 0644)
		if err != nil {
			fmt.Printf("Coudn't open snarf for writing: %s\n", err)
			delete(f.writemap, fid)
			return err
		}
		defer realwrite.Close()
		_, err = f.snarfStream.Write(data)
		if err != nil {
			return err
		}
		if len(data) > 0 && data[len(data)-1] != '\n' {
			f.snarfStream.Write([]byte{'\n'})
		}

		if _, err := realwrite.Write(data); err != nil {
			delete(f.writemap, fid)
			return err
		}
		delete(f.writemap, fid)
	}
	return nil
}

type snarfile struct {
	fs.BaseFile
	snarfStream fs.Stream
	mu          sync.Mutex
	writemap    map[uint64][]byte
}

type snarfFSState struct {
	mu       sync.Mutex
	firstfid uint32
}

type snarfFS struct {
	go9p.Srv
	state *snarfFSState
}

func (srv snarfFS) Attach(connection go9p.Conn, attach *proto.TAttach) (proto.FCall, error) {
	srv.state.mu.Lock()
	if srv.state.firstfid == 0 {
		srv.state.firstfid = attach.Fid
	}
	defer srv.state.mu.Unlock()
	return srv.Srv.Attach(connection, attach)
}

func (srv snarfFS) Clunk(connection go9p.Conn, clunk *proto.TClunk) (proto.FCall, error) {
	srv.state.mu.Lock()
	defer srv.state.mu.Unlock()
	if srv.state.firstfid != 0 {
		if clunk.Fid == srv.state.firstfid {
			os.Exit(1)
		}
	}
	return srv.Srv.Clunk(connection, clunk)
}

func (f *snarfile) Write(fid uint64, offset uint64, data []byte) (uint32, error) {
	f.mu.Lock()
	defer f.mu.Unlock()
	f.writemap[fid] = append(f.writemap[fid], data...)
	return uint32(len(data)), nil
}



func (f *snarfile) Read(fid uint64, offset uint64, count uint64) ([]byte, error) {
	real, err := os.Open("/mnt/snarflog/rsnarf")
	if err != nil {
		return nil, err
	}
	defer real.Close()

	buf := make([]byte, count)
	n, err := real.ReadAt(buf, int64(offset))
	if err != nil && err != io.EOF {
		return nil, err
	}
	return buf[:n], nil
}

func newSnarf(fsys *fs.FS, name, uid, gid string, perm uint32, stream fs.Stream) *snarfile {
	stat := fsys.NewStat(name, uid, gid, perm)
	return &snarfile{
		BaseFile:    *fs.NewBaseFile(stat),
		snarfStream: stream,
		writemap:    make(map[uint64][]byte),
	}
}

func mountmnt(user string) {
	fid, dir := fs.NewFS(user, user, proto.DMDIR|0644)
	stat := fid.NewStat("rsnarf", user, user, 0644)
	mntfile := fs.NewBaseFile(stat)
	dir.AddChild(mntfile)
	var fds [2]int
	var err error
	err = plan9.Pipe(fds[:])
	if err != nil {
		fmt.Printf("Failed to create pipe for /mnt/snarflog: %s\n", err)
		os.Exit(1)
	}
	rw := os.NewFile(uintptr(fds[0]), "")

	go func() {
		if err := go9p.ServeReadWriter(rw, rw, fid.Server()); err != nil {
			os.Exit(1)
		}
		// Might hate life less if we exit if our filesystem dies
		os.Exit(1)
	}()

	plan9.Mount(fds[1], -1, "/mnt/snarflog", plan9.MREPL, "")
	err = plan9.Bind("/dev/snarf", "/mnt/snarflog/rsnarf", plan9.MREPL)
}

func main() {
	_, err := os.Stat("/srv/snarflog")
	if err == nil {
		srvFd, err := plan9.Open("/srv/snarflog", plan9.O_RDWR)
		if err != nil {
			fmt.Printf("bad snarflog in srv: %v\n", err)
			os.Exit(1)
		}
		defer plan9.Close(srvFd)
		err = plan9.Mount(srvFd, -1, "/dev", plan9.MBEFORE, "")
		os.Exit(1)
	}
	
	user := os.Getenv("user")
	
	mountmnt(user)
	fid, dir := fs.NewFS(user, user, proto.DMDIR|0644)

	snarfStream := fs.NewSkippingStream(1024)
	snarfLogStat := fid.NewStat("snarflog", user, user, 0644)
	snarfLogFile := fs.NewStreamFile(snarfLogStat, snarfStream)

	if err := dir.AddChild(snarfLogFile); err != nil {
		fmt.Printf("Err adding snarflog: %s\n", err)
		return
	}
	snarf := newSnarf(fid, "snarf", user, user, 0644, snarfStream)

	if err := dir.AddChild(snarf); err != nil {
		fmt.Printf("Err: %s\n", err)
		return
	}
	
	go func() {
		server := snarfFS{
			Srv:   fid.Server(),
			state: &snarfFSState{},
		}
		if err := go9p.PostSrv("snarflog", server); err != nil {
			fmt.Printf("Failed to post server: %v\n", err)
			os.Exit(1)
		}
		os.Exit(1)
	}()

	for {
		_, err := os.Stat("/srv/snarflog")
		if err == nil {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}

	srvFd, err := plan9.Open("/srv/snarflog", plan9.O_RDWR)
	if err != nil {
		fmt.Printf("Failed to open /srv/snarflog: %v\n", err)
		os.Exit(1)
	}
	defer plan9.Close(srvFd)

	err = plan9.Mount(srvFd, -1, "/dev", plan9.MBEFORE, "")
	if err != nil {
		fmt.Printf("Failed to mount: %v\n", err)
		os.Exit(1)
	}

	select{}
}