From b6e2318111ec35ec2015ddf17679dd31270d77cb Mon Sep 17 00:00:00 2001 From: Adam Chlipala Date: Sat, 13 Aug 2005 16:26:18 +0000 Subject: [PATCH 1/1] Statistics --- .cvsignore | 1 + mlt.conf | 3 +- out/.cvsignore | 1 + portal.mlt | 5 +++ quotas.mlt | 22 ++++++++++ stats.sig | 19 ++++++++ stats.sml | 115 +++++++++++++++++++++++++++++++++++++++++++++++++ webbw.mlt | 47 ++++++++++++++++++++ 8 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 out/.cvsignore create mode 100644 quotas.mlt create mode 100644 stats.sig create mode 100644 stats.sml create mode 100644 webbw.mlt diff --git a/.cvsignore b/.cvsignore index a2a33e4..bb80809 100644 --- a/.cvsignore +++ b/.cvsignore @@ -1,2 +1,3 @@ .cm CM +out \ No newline at end of file diff --git a/mlt.conf b/mlt.conf index 4b27a02..f20b644 100644 --- a/mlt.conf +++ b/mlt.conf @@ -6,7 +6,8 @@ after after exn exn out out -pub /var/www/users.hcoop.net/cgi/hcoop +pub /home/hcoop/public_html/cgi-bin/portal +cm $/smlnj-lib.cm cm /usr/local/share/smlsql/smlsql.cm cm /usr/local/share/smlsql/libpq/sources.cm diff --git a/out/.cvsignore b/out/.cvsignore new file mode 100644 index 0000000..f59ec20 --- /dev/null +++ b/out/.cvsignore @@ -0,0 +1 @@ +* \ No newline at end of file diff --git a/portal.mlt b/portal.mlt index 5839663..ff3a863 100644 --- a/portal.mlt +++ b/portal.mlt @@ -55,6 +55,11 @@ end %>
<% end %> +

Statistics

+ +Apache bandwidth
+Disk usage
+

Public pages

Member directory
diff --git a/quotas.mlt b/quotas.mlt new file mode 100644 index 0000000..9f6a04c --- /dev/null +++ b/quotas.mlt @@ -0,0 +1,22 @@ +<% @header [("title", ["Disk usage stats"])] %> + + + + + +<% foreach du in Stats.getDiskUsage () do %> + +<% end %> + +
User Blocks Files
 
