Skip to content

Commit fac88ab

Browse files
authored
Merge pull request #96 from dims/copy-sigma-inotify-to-k8s.io-utils
Copy sigma inotify to k8s.io utils
2 parents c55fbcf + 26f3915 commit fac88ab

File tree

5 files changed

+467
-0
lines changed

5 files changed

+467
-0
lines changed

inotify/LICENSE

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Copyright (c) 2009 The Go Authors. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without
4+
modification, are permitted provided that the following conditions are
5+
met:
6+
7+
* Redistributions of source code must retain the above copyright
8+
notice, this list of conditions and the following disclaimer.
9+
* Redistributions in binary form must reproduce the above
10+
copyright notice, this list of conditions and the following disclaimer
11+
in the documentation and/or other materials provided with the
12+
distribution.
13+
* Neither the name of Google Inc. nor the names of its
14+
contributors may be used to endorse or promote products derived from
15+
this software without specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

inotify/PATENTS

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
Additional IP Rights Grant (Patents)
2+
3+
"This implementation" means the copyrightable works distributed by
4+
Google as part of the Go project.
5+
6+
Google hereby grants to You a perpetual, worldwide, non-exclusive,
7+
no-charge, royalty-free, irrevocable (except as stated in this section)
8+
patent license to make, have made, use, offer to sell, sell, import,
9+
transfer and otherwise run, modify and propagate the contents of this
10+
implementation of Go, where such license applies only to those patent
11+
claims, both currently owned or controlled by Google and acquired in
12+
the future, licensable by Google that are necessarily infringed by this
13+
implementation of Go. This grant does not include claims that would be
14+
infringed only as a consequence of further modification of this
15+
implementation. If you or your agent or exclusive licensee institute or
16+
order or agree to the institution of patent litigation against any
17+
entity (including a cross-claim or counterclaim in a lawsuit) alleging
18+
that this implementation of Go or any code incorporated within this
19+
implementation of Go constitutes direct or contributory patent
20+
infringement, or inducement of patent infringement, then any patent
21+
rights granted to you under this License for this implementation of Go
22+
shall terminate as of the date such litigation is filed.

inotify/README.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
This is a fork of golang.org/x/exp/inotify before it was deleted.
2+
3+
Please use gopkg.in/fsnotify.v0 instead.
4+
5+
For updates, see: https://fsnotify.org/

inotify/inotify_linux.go

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

0 commit comments

Comments
 (0)