Adding database dropping to dbtool
[hcoop/domtool2.git] / src / main.sml
index fd4ff63..db657da 100644 (file)
@@ -92,7 +92,9 @@ fun check fname =
            end
     end
 
-val notTmp = CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-")
+fun notTmp s =
+    String.sub (s, 0) <> #"."
+    andalso CharVector.all (fn ch => Char.isAlphaNum ch orelse ch = #"." orelse ch = #"_" orelse ch = #"-") s
 
 fun checkDir dname =
     let
@@ -442,6 +444,21 @@ fun requestRegen () =
        OpenSSL.close bio
     end
 
+fun requestRegenTc () =
+    let
+       val (_, bio) = requestBio (fn () => ())
+    in
+       Msg.send (bio, MsgRegenerateTc);
+       case Msg.recv bio of
+           NONE => print "Server closed connection unexpectedly.\n"
+         | SOME m =>
+           case m of
+               MsgOk => print "All configuration validated.\n"
+             | MsgError s => print ("Configuration validation failed: " ^ s ^ "\n")
+             | _ => print "Unexpected server reply.\n";
+       OpenSSL.close bio
+    end
+
 fun requestRmdom dom =
     let
        val (_, bio) = requestBio (fn () => ())
@@ -517,6 +534,21 @@ fun requestDbTable p =
        OpenSSL.close bio
     end
 
+fun requestDbDrop p =
+    let
+       val (user, bio) = requestBio (fn () => ())
+    in
+       Msg.send (bio, MsgDropDb p);
+       case Msg.recv bio of
+           NONE => print "Server closed connection unexpectedly.\n"
+         | SOME m =>
+           case m of
+               MsgOk => print ("Your database " ^ user ^ "_" ^ #dbname p ^ " has been dropped.\n")
+             | MsgError s => print ("Drop failed: " ^ s ^ "\n")
+             | _ => print "Unexpected server reply.\n";
+       OpenSSL.close bio
+    end
+
 fun requestListMailboxes domain =
     let
        val (_, bio) = requestBio (fn () => ())
@@ -815,6 +847,8 @@ fun regenerate context =
 
        val () = Domain.resetGlobal ()
 
+       val ok = ref true
        fun contactNode (node, ip) =
            if node = Config.defaultNode then
                Domain.resetLocal ()
@@ -836,7 +870,8 @@ fun regenerate context =
                          | _ => print ("Slave " ^ node
                                        ^ " returned unexpected command\n");
                    OpenSSL.close bio
-               end             
+               end
+                handle OpenSSL.OpenSSL s => print ("OpenSSL error: " ^ s ^ "\n")
 
        fun doUser user =
            let
@@ -844,40 +879,106 @@ fun regenerate context =
                val _ = ErrorMsg.reset ()
 
                val dname = Config.domtoolDir user
+           in
+               if Posix.FileSys.access (dname, []) then
+                   let
+                       val dir = Posix.FileSys.opendir dname
+
+                       fun loop files =
+                           case Posix.FileSys.readdir dir of
+                               NONE => (Posix.FileSys.closedir dir;
+                                        files)
+                             | SOME fname =>
+                               if notTmp fname then
+                                   loop (OS.Path.joinDirFile {dir = dname,
+                                                              file = fname}
+                                         :: files)
+                               else
+                                   loop files
 
-               val dir = Posix.FileSys.opendir dname
-
-               fun loop files =
-                   case Posix.FileSys.readdir dir of
-                       NONE => (Posix.FileSys.closedir dir;
-                                files)
-                     | SOME fname =>
-                       if notTmp fname then
-                           loop (OS.Path.joinDirFile {dir = dname,
-                                                      file = fname}
-                                 :: files)
+                       val files = loop []
+                       val (_, files) = Order.order (SOME b) files
+                   in
+                       if !ErrorMsg.anyErrors then
+                           (ErrorMsg.reset ();
+                            print ("User " ^ user ^ "'s configuration has errors!\n"))
                        else
-                           loop files
-
-               val files = loop []
-               val (_, files) = Order.order (SOME b) files
-           in
-               if !ErrorMsg.anyErrors then
-                   (ErrorMsg.reset ();
-                    print ("User " ^ user ^ "'s configuration has errors!\n"))
+                           app eval' files
+                   end
                else
-                   app eval' files
+                   ()
            end
-               handle IO.Io _ => ()
-                    | OS.SysErr (s, _) => print ("System error processing user " ^ user ^ ": " ^ s ^ "\n")
-                    | ErrorMsg.Error => (ErrorMsg.reset ();
-                                         print ("User " ^ user ^ " had a compilation error.\n"))
-                    | _ => print "Unknown exception during regeneration!\n"
+           handle IO.Io _ => ()
+                | OS.SysErr (s, _) => (print ("System error processing user " ^ user ^ ": " ^ s ^ "\n");
+                                       ok := false)
+                | ErrorMsg.Error => (ErrorMsg.reset ();
+                                     print ("User " ^ user ^ " had a compilation error.\n");
+                                     ok := false)
+                | _ => (print "Unknown exception during regeneration!\n";
+                        ok := false)
     in
        app contactNode Config.nodeIps;
        Env.pre ();
        app doUser (Acl.users ());
-       Env.post ()
+       Env.post ();
+       !ok
+    end
+
+fun regenerateTc context =
+    let
+       val _ = ErrorMsg.reset ()
+
+       val b = basis ()
+       val () = Tycheck.disallowExterns ()
+
+       val () = Domain.resetGlobal ()
+
+       val ok = ref true
+
+       fun doUser user =
+           let
+               val _ = Domain.setUser user
+               val _ = ErrorMsg.reset ()
+
+               val dname = Config.domtoolDir user
+           in
+               if Posix.FileSys.access (dname, []) then
+                   let
+                       val dir = Posix.FileSys.opendir dname
+
+                       fun loop files =
+                           case Posix.FileSys.readdir dir of
+                               NONE => (Posix.FileSys.closedir dir;
+                                        files)
+                             | SOME fname =>
+                               if notTmp fname then
+                                   loop (OS.Path.joinDirFile {dir = dname,
+                                                              file = fname}
+                                         :: files)
+                               else
+                                   loop files
+
+                       val files = loop []
+                       val (_, files) = Order.order (SOME b) files
+                   in
+                       if !ErrorMsg.anyErrors then
+                           (ErrorMsg.reset ();
+                            print ("User " ^ user ^ "'s configuration has errors!\n");
+                            ok := false)
+                       else
+                           app (ignore o check) files
+                   end
+               else
+                   ()
+           end
+           handle IO.Io _ => ()
+                | OS.SysErr (s, _) => print ("System error processing user " ^ user ^ ": " ^ s ^ "\n")
+                | ErrorMsg.Error => (ErrorMsg.reset ();
+                                     print ("User " ^ user ^ " had a compilation error.\n"))
+                | _ => print "Unknown exception during -tc regeneration!\n"
+    in
+       app doUser (Acl.users ());
+       !ok
     end
 
 fun rmuser user =
@@ -943,8 +1044,10 @@ fun service () =
                              print "\n";
                              Msg.send (bio, MsgOk)))
                         handle e as (OpenSSL.OpenSSL s) =>