<% + val id = Init.userNameToId (#uname du); + switch id of + SOME id => %><% + end + %><% Web.html (#uname du) %><% + switch id of + SOME id => %><% + end +%> <% #blocks du %> <% #files du %>
+ + \ No newline at end of file diff --git a/stats.sig b/stats.sig new file mode 100644 index 0000000..8bf823c --- /dev/null +++ b/stats.sig @@ -0,0 +1,19 @@ +signature STATS = +sig + type host = {ssl : bool, (* Is it HTTPS? *) + hostname : string, (* Internet hostname *) + id : string} (* Name of stats directory *) + + val getWebbw : int -> int * (host * int) list * (string * host list * int) list + (* Get web bandwidth usage stats. The argument tells how many months ago + * to look for the data. The return gives the total b/w usage, a mapping from + * vhosts to kilobytes, and a mapping from usernames to their vhosts and bandwidth + * totals. *) + + type disk = {uname : string, (* UNIX username *) + blocks : int, (* Number of disk blocks used *) + files : int} (* Number of files used *) + + val getDiskUsage : unit -> disk list + (* Get /home disk usage statistics, in descending blocks order. *) +end diff --git a/stats.sml b/stats.sml new file mode 100644 index 0000000..620b71e --- /dev/null +++ b/stats.sml @@ -0,0 +1,115 @@ +structure Stats :> STATS = +struct + val webbw = "/etc/stats/webbw" + val webbw_last = "/etc/stats/webbw.last" + val webbw_last2 = "/etc/stats/webbw.last2" + + type host = {ssl : bool, + hostname : string, + id : string} + + fun checkSsl host = + case String.fields (fn ch => ch = #".") host of + first::rest => + (case String.fields (fn ch => ch = #"_") first of + [first, "ssl"] => {ssl = true, hostname = String.concatWith "." (first::rest), + id = host} + | _ => {ssl = false, hostname = host, id = host}) + | _ => {ssl = false, hostname = host, id = host} + + fun getWebbw last = + let + val fname = + case last of + 2 => webbw_last2 + | 1 => webbw_last + | 0 => webbw + | _ => raise Fail "Asked for too old of a bandwidth file" + + val inf = TextIO.openIn fname + + val sum = case TextIO.inputLine inf of + NONE => raise Fail "Can't read webbw" + | SOME l => + + case String.tokens Char.isSpace l of + [_, n] => valOf (Int.fromString n) + | _ => raise Fail "Bad total in webbw" + + fun readEm L = + case TextIO.inputLine inf of + (NONE | SOME "\n") => List.rev L + | SOME l => + case String.tokens (fn ch => Char.isSpace ch orelse ch = #":") l of + [d, n] => readEm ((checkSsl d, valOf (Int.fromString n)) :: L) + | _ => raise Fail "Bad row in webbw" + + fun splitLast [] = raise Fail "Not enough items for splitLast" + | splitLast [x] = ([], x) + | splitLast (h::t) = + let + val (l, x) = splitLast t + in + (h::l, x) + end + + fun readGroups L = + case TextIO.inputLine inf of + NONE => List.rev L + | SOME l => + case String.tokens (fn ch => Char.isSpace ch orelse ch = #":" orelse ch = #"[" orelse ch = #"]" orelse ch = #",") l of + d :: rest => + let + val (l, x) = splitLast rest + in + readGroups ((d, map checkSsl l, valOf (Int.fromString x)) :: L) + end + | _ => raise Fail "Bad row in webbw, part 2" + in + TextIO.inputLine inf; + (sum, readEm [], readGroups []) + before TextIO.closeIn inf + end + + type disk = {uname : string, + blocks : int, + files : int} + + fun getDiskUsage () = + let + val proc = Unix.execute ("/usr/bin/sudo", ["/usr/sbin/repquota", "-g", "/home"]) + val inf = Unix.textInstreamOf proc + + fun skipUntilLine () = + case TextIO.inputLine inf of + NONE => raise Fail "No dividing line found in repquota output" + | SOME s => + if String.sub (s, 0) = #"-" then + () + else + skipUntilLine () + + fun readData acc = + let + fun done () = + ListMergeSort.sort (fn (d1, d2) => + #blocks d1 < #blocks d2) acc + in + case TextIO.inputLine inf of + NONE => done () + | SOME s => + case String.tokens Char.isSpace s of + [uname, dash, blocks, bsoft, bhard, files, fsoft, fhard] => + readData ({uname = uname, + blocks = valOf (Int.fromString blocks), + files = valOf (Int.fromString files)} :: acc) + | [] => done () + | _ => raise Fail ("Bad repquota line: " ^ s) + end + in + skipUntilLine (); + readData [] + before ignore (Unix.reap proc) + end + +end diff --git a/webbw.mlt b/webbw.mlt new file mode 100644 index 0000000..95a8591 --- /dev/null +++ b/webbw.mlt @@ -0,0 +1,47 @@ +<% @header [("title", ["Web usage stats"])] %> + +Web virtual host bandwidth (bandwidth usage of Apache sites in this calendar month)
+<% if $"last" = "1" then %> + [Previous] | [Current] +<% elseif $"last" = "2" then %> + [Next] +<% else %> + [Last month] +<% end %> +

+ +<% val (sum, doms, groups) = iff $"last" = "" then + Stats.getWebbw 0 + else + Stats.getWebbw (Web.stoi ($"last")) %> + + + + + + +<% foreach (d, n) in doms do %> + +<% end %> + +
Bandwidth (kB)
Total Apache bandwidth <% sum %>
 
<% #hostname d %> [detail]<% n %>
+ +

+Bandwidth by ownership +

+ + + + + +<% foreach (gr, ds, n) in groups do %> + +<% end %> + +
Bandwidth (kB)
 
<% gr %> + <% foreach dom in ds do %> + [<% #hostname dom %> (S)] + <% end %> + <% n %>
+ + \ No newline at end of file -- 2.20.1