    {-# LANGUAGE DeriveDataTypeable #-}

    module Propellor.Property.Service where

    import Propellor.Base
    import Propellor.Types.Info
    import qualified Propellor.Property.File as File

    type ServiceName = String

    -- | Ensures that a service is running. Does not ensure that
    -- any package providing that service is installed. See
    -- Apt.serviceInstalledRunning
    --
    -- Note that due to the general poor state of init scripts, the best
    -- we can do is try to start the service, and if it fails, assume
    -- this means it's already running.
    running :: ServiceName -> Property DebianLike
    running :: ServiceName -> Property DebianLike
    running = ServiceName -> ServiceName -> ServiceName -> Property DebianLike
    signaled ServiceName
    "start" ServiceName
    "running"

    restarted :: ServiceName -> Property DebianLike
    restarted :: ServiceName -> Property DebianLike
    restarted = ServiceName -> ServiceName -> ServiceName -> Property DebianLike
    signaled ServiceName
    "restart" ServiceName
    "restarted"

    reloaded :: ServiceName -> Property DebianLike
    reloaded :: ServiceName -> Property DebianLike
    reloaded = ServiceName -> ServiceName -> ServiceName -> Property DebianLike
    signaled ServiceName
    "reload" ServiceName
    "reloaded"

    signaled :: String -> Desc -> ServiceName -> Property DebianLike
    signaled :: ServiceName -> ServiceName -> ServiceName -> Property DebianLike
    signaled ServiceName
    cmd ServiceName
    desc ServiceName
    svc = Propellor Bool -> Property DebianLike -> Property DebianLike
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (Bool -> Bool
    not (Bool -> Bool) -> Propellor Bool -> Propellor Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Propellor Bool
    servicesDisabled) (Property DebianLike -> Property DebianLike)
    -> Property DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $
    	Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    forall (untightened :: [MetaType]) (tightened :: [MetaType]).
    (TightenTargetsAllowed untightened tightened, SingI tightened) =>
    Property (MetaTypes untightened) -> Property (MetaTypes tightened)
    forall (p :: * -> *) (untightened :: [MetaType])
           (tightened :: [MetaType]).
    (TightenTargets p, TightenTargetsAllowed untightened tightened,
     SingI tightened) =>
    p (MetaTypes untightened) -> p (MetaTypes tightened)
    tightenTargets (Property
       (MetaTypes
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
     -> Property DebianLike)
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    p Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> ServiceName
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall p. IsProp p => p -> ServiceName -> p
    `describe` (ServiceName
    desc ServiceName -> ServiceName -> ServiceName
    forall a. [a] -> [a] -> [a]
    ++ ServiceName
    " " ServiceName -> ServiceName -> ServiceName
    forall a. [a] -> [a] -> [a]
    ++ ServiceName
    svc)
      where
    	p :: Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    p = Script
    -> UncheckedProperty
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    scriptProperty [ServiceName
    "service " ServiceName -> ServiceName -> ServiceName
    forall a. [a] -> [a] -> [a]
    ++ ServiceName -> ServiceName
    shellEscape ServiceName
    svc ServiceName -> ServiceName -> ServiceName
    forall a. [a] -> [a] -> [a]
    ++ ServiceName
    " " ServiceName -> ServiceName -> ServiceName
    forall a. [a] -> [a] -> [a]
    ++ ServiceName
    cmd ServiceName -> ServiceName -> ServiceName
    forall a. [a] -> [a] -> [a]
    ++ ServiceName
    " >/dev/null 2>&1 || true"]
    		UncheckedProperty
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Result
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    NoChange

    -- | This property prevents daemons and other services from being started,
    -- which is often something you want to prevent when building a chroot.
    --
    -- When this is set, `running` and `restarted` will not start services.
    --
    -- On Debian this installs a </usr/sbin/policy-rc.d> script to further
    -- prevent any packages that get installed from starting daemons.
    -- Reverting the property removes the script.
    noServices :: RevertableProperty (HasInfo + UnixLike) UnixLike
    noServices :: RevertableProperty
      (HasInfo
       + MetaTypes
           '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
              'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    noServices = (Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    setup Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Info
    -> Property
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall {k} (metatypes' :: k) metatypes.
    (MetaTypes metatypes' ~ (HasInfo + metatypes), SingI metatypes') =>
    Property metatypes -> Info -> Property (MetaTypes metatypes')
    `setInfoProperty` InfoVal NoServices -> Info
    forall v. IsInfo v => v -> Info
    toInfo (NoServices -> InfoVal NoServices
    forall v. v -> InfoVal v
    InfoVal NoServices
    NoServices)) Property
      (MetaTypes
         '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> RevertableProperty
         (MetaTypes
            '[ 'WithInfo, 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    <!> Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    teardown
      where
    	f :: ServiceName
    f = ServiceName
    "/usr/sbin/policy-rc.d"
    	script :: Script
    script = [ ServiceName
    "#!/bin/sh", ServiceName
    "exit 101" ]
    	setup :: Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    setup = ServiceName
    -> Props
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall {k} (metatypes :: k).
    SingI metatypes =>
    ServiceName
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    combineProperties ServiceName
    "no services started" (Props
       (MetaTypes
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
     -> Property
          (MetaTypes
             '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Props
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $ [Property
       (MetaTypes
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])]
    -> Props
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall {k} (metatypes :: k).
    [Property (MetaTypes metatypes)] -> Props (MetaTypes metatypes)
    toProps
    		[ ServiceName
    -> Script
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    File.hasContent ServiceName
    f Script
    script
    		, ServiceName
    -> FileMode
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    File.mode ServiceName
    f ([FileMode] -> FileMode
    combineModes ([FileMode]
    readModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++ [FileMode]
    executeModes))
    		]
    	teardown :: Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    teardown = ServiceName
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    File.notPresent ServiceName
    f

    -- | Check if the noServices property is in effect.
    servicesDisabled :: Propellor Bool
    servicesDisabled :: Propellor Bool
    servicesDisabled = Maybe NoServices -> Bool
    forall a. Maybe a -> Bool
    isJust (Maybe NoServices -> Bool)
    -> (InfoVal NoServices -> Maybe NoServices)
    -> InfoVal NoServices
    -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . InfoVal NoServices -> Maybe NoServices
    forall v. InfoVal v -> Maybe v
    fromInfoVal
    	(InfoVal NoServices -> Bool)
    -> Propellor (InfoVal NoServices) -> Propellor Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (Propellor (InfoVal NoServices)
    forall v. IsInfo v => Propellor v
    askInfo :: Propellor (InfoVal NoServices))

    data NoServices = NoServices deriving (NoServices -> NoServices -> Bool
    (NoServices -> NoServices -> Bool)
    -> (NoServices -> NoServices -> Bool) -> Eq NoServices
    forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
    $c== :: NoServices -> NoServices -> Bool
    == :: NoServices -> NoServices -> Bool
    $c/= :: NoServices -> NoServices -> Bool
    /= :: NoServices -> NoServices -> Bool
    Eq, Int -> NoServices -> ServiceName -> ServiceName
    [NoServices] -> ServiceName -> ServiceName
    NoServices -> ServiceName
    (Int -> NoServices -> ServiceName -> ServiceName)
    -> (NoServices -> ServiceName)
    -> ([NoServices] -> ServiceName -> ServiceName)
    -> Show NoServices
    forall a.
    (Int -> a -> ServiceName -> ServiceName)
    -> (a -> ServiceName)
    -> ([a] -> ServiceName -> ServiceName)
    -> Show a
    $cshowsPrec :: Int -> NoServices -> ServiceName -> ServiceName
    showsPrec :: Int -> NoServices -> ServiceName -> ServiceName
    $cshow :: NoServices -> ServiceName
    show :: NoServices -> ServiceName
    $cshowList :: [NoServices] -> ServiceName -> ServiceName
    showList :: [NoServices] -> ServiceName -> ServiceName
    Show, Typeable)