From a65420af2552ccdae43d64e00221285f1cb20ebc Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Fri, 28 Aug 2015 20:42:46 -0600 Subject: switch to my own inotify bindings, the golang.org/x/exp bindings are crap --- inotify/bits.go | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ inotify/inotify.go | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++ inotify/syscall.go | 55 +++++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 inotify/bits.go create mode 100644 inotify/inotify.go create mode 100644 inotify/syscall.go diff --git a/inotify/bits.go b/inotify/bits.go new file mode 100644 index 0000000..f086b10 --- /dev/null +++ b/inotify/bits.go @@ -0,0 +1,96 @@ +package inotify + +/* Flags for the parameter of inotify_init1. */ +const ( + /* These are, oddly, 24-bit numbers */ + IN_CLOEXEC uint32 = 02000000 + IN_NONBLOCK uint32 = 00004000 +) + +type Mask uint32 + +const ( + /* Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. */ + IN_ACCESS Mask = 0x00000001 /* File was accessed. */ + IN_MODIFY Mask = 0x00000002 /* File was modified. */ + IN_ATTRIB Mask = 0x00000004 /* Metadata changed. */ + IN_CLOSE_WRITE Mask = 0x00000008 /* Writtable file was closed. */ + IN_CLOSE_NOWRITE Mask = 0x00000010 /* Unwrittable file closed. */ + IN_OPEN Mask = 0x00000020 /* File was opened. */ + IN_MOVED_FROM Mask = 0x00000040 /* File was moved from X. */ + IN_MOVED_TO Mask = 0x00000080 /* File was moved to Y. */ + IN_CREATE Mask = 0x00000100 /* Subfile was created. */ + IN_DELETE Mask = 0x00000200 /* Subfile was deleted. */ + IN_DELETE_SELF Mask = 0x00000400 /* Self was deleted. */ + IN_MOVE_SELF Mask = 0x00000800 /* Self was moved. */ + + /* Events sent by the kernel. */ + IN_UNMOUNT Mask = 0x00002000 /* Backing fs was unmounted. */ + IN_Q_OVERFLOW Mask = 0x00004000 /* Event queued overflowed. */ + IN_IGNORED Mask = 0x00008000 /* File was ignored. */ + + /* Special flags. */ + IN_ONLYDIR Mask = 0x01000000 /* Only watch the path if it is a directory. */ + IN_DONT_FOLLOW Mask = 0x02000000 /* Do not follow a sym link. */ + IN_EXCL_UNLINK Mask = 0x04000000 /* Exclude events on unlinked objects. */ + IN_MASK_ADD Mask = 0x20000000 /* Add to the mask of an already existing watch. */ + IN_ISDIR Mask = 0x40000000 /* Event occurred against dir. */ + IN_ONESHOT Mask = 0x80000000 /* Only send event once. */ + + /* Convenience macros */ + IN_CLOSE Mask = (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* Close. */ + IN_MOVE Mask = (IN_MOVED_FROM | IN_MOVED_TO) /* Moves. */ + IN_ALL_EVENTS Mask = 0x00000FFF /* All events which a program can wait on. */ + +) + +var in_bits [32]string = [32]string{ + // mask + /* 0 */ "IN_ACCESS", + /* 1 */ "IN_MODIFY", + /* 2 */ "IN_ATTRIB", + /* 3 */ "IN_CLOSE_WRITE", + /* 4 */ "IN_CLOSE_NOWRITE", + /* 5 */ "IN_OPEN", + /* 6 */ "IN_MOVED_FROM", + /* 7 */ "IN_MOVED_TO", + /* 8 */ "IN_CREATE", + /* 9 */ "IN_DELETE", + /* 10 */ "IN_DELETE_SELF", + /* 11 */ "IN_MOVE_SELF", + /* 12 */ "(1<<12)", + // events sent by the kernel + /* 13 */ "IN_UNMOUNT", + /* 14 */ "IN_Q_OVERFLOW", + /* 15 */ "IN_IGNORED", + /* 16 */ "(1<<16)", + /* 17 */ "(1<<17)", + /* 18 */ "(1<<18)", + /* 19 */ "(1<<19)", + /* 20 */ "(1<<20)", + /* 21 */ "(1<<21)", + /* 22 */ "(1<<22)", + /* 23 */ "(1<<23)", + // special flags + /* 24 */ "IN_ONLYDIR", + /* 25 */ "IN_DONT_FOLLOW", + /* 26 */ "IN_EXCL_UNLINK", + /* 27 */ "(1<<27)", + /* 28 */ "(1<<28)", + /* 29 */ "IN_MASK_ADD", + /* 30 */ "IN_ISDIR", + /* 31 */ "IN_ONESHOT", +} + +func (mask Mask) String() string { + out := "" + for i, name := range in_bits { + if mask&(Mask(1)< 0 { + out += "|" + } + out += name + } + } + return out +} diff --git a/inotify/inotify.go b/inotify/inotify.go new file mode 100644 index 0000000..976bdae --- /dev/null +++ b/inotify/inotify.go @@ -0,0 +1,94 @@ +package inotify + +import ( + "errors" + "syscall" + "unsafe" +) + +var InotifyAlreadyClosedError error = errors.New("inotify instance already closed") + +type Inotify struct { + fd Cint + isClosed bool + + fullbuff [4096]byte + buff []byte +} + +type Event struct { + Wd Cint /* Watch descriptor */ + Mask Mask /* Mask describing event */ + Cookie uint32 /* Unique cookie associating related events (for rename(2)) */ + Name *string /* Optional name */ +} + +func InotifyInit() (*Inotify, error) { + fd, err := inotify_init() + o := Inotify{ + fd: Cint(fd), + isClosed: false, + } + o.buff = o.fullbuff[:] + return &o, err +} + +func InotifyInit1(flags Cint) (*Inotify, error) { + fd, err := inotify_init1(flags) + o := Inotify{ + fd: Cint(fd), + isClosed: false, + } + o.buff = o.fullbuff[:] + return &o, err +} + +func (o *Inotify) AddWatch(path string, mask Mask) (Cint, error) { + if o.isClosed { + return -1, InotifyAlreadyClosedError + } + return inotify_add_watch(o.fd, path, uint32(mask)) +} + +func (o *Inotify) RmWatch(wd Cint) error { + if o.isClosed { + return InotifyAlreadyClosedError + } + return inotify_rm_watch(o.fd, wd) +} + +func (o *Inotify) Close() error { + if o.isClosed { + return InotifyAlreadyClosedError + } + o.isClosed = true + return sysclose(o.fd) +} + +func (o *Inotify) Read() (*Event, error) { + if len(o.buff) == 0 { + if o.isClosed { + return nil, InotifyAlreadyClosedError + } + len, err := sysread(o.fd, o.buff) + if len == 0 { + return nil, o.Close() + } else if len <= 0 { + return nil, err + } + o.buff = o.fullbuff[0:len] + } + raw := (*syscall.InotifyEvent)(unsafe.Pointer(&o.buff[0])) + var ret Event + ret.Wd = Cint(raw.Wd) + ret.Mask = Mask(raw.Mask) + ret.Cookie = raw.Cookie + ret.Name = nil + if raw.Len > 0 { + bytes := (*[syscall.NAME_MAX]byte)(unsafe.Pointer(&o.buff[syscall.SizeofInotifyEvent])) + name := string(bytes[:raw.Len-1]) + ret.Name = &name + } + o.buff = o.buff[0 : syscall.SizeofInotifyEvent+raw.Len] + return &ret, nil +} diff --git a/inotify/syscall.go b/inotify/syscall.go new file mode 100644 index 0000000..245bc41 --- /dev/null +++ b/inotify/syscall.go @@ -0,0 +1,55 @@ +package inotify + +import ( + "C" + "os" + "syscall" +) + +type Cint C.int + +/* Create and initialize inotify instance. */ +func inotify_init() (Cint, error) { + fd, errno := syscall.InotifyInit() + return Cint(fd), os.NewSyscallError("inotify_init", errno) +} + +/* Create and initialize inotify instance. */ +func inotify_init1(flags Cint) (Cint, error) { + fd, errno := syscall.InotifyInit1(int(flags)) + return Cint(fd), os.NewSyscallError("inotify_init1", errno) +} + +/* Add watch of object NAME to inotify instance FD. Notify about + events specified by MASK. */ +func inotify_add_watch(fd Cint, name string, mask uint32) (Cint, error) { + wd, errno := syscall.InotifyAddWatch(int(fd), name, mask) + return Cint(wd), os.NewSyscallError("inotify_add_watch", errno) +} + +/* Remove the watch specified by WD from the inotify instance FD. */ +func inotify_rm_watch(fd Cint, wd Cint) error { + success, errno := syscall.InotifyRmWatch(int(fd), uint32(wd)) + switch success { + case -1: + if errno == nil { + panic("should never happen") + } + os.NewSyscallError("inotify_rm_watch", errno) + case 0: + if errno != nil { + panic("should never happen") + } + return nil + } + panic("should never happen") +} + +func sysclose(fd Cint) error { + return os.NewSyscallError("close", syscall.Close(int(fd))) +} + +func sysread(fd Cint, p []byte) (Cint, error) { + n, err := syscall.Read(int(fd), p) + return Cint(n), os.NewSyscallError("read", err) +} -- cgit v1.1-4-g5e80