OSDN Git Service

libgo: Update to weekly.2011-11-02.
[pf3gnuchains/gcc-fork.git] / libgo / go / exp / inotify / inotify_linux.go
1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 /*
6 Package inotify implements a wrapper for the Linux inotify system.
7
8 Example:
9     watcher, err := inotify.NewWatcher()
10     if err != nil {
11         log.Fatal(err)
12     }
13     err = watcher.Watch("/tmp")
14     if err != nil {
15         log.Fatal(err)
16     }
17     for {
18         select {
19         case ev := <-watcher.Event:
20             log.Println("event:", ev)
21         case err := <-watcher.Error:
22             log.Println("error:", err)
23         }
24     }
25
26 */
27 package inotify
28
29 import (
30         "errors"
31         "fmt"
32         "os"
33         "strings"
34         "syscall"
35         "unsafe"
36 )
37
38 type Event struct {
39         Mask   uint32 // Mask of events
40         Cookie uint32 // Unique cookie associating related events (for rename(2))
41         Name   string // File name (optional)
42 }
43
44 type watch struct {
45         wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall)
46         flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags)
47 }
48
49 type Watcher struct {
50         fd       int               // File descriptor (as returned by the inotify_init() syscall)
51         watches  map[string]*watch // Map of inotify watches (key: path)
52         paths    map[int]string    // Map of watched paths (key: watch descriptor)
53         Error    chan error        // Errors are sent on this channel
54         Event    chan *Event       // Events are returned on this channel
55         done     chan bool         // Channel for sending a "quit message" to the reader goroutine
56         isClosed bool              // Set to true when Close() is first called
57 }
58
59 // NewWatcher creates and returns a new inotify instance using inotify_init(2)
60 func NewWatcher() (*Watcher, error) {
61         fd, errno := syscall.InotifyInit()
62         if fd == -1 {
63                 return nil, os.NewSyscallError("inotify_init", errno)
64         }
65         w := &Watcher{
66                 fd:      fd,
67                 watches: make(map[string]*watch),
68                 paths:   make(map[int]string),
69                 Event:   make(chan *Event),
70                 Error:   make(chan error),
71                 done:    make(chan bool, 1),
72         }
73
74         go w.readEvents()
75         return w, nil
76 }
77
78 // Close closes an inotify watcher instance
79 // It sends a message to the reader goroutine to quit and removes all watches
80 // associated with the inotify instance
81 func (w *Watcher) Close() error {
82         if w.isClosed {
83                 return nil
84         }
85         w.isClosed = true
86
87         // Send "quit" message to the reader goroutine
88         w.done <- true
89         for path := range w.watches {
90                 w.RemoveWatch(path)
91         }
92
93         return nil
94 }
95
96 // AddWatch adds path to the watched file set.
97 // The flags are interpreted as described in inotify_add_watch(2).
98 func (w *Watcher) AddWatch(path string, flags uint32) error {
99         if w.isClosed {
100                 return errors.New("inotify instance already closed")
101         }
102
103         watchEntry, found := w.watches[path]
104         if found {
105                 watchEntry.flags |= flags
106                 flags |= syscall.IN_MASK_ADD
107         }
108         wd, errno := syscall.InotifyAddWatch(w.fd, path, flags)
109         if wd == -1 {
110                 return &os.PathError{"inotify_add_watch", path, os.Errno(errno)}
111         }
112
113         if !found {
114                 w.watches[path] = &watch{wd: uint32(wd), flags: flags}
115                 w.paths[wd] = path
116         }
117         return nil
118 }
119
120 // Watch adds path to the watched file set, watching all events.
121 func (w *Watcher) Watch(path string) error {
122         return w.AddWatch(path, IN_ALL_EVENTS)
123 }
124
125 // RemoveWatch removes path from the watched file set.
126 func (w *Watcher) RemoveWatch(path string) error {
127         watch, ok := w.watches[path]
128         if !ok {
129                 return errors.New(fmt.Sprintf("can't remove non-existent inotify watch for: %s", path))
130         }
131         success, errno := syscall.InotifyRmWatch(w.fd, watch.wd)
132         if success == -1 {
133                 return os.NewSyscallError("inotify_rm_watch", errno)
134         }
135         delete(w.watches, path)
136         return nil
137 }
138
139 // readEvents reads from the inotify file descriptor, converts the
140 // received events into Event objects and sends them via the Event channel
141 func (w *Watcher) readEvents() {
142         var (
143                 buf   [syscall.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
144                 n     int                                     // Number of bytes read with read()
145                 errno int                                     // Syscall errno
146         )
147
148         for {
149                 n, errno = syscall.Read(w.fd, buf[0:])
150                 // See if there is a message on the "done" channel
151                 var done bool
152                 select {
153                 case done = <-w.done:
154                 default:
155                 }
156
157                 // If EOF or a "done" message is received
158                 if n == 0 || done {
159                         errno := syscall.Close(w.fd)
160                         if errno == -1 {
161                                 w.Error <- os.NewSyscallError("close", errno)
162                         }
163                         close(w.Event)
164                         close(w.Error)
165                         return
166                 }
167                 if n < 0 {
168                         w.Error <- os.NewSyscallError("read", errno)
169                         continue
170                 }
171                 if n < syscall.SizeofInotifyEvent {
172                         w.Error <- errors.New("inotify: short read in readEvents()")
173                         continue
174                 }
175
176                 var offset uint32 = 0
177                 // We don't know how many events we just read into the buffer
178                 // While the offset points to at least one whole event...
179                 for offset <= uint32(n-syscall.SizeofInotifyEvent) {
180                         // Point "raw" to the event in the buffer
181                         raw := (*syscall.InotifyEvent)(unsafe.Pointer(&buf[offset]))
182                         event := new(Event)
183                         event.Mask = uint32(raw.Mask)
184                         event.Cookie = uint32(raw.Cookie)
185                         nameLen := uint32(raw.Len)
186                         // If the event happened to the watched directory or the watched file, the kernel
187                         // doesn't append the filename to the event, but we would like to always fill the
188                         // the "Name" field with a valid filename. We retrieve the path of the watch from
189                         // the "paths" map.
190                         event.Name = w.paths[int(raw.Wd)]
191                         if nameLen > 0 {
192                                 // Point "bytes" at the first byte of the filename
193                                 bytes := (*[syscall.PathMax]byte)(unsafe.Pointer(&buf[offset+syscall.SizeofInotifyEvent]))
194                                 // The filename is padded with NUL bytes. TrimRight() gets rid of those.
195                                 event.Name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
196                         }
197                         // Send the event on the events channel
198                         w.Event <- event
199
200                         // Move to the next event in the buffer
201                         offset += syscall.SizeofInotifyEvent + nameLen
202                 }
203         }
204 }
205
206 // String formats the event e in the form
207 // "filename: 0xEventMask = IN_ACCESS|IN_ATTRIB_|..."
208 func (e *Event) String() string {
209         var events string = ""
210
211         m := e.Mask
212         for _, b := range eventBits {
213                 if m&b.Value != 0 {
214                         m &^= b.Value
215                         events += "|" + b.Name
216                 }
217         }
218
219         if m != 0 {
220                 events += fmt.Sprintf("|%#x", m)
221         }
222         if len(events) > 0 {
223                 events = " == " + events[1:]
224         }
225
226         return fmt.Sprintf("%q: %#x%s", e.Name, e.Mask, events)
227 }
228
229 const (
230         // Options for inotify_init() are not exported
231         // IN_CLOEXEC    uint32 = syscall.IN_CLOEXEC
232         // IN_NONBLOCK   uint32 = syscall.IN_NONBLOCK
233
234         // Options for AddWatch
235         IN_DONT_FOLLOW uint32 = syscall.IN_DONT_FOLLOW
236         IN_ONESHOT     uint32 = syscall.IN_ONESHOT
237         IN_ONLYDIR     uint32 = syscall.IN_ONLYDIR
238
239         // The "IN_MASK_ADD" option is not exported, as AddWatch
240         // adds it automatically, if there is already a watch for the given path
241         // IN_MASK_ADD      uint32 = syscall.IN_MASK_ADD
242
243         // Events
244         IN_ACCESS        uint32 = syscall.IN_ACCESS
245         IN_ALL_EVENTS    uint32 = syscall.IN_ALL_EVENTS
246         IN_ATTRIB        uint32 = syscall.IN_ATTRIB
247         IN_CLOSE         uint32 = syscall.IN_CLOSE
248         IN_CLOSE_NOWRITE uint32 = syscall.IN_CLOSE_NOWRITE
249         IN_CLOSE_WRITE   uint32 = syscall.IN_CLOSE_WRITE
250         IN_CREATE        uint32 = syscall.IN_CREATE
251         IN_DELETE        uint32 = syscall.IN_DELETE
252         IN_DELETE_SELF   uint32 = syscall.IN_DELETE_SELF
253         IN_MODIFY        uint32 = syscall.IN_MODIFY
254         IN_MOVE          uint32 = syscall.IN_MOVE
255         IN_MOVED_FROM    uint32 = syscall.IN_MOVED_FROM
256         IN_MOVED_TO      uint32 = syscall.IN_MOVED_TO
257         IN_MOVE_SELF     uint32 = syscall.IN_MOVE_SELF
258         IN_OPEN          uint32 = syscall.IN_OPEN
259
260         // Special events
261         IN_ISDIR      uint32 = syscall.IN_ISDIR
262         IN_IGNORED    uint32 = syscall.IN_IGNORED
263         IN_Q_OVERFLOW uint32 = syscall.IN_Q_OVERFLOW
264         IN_UNMOUNT    uint32 = syscall.IN_UNMOUNT
265 )
266
267 var eventBits = []struct {
268         Value uint32
269         Name  string
270 }{
271         {IN_ACCESS, "IN_ACCESS"},
272         {IN_ATTRIB, "IN_ATTRIB"},
273         {IN_CLOSE, "IN_CLOSE"},
274         {IN_CLOSE_NOWRITE, "IN_CLOSE_NOWRITE"},
275         {IN_CLOSE_WRITE, "IN_CLOSE_WRITE"},
276         {IN_CREATE, "IN_CREATE"},
277         {IN_DELETE, "IN_DELETE"},
278         {IN_DELETE_SELF, "IN_DELETE_SELF"},
279         {IN_MODIFY, "IN_MODIFY"},
280         {IN_MOVE, "IN_MOVE"},
281         {IN_MOVED_FROM, "IN_MOVED_FROM"},
282         {IN_MOVED_TO, "IN_MOVED_TO"},
283         {IN_MOVE_SELF, "IN_MOVE_SELF"},
284         {IN_OPEN, "IN_OPEN"},
285         {IN_ISDIR, "IN_ISDIR"},
286         {IN_IGNORED, "IN_IGNORED"},
287         {IN_Q_OVERFLOW, "IN_Q_OVERFLOW"},
288         {IN_UNMOUNT, "IN_UNMOUNT"},
289 }