Commit | Line | Data |
---|---|---|
42a86d96 AC |
1 | (* |
2 | Domtool (http://hcoop.sf.net/) | |
3 | Copyright (C) 2004-2005 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 | (* Generation of aggregate per-user/per-vhost web bandwidth statistics *) | |
21 | ||
22 | open Config | |
23 | open ApacheConfig | |
4471dd55 | 24 | open Util |
42a86d96 | 25 | |
36f5ca38 AC |
26 | val groupsBase = wblDocDir ^ "/" ^ defaultHost ^ "/url_" |
27 | (* Where to look for grouped user statistics *) | |
28 | ||
42a86d96 AC |
29 | fun mots m = |
30 | let | |
31 | open Date | |
32 | in | |
33 | case m of | |
34 | Jan => "Jan" | |
35 | | Feb => "Feb" | |
36 | | Mar => "Mar" | |
37 | | Apr => "Apr" | |
38 | | May => "May" | |
39 | | Jun => "Jun" | |
40 | | Jul => "Jul" | |
41 | | Aug => "Aug" | |
42 | | Sep => "Sep" | |
43 | | Oct => "Oct" | |
44 | | Nov => "Nov" | |
45 | | Dec => "Dec" | |
46 | end | |
47 | ||
48 | fun motn m = | |
49 | let | |
50 | open Date | |
51 | in | |
52 | case m of | |
53 | Jan => "01" | |
54 | | Feb => "02" | |
55 | | Mar => "03" | |
56 | | Apr => "04" | |
57 | | May => "05" | |
58 | | Jun => "06" | |
59 | | Jul => "07" | |
60 | | Aug => "08" | |
61 | | Sep => "09" | |
62 | | Oct => "10" | |
63 | | Nov => "11" | |
64 | | Dec => "12" | |
65 | end | |
66 | ||
67 | val monthInc = Time.fromSeconds (LargeInt.fromInt 2592000) | |
68 | ||
69 | fun doit () = | |
70 | let | |
71 | val inf = TextIO.openIn nogroups | |
72 | fun readNogr L = | |
73 | case TextIO.inputLine inf of | |
74 | NONE => L | |
75 | | SOME s => readNogr ((hd (String.tokens Char.isSpace s)) :: L) | |
76 | val nogr = readNogr [] | |
77 | val _ = TextIO.closeIn inf | |
78 | ||
79 | val now = Date.fromTimeLocal (Time.now ()) | |
80 | ||
81 | val dir = Posix.FileSys.opendir wblDocDir | |
82 | ||
83 | fun backupMonth t = | |
84 | let | |
85 | val now = Date.fromTimeLocal t | |
86 | ||
87 | fun backupMonth' t = | |
88 | let | |
89 | val d = Date.fromTimeLocal t | |
90 | in | |
91 | if Date.month d = Date.month now then | |
92 | backupMonth' (Time.- (t, monthInc)) | |
93 | else | |
94 | t | |
95 | end | |
96 | in | |
97 | backupMonth' t | |
98 | end | |
99 | ||
100 | fun backupMulti n = | |
101 | if n = 0 then | |
102 | Time.now () | |
103 | else | |
104 | backupMonth (backupMulti (n-1)) | |
105 | ||
106 | val now = | |
107 | case CommandLine.arguments () of | |
108 | [n] => | |
109 | (case Int.fromString n of | |
110 | NONE => raise Fail "Invalid integer parameter" | |
111 | | SOME n => | |
112 | if n >= 0 then | |
113 | Date.fromTimeLocal (backupMulti n) | |
114 | else | |
115 | raise Fail "Negative parameter") | |
116 | | _ => now | |
117 | ||
118 | val when = mots (Date.month now) ^ " " ^ Int.toString (Date.year now) | |
119 | ||
120 | val groups = let | |
121 | val inf = TextIO.openIn (groupsBase ^ Int.toString (Date.year now) ^ motn (Date.month now) ^ ".html") | |
122 | val _ = TextIO.inputLine inf | |
123 | val _ = TextIO.inputLine inf | |
124 | val _ = TextIO.inputLine inf | |
125 | val _ = TextIO.inputLine inf | |
126 | ||
127 | fun loop groups = | |
128 | case TextIO.inputLine inf of | |
129 | NONE => groups | |
130 | | SOME line => | |
131 | case String.tokens Char.isSpace line of | |
132 | [hits, perc, kb, kbperc, url] => | |
133 | if size url >= 4 | |
134 | andalso String.sub (url, 0) = #"/" | |
135 | andalso String.sub (url, 1) = #"~" | |
136 | andalso String.sub (url, size url - 2) = #"/" | |
137 | andalso String.sub (url, size url - 1) = #"*" then | |
138 | let | |
139 | val uname = String.substring (url, 2, size url - 4) | |
140 | in | |
141 | loop (((uname, ["www.hcoop.net"]), | |
142 | valOf (Int.fromString kb)) :: groups) | |
143 | end | |
144 | else | |
145 | loop groups | |
146 | | _ => groups | |
147 | ||
148 | val groups : ((string * string list) * int) list ref = ref (loop []) | |
149 | val _ = TextIO.closeIn inf | |
150 | in | |
151 | groups | |
152 | end handle ex => ref [] | |
153 | ||
154 | fun addGroup (group, n, d) = | |
155 | if List.exists (fn x => x = d) nogr then | |
156 | () | |
157 | else let | |
158 | val groups' = if List.exists (fn ((x, _), _) => x = group) (!groups) then | |
159 | map (fn v as ((gr, ds), n') => if gr = group then ((gr, d :: ds), n + n') else v) (!groups) | |
160 | else | |
161 | ((group, [d]), n) :: (!groups) | |
162 | in | |
163 | groups := groups' | |
164 | end | |
165 | ||
166 | fun dodir d = | |
167 | let | |
168 | val inf = TextIO.openIn (wblDocDir ^ "/" ^ d ^ "/index.html") | |
169 | ||
170 | fun andWeep () = | |
171 | let | |
172 | fun waste n = | |
173 | if n <= 0 then | |
174 | () | |
175 | else | |
176 | (TextIO.inputLine inf; | |
177 | waste (n-1)) | |
178 | ||
179 | val _ = waste 5 | |
180 | ||
181 | val l = valOf (TextIO.inputLine inf) | |
182 | val num = String.extract (l, 32, NONE) | |
183 | ||
184 | fun getNum i = | |
185 | if Char.isDigit (String.sub (num, i)) then | |
186 | getNum (i+1) | |
187 | else | |
188 | valOf (Int.fromString (String.substring (num, 0, i))) | |
189 | in | |
190 | getNum 0 | |
191 | end | |
192 | ||
193 | fun readEm () = | |
194 | case TextIO.inputLine inf of | |
195 | NONE => NONE | |
196 | | SOME l => | |
197 | if Substring.isSubstring when (Substring.full l) then | |
198 | SOME (andWeep ()) | |
199 | else | |
200 | readEm () | |
201 | ||
202 | val ret = readEm () | |
203 | ||
204 | val group = | |
205 | if d <> defaultHost then | |
206 | let | |
207 | val domdir = "/etc/domains/" ^ String.concatWith "/" (rev (String.tokens (fn ch => ch = #".") d)) | |
208 | val stat = Posix.FileSys.stat domdir | |
209 | in | |
210 | SOME (Posix.SysDB.Group.name (Posix.SysDB.getgrgid (Posix.FileSys.ST.gid stat))) | |
211 | end | |
212 | else | |
213 | NONE | |
214 | in | |
215 | (case (ret, group) of | |
216 | (SOME ret, SOME group) => addGroup (group, ret, d) | |
217 | | _ => ()); | |
218 | TextIO.closeIn inf; | |
219 | ret | |
220 | end handle IO => NONE | |
221 | ||
222 | fun loop L = | |
223 | case Posix.FileSys.readdir dir of | |
224 | NONE => L | |
225 | | SOME d => (case dodir d of | |
226 | NONE => loop L | |
227 | | SOME n => loop ((d, n) :: L)) | |
228 | ||
229 | val doms = loop [] | |
230 | val doms = mergeSort doms | |
231 | val groups = mergeSort (!groups) | |
232 | ||
233 | val sum = List.foldl (fn ((_, n), s) => s+n) 0 doms | |
234 | in | |
235 | print ("TOTAL: " ^ Int.toString sum ^ "\n\n"); | |
236 | List.app (fn (d, n) => print (d ^ ": " ^ Int.toString n ^ "\n")) doms; | |
237 | print "\n"; | |
238 | List.app (fn ((d, ds), n) => print (d ^ "[" ^ String.concatWith "," ds ^ "]: " ^ Int.toString n ^ "\n")) groups; | |
239 | Posix.FileSys.closedir dir | |
240 | end | |
241 | ||
242 | val _ = doit () |