.PHONY: all
configDefault/config.sig: src/config.sig.header \
- configDefault/*.cfs src/config.sig.footer
- cat src/config.sig.header \
+ configDefault/*.csg configDefault/*.cfs \
+ src/config.sig.footer
+ cat configDefault/*.csg \
+ src/config.sig.header \
configDefault/*.cfs \
src/config.sig.footer \
>configDefault/config.sig
val resultRoot = "/home/adamc/domtool"
val tmpDir = "/tmp"
+val cat = "/bin/cat"
val cp = "/bin/cp"
val diff = "/usr/bin/diff"
val rm = "/bin/rm"
(* Filesystem location for creating temporary directories *)
(* Paths to standard UNIX utilities *)
+val cat : string
val cp : string
val diff : string
val rm : string
--- /dev/null
+structure Exim :> EXIM_CONFIG = struct
+
+val aliases = "/home/adamc/fake/aliases"
+val aliasesDefault = "/home/adamc/fake/aliases.default"
+
+val reload = "echo \"I would reload exim now.\""
+(*"/etc/init.d/exim4 reload"*)
+
+end
--- /dev/null
+structure Exim : EXIM_CONFIG
+
--- /dev/null
+signature EXIM_CONFIG = sig
+
+val aliases : string
+val aliasesDefault : string
+(* Paths to e-mail alias files.
+ * The "default" version is for catch-alls. *)
+
+val reload : string
+(* Command-line to reload exim configuration *)
+
+end
| Delete' of string
| Modify' of {src : string, dst : string}
-fun shell ss = OS.Process.isSuccess (OS.Process.system (String.concat ss))
-
-fun shellF (ss, msg) =
- let
- val s = String.concat ss
- in
- if OS.Process.isSuccess (OS.Process.system s) then
- ()
- else
- ErrorMsg.error NONE (msg s)
- end
-
fun findDiffs dom =
let
val realPath = getPath dom Config.resultRoot
if Posix.FileSys.ST.isDir (Posix.FileSys.stat real) then
loopReal acts
else if Posix.FileSys.access (tmp, []) then
- if shell [Config.diff, " ", real, " ", tmp] then
+ if Slave.shell [Config.diff, " ", real, " ", tmp] then
loopReal acts
else
loopReal (Modify' {src = tmp, dst = real} :: acts)
val acts = loopReal []
- val dir = Posix.FileSys.opendir realPath
+ val dir = Posix.FileSys.opendir tmpPath
fun loopTmp acts =
case Posix.FileSys.readdir dir of
val diffs = findDiffs dom
val diffs = map (fn Add' {src, dst} =>
- (shellF ([Config.cp, " ", src, " ", dst],
+ (Slave.shellF ([Config.cp, " ", src, " ", dst],
fn cl => "Copy failed: " ^ cl);
{action = Slave.Add,
domain = dom,
domain = dom,
file = dst})
| Modify' {src, dst} =>
- (shellF ([Config.cp, " ", src, " ", dst],
+ (Slave.shellF ([Config.cp, " ", src, " ", dst],
fn cl => "Copy failed: " ^ cl);
{action = Slave.Modify,
domain = dom,
()
else
Slave.handleChanges diffs;
- ignore (shellF ([Config.rm, " -rf ", Config.tmpDir, "/*"],
+ ignore (Slave.shellF ([Config.rm, " -rf ", Config.tmpDir, "/*"],
fn cl => "Temp file cleanup failed: " ^ cl))
end)
domain.sig
domain.sml
-plugins/plugins.cm
+plugins/alias.sig
+plugins/alias.sml
+
+plugins/exim.sig
+plugins/exim.sml
order.sig
order.sml
--- /dev/null
+(* HCoop Domtool (http://hcoop.sourceforge.net/)
+ * Copyright (c) 2006, Adam Chlipala
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *)
+
+(* Exim MTA handling *)
+
+signature EXIM = sig
+
+end
--- /dev/null
+(* HCoop Domtool (http://hcoop.sourceforge.net/)
+ * Copyright (c) 2006, Adam Chlipala
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *)
+
+(* Exim MTA handling *)
+
+structure Exim :> EXIM = struct
+
+val aliasesChanged = ref false
+val aliasesDefaultChanged = ref false
+
+val () = Slave.registerPreHandler
+ (fn () => (aliasesChanged := false;
+ aliasesDefaultChanged := false))
+
+val () = Slave.registerFileHandler (fn fs =>
+ let
+ val spl = OS.Path.splitDirFile (#file fs)
+ in
+ if #file spl = "aliases" then
+ aliasesChanged := true
+ else if #file spl = "aliases.default" then
+ aliasesDefaultChanged := true
+ else
+ ()
+ end)
+
+val () = Slave.registerPostHandler
+ (fn () =>
+ (if !aliasesChanged then
+ Slave.concatTo (fn s => s = "aliases") Config.Exim.aliases
+ else
+ ();
+ if !aliasesDefaultChanged then
+ Slave.concatTo (fn s => s = "aliases.default") Config.Exim.aliasesDefault
+ else
+ ();
+ if !aliasesChanged orelse !aliasesDefaultChanged then
+ Slave.shellF ([Config.Exim.reload],
+ fn cl => "Error reloading exim with " ^ cl)
+ else
+ ()))
+
+end
+++ /dev/null
-Library
-
-signature ALIAS
-structure Alias
-
-is
-
-alias.sig
-alias.sml
(* Register code to run before or after making all changes. *)
val handleChanges : file_status list -> unit
+
+ val shell : string list -> bool
+ val shellF : string list * (string -> string) -> unit
+
+ val concatTo : (string -> bool) -> string -> unit
+ (* Search through the result configuration hierarchy for all files matching
+ * the predicate, concatenating their contents in arbitrary order to the
+ * given file. *)
end
app (!fileHandler) fs;
!postHandler ())
+fun shell ss = OS.Process.isSuccess (OS.Process.system (String.concat ss))
+
+fun shellF (ss, msg) =
+ let
+ val s = String.concat ss
+ in
+ if OS.Process.isSuccess (OS.Process.system s) then
+ ()
+ else
+ ErrorMsg.error NONE (msg s)
+ end
+
+fun concatTo p fname =
+ let
+ fun visitDir dname =
+ let
+ val dir = Posix.FileSys.opendir dname
+
+ fun loop () =
+ case Posix.FileSys.readdir dir of
+ NONE => Posix.FileSys.closedir dir
+ | SOME fname' =>
+ let
+ val path = OS.Path.joinDirFile {dir = dname, file = fname'}
+ in
+ if Posix.FileSys.ST.isDir (Posix.FileSys.stat path) then
+ visitDir path
+ else if p fname' then
+ shellF ([Config.cat, " ", path, " >>", fname],
+ fn cl => "Error concatenating: " ^ cl)
+ else
+ ();
+ loop ()
+ end
+ in
+ TextIO.closeOut (TextIO.openOut fname);
+ loop ()
+ end
+ in
+ visitDir Config.resultRoot
+ end
+
end