#pike __REAL_VERSION__ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
inherit "basic.pike" : basic; |
|
#if constant(readlink) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected class DefaultMonitor |
{ |
|
inherit basic::DefaultMonitor; |
|
|
int symlinks; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void low_call_callback(function(string, mixed ...:void) cb, |
mapping(string:int) state, |
mapping(string:string) symlink_targets, |
string path, Stdio.Stat|void st, |
string|void symlink) |
{ |
if (!cb || state[path] || (st && st->islnk)) return; |
state[path] = 1; |
if (!symlink || !symlink_targets[symlink]) { |
cb(path, st); |
} |
if (sizeof(symlink_targets)) { |
|
foreach(reverse(sort(indices(symlink_targets))), string src) { |
string dest = symlink_targets[src]; |
if (has_prefix(path, dest)) { |
low_call_callback(cb, state, symlink_targets - ([ src : dest ]), |
src + path[sizeof(dest)..], st, symlink); |
} |
} |
} |
} |
|
|
|
|
|
|
|
|
protected void call_callback(function(string, mixed ...:void) cb, |
string path, Stdio.Stat|void st) |
{ |
if (!cb) return; |
low_call_callback(cb, ([]), global::symlink_targets, path, st); |
} |
|
protected void notify_symlink(function(string, mixed ...:void) cb, |
string sym) |
{ |
int sym_id = global::symlink_ids[sym]; |
if (sym_id) { |
|
foreach(reverse(sort(filter(values(monitors), |
lambda(Monitor m, int sym_id) { |
return m->symlinks & sym_id; |
}, sym_id)->path)), |
string m_path) { |
Monitor m = monitors[m_path]; |
m->low_call_callback(cb, ([]), global::symlink_targets, |
m_path, m->st, sym); |
} |
} |
} |
|
|
protected void zap_symlink(string path) |
{ |
string old_dest = global::symlink_targets[path]; |
|
if (old_dest) { |
int sym_id = global::symlink_ids[path]; |
foreach(monitors; string m_path; Monitor m) { |
if (m->symlinks & sym_id) { |
m->low_call_callback(global::file_deleted, ([]), |
global::symlink_targets, |
m_path, UNDEFINED, path); |
m->symlinks -= sym_id; |
|
|
m->check_for_release(MF_AUTO|MF_HARD, MF_AUTO); |
} |
} |
global::available_ids |= m_delete(symlink_ids, path); |
m_delete(global::symlink_targets, path); |
} |
} |
|
|
protected void check_symlink(string path, Stdio.Stat st, |
int|void inhibit_notify) |
{ |
string dest; |
if (st && st->islnk) { |
dest = readlink(path); |
if (dest) { |
dest = canonic_path(combine_path(path, "..", dest)); |
if (symlink_targets[path] == dest) return; |
} |
} |
if (symlink_targets[path]) { |
zap_symlink(path); |
} |
if (dest) { |
|
symlink_targets[path] = dest; |
int sym_id = allocate_symlink(path); |
int sym_mask = sym_id | symlink_ids[dest]; |
int sym_done = sym_id; |
Monitor m; |
if (!(m = monitors[dest])) { |
MonitorFlags m_flags = (flags & ~MF_HARD) | MF_AUTO; |
if (inhibit_notify) { |
m_flags &= ~MF_INITED; |
} |
monitor(dest, m_flags, |
max_dir_check_interval, |
file_interval_factor, |
stable_time); |
m = monitors[dest]; |
} |
m->symlinks |= sym_id; |
if (!has_suffix(dest, "/")) { |
dest += "/"; |
} |
foreach(monitors; string mm_path; Monitor mm) { |
if (has_prefix(mm_path, dest)) { |
mm->symlinks |= sym_id; |
sym_mask |= symlink_ids[mm_path]; |
} |
} |
|
while (sym_mask != sym_done) { |
int mask = sym_mask - sym_done; |
foreach(monitors; string mm_path; Monitor mm) { |
if ((mm->symlinks & mask) && !(mm->symlinks & sym_id)) { |
mm->symlinks |= sym_id; |
sym_mask |= symlink_ids[mm_path]; |
} |
} |
sym_done |= mask; |
} |
if (!inhibit_notify) { |
notify_symlink(global::file_created, path); |
} |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void attr_changed(string path, Stdio.Stat st) |
{ |
check_symlink(path, st); |
if (st && st->islnk) { |
return; |
} |
::attr_changed(path, st); |
} |
|
protected void low_file_exists(string path, Stdio.Stat st) |
{ |
|
|
if (!st || !global::file_exists) return; |
global::file_exists(path, st); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void file_exists(string path, Stdio.Stat st) |
{ |
check_symlink(path, st, 1); |
if (st && st->islnk) { |
notify_symlink(low_file_exists, path); |
return; |
} |
::file_exists(path, st); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
protected void file_created(string path, Stdio.Stat st) |
{ |
check_symlink(path, st); |
if (st && st->islnk) { |
|
|
return; |
} |
::file_created(path, st); |
} |
|
|
|
|
|
|
|
|
|
|
|
protected void file_deleted(string path, Stdio.Stat old_st) |
{ |
check_symlink(path, UNDEFINED); |
if (old_st && old_st->islnk) { |
return; |
} |
::file_deleted(path, old_st); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected void stable_data_change(string path, Stdio.Stat st) |
{ |
if (st && st->islnk) { |
int sym_id = global::symlink_ids[path]; |
if (sym_id) { |
foreach(monitors; string m_path; Monitor m) { |
if ((m->symlinks & sym_id) && (m->last_change == 0x7fffffff)) { |
m->low_call_callback(global::stable_data_change, ([]), |
global::symlink_targets, m_path, m->st, path); |
} |
} |
} |
return; |
} |
::stable_data_change(path, st); |
} |
|
|
void check_for_release(int mask, int flags) |
{ |
if (symlinks) { |
|
foreach(symlink_targets;; string dest) { |
if (path == dest) { |
|
return; |
} |
} |
} |
::check_for_release(mask, flags); |
} |
|
|
protected void monitor(string path, int flags, int max_dir_interval, |
int file_interval_factor, int stable_time) |
{ |
object m; |
::monitor(path, flags, max_dir_check_interval, |
file_interval_factor, stable_time); |
if((m = monitors[path])) m->symlinks |= symlinks; |
} |
|
|
protected int(0..1) status_change(Stdio.Stat old_st, Stdio.Stat st, |
int orig_flags, int flags) |
{ |
check_symlink(path, st); |
if (st && st->islnk) { |
return 1; |
} |
return ::status_change(old_st, st, orig_flags, flags); |
} |
|
} |
|
#endif /* constant(readlink) */ |
|
|
protected mapping(string:string) symlink_targets = ([]); |
|
|
protected mapping(string:int) symlink_ids = ([]); |
|
|
protected int available_ids = -1; |
|
|
protected int allocate_symlink(string sym) |
{ |
int res = symlink_ids[sym]; |
if (res) return res; |
res = available_ids & ~(available_ids - 1); |
available_ids -= res; |
return symlink_ids[sym] = res; |
} |
|
|