packages feed

infinity-0.3: IMain.hs

{-# OPTIONS -fglasgow-exts #-}
module IMain where
import Control.Concurrent.STM.TMVar
import Control.Concurrent.STM
import Control.Concurrent
import Control.Monad.STM
import System.Plugins
import Control.Arrow
import Control.Monad
import Data.Maybe
import System.IO
import Network
import Data.Map as Map
import qualified Config as C
import Monitor
import PLoader
import Logger
import List
import API
import Net

-- takes the Module referring to itself,
-- and a reboot function; starts dynmain
-- through main' with a state 
main :: Module -> RebootT -> IO ()
main mod reboot = do
  hSetBuffering stdout NoBuffering
  servs <- forM (C.servers C.config) $ \s -> do
              let sname = C.address s
              h <- connectTo (C.address s) (PortNumber . fromIntegral $ C.port s)
              hSetBuffering h NoBuffering
              return (sname,(h,s))
  putStrLn "Connected to servers..."
  chan <- newChan
  let st     = MState undefined chan (fromList servs) C.config (C.logFile C.config) reboot mod []
  main' st mod

-- thin wrapper which is what we'll reboot to so we don't have
-- to deal with reentrancy issues

-- UPDATE: 10-08-07: my idea on how to approach this is roughly
-- to fall through to dynmain in the main thread in a 'Monitor'
-- monad that'll let us monitor other threads. Meanwhile, here in
-- main' since its our thin reboot wrapper, we'll just spawn threads
-- for each server connection.
main' st m = do
  let s = (toList . servers) st
  (plugs,mods) <- getPlugins >>= return . unzip
  tvar         <- atomically $ newEmptyTMVar
  forM_ s $ \(name,(s,h)) -> do
               let ist = IState tvar (rchan st) plugs (s,h) (conf st) (lFile st)
               forkIO $ runNet listener ist
  runMonitor dynmain $ st{rebootvar=tvar,imodule=m,pmodules=mods}

-- this is the monitor thread, which keeps tracks and does reboots, etc. etc..
dynmain :: Monitor ()
dynmain = do
  mlog Normal "Servers connected, threads forked..."
  n <- serverNum
  chanloop n
      where 
        chanloop n = do
            l <- cGetLine
            case l of
              Reboot -> do
                  mlog Normal "Got reboot msg..."
                  setrebootvar
                  s <- cGetLines
                  wait n s 0
                  unloadplugs
                  restart
              Msg s  -> Monitor.cprint s
              Quit s -> do
                       threadQuit s
                       let x = n-1 in if x == 0 then exit else chanloop x
            chanloop n

        wait n x i = when (i < n) $ case x of
                  Nil:xs -> wait n xs $! i+1
                  _:xs   -> wait n xs i

-- entry point for the subsequent threads.
listener :: Net ()
listener = do
  start -- this is idempotent, so it's reentry safe
  infinity $ \s -> do
    if ping s then pong s
     else parseIRCmsg s >>= eval
  return ()
      where start      = setupNick >> identify >> joinC 
            ischan     = isPrefixOf "#"
            infinity a = do
              i <- waitForInput 1
              if i then
                  do s <- recv
                     a s
                     infinity a
               else
                   do re <- chkreboot
                      if re then die else (infinity a)
            eval s | (Err e)            <- s = netlog Error $ "eval: " ++ e
                   | (Line u c x)       <- s = do name <- getnick
                                                  when (x /= [] && ischan c && u /= name) (logmsg (u,c,x))
                                                  let n' = concat [name,":"]
                                                  when (n' `isPrefixOf` x) $ do
                                                    let str       = drop (length n') x
                                                        (cmd',av) = ((tail.head.words) &&& (unwords.tail.words)) str
                                                    eval $ Cmd u c (cmd',av)
                   | (Cmd u c (cmd,av)) <- s = do
                                      case cmd of
                                        []       -> return ()
                                        "quit"   -> ifadmin u quit (privmsg c "Can't do that...")
                                        "reboot" -> ifadmin u (privmsg c "Rebooting..." >> rebootmsg) (privmsg c "Can't do that...")
                                        x        -> do logmsg (u,c,cmd++" "++av)
                                                       s <- runPlugin x u c (if List.null av then Nothing else Just av)
                                                       case s of
                                                         Nothing -> return ()
                                                         Just r  -> mapM_ (privmsg $ if ischan c then c else u) (lines r)