package main import ( "bytes" "code.google.com/p/go9p/p" "code.google.com/p/go9p/p/srv" "errors" "flag" "fmt" "log" "reflect" "strconv" ) type conffs struct { *srv.Fsrv } type confFile struct { srv.File cval reflect.Value } var addr = flag.String("addr", ":5640", "network address") var debug = flag.Bool("d", false, "print debug messages") var conf = struct { A string `9p:"asdf"`; B bool; c int; } { A: "a", B: true, c: 5, } func (cfile *confFile) Read(fid *srv.FFid, buf []byte, offset uint64) (int, error) { if offset != 0 { return 0, nil } v := cfile.cval var cstr string switch kind := v.Kind(); kind { case reflect.Bool: cstr = fmt.Sprint(v.Bool()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: cstr = fmt.Sprint(v.Int()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: cstr = fmt.Sprint(v.Uint()) case reflect.String: cstr = v.String() default: return 0, errors.New("unreadable type: " + kind.String()) } cstr += "\n" if len(cstr) > len(buf) { return 0, errors.New("too big") } copy(buf, cstr) return len(cstr), nil } func (cfile *confFile) Write(fid *srv.FFid, buf []byte, offset uint64) (int, error) { if offset != 0 { return 0, errors.New("offset must be 0") } v := cfile.cval if v.CanSet() == false { // should be protected by permissions, but check anyway return 0, errors.New("unsettable") } s := string(bytes.TrimSpace(buf)) switch kind := v.Kind(); kind { case reflect.Bool: b, err := strconv.ParseBool(s) if err != nil { return 0, err } v.SetBool(b) case reflect.String: v.SetString(s) default: return 0, errors.New("unwriteable type: " + kind.String()) } return len(buf), nil } func (dir *confFile) Create(fid *srv.FFid, name string, perm uint32) (*srv.File, error) { log.Printf("create %s %#o\n", name, perm) x := false f := new(confFile) f.cval = reflect.ValueOf(&x).Elem() if err := f.Add(&dir.File, name, nil, nil, 0666, f); err != nil { return nil, err } return &f.File, nil } func (*conffs) ConnOpened(*srv.Conn) { log.Print("conn opened") } func (*conffs) ConnClosed(*srv.Conn) { log.Print("conn closed") } func main() { var cfs conffs log.SetFlags(0) flag.Parse() root := new(confFile) if err := root.Add(nil, "/", nil, nil, p.DMDIR|0777, root); err != nil { log.Fatal("add /: " + err.Error()) } conftype := reflect.TypeOf(&conf).Elem() confval := reflect.ValueOf(&conf).Elem() for i:=0; i < conftype.NumField(); i++ { field := conftype.Field(i) name := field.Tag.Get("9p") if name == "" { name = field.Name } var perms uint32 switch w, err := strconv.ParseBool(field.Tag.Get("w")); { case err == nil && w == false: perms = 0444 case confval.Field(i).CanSet(): perms = 0666 case !confval.Field(i).CanSet(): perms = 0444 default: panic("bad programming") } cfile := &confFile{cval: confval.Field(i)} log.Printf("%s %#o %s %t\n", field.Name, perms, name, cfile.cval.CanSet()) if err := cfile.Add(&root.File, name, nil, nil, perms, cfile); err != nil { log.Fatal("add /" + name + ": " + err.Error()) } } cfs.Fsrv = srv.NewFileSrv(&root.File) if *debug { cfs.Debuglevel = 1 } cfs.Start(cfs) if err := cfs.StartNetListener("tcp", *addr); err != nil { log.Fatal("9p listen: " + err.Error()) } }