propellor

propellor config for hosts.
git clone git://git.ricketyspace.net/propellor.git
Log | Files | Refs | LICENSE

Cron.hs (3995B)


      1 module Propellor.Property.Cron (
      2 	Times(..),
      3 	job,
      4 	niceJob,
      5 	jobDropped,
      6 	Propellor.Property.Cron.runPropellor
      7 ) where
      8 
      9 import Propellor.Base
     10 import qualified Propellor.Property.File as File
     11 import qualified Propellor.Property.Apt as Apt
     12 import Propellor.Bootstrap
     13 
     14 import Data.Char
     15 
     16 -- | When to run a cron job.
     17 --
     18 -- The Daily, Monthly, and Weekly options allow the cron job to be run
     19 -- by anacron, which is useful for non-servers.
     20 data Times
     21 	= Times String -- ^ formatted as in crontab(5)
     22 	| Daily
     23 	| Weekly
     24 	| Monthly
     25 
     26 -- | Installs a cron job, that will run as a specified user in a particular
     27 -- directory. Note that the Desc must be unique, as it is used for the
     28 -- cron job filename.
     29 --
     30 -- Only one instance of the cron job is allowed to run at a time, no matter
     31 -- how long it runs. This is accomplished using flock locking of the cron
     32 -- job file.
     33 --
     34 -- The cron job's output will only be emailed if it exits nonzero.
     35 job :: Desc -> Times -> User -> FilePath -> String -> Property DebianLike
     36 job desc times (User u) cddir command = combineProperties ("cronned " ++ desc) $ props
     37 	& Apt.serviceInstalledRunning "cron"
     38 	& Apt.installed ["util-linux", "moreutils"]
     39 	& cronjobfile desc times `File.hasContent`
     40 		[ case times of
     41 			Times _ -> ""
     42 			_ -> "#!/bin/sh\nset -e"
     43 		, "# Generated by propellor"
     44 		, ""
     45 		, "SHELL=/bin/sh"
     46 		, "PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
     47 		, ""
     48 		, case times of
     49 			Times t -> t ++ "\t" ++ u ++ "\tchronic "
     50 				++ shellEscape (scriptfile desc)
     51 			_ -> case u of
     52 				"root" -> "chronic " ++ shellEscape (scriptfile desc)
     53 				_ -> "chronic su " ++ u ++ " -c "
     54 					++ shellEscape (scriptfile desc)
     55 		]
     56 	& case times of
     57 		Times _ -> doNothing
     58 		_ -> (cronjobfile desc times)
     59 			`File.mode` combineModes (readModes ++ executeModes)
     60 	-- Use a separate script because it makes the cron job name
     61 	-- prettier in emails, and also allows running the job manually.
     62 	& scriptfile desc `File.hasContent`
     63 		[ "#!/bin/sh"
     64 		, "# Generated by propellor"
     65 		, "set -e"
     66 		, "flock -n " ++ shellEscape (cronjobfile desc times)
     67 			++ " sh -c " ++ shellEscape cmdline
     68 		]
     69 	& scriptfile desc `File.mode` combineModes (readModes ++ executeModes)
     70   where
     71 	cmdline = "cd " ++ cddir ++ " && ( " ++ command ++ " )"
     72 
     73 -- | Removes a cron job created by 'job' or 'niceJob', as identified by the
     74 -- 'Desc' passed to those properties when the cronjob was set up
     75 --
     76 -- Those properties are not revertable because simply removing a cronjob does
     77 -- not undo the changes it might have made to the system, i.e., 'jobDropped' is
     78 -- not in the general case a reversion of 'job' or 'niceJob'
     79 jobDropped :: Desc -> Times -> Property UnixLike
     80 jobDropped desc times = combineProperties ("uncronned " ++ desc) $ props
     81 	& File.notPresent (scriptfile desc)
     82 	& File.notPresent (cronjobfile desc times)
     83 
     84 -- | Installs a cron job, and runs it niced and ioniced.
     85 niceJob :: Desc -> Times -> User -> FilePath -> String -> Property DebianLike
     86 niceJob desc times user cddir command = job desc times user cddir
     87 	("nice ionice -c 3 sh -c " ++ shellEscape command)
     88 
     89 -- | Installs a cron job to run propellor.
     90 runPropellor :: Times -> RevertableProperty DebianLike UnixLike
     91 runPropellor times = cronned <!> uncronned
     92   where
     93 	cronned = withOS "propellor cron job" $ \w o -> do
     94 		bootstrapper <- getBootstrapper
     95 		ensureProperty w $
     96 			niceJob "propellor" times (User "root") localdir
     97 				(bootstrapPropellorCommand bootstrapper o ++ "; ./propellor")
     98 	uncronned = jobDropped "propellor" times
     99 
    100 -- Utility functions
    101 
    102 cronjobname :: Desc -> String
    103 cronjobname d = map sanitize d
    104   where
    105 	sanitize c
    106 		| isAlphaNum c = c
    107 		| otherwise = '_'
    108 
    109 scriptfile :: Desc -> FilePath
    110 scriptfile d = "/usr/local/bin/" ++ (cronjobname d) ++ "_cronjob"
    111 
    112 cronjobfile :: Desc -> Times -> FilePath
    113 cronjobfile d times = "/etc" </> cronjobdir </> (cronjobname d)
    114   where
    115 	cronjobdir = case times of
    116 		Times _ -> "cron.d"
    117 		Daily -> "cron.daily"
    118 		Weekly -> "cron.weekly"
    119 		Monthly -> "cron.monthly"