562289 | 2013-09-20 | Henrik Grubbström (Grubba) | |
#if constant(Filesystem.Monitor.basic)
|
3c5dd4 | 2013-10-02 | Henrik Grubbström (Grubba) | |
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | |
#ifdef FSGC_DEBUG
#define GC_WERR(X...) werror(X)
#else
#define GC_WERR(X...)
#endif
class FSGarb
{
inherit Filesystem.Monitor.basic : basic;
int num_files;
int total_size;
|
431a48 | 2013-09-26 | Henrik Grubbström (Grubba) | | string modid;
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | string root;
int max_age;
int max_files;
int max_size;
|
631194 | 2019-10-02 | Jonas Walldén | | int(0..1) cleanup_parent_dirs;
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | mapping(string:object) handle_lookup = ([]);
ADT.Priority_queue pending_gc = ADT.Priority_queue();
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | |
string quarantine;
|
631194 | 2019-10-02 | Jonas Walldén | | Configuration owner_mod_conf;
protected void log_remove(string path, string op)
{
if (!owner_mod_conf) {
if (RoxenModule mod = Roxen.get_module(modid))
owner_mod_conf = mod->my_configuration();
}
if (owner_mod_conf)
owner_mod_conf->log_event("fsgc", op, path, ([ ]) );
}
protected int rm_and_parent_cleanup(string path, int(0..1) is_quarantined)
{
path = canonic_path(path);
int res = predef::rm(path);
if (res || is_quarantined) {
log_remove(path, is_quarantined ? "quarantined-file" : "delete-file");
if (cleanup_parent_dirs) {
while (1) {
path = dirname(path);
if (!has_prefix(path, root + "/"))
break;
if (!predef::rm(path))
break;
}
}
}
return res;
}
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | protected int rm(string path)
{
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | GC_WERR("FSGC: Zap %O\n", path);
if (quarantine) {
if ((quarantine == root) || (quarantine == "")) return 0;
|
631194 | 2019-10-02 | Jonas Walldén | | if (!has_prefix(path, root + "/")) return 0;
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | string rel = path[sizeof(root)..];
|
631194 | 2019-10-02 | Jonas Walldén | | if (mv(path, quarantine + rel)) {
rm_and_parent_cleanup(path, 1);
return 1;
}
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | |
string dirs = dirname(rel);
if (sizeof(dirs)) {
if (Stdio.mkdirhier(quarantine + dirs)) {
|
631194 | 2019-10-02 | Jonas Walldén | | if (mv(path, quarantine + rel)) {
rm_and_parent_cleanup(path, 1);
return 1;
}
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | }
}
if (Stdio.cp(path, quarantine + rel)) {
|
631194 | 2019-10-02 | Jonas Walldén | | return rm_and_parent_cleanup(path, 1);
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | }
werror("FSGC: Failed to copy file %O to %O: %s.\n",
path, quarantine + rel, strerror(errno()));
return 0;
} else {
|
631194 | 2019-10-02 | Jonas Walldén | | return rm_and_parent_cleanup(path, 0);
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | }
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | }
void check_threshold()
{
GC_WERR("FSGC: Checking thresholds...\n"
" total_size: %d max_size: %d\n"
" num_files: %d max_files: %d\n",
total_size, max_size,
num_files, max_files);
while ((max_size && (total_size > max_size)) ||
(max_files && (num_files > max_files))) {
GC_WERR("FSGC: Filesystem limits exceeded forcing early removal.\n");
if (!zap_one_file()) break;
}
}
protected int zap_one_file()
{
if (!sizeof(pending_gc)) return 0;
Monitor m = pending_gc->pop();
m_delete(handle_lookup, m->path);
int bytes = m->st->size;
m->st->size = 0;
m->st->isreg = 0;
GC_WERR("Deleting file %O...\n", m->path);
if (rm(m->path)) {
num_files--;
total_size -= bytes;
m->next_poll = time(1);
monitor_queue->adjust(m);
} else {
GC_WERR("Failed to delete file %O: %s\n",
m->path, strerror(errno()));
m->st->size = bytes;
m->st->isreg = 1;
}
return 1;
}
int st_to_pri(Stdio.Stat st)
{
return st->mtime - st->size / 1024;
}
protected void remove_pending(Monitor m)
{
object handle = m_delete(handle_lookup, m->path);
if (handle) {
pending_gc->adjust_pri(handle, -0x80000000);
pending_gc->pop();
}
}
protected class Monitor {
inherit basic::Monitor;
protected void create(string path,
MonitorFlags flags,
int max_dir_check_interval,
int file_interval_factor,
int stable_time)
{
::create(path, flags, max_dir_check_interval,
file_interval_factor, stable_time);
GC_WERR("%O->create(%O, %O, %O, %O, %O)\n",
this_object(), path, flags, max_dir_check_interval,
file_interval_factor, stable_time);
}
void check_for_release(int mask, int flags)
{
GC_WERR("%O->check_for_relase(0x%x, 0x%x)\n",
this_object(), mask, flags);
::check_for_release(mask, flags);
if (!monitors[path]) {
array a = path/"/";
Monitor m = monitors[canonic_path(a[..sizeof(a)-2]*"/")];
if (m) {
GC_WERR("Waking up our parent dir: %O\n", m);
m->next_poll = time(1)-1;
monitor_queue->adjust(m);
}
}
}
protected void file_exists(string path, Stdio.Stat st)
{
::file_exists(path, st);
last_change = st->mtime;
if (st->isreg) {
num_files++;
total_size += st->size;
handle_lookup[path] = pending_gc->push(st_to_pri(st), this);
check_threshold();
}
}
void update(Stdio.Stat st)
{
int delta = max_dir_check_interval || basic::max_dir_check_interval;
if (!next_poll) {
delta = 1 + random(delta);
if (st) {
last_change = st->mtime;
}
}
::update(st);
if (last_change <= time(1)) {
int d = last_change + (stable_time || basic::stable_time) - time(1);
GC_WERR("%O: last: %s, d: %d, delta: %d\n",
this_object(), ctime(last_change) - "\n", d, delta);
if (d < 0) d = 1;
if (d < delta) delta = d;
}
next_poll = time(1) + (delta || 1);
GC_WERR("%O->update(%O) ==> next: %s\n",
this_object(), st, ctime(next_poll) - "\n");
monitor_queue->adjust(this);
}
protected string _sprintf(int c)
{
return sprintf("FSGarb.Monitor(%O, %O, last: %d, next: %s, st: %O)",
path, flags, last_change, ctime(next_poll) - "\n", st);
}
int(0..1) check(MonitorFlags|void flags)
{
int(0..1) ret = ::check(flags);
return ret;
}
int(0..1) status_change(Stdio.Stat old_st, Stdio.Stat st,
MonitorFlags old_flags, MonitorFlags flags)
{
GC_WERR("Status change %O(0x%x) ==> %O(0x%x) for %O!\n",
old_st, old_flags, st, flags, this_object());
int res = ::status_change(old_st, st, old_flags, flags);
if (st->isdir && (flags & MF_RECURSE)) {
foreach(files, string file) {
file = canonic_path(Stdio.append_path(path, file));
if (!monitors[file]) {
res = 1;
monitor(file, old_flags | MF_AUTO | MF_HARD,
max_dir_check_interval,
file_interval_factor,
stable_time);
monitors[file]->check();
}
}
}
num_files += st->isreg - old_st->isreg;
if (old_st->isreg) {
total_size -= old_st->size;
if (!st->isreg) {
remove_pending(this);
}
}
if (st->isreg) {
total_size += st->size;
if (!old_st->isreg) {
handle_lookup[path] = pending_gc->push(st_to_pri(st), this);
} else {
object handle = handle_lookup[path];
if (handle && (st_to_pri(st) != st_to_pri(old_st))) {
pending_gc->adjust_pri(handle, st_to_pri(st));
}
}
}
check_threshold();
return res;
}
void file_created(string path, Stdio.Stat st)
{
GC_WERR("File %O %O created (%O).\n", path, st, this_object());
if (st->isreg) {
num_files++;
total_size += st->size;
handle_lookup[path] = pending_gc->push(st_to_pri(st), this);
check_threshold();
}
}
void file_deleted(string path, Stdio.Stat old_st)
{
GC_WERR("File %O %O deleted (%O).\n", path, old_st, this_object());
if (old_st->isreg) {
num_files--;
total_size -= old_st->size;
remove_pending(this);
check_threshold();
}
}
}
|
6b2b62 | 2017-11-17 | Anders Johansson | | constant DefaultMonitor = Monitor;
|
431a48 | 2013-09-26 | Henrik Grubbström (Grubba) | | protected void create(string modid, string path, int max_age,
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | int|void max_size, int|void max_files,
|
631194 | 2019-10-02 | Jonas Walldén | | string|void quarantine,
int(0..1)|void cleanup_parent_dirs)
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | {
GC_WERR("FSGC: Max age: %d\n", max_age);
GC_WERR("FSGC: Max size: %d\n", max_size);
GC_WERR("FSGC: Max files: %d\n", max_files);
|
431a48 | 2013-09-26 | Henrik Grubbström (Grubba) | | this_program::modid = modid;
|
631194 | 2019-10-02 | Jonas Walldén | | this_program::cleanup_parent_dirs = cleanup_parent_dirs;
|
431a48 | 2013-09-26 | Henrik Grubbström (Grubba) | |
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | this_program::max_age = max_age;
this_program::max_size = max_size;
this_program::max_files = max_files;
|
431a48 | 2013-09-26 | Henrik Grubbström (Grubba) | | root = canonic_path(path);
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | |
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | if (quarantine) {
if (sizeof(quarantine)) {
quarantine = canonic_path(quarantine);
}
this::quarantine = quarantine;
}
|
8efa3b | 2019-10-02 | Jonas Walldén | |
::create(min(max_age, 24 * 3600) / file_interval_factor, 0, max_age);
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | |
|
60f1a3 | 2013-09-27 | Henrik Grubbström (Grubba) | |
int flags = 3;
monitor(root, flags);
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | }
void stable_data_change(string path, Stdio.Stat st)
{
|
146dc6 | 2013-09-25 | Henrik Grubbström (Grubba) | | if (path == root) return;
|
ca76c5 | 2017-11-16 | Henrik Grubbström (Grubba) | | GC_WERR("FSGC: Deleting stale file: %O\n", path);
|
6b2b62 | 2017-11-17 | Anders Johansson | |
#if 0
|
fce991 | 2017-11-15 | Anders Johansson | |
|
94cf53 | 2017-11-16 | Henrik Grubbström (Grubba) | | if (st->mtime >= time(1) - stable_time) {
|
fce991 | 2017-11-15 | Anders Johansson | | GC_WERR("FSGC: Keeping file: %O\n", path);
|
844598 | 2017-11-16 | Henrik Grubbström (Grubba) | |
|
1d38ee | 2017-11-16 | Henrik Grubbström (Grubba) | | Monitor m = monitor(path, MF_AUTO);
m->last_change = st->mtime;
m->check();
|
fce991 | 2017-11-15 | Anders Johansson | | return;
}
|
6b2b62 | 2017-11-17 | Anders Johansson | | #endif
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | rm(path);
}
void reconfigure(int new_max_age, int|void new_max_size,
int|void new_max_files)
{
if (!zero_type(new_max_size)) {
GC_WERR("FSGC: New max size: %d\n", new_max_size);
max_size = new_max_size;
}
if (!zero_type(new_max_files)) {
GC_WERR("FSGC: New max files: %d\n", new_max_files);
max_files = new_max_files;
}
if (new_max_age != max_age) {
GC_WERR("FSGC: New max age: %d\n", new_max_age);
this_program::max_age = new_max_age;
int old_stable_time = stable_time;
set_max_dir_check_interval(stable_time = new_max_age);
if (stable_time < old_stable_time) {
foreach(values(monitors), Monitor m) {
m->next_poll = 0;
m->update(m->st);
}
}
}
check_threshold();
}
int check(mixed ... args)
{
int res = ::check(@args);
GC_WERR("FSGC: check(%{%O, %}) ==> %O\n", args, res);
return res;
}
protected string _sprintf(int c, mapping|void opts)
{
return sprintf("FSGarb(%O, %d)", root, stable_time);
}
array(Stdio.Stat) get_stats()
{
return filter(values(monitors)->st,
lambda(Stdio.Stat st) {
return st && st->isreg;
});
}
}
mapping(string:FSGarb) fsgarbs = ([]);
Thread.Thread meta_fsgc_thread;
void meta_fsgc()
{
sleep(60);
while(meta_fsgc_thread) {
int max_sleep = 60;
foreach(fsgarbs; string id; FSGarb g) {
int seconds = g && g->check();
if (seconds < max_sleep) max_sleep = seconds;
}
if (max_sleep < 1) max_sleep = 1;
GC_WERR("FSGC: Sleeping %d seconds...\n", max_sleep);
while(meta_fsgc_thread && max_sleep--) {
sleep(1);
}
}
}
class FSGarbWrapper(string id)
{
protected void destroy()
{
GC_WERR("FSGC: FSGarbWrapper %O destructed.\n", id);
FSGarb g = m_delete(fsgarbs, id);
if (g) destruct(g);
}
protected string _sprintf(int c, mapping|void opts)
{
return sprintf("FSGarbWrapper(%O)", id);
}
void reconfigure(int max_age, int|void max_size, int|void max_files)
{
FSGarb g = fsgarbs[id];
if (g) g->reconfigure(max_age, max_size, max_files);
}
}
|
431a48 | 2013-09-26 | Henrik Grubbström (Grubba) | | FSGarbWrapper register_fsgarb(string modid, string path, int max_age,
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | int|void max_size, int|void max_files,
|
631194 | 2019-10-02 | Jonas Walldén | | string|void quarantine,
int(0..1)|void cleanup_parent_dirs)
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | {
|
9ab0b3 | 2013-09-27 | Henrik Grubbström (Grubba) | | if ((path == "") || (path == "/") || (max_age <= 0)) return 0;
|
431a48 | 2013-09-26 | Henrik Grubbström (Grubba) | | string id = modid + "\0" + path + "\0" + gethrtime();
|
e9b8c2 | 2016-11-17 | Henrik Grubbström (Grubba) | | FSGarb g = FSGarb(modid, path, max_age, max_size, max_files,
|
631194 | 2019-10-02 | Jonas Walldén | | quarantine, cleanup_parent_dirs);
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | fsgarbs[id] = g;
GC_WERR("FSGC: Register garb on %O ==> id: %O\n", path, id);
return FSGarbWrapper(id);
}
protected void start_fsgarb()
{
meta_fsgc_thread = Thread.Thread(meta_fsgc);
|
6aceb3 | 2018-04-04 | Jonas Walldén | | Roxen.name_thread(meta_fsgc_thread, "Filesystem GC");
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | }
protected void stop_fsgarb()
{
Thread.Thread th = meta_fsgc_thread;
if (th) {
meta_fsgc_thread = UNDEFINED;
th->wait();
|
6aceb3 | 2018-04-04 | Jonas Walldén | | Roxen.name_thread(th, UNDEFINED);
|
562289 | 2013-09-20 | Henrik Grubbström (Grubba) | | }
}
#endif /* Filesystem.Monitor.basic */
|