blob: 4e29caff5d79514d65f493ea4ec83838445e0053 [file] [log] [blame]
module Listen
module Adapter
# @see https://github.com/nex3/rb-inotify
class Linux < Base
OS_REGEXP = /linux/i
DEFAULTS = {
events: [
:recursive,
:attrib,
:create,
:delete,
:move,
:close_write
],
wait_for_delay: 0.1
}
private
WIKI_URL = 'https://github.com/guard/listen'\
'/wiki/Increasing-the-amount-of-inotify-watchers'
INOTIFY_LIMIT_MESSAGE = <<-EOS.gsub(/^\s*/, '')
FATAL: Listen error: unable to monitor directories for changes.
Visit #{WIKI_URL} for info on how to fix this.
EOS
def _configure(directory, &callback)
require 'rb-inotify'
@worker ||= ::INotify::Notifier.new
@worker.watch(directory.to_s, *options.events, &callback)
rescue Errno::ENOSPC
abort(INOTIFY_LIMIT_MESSAGE)
end
def _run
Thread.current[:listen_blocking_read_thread] = true
@worker.run
Thread.current[:listen_blocking_read_thread] = false
end
def _process_event(dir, event)
# NOTE: avoid using event.absolute_name since new API
# will need to have a custom recursion implemented
# to properly match events to configured directories
path = Pathname.new(event.watcher.path) + event.name
rel_path = path.relative_path_from(dir).to_s
_log(:debug) { "inotify: #{rel_path} (#{event.flags.inspect})" }
if /1|true/ =~ ENV['LISTEN_GEM_SIMULATE_FSEVENT']
if (event.flags & [:moved_to, :moved_from]) || _dir_event?(event)
rel_path = path.dirname.relative_path_from(dir).to_s
_queue_change(:dir, dir, rel_path, {})
else
_queue_change(:dir, dir, rel_path, {})
end
return
end
return if _skip_event?(event)
cookie_params = event.cookie.zero? ? {} : { cookie: event.cookie }
# Note: don't pass options to force rescanning the directory, so we can
# detect moving/deleting a whole tree
if _dir_event?(event)
_queue_change(:dir, dir, rel_path, cookie_params)
return
end
params = cookie_params.merge(change: _change(event.flags))
_queue_change(:file, dir, rel_path, params)
end
def _skip_event?(event)
# Event on root directory
return true if event.name == ''
# INotify reports changes to files inside directories as events
# on the directories themselves too.
#
# @see http://linux.die.net/man/7/inotify
_dir_event?(event) && (event.flags & [:close, :modify]).any?
end
def _change(event_flags)
{ modified: [:attrib, :close_write],
moved_to: [:moved_to],
moved_from: [:moved_from],
added: [:create],
removed: [:delete] }.each do |change, flags|
return change unless (flags & event_flags).empty?
end
nil
end
def _dir_event?(event)
event.flags.include?(:isdir)
end
def _stop
@worker && @worker.close
end
end
end
end