TBR
v: renaming the v directory to go
Change-Id: I4fd9f6ee2895d8034c23b65927eb118980b3c17a
diff --git a/examples/inspector/inspectord/services.go b/examples/inspector/inspectord/services.go
new file mode 100644
index 0000000..b39a073
--- /dev/null
+++ b/examples/inspector/inspectord/services.go
@@ -0,0 +1,224 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "time"
+
+ "veyron2/ipc"
+ "veyron2/security"
+
+ "veyron/examples/inspector"
+)
+
+// There are three service types: files, /proc and /dev.
+type serviceType int
+
+const (
+ fileSvc serviceType = iota
+ procSvc
+ deviceSvc
+)
+
+// Dispatchers create servers, based on the names used. Dispatchers
+// create stubbed or stubless servers.
+type dispatcher struct {
+ service serviceType
+ stubbed bool
+}
+
+// A service represents one of the file, proc or device service
+type server struct {
+ service serviceType
+ root, suffix string
+}
+
+// ServerInterface defines the common methods that both stubbed and stubless
+// implementations must provided.
+type serverInterface interface {
+ send(fi os.FileInfo, details bool) error
+ cancelled() bool
+}
+
+type stublessServer struct {
+ call ipc.ServerCall
+}
+
+func (s *stublessServer) send(fi os.FileInfo, details bool) error {
+ if !details {
+ return s.call.Send(fi.Name())
+ }
+ d := struct {
+ Name string
+ Size int64
+ Mode os.FileMode
+ ModTime time.Time
+ IsDir bool
+ }{
+ Name: fi.Name(),
+ Size: fi.Size(),
+ Mode: fi.Mode(),
+ ModTime: fi.ModTime(),
+ IsDir: fi.IsDir(),
+ }
+ return s.call.Send(d)
+}
+
+func (s *stublessServer) cancelled() bool {
+ return s.call.IsClosed()
+}
+
+type stubbedServer struct {
+ context ipc.Context
+ names inspector.InspectorServiceLsStream
+ details inspector.InspectorServiceLsDetailsStream
+}
+
+func (s *stubbedServer) send(fi os.FileInfo, details bool) error {
+ if !details {
+ return s.names.Send(fi.Name())
+ }
+ return s.details.Send(inspector.Details{
+ Name: fi.Name(),
+ Size: fi.Size(),
+ Mode: uint32(fi.Mode()),
+ ModUnixSecs: fi.ModTime().Unix(),
+ ModNano: int32(fi.ModTime().Nanosecond()),
+ IsDir: fi.IsDir(),
+ })
+}
+
+func (s *stubbedServer) cancelled() bool {
+ return s.context.IsClosed()
+}
+
+// ls is shared by both stubbed and stubless calls and contains
+// the bulk of the actual server code.
+func (s *server) ls(glob string, details bool, impl serverInterface) error {
+ // validate the glob pattern
+ if _, err := filepath.Match(glob, ""); err != nil {
+ return err
+ }
+ ch := make(chan []os.FileInfo, 10)
+ errch := make(chan error, 1)
+ go readdir(filepath.Join(s.root, s.suffix), glob, ch, errch)
+ for {
+ select {
+ case fs := <-ch:
+ if len(fs) == 0 {
+ return nil
+ }
+ for _, f := range fs {
+ if err := impl.send(f, details); err != nil {
+ return err
+ }
+ }
+ case err := <-errch:
+ // Flush any buffered data in the data channel when
+ // an error is encountered.
+ for fs := range ch {
+ for _, f := range fs {
+ impl.send(f, details)
+ }
+ }
+ return err
+ case <-time.After(time.Second):
+ return fmt.Errorf("timed out reading info")
+ }
+ if impl.cancelled() {
+ // TODO(cnicolaou): we most likely need to flush
+ // and clear channels here, otherwise we're leaking
+ // goroutines and channels.
+ // TODO(cnicolaou): test this...
+ return fmt.Errorf("cancelled")
+ }
+ }
+ return nil
+}
+
+// List is a stubless server method
+func (s *server) List(call ipc.ServerCall, glob string, details bool) error {
+ log.Infof("List: %q details %t", glob, details)
+ return s.ls(glob, details, &stublessServer{call})
+}
+
+// Ls is a stubbed server method
+func (s *server) Ls(context ipc.Context, Glob string, Stream inspector.InspectorServiceLsStream) error {
+ log.Infof("Ls %q", Glob)
+ return s.ls(Glob, false, &stubbedServer{context: context, names: Stream})
+}
+
+// LsDetails is a stubbed server method
+func (s *server) LsDetails(context ipc.Context, Glob string, Stream inspector.InspectorServiceLsDetailsStream) error {
+ log.Infof("LsDetails %q", Glob)
+ return s.ls(Glob, true, &stubbedServer{context: context, details: Stream})
+}
+
+func (d *dispatcher) Lookup(suffix string) (ipc.Invoker, security.Authorizer, error) {
+ s := &server{service: d.service, suffix: suffix}
+ switch d.service {
+ case fileSvc:
+ cwd, err := os.Getwd()
+ if err != nil {
+ return nil, nil, err
+ }
+ s.root = cwd
+ case deviceSvc:
+ s.root = "/dev"
+ case procSvc:
+ s.root = "/proc"
+ }
+ if d.stubbed {
+ return ipc.ReflectInvoker(inspector.NewServerInspector(s)), nil, nil
+ } else {
+ return ipc.ReflectInvoker(s), nil, nil
+ }
+}
+
+func NewFileSvc(stubbed bool) ipc.Dispatcher {
+ return &dispatcher{service: fileSvc, stubbed: stubbed}
+}
+
+func NewProcSvc(stubbed bool) ipc.Dispatcher {
+ return &dispatcher{service: procSvc, stubbed: stubbed}
+}
+
+func NewDeviceSvc(stubbed bool) ipc.Dispatcher {
+ return &dispatcher{service: deviceSvc, stubbed: stubbed}
+}
+
+func readdir(dirname, glob string, ch chan []os.FileInfo, errch chan error) {
+ defer close(ch)
+ defer close(errch)
+ dir, err := os.Open(dirname)
+ if err != nil {
+ errch <- err
+ return
+ }
+ n := cap(ch)
+ for {
+ entries, err := dir.Readdir(n)
+ if err != nil && err != io.EOF {
+ errch <- err
+ return
+ }
+ if len(glob) == 0 {
+ ch <- entries
+ } else {
+ matches := make([]os.FileInfo, 0, len(entries))
+ for _, e := range entries {
+ if m, _ := filepath.Match(glob, e.Name()); m {
+ matches = append(matches, e)
+ }
+ }
+ if len(matches) > 0 {
+ ch <- matches
+ }
+ }
+ if err == io.EOF {
+ return
+ }
+ }
+}