OSDN Git Service

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