blob: 1a087a69651f806cfe2e738f423bc3193aa540c7 [file] [log] [blame]
Jiri Simsa5293dcb2014-05-10 09:56:38 -07001package main
2
3import (
4 "fmt"
5 "io"
6 "os"
7 "path/filepath"
8 "time"
9
10 "veyron2/ipc"
11 "veyron2/security"
12
13 "veyron/examples/inspector"
14)
15
16// There are three service types: files, /proc and /dev.
17type serviceType int
18
19const (
20 fileSvc serviceType = iota
21 procSvc
22 deviceSvc
23)
24
25// Dispatchers create servers, based on the names used. Dispatchers
26// create stubbed or stubless servers.
27type dispatcher struct {
28 service serviceType
29 stubbed bool
30}
31
32// A service represents one of the file, proc or device service
33type server struct {
34 service serviceType
35 root, suffix string
36}
37
38// ServerInterface defines the common methods that both stubbed and stubless
39// implementations must provided.
40type serverInterface interface {
41 send(fi os.FileInfo, details bool) error
42 cancelled() bool
43}
44
45type stublessServer struct {
46 call ipc.ServerCall
47}
48
49func (s *stublessServer) send(fi os.FileInfo, details bool) error {
50 if !details {
51 return s.call.Send(fi.Name())
52 }
53 d := struct {
54 Name string
55 Size int64
56 Mode os.FileMode
57 ModTime time.Time
58 IsDir bool
59 }{
60 Name: fi.Name(),
61 Size: fi.Size(),
62 Mode: fi.Mode(),
63 ModTime: fi.ModTime(),
64 IsDir: fi.IsDir(),
65 }
66 return s.call.Send(d)
67}
68
69func (s *stublessServer) cancelled() bool {
70 return s.call.IsClosed()
71}
72
73type stubbedServer struct {
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -070074 context ipc.ServerContext
Jiri Simsa5293dcb2014-05-10 09:56:38 -070075 names inspector.InspectorServiceLsStream
76 details inspector.InspectorServiceLsDetailsStream
77}
78
79func (s *stubbedServer) send(fi os.FileInfo, details bool) error {
80 if !details {
81 return s.names.Send(fi.Name())
82 }
83 return s.details.Send(inspector.Details{
84 Name: fi.Name(),
85 Size: fi.Size(),
86 Mode: uint32(fi.Mode()),
87 ModUnixSecs: fi.ModTime().Unix(),
88 ModNano: int32(fi.ModTime().Nanosecond()),
89 IsDir: fi.IsDir(),
90 })
91}
92
93func (s *stubbedServer) cancelled() bool {
94 return s.context.IsClosed()
95}
96
97// ls is shared by both stubbed and stubless calls and contains
98// the bulk of the actual server code.
99func (s *server) ls(glob string, details bool, impl serverInterface) error {
100 // validate the glob pattern
101 if _, err := filepath.Match(glob, ""); err != nil {
102 return err
103 }
104 ch := make(chan []os.FileInfo, 10)
105 errch := make(chan error, 1)
106 go readdir(filepath.Join(s.root, s.suffix), glob, ch, errch)
107 for {
108 select {
109 case fs := <-ch:
110 if len(fs) == 0 {
111 return nil
112 }
113 for _, f := range fs {
114 if err := impl.send(f, details); err != nil {
115 return err
116 }
117 }
118 case err := <-errch:
119 // Flush any buffered data in the data channel when
120 // an error is encountered.
121 for fs := range ch {
122 for _, f := range fs {
123 impl.send(f, details)
124 }
125 }
126 return err
127 case <-time.After(time.Second):
128 return fmt.Errorf("timed out reading info")
129 }
130 if impl.cancelled() {
131 // TODO(cnicolaou): we most likely need to flush
132 // and clear channels here, otherwise we're leaking
133 // goroutines and channels.
134 // TODO(cnicolaou): test this...
135 return fmt.Errorf("cancelled")
136 }
137 }
138 return nil
139}
140
141// List is a stubless server method
142func (s *server) List(call ipc.ServerCall, glob string, details bool) error {
143 log.Infof("List: %q details %t", glob, details)
144 return s.ls(glob, details, &stublessServer{call})
145}
146
147// Ls is a stubbed server method
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700148func (s *server) Ls(context ipc.ServerContext, Glob string, Stream inspector.InspectorServiceLsStream) error {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700149 log.Infof("Ls %q", Glob)
150 return s.ls(Glob, false, &stubbedServer{context: context, names: Stream})
151}
152
153// LsDetails is a stubbed server method
Matt Rosencrantzf5afcaf2014-06-02 11:31:22 -0700154func (s *server) LsDetails(context ipc.ServerContext, Glob string, Stream inspector.InspectorServiceLsDetailsStream) error {
Jiri Simsa5293dcb2014-05-10 09:56:38 -0700155 log.Infof("LsDetails %q", Glob)
156 return s.ls(Glob, true, &stubbedServer{context: context, details: Stream})
157}
158
159func (d *dispatcher) Lookup(suffix string) (ipc.Invoker, security.Authorizer, error) {
160 s := &server{service: d.service, suffix: suffix}
161 switch d.service {
162 case fileSvc:
163 cwd, err := os.Getwd()
164 if err != nil {
165 return nil, nil, err
166 }
167 s.root = cwd
168 case deviceSvc:
169 s.root = "/dev"
170 case procSvc:
171 s.root = "/proc"
172 }
173 if d.stubbed {
174 return ipc.ReflectInvoker(inspector.NewServerInspector(s)), nil, nil
175 } else {
176 return ipc.ReflectInvoker(s), nil, nil
177 }
178}
179
180func NewFileSvc(stubbed bool) ipc.Dispatcher {
181 return &dispatcher{service: fileSvc, stubbed: stubbed}
182}
183
184func NewProcSvc(stubbed bool) ipc.Dispatcher {
185 return &dispatcher{service: procSvc, stubbed: stubbed}
186}
187
188func NewDeviceSvc(stubbed bool) ipc.Dispatcher {
189 return &dispatcher{service: deviceSvc, stubbed: stubbed}
190}
191
192func readdir(dirname, glob string, ch chan []os.FileInfo, errch chan error) {
193 defer close(ch)
194 defer close(errch)
195 dir, err := os.Open(dirname)
196 if err != nil {
197 errch <- err
198 return
199 }
200 n := cap(ch)
201 for {
202 entries, err := dir.Readdir(n)
203 if err != nil && err != io.EOF {
204 errch <- err
205 return
206 }
207 if len(glob) == 0 {
208 ch <- entries
209 } else {
210 matches := make([]os.FileInfo, 0, len(entries))
211 for _, e := range entries {
212 if m, _ := filepath.Match(glob, e.Name()); m {
213 matches = append(matches, e)
214 }
215 }
216 if len(matches) > 0 {
217 ch <- matches
218 }
219 }
220 if err == io.EOF {
221 return
222 }
223 }
224}