packages feed

bein-0.3: Bein/Daemon/Commands.hs

{-# LANGUAGE ScopedTypeVariables #-}
module Bein.Daemon.Commands (
  module Bein.Commands,
  exitDaemon,
  reconfigureDaemon,
  updateJobs,
  database,
  runMinion
) where

import Control.Concurrent.QSem ( signalQSem, waitQSem )
import System.Posix.Process
    ( executeFile, exitImmediately, forkProcess )
import System.Random ( Random(randomR), getStdGen, setStdGen )
import Database.HDBC.PostgreSQL ( Connection )
import Database.HDBC
    ( fromSql, toSql )
import Control.Monad.Trans ()
import Control.Monad.Reader ( MonadReader(ask), MonadIO(..) )
import Control.Concurrent.STM ( atomically, writeTVar )
import Control.Exception ()
import System.Exit ( ExitCode(ExitSuccess) )
import System.Posix.Process ()
import System.Posix.Syslog ( syslog, Priority(Notice, Error) )
import System.Posix.Files ( removeLink, fileExist )
import Bein.Commands ( encodeResponse, forkR, catchR, query, update )
import Bein.Configuration ( readConfiguration )
import Bein.Daemon.Types
    ( Configuration(max_executions, minion_command),
      BeinState(..),
      BeinM,
      ExecutionID(..),
      configField,
      DaemonState(stSem) )

exitDaemon :: BeinM a ()
exitDaemon = liftIO $ do
  removeLink "/var/run/beind.pid"
  syslog Notice "Exiting."
  exitImmediately ExitSuccess

reconfigureDaemon :: BeinM DaemonState ()
reconfigureDaemon = do
  liftIO $ syslog Notice "Reconfiguring."
  st <- ask
  config <- liftIO $ readConfiguration (db st)
  liftIO $ atomically $ writeTVar (configT st) config

updateJobs :: BeinM DaemonState ()
updateJobs = do
  jobSemaphore <- ask >>= return.stSem
  liftIO $ waitQSem jobSemaphore
  maxExecutions <- configField max_executions
  toRun <- query queryString [toSql maxExecutions]
  mapM_ runMinion (map (fromSql.head) toRun)
  liftIO $ signalQSem jobSemaphore
    where queryString = "select id from current_jobs where status = 'pending' " ++ 
                        "limit (select ?-count(id) from current_jobs where status='running')"

database :: BeinM DaemonState Connection
database = ask >>= return.db

randomFileName :: Int -> IO String
randomFileName n = do
  g <- getStdGen
  let (s, g') = randomFileName' g n
  setStdGen g'
  return s
    where randomFileName' g 0 = ([],g)
          randomFileName' g m = let (k,g') = randomR (0,61) g
                                    (str,g'') = randomFileName' g' (m-1)
                                in (toChar k : str, g'')
          toChar :: Int -> Char
          toChar q = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" !! q

runMinion :: ExecutionID -> BeinM DaemonState ()
runMinion (ExecutionID ex) = do
  minionCmd <- configField minion_command
  d <- liftIO $ randomFileName 50
  (liftIO $ fileExist minionCmd) >>= \b -> if b  
    then do p <- liftIO $ forkProcess $ executeFile minionCmd False ["-x",show ex,"-d",d] Nothing
            update "update current_jobs set (status,running_as_pid,scratch_dir) = ('running',?,?) where id = ?" 
              [toSql $ toInteger p, toSql d, toSql ex]
    else do update "update executions set status='failed' where id=?" [toSql ex]
            liftIO $ syslog Error "beinminion does not exist.  Can't execute jobs."