domtool-tail
[hcoop/domtool2.git] / src / tail / tail.sml
1 (* HCoop Domtool (http://hcoop.sourceforge.net/)
2 * Copyright (c) 2008, Adam Chlipala
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *)
18
19 (* Tailing Apache log files (locally) that you are allowed to see *)
20
21 fun hostname () =
22 let
23 val inf = TextIO.openIn "/etc/hostname"
24 in
25 case TextIO.inputLine inf of
26 NONE => (TextIO.closeIn inf; raise Fail "No line in /etc/hostname")
27 | SOME line => (TextIO.closeIn inf; String.substring (line, 0, size line - 1))
28 end
29
30 fun main args =
31 let
32 val (f, args) = foldl (fn (arg, (f, args)) =>
33 case arg of
34 "-f" => (true, args)
35 | _ => (f, arg :: args))
36 (false, []) args
37 val args = rev args
38 in
39 case args of
40 [vhost] =>
41 let
42 val uid = Posix.ProcEnv.getuid ()
43 val uname = Posix.SysDB.Passwd.name (Posix.SysDB.getpwuid uid)
44
45 val proc = Unix.execute ("/usr/local/bin/domtool-admin-sudo-noisy", ["perms", uname])
46 val inf = Unix.textInstreamOf proc
47
48 fun allowed () =
49 case TextIO.inputLine inf of
50 NONE => []
51 | SOME line =>
52 case String.tokens Char.isSpace line of
53 "domain:" :: domains => domains
54 | _ => allowed ()
55
56 val domains = allowed ()
57
58 fun inDomains d = List.exists (fn s => s = d) domains
59
60 fun checker pieces =
61 case pieces of
62 [] => false
63 | _ :: pieces =>
64 inDomains (String.concatWith "." pieces)
65 orelse checker pieces
66
67 val tailArgs = ["/var/log/apache2/user/"
68 ^ String.substring (uname, 0, 1)
69 ^ "/"
70 ^ String.substring (uname, 0, 2)
71 ^ "/"
72 ^ uname
73 ^ "/apache/log/"
74 ^ hostname ()
75 ^ "/"
76 ^ vhost]
77
78 val tailArgs =
79 if f then
80 "-f" :: tailArgs
81 else
82 tailArgs
83 in
84 ignore (Unix.reap proc);
85 if inDomains vhost orelse checker (String.fields (fn ch => ch = #".") vhost) then
86 Posix.Process.exec ("/usr/bin/tail", "/usr/bin/tail" :: tailArgs)
87 else
88 (print "You're not authorized to view the logs for that vhost.\n";
89 OS.Process.exit OS.Process.failure)
90 end
91 | _ => (print "Invalid arguments\n";
92 OS.Process.exit OS.Process.failure)
93 end
94
95 val () = main (CommandLine.arguments ())