#pike __REAL_VERSION__ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef FILESYSTEM_MONITOR_DEBUG |
#define MON_WERR(X...) werror(X) |
#else |
#define MON_WERR(X...) |
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
protected constant default_max_dir_check_interval = 60; |
|
|
|
|
|
|
|
|
|
|
protected constant default_file_interval_factor = 5; |
|
|
|
protected constant default_stable_time = 5; |
|
protected int max_dir_check_interval = default_max_dir_check_interval; |
protected int file_interval_factor = default_file_interval_factor; |
protected int stable_time = default_stable_time; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void data_changed(string path); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void attr_changed(string path, Stdio.Stat st); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void file_exists(string path, Stdio.Stat st); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void file_created(string path, Stdio.Stat st); |
|
|
|
|
|
|
|
|
|
|
|
|
|
void file_deleted(string path); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void stable_data_change(string path, Stdio.Stat st); |
|
|
enum MonitorFlags { |
MF_RECURSE = 1, |
MF_AUTO = 2, |
MF_INITED = 4, |
MF_HARD = 8, |
}; |
|
protected constant S_IFMT = 0x7ffff000; |
|
|
|
|
|
protected class Monitor(string path, |
MonitorFlags flags, |
int max_dir_check_interval, |
int file_interval_factor, |
int stable_time) |
{ |
inherit ADT.Heap.Element; |
|
int next_poll; |
Stdio.Stat st; |
int last_change = 0x7fffffff; |
|
|
|
array(string) files; |
|
|
|
|
|
protected void register_path(int|void initial) |
{ |
if (initial) { |
|
MON_WERR("Registering %O for polling.\n", path); |
monitor_queue->push(this); |
} |
} |
|
|
|
|
|
protected void unregister_path(int|void dying) |
{ |
if (dying) { |
|
|
MON_WERR("Unregistering %O from polling.\n", path); |
monitor_queue->remove(this); |
} |
} |
|
int `<(mixed m) { return next_poll < m; } |
int `>(mixed m) { return next_poll > m; } |
|
void create() |
{ |
Element::create(this); |
register_path(1); |
} |
|
|
|
|
|
|
|
|
|
|
|
protected void call_callback(function(string, Stdio.Stat|void:void) cb, |
string path, Stdio.Stat|void st) |
{ |
if (!cb) return; |
cb(path, st); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void attr_changed(string path, Stdio.Stat st) |
{ |
if (global::data_changed) { |
call_callback(global::data_changed, path); |
} else { |
call_callback(global::attr_changed, path, st); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void file_exists(string path, Stdio.Stat st) |
{ |
register_path(); |
int t = time(1); |
call_callback(global::file_exists, path, st); |
if (st->mtime + (stable_time || global::stable_time) >= t) { |
|
|
last_change = st->mtime; |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
protected void file_created(string path, Stdio.Stat st) |
{ |
register_path(); |
call_callback(global::file_created, path, st); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void file_deleted(string path, Stdio.Stat|void old_st) |
{ |
unregister_path(); |
call_callback(global::file_deleted, path); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void stable_data_change(string path, Stdio.Stat st) |
{ |
call_callback(global::stable_data_change, path, st); |
} |
|
protected string _sprintf(int c) |
{ |
return sprintf("Monitor(%O, %O, next: %s, st: %O)", |
path, flags, ctime(next_poll) - "\n", st); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void bump(MonitorFlags|void flags, int|void seconds) |
{ |
int now = time(1); |
if (seconds) |
next_poll = now + seconds; |
else if (next_poll > now) |
next_poll -= (next_poll - now) / 2; |
adjust_monitor(this); |
|
if ((flags & MF_RECURSE) && st->isdir && files) { |
|
foreach(files, string file) { |
file = canonic_path(Stdio.append_path(path, file)); |
Monitor m2 = monitors[file]; |
if (m2) { |
m2->bump(flags, seconds); |
} |
} |
} |
} |
|
|
|
|
|
|
|
|
protected void update(Stdio.Stat st) |
{ |
int delta = max_dir_check_interval || global::max_dir_check_interval; |
this::st = st; |
|
if (st) { |
|
|
|
int d = |
(stable_time || global::stable_time) + |
((time(1) - max(st->mtime, st->ctime)) >> 2); |
if (d < 0) d = max_dir_check_interval || global::max_dir_check_interval; |
if (d < delta) delta = d; |
} |
if (last_change <= time(1)) { |
|
int d = last_change + (stable_time || global::stable_time) - time(1); |
d >>= 1; |
if (d < 0) d = 1; |
if (d < delta) delta = d; |
} else if (!st || !st->isdir) { |
delta *= file_interval_factor || global::file_interval_factor; |
} |
|
if (!next_poll) { |
|
delta = 1 + random(delta); |
} |
|
next_poll = time(1) + (delta || 1); |
adjust_monitor(this); |
} |
|
|
void check_for_release(int mask, int flags) |
{ |
if ((this::flags & mask) == flags) { |
m_delete(monitors, path); |
release_monitor(this); |
} |
} |
|
|
protected void monitor(string path, int flags, int max_dir_interval, |
int file_interval_factor, int stable_time) |
{ |
global::monitor(path, flags, max_dir_check_interval, |
file_interval_factor, stable_time); |
} |
|
|
protected int(0..1) status_change(Stdio.Stat old_st, Stdio.Stat st, |
int orig_flags, int flags) |
{ |
if (st->isdir) { |
int res = 0; |
array(string) files = get_dir(path) || ({}); |
array(string) new_files = files; |
array(string) deleted_files = ({}); |
if (this::files) { |
new_files -= this::files; |
deleted_files = this::files - files; |
} |
this::files = files; |
foreach(new_files, string file) { |
res = 1; |
file = canonic_path(Stdio.append_path(path, file)); |
if(filter_file(file)) continue; |
Monitor m2 = monitors[file]; |
mixed err = catch { |
if (m2) { |
|
|
m2->check(flags); |
} |
}; |
if (this::flags & MF_RECURSE) { |
monitor(file, orig_flags | MF_AUTO | MF_HARD, |
max_dir_check_interval, |
file_interval_factor, |
stable_time); |
monitors[file]->check(); |
} else if (!m2) { |
file_created(file, file_stat(file, 1)); |
} |
} |
foreach(deleted_files, string file) { |
res = 1; |
file = canonic_path(Stdio.append_path(path, file)); |
if(filter_file(file)) continue; |
Monitor m2 = monitors[file]; |
mixed err = catch { |
if (m2) { |
|
|
m2->check(flags); |
} |
}; |
if (this::flags & MF_RECURSE) { |
|
|
|
} else if (!m2) { |
file_deleted(file); |
} |
if (err) throw(err); |
} |
if (flags & MF_RECURSE) { |
|
foreach(((files - new_files) - deleted_files), string file) { |
file = canonic_path(Stdio.append_path(path, file)); |
if(filter_file(file)) continue; |
Monitor m2 = monitors[file]; |
if (m2) { |
m2->bump(flags); |
} else { |
|
|
|
|
|
|
|
res = 1; |
monitor(file, orig_flags | MF_AUTO | MF_HARD, |
max_dir_check_interval, |
file_interval_factor, |
stable_time); |
monitors[file]->check(); |
} |
} |
} |
return res; |
} else { |
attr_changed(path, st); |
return 1; |
} |
return 0; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int(0..1) check(MonitorFlags|void flags) |
{ |
MON_WERR("Checking monitor %O...\n", this); |
Stdio.Stat st = file_stat(path, 1); |
Stdio.Stat old_st = this::st; |
int orig_flags = this::flags; |
this::flags |= MF_INITED; |
update(st); |
if (!(orig_flags & MF_INITED)) { |
|
if (st) { |
if (st->isdir) { |
array(string) files = get_dir(path) || ({}); |
this::files = files; |
foreach(files, string file) { |
file = canonic_path(Stdio.append_path(path, file)); |
if(filter_file(file)) continue; |
if (monitors[file]) { |
|
|
continue; |
} |
if (this::flags & MF_RECURSE) { |
monitor(file, orig_flags | MF_AUTO | MF_HARD, |
max_dir_check_interval, |
file_interval_factor, |
stable_time); |
check_monitor(monitors[file]); |
} else { |
file_exists(file, file_stat(file, 1)); |
} |
} |
} |
|
file_exists(path, st); |
} |
return 1; |
} |
if (old_st) { |
if (!st || ((old_st->mode & S_IFMT) != (st->mode & S_IFMT))) { |
|
|
int delay; |
|
if (files) { |
foreach(files, string file) { |
file = canonic_path(Stdio.append_path(path, file)); |
if (monitors[file]) { |
|
monitors[file]->next_poll = time(1)-1; |
adjust_monitor(monitors[file]); |
delay = 1; |
} |
} |
} |
if (delay) { |
|
st = old_st; |
next_poll = time(1); |
adjust_monitor(this); |
} else { |
if (st) { |
|
|
st = UNDEFINED; |
|
|
next_poll = time(1); |
adjust_monitor(this); |
} else { |
|
this::flags &= ~MF_HARD; |
|
|
check_for_release(MF_AUTO, MF_AUTO); |
} |
|
file_deleted(path, old_st); |
return 1; |
} |
return 0; |
} |
} else if (st) { |
|
|
last_change = time(1); |
file_created(path, st); |
if (st->isdir) { |
array(string) files = get_dir(path) || ({}); |
this::files = files; |
foreach(files, string file) { |
file = canonic_path(Stdio.append_path(path, file)); |
if (monitors[file]) { |
|
|
continue; |
} |
if (this::flags & MF_RECURSE) { |
monitor(file, orig_flags | MF_AUTO | MF_HARD, |
max_dir_check_interval, |
file_interval_factor, |
stable_time); |
check_monitor(monitors[file]); |
} else { |
file_created(file, file_stat(file, 1)); |
} |
} |
} |
update(st); |
return 1; |
} else { |
return 0; |
} |
|
|
|
if (last_change != -0x7fffffff && |
((st->mtime != old_st->mtime) || |
|
(st->size != old_st->size))) { |
last_change = time(1); |
update(st); |
if (status_change(old_st, st, orig_flags, flags)) return 1; |
} else if (last_change < time(1) - (stable_time || global::stable_time)) { |
last_change = 0x7fffffff; |
stable_data_change(path, st); |
return 1; |
} else if (last_change != 0x7fffffff && |
st->isdir && status_change(old_st, st, orig_flags, flags)) { |
|
last_change = time(1); |
update(st); |
return 1; |
} |
return 0; |
} |
|
protected void destroy() |
{ |
unregister_path(1); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if constant(System.FSEvents.EventStream) |
#define HAVE_EVENTSTREAM 1 |
#endif |
|
#if constant(System.Inotify) |
#define HAVE_INOTIFY 1 |
#endif |
|
#if HAVE_EVENTSTREAM |
protected System.FSEvents.EventStream eventstream; |
protected array(string) eventstream_paths = ({}); |
|
|
|
protected void eventstream_callback(string path, int flags, int event_id) |
{ |
MON_WERR("eventstream_callback(%O, 0x%08x, %O)\n", path, flags, event_id); |
if(path[-1] == '/') path = path[0..<1]; |
MON_WERR("Normalized path: %O\n", path); |
if(monitors[path]) { |
monitors[path]->check(0); |
} else { |
MON_WERR("No monitor found for path %O. Checking all monitors...\n", path); |
check_all(); |
} |
} |
|
|
protected class EventStreamMonitor |
{ |
inherit Monitor; |
|
int(0..1) accellerated; |
|
protected void file_exists(string path, Stdio.Stat st) |
{ |
::file_exists(path, st); |
if ((last_change != 0x7fffffff) && accellerated) { |
|
int t = time(1) - last_change; |
if (t < 0) t = 0; |
(backend || Pike.DefaultBackend)-> |
call_out(check, (stable_time || global::stable_time) + 1 - t); |
} |
} |
|
protected void file_created(string path, Stdio.Stat st) |
{ |
if (accellerated) { |
(backend || Pike.DefaultBackend)-> |
call_out(check, (stable_time || global::stable_time) + 1); |
} |
::file_created(path, st); |
} |
|
protected void attr_changed(string path, Stdio.Stat st) |
{ |
if (accellerated) { |
(backend || Pike.DefaultBackend)-> |
call_out(check, (stable_time || global::stable_time) + 1); |
} |
::attr_changed(path, st); |
} |
|
protected void register_path(int|void initial) |
{ |
if (backend) { |
|
::register_path(initial); |
return; |
} |
#ifndef INHIBIT_EVENTSTREAM_MONITOR |
if (!initial) return; |
|
if (!eventstream) { |
|
Pike.DefaultBackend.enable_core_foundation(1); |
|
MON_WERR("Creating event stream.\n"); |
eventstream = |
System.FSEvents.EventStream(({}), 3.0, |
System.FSEvents.kFSEventStreamEventIdSinceNow, |
System.FSEvents.kFSEventStreamCreateFlagNone); |
eventstream->callback_func = eventstream_callback; |
} else { |
string found; |
foreach(eventstream_paths;;string p) { |
if (path == p) { |
|
|
|
MON_WERR("Path %O already monitored.\n", path); |
::register_path(initial); |
return; |
} |
if((path == p) || has_prefix(path, p + "/")) { |
MON_WERR("Path %O already monitored via path %O.\n", path, p); |
found = p; |
} |
} |
if (found) { |
MON_WERR("Path %O is accellerated via %O.\n", path, found); |
accellerated = 1; |
return; |
} |
} |
|
MON_WERR("Adding %O to the set of monitored paths.\n", path); |
eventstream_paths += ({path}); |
if(eventstream->is_started()) |
eventstream->stop(); |
eventstream->add_path(path); |
eventstream->start(); |
#endif /* !INHIBIT_EVENTSTREAM_MONITOR */ |
|
|
::register_path(initial); |
} |
|
int(0..1) check(MonitorFlags|void flags) |
{ |
MON_WERR("Checking path %O...\n", path); |
int orig_flags = this::flags; |
|
int(0..1) ret = ::check(flags); |
if(orig_flags & MF_RECURSE) { |
|
|
array(string) files = get_dir(path) || ({}); |
this::files = files; |
foreach(files, string file) { |
file = canonic_path(Stdio.append_path(path, file)); |
if (monitors[file]) { |
if(check_monitor(monitors[file])) ret = 1; |
} |
} |
} |
return ret; |
} |
} |
|
constant DefaultMonitor = EventStreamMonitor; |
|
#elseif HAVE_INOTIFY |
|
protected System.Inotify._Instance instance; |
|
protected string(8bit) inotify_cookie(int wd) |
{ |
|
return sprintf("\0%8c", wd); |
} |
|
|
protected void inotify_event(int wd, int event, int cookie, string(8bit) path) |
{ |
MON_WERR("inotify_event(%O, %s, %O, %O)...\n", |
wd, System.Inotify.describe_mask(event), cookie, path); |
string(8bit) icookie = inotify_cookie(wd); |
Monitor m; |
if((m = monitors[icookie])) { |
if (event == System.Inotify.IN_IGNORED) { |
|
|
MON_WERR("### Monitor watch descriptor %d is no more.\n", wd); |
m_delete(monitors, icookie); |
} |
mixed err = catch { |
m->check(0); |
}; |
if (err) { |
master()->handler_error(err); |
} |
} else { |
|
MON_WERR("### Unknown monitor! Performing full scan.\n"); |
check_all(); |
|
|
} |
} |
|
|
protected class InotifyMonitor |
{ |
inherit Monitor; |
|
protected int wd = -1; |
int `accellerated() { return wd != -1; } |
|
protected void file_exists(string path, Stdio.Stat st) |
{ |
::file_exists(path, st); |
if ((last_change != 0x7fffffff) && (wd != -1)) { |
|
int t = time(1) - last_change; |
if (t < 0) t = 0; |
(backend || Pike.DefaultBackend)-> |
call_out(check, (stable_time || global::stable_time) + 1 - t); |
} |
} |
|
protected void file_created(string path, Stdio.Stat st) |
{ |
if (wd != -1) { |
(backend || Pike.DefaultBackend)-> |
call_out(check, (stable_time || global::stable_time) + 1); |
} |
::file_created(path, st); |
} |
|
protected void attr_changed(string path, Stdio.Stat st) |
{ |
if (wd != -1) { |
(backend || Pike.DefaultBackend)-> |
call_out(check, (stable_time || global::stable_time) + 1); |
} |
::attr_changed(path, st); |
} |
|
protected void register_path(int|void initial) |
{ |
if (wd != -1) return; |
|
#ifndef INHIBIT_INOTIFY_MONITOR |
if (initial && !instance) { |
MON_WERR("Creating Inotify monitor instance.\n"); |
instance = System.Inotify._Instance(); |
if (backend) instance->set_backend(backend); |
instance->set_event_callback(inotify_event); |
if (co_id) { |
MON_WERR("Turning on nonblocking mode for Inotify.\n"); |
instance->set_nonblocking(); |
} |
} |
|
catch { |
int new_wd = instance->add_watch(path, |
System.Inotify.IN_MOVED_FROM | |
System.Inotify.IN_UNMOUNT | |
System.Inotify.IN_MOVED_TO | |
System.Inotify.IN_MASK_ADD | |
System.Inotify.IN_MOVE_SELF | |
System.Inotify.IN_DELETE | |
System.Inotify.IN_MOVE | |
System.Inotify.IN_MODIFY | |
System.Inotify.IN_ATTRIB | |
System.Inotify.IN_DELETE_SELF | |
System.Inotify.IN_CREATE); |
|
MON_WERR("Registered %O with %O ==> %d.\n", path, instance, new_wd); |
if (new_wd != -1) { |
|
if (!initial) { |
MON_WERR("Unregistering from polling.\n"); |
release_monitor(this); |
} |
wd = new_wd; |
monitors[inotify_cookie(wd)] = this; |
if (initial) { |
|
|
check(); |
} |
return; |
} |
}; |
#endif /* !INHIBIT_INOTIFY_MONITOR */ |
MON_WERR("Registering %O for polling.\n", path); |
::register_path(initial); |
} |
|
protected void unregister_path(int|void dying) |
{ |
MON_WERR("Unregistering %O...\n", path); |
if (wd != -1) { |
|
|
if (instance && dying) { |
MON_WERR("### Unregistering from inotify.\n"); |
|
|
|
|
|
mixed err = catch { |
instance->rm_watch(wd); |
}; |
if (err) { |
MON_WERR("### Failed: %s\n", describe_backtrace(err)); |
} |
} |
wd = -1; |
if (!dying) { |
|
MON_WERR("Registering for polling.\n"); |
monitor_queue->push(this); |
} |
} |
::unregister_path(dying); |
} |
} |
|
constant DefaultMonitor = InotifyMonitor; |
|
#else |
|
constant DefaultMonitor = Monitor; |
|
#endif /* HAVE_EVENTSTREAM || HAVE_INOTIFY */ |
|
|
|
|
|
|
|
|
|
protected string canonic_path(string path) |
{ |
#if HAVE_EVENTSTREAM |
if (!backend) { |
path = System.resolvepath(path); |
} |
#endif |
return combine_path(path, "."); |
} |
|
|
|
|
|
|
|
|
|
protected mapping(string:Monitor) monitors = ([]); |
|
|
|
|
protected ADT.Heap monitor_queue = ADT.Heap(); |
|
|
|
|
|
|
|
|
|
|
|
protected void create(int|void max_dir_check_interval, |
int|void file_interval_factor, |
int|void stable_time) |
{ |
if (max_dir_check_interval > 0) { |
this::max_dir_check_interval = max_dir_check_interval; |
} |
if (file_interval_factor > 0) { |
this::file_interval_factor = file_interval_factor; |
} |
if (stable_time > 0) { |
this::stable_time = stable_time; |
} |
clear(); |
} |
|
|
|
|
|
|
void clear() |
{ |
monitors = ([]); |
monitor_queue = ADT.Heap(); |
#if HAVE_EVENTSTREAM |
eventstream = 0; |
#elseif HAVE_INOTIFY |
instance = 0; |
#endif |
} |
|
|
|
|
|
|
|
|
|
|
|
protected void update_monitor(Monitor m, Stdio.Stat st) |
{ |
m->update(st); |
} |
|
|
|
|
|
protected void release_monitor(Monitor m) |
{ |
if (m->accellerated) return; |
monitor_queue->remove(m); |
} |
|
|
|
protected void adjust_monitor(Monitor m) |
{ |
if (m->accellerated) return; |
monitor_queue->adjust(m); |
} |
|
|
|
|
|
|
|
|
|
|
|
protected DefaultMonitor monitor_factory(string path, MonitorFlags|void flags, |
int(0..)|void max_dir_check_interval, |
int(0..)|void file_interval_factor, |
int(0..)|void stable_time) |
{ |
return DefaultMonitor(path, flags, max_dir_check_interval, |
file_interval_factor, stable_time); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void monitor(string path, MonitorFlags|void flags, |
int(0..)|void max_dir_check_interval, |
int(0..)|void file_interval_factor, |
int(0..)|void stable_time) |
{ |
path = canonic_path(path); |
if(filter_file(path)) return; |
Monitor m = monitors[path]; |
if (m) { |
if (!(flags & MF_AUTO)) { |
|
|
m->flags = flags; |
m->max_dir_check_interval = max_dir_check_interval; |
m->file_interval_factor = file_interval_factor; |
m->stable_time = stable_time; |
m->next_poll = 0; |
adjust_monitor(m); |
} |
if (flags & MF_HARD) { |
m->flags |= MF_HARD; |
} |
|
|
} else { |
m = monitor_factory(path, flags, max_dir_check_interval, |
file_interval_factor, stable_time); |
monitors[path] = m; |
|
|
} |
} |
|
int filter_file(string path) |
{ |
array x = path/"/"; |
foreach(x;; string pc) |
if(pc && strlen(pc) && pc[0]=='.' && pc != "..") { |
MON_WERR("skipping %O\n", path); |
return 1; |
} |
|
return 0; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void release(string path, MonitorFlags|void flags) |
{ |
path = canonic_path(path); |
Monitor m = m_delete(monitors, path); |
if (!m) return; |
|
release_monitor(m); |
if (flags && m->st && m->st->isdir) { |
if (!sizeof(path) || path[-1] != '/') { |
path += "/"; |
} |
foreach(monitors; string mpath; m) { |
if (has_prefix(mpath, path)) { |
m->check_for_release(flags, flags); |
} |
} |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
int(0..1) is_monitored(string path) |
{ |
return !!monitors[canonic_path(path)]; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected int(0..1) check_monitor(Monitor m, MonitorFlags|void flags) |
{ |
return m->check(flags); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void check_all(mapping(string:int)|void ret_stats) |
{ |
int cnt; |
int scanned_cnt; |
foreach(monitors; string path; Monitor m) { |
scanned_cnt++; |
mixed err = catch { |
cnt += check_monitor(m); |
}; |
if (err) { |
master()->handle_error(err); |
} |
} |
if (ret_stats) { |
ret_stats->num_monitors = sizeof(monitors); |
ret_stats->scanned_monitors = scanned_cnt; |
ret_stats->updated_monitors = cnt; |
ret_stats->idle_time = 0; |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int check(int|void max_wait, int|void max_cnt, |
mapping(string:int)|void ret_stats) |
{ |
#if HAVE_INOTIFY |
if (instance) { |
|
instance->poll(); |
} |
#endif |
int scan_cnt = max_cnt; |
int scan_wait = max_wait; |
while(1) { |
int ret = max_dir_check_interval; |
int cnt; |
int t = time(); |
if (sizeof(monitor_queue)) { |
Monitor m; |
while ((m = monitor_queue->peek()) && (m->next_poll <= t)) { |
cnt += check_monitor(m); |
if (!(--scan_cnt)) { |
m = monitor_queue->peek(); |
break; |
} |
} |
if (m) { |
ret = m->next_poll - t; |
if (ret <= 0) ret = 1; |
} else { |
scan_cnt--; |
} |
} |
if (cnt || !scan_wait || !scan_cnt) { |
if (ret_stats) { |
ret_stats->num_monitors = sizeof(monitors); |
ret_stats->scanned_monitors = max_cnt - scan_cnt; |
ret_stats->updated_monitors = cnt; |
ret_stats->idle_time = max_wait - scan_wait; |
} |
return ret; |
} |
if (ret < scan_wait) { |
scan_wait -= ret; |
sleep(ret); |
} else { |
if (scan_wait > 0) scan_wait--; |
sleep(1); |
} |
} |
} |
|
|
|
|
protected Pike.Backend backend; |
|
|
|
|
|
|
|
|
|
protected mixed co_id; |
|
|
|
|
|
void set_backend(Pike.Backend|void backend) |
{ |
int was_nonblocking = !!co_id; |
set_blocking(); |
this::backend = backend; |
#if HAVE_EVENTSTREAM |
#if 0 /* FIXME: The following does NOT work properly. */ |
if (eventstream && backend) { |
foreach(monitors; string path; Monitor m) { |
if (m->accellerated) { |
m->accellerated = 0; |
monitor_queue->push(m); |
} |
} |
} |
#endif |
#elif HAVE_INOTIFY |
if (instance) { |
instance->set_backend(backend || Pike.DefaultBackend); |
} |
#endif |
if (was_nonblocking) { |
set_nonblocking(); |
} |
} |
|
|
|
|
|
void set_blocking() |
{ |
if (co_id) { |
if (backend) backend->remove_call_out(co_id); |
else remove_call_out(co_id); |
co_id = 0; |
} |
#if HAVE_INOTIFY |
if (instance) { |
instance->set_blocking(); |
} |
#endif |
} |
|
|
|
|
|
|
|
|
|
protected void backend_check() |
{ |
if (co_id != 1) co_id = 0; |
int t; |
mixed err = catch { |
t = check(0); |
}; |
set_nonblocking(t); |
if (err) throw(err); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void set_nonblocking(int|void t) |
{ |
#if HAVE_INOTIFY |
if (instance) { |
instance->set_nonblocking(); |
} |
#endif |
if (co_id) return; |
|
|
|
|
|
if (undefinedp(t)) { |
if (sizeof(monitor_queue)) { |
Monitor m = monitor_queue->peek(); |
t = (m && m->next_poll - time(1)) || max_dir_check_interval; |
} else { |
t = max_dir_check_interval; |
} |
if (t > max_dir_check_interval) t = max_dir_check_interval; |
if (t < 0) t = 0; |
} |
if (backend) co_id = backend->call_out(backend_check, t); |
else co_id = call_out(backend_check, t); |
} |
|
|
void set_max_dir_check_interval(int max_dir_check_interval) |
{ |
if (max_dir_check_interval > 0) { |
this::max_dir_check_interval = max_dir_check_interval; |
} else { |
this::max_dir_check_interval = default_max_dir_check_interval; |
} |
} |
|
|
void set_file_interval_factor(int file_interval_factor) |
{ |
if (file_interval_factor > 0) { |
this::file_interval_factor = file_interval_factor; |
} else { |
this::file_interval_factor = default_file_interval_factor; |
} |
} |
|
|