217d1214129b0c51656db27cecbd860e7d10f1b7
[hcoop/domtool2.git] / src / stats / quotas.sml
1 (*
2 Domtool 2 (http://hcoop.sf.net/)
3 Copyright (C) 2007 Adam Chlipala
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 *)
19
20 structure Quotas = struct
21
22 fun homedir s = Posix.SysDB.Passwd.home (Posix.SysDB.getpwnam s)
23 fun userVols s = [homedir s]
24
25 fun used path =
26 let
27 val proc = Unix.execute ("/bin/sh", ["-c", "/usr/bin/fs listquota -path " ^ path])
28 val inf = Unix.textInstreamOf proc
29 in
30 (case TextIO.inputLine inf of
31 NONE => NONE
32 | SOME line =>
33 if String.isPrefix "fs: " line then
34 NONE
35 else
36 case TextIO.inputLine inf of
37 NONE => NONE
38 | SOME line =>
39 case String.tokens Char.isSpace line of
40 _ :: _ :: n :: _ => Int.fromString n
41 | _ => NONE)
42 before ignore (Unix.reap proc)
43 end
44
45 fun getDiskUsage () =
46 let
47 fun explorer (level, path, acc) =
48 let
49 val dir = Posix.FileSys.opendir path
50
51 fun loop acc =
52 case Posix.FileSys.readdir dir of
53 NONE => acc
54 | SOME file =>
55 let
56 val acc =
57 if level = 2 then
58 file :: acc
59 else
60 explorer (level+1, OS.Path.joinDirFile {dir = path, file = file}, acc)
61 in
62 loop acc
63 end
64 in
65 loop acc
66 before Posix.FileSys.closedir dir
67 end
68
69 val users = explorer (0, "/afs/hcoop.net/user", [])
70
71 fun count uname =
72 foldl (fn (vol, total) => case used vol of
73 NONE => total
74 | SOME n => total + n) 0 (userVols uname)
75
76 val users = ListMergeSort.sort (fn (s1, s2) => String.compare (s1, s2) = GREATER) users
77 val all = List.map (fn user => {uname = user, kbs = count user}) users
78 val all = ListMergeSort.sort (fn ({kbs = kb1, ...}, {kbs = kb2, ...}) => kb1 > kb2) all
79 in
80 app (fn {uname, kbs} => (print uname;
81 print " ";
82 print (Int.toString kbs);
83 print "\n")) all
84 end
85
86 end