ref: eb5ed3dfd12852c55d0ca23d3480c37f7eafc73e
dir: /main.go/
package main
import (
"fmt"
"os"
"io"
"os/exec"
"sync"
"syscall"
"time"
"flag"
"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 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]), "")
server := snarfFS{
Srv: fid.Server(),
state: &snarfFSState{},
}
go func() {
if err := go9p.ServeReadWriter(rw, rw, 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() {
foreground := flag.Bool("f", false, "run in foreground")
flag.Parse()
if *foreground {
os.Args = append(os.Args[:1], os.Args[2:]...)
} else {
path, err := exec.LookPath(os.Args[0])
if err != nil {
fmt.Fprintf(os.Stderr, "snarflog: couldn't find myself: %s\n", err)
os.Exit(1)
}
args := append([]string{os.Args[0], "-f"}, os.Args[1:]...)
_, err = syscall.ForkExec(path, args, &syscall.ProcAttr{
Files: []uintptr{os.Stdin.Fd(), os.Stdout.Fd(), os.Stderr.Fd()},
})
if err != nil {
fmt.Fprintf(os.Stderr, "snarflog: fork failed: %s\n", err)
os.Exit(1)
}
os.Exit(0)
}
_, 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{}
}