Module: Ligarb::Inotify

Defined in:
lib/ligarb/inotify.rb

Constant Summary collapse

LIBC =
Fiddle.dlopen(nil)
InotifyInit1 =
Fiddle::Function.new(
  LIBC["inotify_init1"],
  [Fiddle::TYPE_INT],
  Fiddle::TYPE_INT
)
InotifyAddWatch =
Fiddle::Function.new(
  LIBC["inotify_add_watch"],
  [Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP, Fiddle::TYPE_UINT32_T],
  Fiddle::TYPE_INT
)
InotifyRmWatch =
Fiddle::Function.new(
  LIBC["inotify_rm_watch"],
  [Fiddle::TYPE_INT, Fiddle::TYPE_INT],
  Fiddle::TYPE_INT
)
IN_CLOSE_WRITE =

inotify event masks

0x00000008
IN_CLOEXEC =

O_CLOEXEC on x86_64

0x00080000
EVENT_HEADER_SIZE =

struct inotify_event fixed part: int wd, uint32 mask, uint32 cookie, uint32 len

16

Class Method Summary collapse

Class Method Details

.add_watch(fd, path) ⇒ Object

Raises:

  • (SystemCallError)


69
70
71
72
73
# File 'lib/ligarb/inotify.rb', line 69

def self.add_watch(fd, path)
  wd = InotifyAddWatch.call(fd, path, IN_CLOSE_WRITE)
  raise SystemCallError.new("inotify_add_watch: #{path}", Fiddle.last_error) if wd < 0
  wd
end

.watch_file(path, &block) ⇒ Object

Watch a file for writes. Yields each time the file is written and closed. Blocks the calling thread. Caller should wrap in Thread.new.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/ligarb/inotify.rb', line 39

def self.watch_file(path, &block)
  fd = InotifyInit1.call(IN_CLOEXEC)
  raise SystemCallError.new("inotify_init1", Fiddle.last_error) if fd < 0

  io = IO.for_fd(fd, autoclose: true)
  wd = add_watch(fd, path)

  loop do
    # IO.select releases GVL while waiting
    IO.select([io])
    buf = io.read_nonblock(4096, exception: false)
    next if buf == :wait_readable || buf.nil?

    # Parse events — may contain multiple events
    offset = 0
    while offset + EVENT_HEADER_SIZE <= buf.bytesize
      _wd, mask, _cookie, name_len = buf.byteslice(offset, EVENT_HEADER_SIZE).unpack("iIII")
      offset += EVENT_HEADER_SIZE + name_len

      if mask & IN_CLOSE_WRITE != 0
        block.call
      end
    end
  rescue IOError, Errno::EBADF
    break
  end
ensure
  io&.close rescue nil
end