-                               (print "OpenSSL error\n";
-                                app (fn x => print (x ^ "\n")) (SMLofNJ.exnHistory e))
+                               (print ("OpenSSL error: " ^ s ^ "\n");
+                                app (fn x => print (x ^ "\n")) (SMLofNJ.exnHistory e);
+                                Msg.send (bio, MsgError ("OpenSSL error: " ^ s))
+                                handle OpenSSL.OpenSSL _ => ())
                              | OS.SysErr (s, _) =>
                                (print "System error: ";
                                 print s;
@@ -1083,14 +1186,32 @@ fun service () =
                                doIt (fn () =>
                                         if Acl.query {user = user, class = "priv", value = "regen"}
                                            orelse Acl.query {user = user, class = "priv", value = "all"} then
-                                            (regenerate context;
-                                             ("Regenerated all configuration.",
-                                              NONE))
+                                            (if regenerate context then
+                                                 ("Regenerated all configuration.",
+                                                  NONE)
+                                             else
+                                                 ("Error regenerating configuration!",
+                                                  SOME "Error regenerating configuration!  Consult /var/log/domtool.log."))
                                         else
                                             ("Unauthorized user asked to regenerate!",
                                              SOME "Not authorized to regenerate"))
                                     (fn () => ())
 
+                             | MsgRegenerateTc =>
+                               doIt (fn () =>
+                                        if Acl.query {user = user, class = "priv", value = "regen"}
+                                           orelse Acl.query {user = user, class = "priv", value = "all"} then
+                                            (if regenerateTc context then
+                                                 ("Checked all configuration.",
+                                                  NONE)
+                                             else
+                                                 ("Found a compilation error!",
+                                                  SOME "Found a compilation error!  Consult /var/log/domtool.log."))
+                                        else
+                                            ("Unauthorized user asked to regenerate -tc!",
+                                             SOME "Not authorized to regenerate -tc"))
+                                    (fn () => ())
+
                              | MsgRmuser user' =>
                                doIt (fn () =>
                                         if Acl.query {user = user, class = "priv", value = "all"} then
@@ -1148,6 +1269,23 @@ fun service () =
                                              SOME ("Invalid database name " ^ dbname)))
                                     (fn () => ())
 
+                             | MsgDropDb {dbtype, dbname} =>
+                               doIt (fn () =>
+                                        if Dbms.validDbname dbname then
+                                            case Dbms.lookup dbtype of
+                                                NONE => ("Database drop request with unknown datatype type " ^ dbtype,
+                                                         SOME ("Unknown database type " ^ dbtype))
+                                              | SOME handler =>
+                                                case #dropdb handler {user = user, dbname = dbname} of
+                                                    NONE => ("Drop database " ^ user ^ "_" ^ dbname ^ ".",
+                                                             NONE)
+                                                  | SOME msg => ("Error dropping database " ^ user ^ "_" ^ dbname ^ ": " ^ msg,
+                                                                 SOME ("Error dropping database: " ^ msg))
+                                        else
+                                            ("Invalid database name " ^ user ^ "_" ^ dbname,
+                                             SOME ("Invalid database name " ^ dbname)))
+                                    (fn () => ())
+
                              | MsgListMailboxes domain =>
                                doIt (fn () =>
                                         if not (Domain.yourDomain domain) then