e4a6262013-09-20Henrik Grubbström (Grubba) // // "Globals"/"Filesystem GC". // // 2013-09-20 Henrik Grubbström // #include <config.h> #include <roxen.h> //<locale-token project="roxen_config">LOCALE</locale-token> #define LOCALE(X,Y) _STR_LOCALE("roxen_config",X,Y)
3c0eb62013-12-06Henrik Grubbström (Grubba) constant action="status";
0c7a1e2016-09-27Anders Johansson string name = LOCALE(1093, "Filesystem garbage collector status");
3c0eb62013-12-06Henrik Grubbström (Grubba) string doc =
0c7a1e2016-09-27Anders Johansson  LOCALE(1094, "Show the status for the filesystem garbage collectors.");
3c0eb62013-12-06Henrik Grubbström (Grubba) 
dd70a42013-12-05Jenny Sergent string fill_color = "#a6baf3"; string bg_color = "#e9eefc";
ed9b772013-12-06Henrik Grubbström (Grubba) string format_time(int t) { string res;
0c7a1e2016-09-27Anders Johansson  foreach(({ ({ 604800, LOCALE(1095, "1 week"), LOCALE(1096, "%d weeks") }), ({ 86400, LOCALE(1097, "1 day"), LOCALE(1098, "%d days") }), ({ 3600, LOCALE(1099, "1 hour"), LOCALE(1100, "%d hours") }), ({ 60, LOCALE(1101, "1 minute"), LOCALE(1102, "%d minutes") }),
ed9b772013-12-06Henrik Grubbström (Grubba)  }), [ int unit, string singular, string plural ]) { if (t < unit) continue; int c = t/unit; string frag; if (c == 1) frag = singular; else frag = sprintf(plural, c); if (res) res += " " + frag; else res = frag; t -= c * unit; }
0c7a1e2016-09-27Anders Johansson  if (!res) return LOCALE(1103, "none");
ed9b772013-12-06Henrik Grubbström (Grubba)  return res; }
e6d8792013-10-25Jenny Sergent // Linear histogram string lin_histogram(string|object title, int num_buckets,
dd70a42013-12-05Jenny Sergent  array(int) value_set, int max)
e4a6262013-09-20Henrik Grubbström (Grubba) {
ed9b772013-12-06Henrik Grubbström (Grubba)  int num_files = sizeof(value_set) || 1;
e4a6262013-09-20Henrik Grubbström (Grubba)  max++;
ed9b772013-12-06Henrik Grubbström (Grubba)  // Adjust to a reasonably even bucket size. int bsize = max / num_buckets; if (max > bsize * num_buckets) bsize++; int threshold_found; foreach(({ 604800, 86400, 3600, 60, 1 }), int quanta) { if (bsize <= quanta) continue; if (!threshold_found) { threshold_found = 1; continue; } int rest = bsize % quanta; if (rest) bsize += quanta - rest; break; } max = bsize * num_buckets;
e4a6262013-09-20Henrik Grubbström (Grubba)  array(int) buckets = allocate(num_buckets); foreach(value_set, int v) { int b = (v * num_buckets)/max; if (b < 0) b = 0;
dd70a42013-12-05Jenny Sergent  if (b >= num_buckets) b = num_buckets - 1;
e4a6262013-09-20Henrik Grubbström (Grubba)  buckets[b]++; }
dd70a42013-12-05Jenny Sergent 
ed9b772013-12-06Henrik Grubbström (Grubba)  int max_height = 60;
e6d8792013-10-25Jenny Sergent  int max_width = 0; int x_pos = 0;
dd70a42013-12-05Jenny Sergent  int y_pos = 0;
e6d8792013-10-25Jenny Sergent  string res;
dd70a42013-12-05Jenny Sergent 
e4a6262013-09-20Henrik Grubbström (Grubba)  foreach(buckets; int bno; int count) {
dd70a42013-12-05Jenny Sergent  int height = (count*max_height)/num_files; int min_val = (bno * max) / num_buckets; int max_val = ((bno + 1) * max) / num_buckets - 1; float percent = ((float)count/(float)num_files)*100;
e6d8792013-10-25Jenny Sergent  // x_pos = horizontal pos of bar, starting point: 0 // y_pos = vertical pos of bar, origin at upper left corner
dd70a42013-12-05Jenny Sergent  // fill-color: #808080 | #f2f1eb y_pos = max_height - height;
0c7a1e2016-09-27Anders Johansson  res += sprintf(LOCALE(1104, "<rect x='%d' y='%d'"
e6d8792013-10-25Jenny Sergent  " width='10' height='%d'"
dd70a42013-12-05Jenny Sergent  " style='fill:%s;'>\n"
ed9b772013-12-06Henrik Grubbström (Grubba)  " <title>%s: %s - %s\n%f&#37; (%d)</title>\n"
e6d8792013-10-25Jenny Sergent  "</rect>\n" "<rect x='%d' y='0'" " width='10' height='%d'"
dd70a42013-12-05Jenny Sergent  " style='fill:%s;'>\n"
ed9b772013-12-06Henrik Grubbström (Grubba)  " <title>%s: %s - %s\n%f&#37; (%d)</title>\n"
dd70a42013-12-05Jenny Sergent  "</rect>\n"), x_pos, y_pos, height, fill_color,
ed9b772013-12-06Henrik Grubbström (Grubba)  Roxen.html_encode_string(title), format_time(min_val), format_time(max_val+1), percent, count,
dd70a42013-12-05Jenny Sergent  x_pos, y_pos, bg_color,
ed9b772013-12-06Henrik Grubbström (Grubba)  Roxen.html_encode_string(title), format_time(min_val), format_time(max_val+1), percent, count);
dd70a42013-12-05Jenny Sergent 
e6d8792013-10-25Jenny Sergent  x_pos = x_pos + 12; max_width = max_width + 12;
e4a6262013-09-20Henrik Grubbström (Grubba)  }
e6d8792013-10-25Jenny Sergent  // Using svg return sprintf("<svg width='%d' height='%d'" " viewPort='0 0 %d %d' version='1.1'" " xmlns='http://www.w3.org/2000/svg'>", max_width, max_height, max_width, max_height) + res + "</svg>"; } // Exponential histogram string exp_histogram(string|object title, int num_buckets,
dd70a42013-12-05Jenny Sergent  array(int) value_set, int max)
e6d8792013-10-25Jenny Sergent { sort(value_set);
dd70a42013-12-05Jenny Sergent  int chunk_sz = 1024;
e6d8792013-10-25Jenny Sergent  int bucket_max = 1024; mapping(int:int) buckets = ([]);
dd70a42013-12-05Jenny Sergent 
ed9b772013-12-06Henrik Grubbström (Grubba)  buckets[bucket_max] = 0; while (sizeof(buckets) < num_buckets) { bucket_max *= 2; buckets[bucket_max] = 0; } int bm = 1024;
e6d8792013-10-25Jenny Sergent  foreach(value_set, int val) {
ed9b772013-12-06Henrik Grubbström (Grubba)  while (val > bm) { bm *= 2; } if (bm > bucket_max) { buckets[bucket_max]++; } else { buckets[bm]++;
e6d8792013-10-25Jenny Sergent  } }
ed9b772013-12-06Henrik Grubbström (Grubba)  int max_height = 60;
e6d8792013-10-25Jenny Sergent  int max_width = 0;
dd70a42013-12-05Jenny Sergent  int min = 0;
e6d8792013-10-25Jenny Sergent  int x_pos = 0; int y_pos = 0; string res;
dd70a42013-12-05Jenny Sergent 
e6d8792013-10-25Jenny Sergent  foreach(sort(indices(buckets)), int sz) { int count = buckets[sz];
ed9b772013-12-06Henrik Grubbström (Grubba)  if (sz == bucket_max && bm > bucket_max) sz = bm;
dd70a42013-12-05Jenny Sergent  int max = sz / chunk_sz; // part in KB
ed9b772013-12-06Henrik Grubbström (Grubba)  int height = (count*max_height)/(sizeof(value_set) || 1); float percent = ((float)count/(float)(sizeof(value_set)||1))*100;
dd70a42013-12-05Jenny Sergent 
e6d8792013-10-25Jenny Sergent  // x_pos = horizontal pos of bar, starting point: 0 // y_pos = vertical pos of bar, origin at upper left corner
dd70a42013-12-05Jenny Sergent  y_pos = max_height - height;
819c622013-12-02Anders Johansson  res += sprintf(LOCALE(1068, "<rect x='%d' y='%d'"
e6d8792013-10-25Jenny Sergent  " width='10' height='%d'"
dd70a42013-12-05Jenny Sergent  " style='fill:%s;'>\n" " <title>%s: %d - %d KB\n%f&#37; (%d)</title>\n"
e6d8792013-10-25Jenny Sergent  "</rect>\n" "<rect x='%d' y='0'" " width='10' height='%d'"
dd70a42013-12-05Jenny Sergent  " style='fill:%s;'>\n" " <title>%s: %d - %d KB\n%f&#37; (%d)</title>\n" "</rect>\n"), x_pos, y_pos, height, fill_color, Roxen.html_encode_string(title), min, max, percent, count, x_pos, y_pos, bg_color, Roxen.html_encode_string(title), min, max, percent, count); min = max; // sz
e6d8792013-10-25Jenny Sergent  x_pos = x_pos + 12; max_width = max_width + 12; }
dd70a42013-12-05Jenny Sergent 
e6d8792013-10-25Jenny Sergent  // Using svg return sprintf("<svg width='%d' height='%d'" " viewPort='0 0 %d %d' version='1.1'" " xmlns='http://www.w3.org/2000/svg'>", max_width, max_height, max_width, max_height) + res + "</svg>";
e4a6262013-09-20Henrik Grubbström (Grubba) } string parse(RequestID id) { #if constant(roxen.register_fsgarb)
1175752022-08-15Jonas Walldén  // Sort according to config, module and path array(string) garb_sort_keys = ({ });
e4a6262013-09-20Henrik Grubbström (Grubba)  array(object/*(roxen.FSGarb)*/) garbs = values(roxen->fsgarbs);
1175752022-08-15Jonas Walldén  foreach (garbs, object/*(roxen.FSGarb)*/ g) { RoxenModule mod = Roxen.get_module(g->modid); Configuration conf = mod && mod->my_configuration(); garb_sort_keys += ({ (conf ? conf->name : "") + "|" + (mod ? Roxen.get_modfullname(mod) : g->modid) + "|" + g->root }); } sort(garb_sort_keys, garbs);
e4a6262013-09-20Henrik Grubbström (Grubba) 
dd70a42013-12-05Jenny Sergent  int size_unit = 1024;
4c173a2022-09-13Henrik Grubbström (Grubba)  string res = "<h2>Filesystem Garbage Collector Status</h2>\n\n";
9a86962022-09-05Henrik Grubbström (Grubba) 
4c173a2022-09-13Henrik Grubbström (Grubba) #ifndef DISABLE_FSGARB if (roxen.getvar("fsgc_starttime")->get_next(0) < 0)
9a86962022-09-05Henrik Grubbström (Grubba) #endif
4c173a2022-09-13Henrik Grubbström (Grubba)  { res += "<p><font color='&usr.warncolor;'><img src='&usr.err-2;' />" "&nbsp;<b>" + LOCALE(0, "The filesystem garbage collector is disabled.") + "</b></font></p>\n\n"; }
9a86962022-09-05Henrik Grubbström (Grubba) 
08fbe72013-12-06Henrik Grubbström (Grubba)  string modid;
e4a6262013-09-20Henrik Grubbström (Grubba)  foreach(garbs, object/*(roxen.FSGarb)*/ g) {
dd70a42013-12-05Jenny Sergent  // werror("FSGARG DEBUG object g: %O\n", g);
08fbe72013-12-06Henrik Grubbström (Grubba)  if (g->modid != modid) { if (modid) { res += " </table>\n" " </td>\n" "</tr>\n"; } modid = g->modid;
b415dd2013-12-06Henrik Grubbström (Grubba)  RoxenModule mod = Roxen.get_module(modid); string name = Roxen.html_encode_string(modid); if (mod) { Configuration conf = mod->my_configuration(); string curl = replace(conf->name, " ", "%20") + "/"; string mname = Roxen.get_modfullname(mod); string mgroup = "zz_misc"; if (sscanf(mname, "%s:%*s", mgroup) != 2) mgroup = "zz_misc"; if (mgroup == "zz_misc") mgroup = LOCALE(525, "Other"); string murl = curl + Roxen.http_encode_invalids(mgroup) + "!0/" + replace(mod->sname(), "#", "!") + "/?section=Status";
cff1012016-01-15Henrik Grubbström (Grubba)  name = sprintf("<a href='/sites/site.html/%s&amp;&usr.set-wiz-id;'>%s</a>/" "<a href='/sites/site.html/%s&amp;&usr.set-wiz-id;'>%s</a>",
b415dd2013-12-06Henrik Grubbström (Grubba)  Roxen.html_encode_string(curl), replace(Roxen.html_encode_string(conf->query_name()), " ", "&nbsp;"), Roxen.html_encode_string(murl), replace(Roxen.get_modfullname(mod), " ", "&nbsp;")); }
08fbe72013-12-06Henrik Grubbström (Grubba)  res += "<tr><td><h3>" +
0c7a1e2016-09-27Anders Johansson  sprintf(LOCALE(1105, "Registered by %s"), name) +
08fbe72013-12-06Henrik Grubbström (Grubba)  "</h3></td></tr>\n" "<tr>\n" " <td>\n"
1175752022-08-15Jonas Walldén  " <table class='entry'>\n";
08fbe72013-12-06Henrik Grubbström (Grubba)  }
e4a6262013-09-20Henrik Grubbström (Grubba)  array(Stdio.Stat) stats = g->get_stats(); int local_max_size = 0; int local_min_mtime = 0x7fffffff;
ed9b772013-12-06Henrik Grubbström (Grubba)  string age = format_time(g->max_age);
dd70a42013-12-05Jenny Sergent 
e4a6262013-09-20Henrik Grubbström (Grubba)  foreach(stats, Stdio.Stat st) { if (st->size > local_max_size) local_max_size = st->size; if (st->mtime < local_min_mtime) local_min_mtime = st->mtime; }
819c622013-12-02Anders Johansson  string sizes = exp_histogram(LOCALE(377, "Size"),
ed9b772013-12-06Henrik Grubbström (Grubba)  20, stats->size, local_max_size);
dd70a42013-12-05Jenny Sergent  // divide time in minutes.
819c622013-12-02Anders Johansson  string ages = lin_histogram(LOCALE(1070, "Age"),
ed9b772013-12-06Henrik Grubbström (Grubba)  20, map(stats->mtime, `-, local_min_mtime), g->max_age || time(1) - local_min_mtime);
dd70a42013-12-05Jenny Sergent 
e6d8792013-10-25Jenny Sergent  res +=
1175752022-08-15Jonas Walldén  sprintf(" <tr>" " <th>&nbsp;</th>\n" " <th class='path' colspan='3'><tt>%s</tt></th>" " </tr>\n"
08fbe72013-12-06Henrik Grubbström (Grubba)  " <tr>\n"
1175752022-08-15Jonas Walldén  " <th style='width: 0%%'>&nbsp;</th>\n" " <th>%s</th>\n" " <th>%s</th>\n" " <th>%s</th>\n"
08fbe72013-12-06Henrik Grubbström (Grubba)  " </tr>\n"
1175752022-08-15Jonas Walldén  " <tr class='sub-table'>\n"
08fbe72013-12-06Henrik Grubbström (Grubba)  " <td>&nbsp;</td>\n"
1175752022-08-15Jonas Walldén  " <td class='status'>\n"
08fbe72013-12-06Henrik Grubbström (Grubba)  " " +
1175752022-08-15Jonas Walldén  LOCALE(1071, "%d files <span class='dim'>(max: %d)</span>") + "<br/>\n"
08fbe72013-12-06Henrik Grubbström (Grubba)  " " +
1175752022-08-15Jonas Walldén  LOCALE(1106, "%d KiB <span class='dim'>(max: %d)</span>") + "<br/>\n"
08fbe72013-12-06Henrik Grubbström (Grubba)  " Age limit: %s\n" " </td>\n"
1175752022-08-15Jonas Walldén  " <td>\n%s</td>\n" " <td>\n%s</td>\n"
08fbe72013-12-06Henrik Grubbström (Grubba)  " </tr>\n",
dd70a42013-12-05Jenny Sergent  Roxen.html_encode_string(g->root), // Mount point
0c7a1e2016-09-27Anders Johansson  LOCALE(228, "Status"), LOCALE(1107, "File age distribution"), LOCALE(1108, "File size distribution"),
dd70a42013-12-05Jenny Sergent  g->num_files, g->max_files, // files (g->total_size/size_unit), (g->max_size/size_unit), // size (KiB) age, // age (seconds or minutes)
ed9b772013-12-06Henrik Grubbström (Grubba)  ages, // age distribution histogram sizes); // size distribution histogram
e4a6262013-09-20Henrik Grubbström (Grubba)  }
08fbe72013-12-06Henrik Grubbström (Grubba)  if (modid) { res += " </table>\n" " </td>\n" "</tr>\n"; }
dd70a42013-12-05Jenny Sergent 
521d0e2013-12-06Henrik Grubbström (Grubba)  if (!sizeof(res)) { res = "<tr><th>" + LOCALE(1069, "No filesystem garbage collectors active.") + "</th></tr>\n"; }
b415dd2013-12-06Henrik Grubbström (Grubba)  return
1175752022-08-15Jonas Walldén  "<style type='text/css'>\n" "#fsgc-table h3 { font-size: 14px; margin: 0; }\n" "#fsgc-table .entry { font-size: 12px; margin-bottom: 12px; }\n" "#fsgc-table .entry tt { font-size: 14px; }\n" "#fsgc-table .entry .dim { color: #888; }\n" "#fsgc-table th { text-align: left; vertical-align: top; }\n" "#fsgc-table th.path { color: #68a; padding: 8px 0; }\n" "#fsgc-table .sub-table td { vertical-align: top; padding-right: 20px; }\n" "#fsgc-table .sub-table td.status { width: 250px; }\n" "</style>\n" "<table id='fsgc-table' width='100%'>\n" + res + "</table>\n"
521d0e2013-12-06Henrik Grubbström (Grubba)  "<input type='hidden' name='action' value='fsgarb.pike' />" "<br />\n" "<cf-ok-button href='./'/> <cf-refresh/>\n";
dd70a42013-12-05Jenny Sergent 
e4a6262013-09-20Henrik Grubbström (Grubba) #else
819c622013-12-02Anders Johansson  return LOCALE(1072, "Not available in this installation of Roxen.");
e4a6262013-09-20Henrik Grubbström (Grubba) #endif
e6d8792013-10-25Jenny Sergent }