Slaves and Exim
[hcoop/domtool2.git] / src / slave.sml
1 (* HCoop Domtool (http://hcoop.sourceforge.net/)
2 * Copyright (c) 2006, 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 (* Code for receiving and executing configuration files *)
20
21 structure Slave :> SLAVE = struct
22
23 datatype file_action =
24 Add
25 | Delete
26 | Modify
27
28 type file_status = {action : file_action,
29 domain : string,
30 file : string}
31
32 val fileHandler = ref (fn _ : file_status => ())
33 val preHandler = ref (fn () => ())
34 val postHandler = ref (fn () => ())
35
36 fun registerFileHandler handler =
37 let
38 val old = !fileHandler
39 in
40 fileHandler := (fn x => (handler x; old x))
41 end
42
43 fun registerPreHandler handler =
44 let
45 val old = !preHandler
46 in
47 preHandler := (fn () => (handler (); old ()))
48 end
49
50 fun registerPostHandler handler =
51 let
52 val old = !postHandler
53 in
54 postHandler := (fn () => (handler (); old ()))
55 end
56
57 fun handleChanges fs = (!preHandler ();
58 app (!fileHandler) fs;
59 !postHandler ())
60
61 fun shell ss = OS.Process.isSuccess (OS.Process.system (String.concat ss))
62
63 fun shellF (ss, msg) =
64 let
65 val s = String.concat ss
66 in
67 if OS.Process.isSuccess (OS.Process.system s) then
68 ()
69 else
70 ErrorMsg.error NONE (msg s)
71 end
72
73 fun concatTo p fname =
74 let
75 fun visitDir dname =
76 let
77 val dir = Posix.FileSys.opendir dname
78
79 fun loop () =
80 case Posix.FileSys.readdir dir of
81 NONE => Posix.FileSys.closedir dir
82 | SOME fname' =>
83 let
84 val path = OS.Path.joinDirFile {dir = dname, file = fname'}
85 in
86 if Posix.FileSys.ST.isDir (Posix.FileSys.stat path) then
87 visitDir path
88 else if p fname' then
89 shellF ([Config.cat, " ", path, " >>", fname],
90 fn cl => "Error concatenating: " ^ cl)
91 else
92 ();
93 loop ()
94 end
95 in
96 TextIO.closeOut (TextIO.openOut fname);
97 loop ()
98 end
99 in
100 visitDir Config.resultRoot
101 end
102
103 end