    module Propellor.Property.Network where

    import Propellor.Base
    import Propellor.Property.File

    import Data.Char

    type Interface = String

    -- | Options to put in a stanza of an ifupdown interfaces file.
    type InterfaceOptions = [(String, String)]

    -- | Stanza of an ifupdown interfaces file, with header lines and options.
    type InterfaceStanza = ([String], InterfaceOptions)

    ifUp :: Interface -> Property DebianLike
    ifUp :: Interface -> Property DebianLike
    ifUp Interface
    iface = 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
    $ Interface
    -> [Interface]
    -> UncheckedProperty
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    cmdProperty Interface
    "ifup" [Interface
    iface]
    	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
    MadeChange

    -- | Resets /etc/network/interfaces to a clean and empty state,
    -- containing just the standard loopback interface, and with
    -- interfacesD enabled.
    --
    -- This can be used as a starting point to defining other interfaces.
    --
    -- No interfaces are brought up or down by this property.
    cleanInterfacesFile :: Property DebianLike
    cleanInterfacesFile :: Property DebianLike
    cleanInterfacesFile = Interface -> [Interface] -> InterfaceOptions -> Property DebianLike
    interfaceFileContains Interface
    interfacesFile
    	[ Interface
    "source-directory interfaces.d"
    	, Interface
    ""
    	, Interface
    "# The loopback network interface"
    	, Interface
    "auto lo"
    	, Interface
    "iface lo inet loopback"
    	]
    	[]
    	Property DebianLike -> Interface -> Property DebianLike
    forall p. IsProp p => p -> Interface -> p
    `describe` (Interface
    "clean " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    interfacesFile)

    -- | Configures an interface to get its address via dhcp.
    dhcp :: Interface -> Property DebianLike
    dhcp :: Interface -> Property DebianLike
    dhcp Interface
    iface = Interface -> InterfaceOptions -> Property DebianLike
    dhcp' Interface
    iface InterfaceOptions
    forall a. Monoid a => a
    mempty

    dhcp' :: Interface -> InterfaceOptions -> Property DebianLike
    dhcp' :: Interface -> InterfaceOptions -> Property DebianLike
    dhcp' Interface
    iface InterfaceOptions
    options = Interface -> [Interface] -> InterfaceOptions -> Property DebianLike
    interfaceFileContains (Interface -> Interface
    interfaceDFile Interface
    iface)
    	[ Interface
    "auto " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface
    	, Interface
    "iface " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    " inet dhcp"
    	] InterfaceOptions
    options
    	Property DebianLike -> Interface -> Property DebianLike
    forall p. IsProp p => p -> Interface -> p
    `describe` (Interface
    "dhcp " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface)
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    interfacesDEnabled

    newtype Gateway = Gateway IPAddr

    -- | Configures an interface with a static address and gateway.
    static :: Interface -> IPAddr -> Maybe Gateway -> Property DebianLike
    static :: Interface -> IPAddr -> Maybe Gateway -> Property DebianLike
    static Interface
    iface IPAddr
    addr Maybe Gateway
    gateway = Interface
    -> IPAddr
    -> Maybe Gateway
    -> InterfaceOptions
    -> Property DebianLike
    static' Interface
    iface IPAddr
    addr Maybe Gateway
    gateway InterfaceOptions
    forall a. Monoid a => a
    mempty

    static' :: Interface -> IPAddr -> Maybe Gateway -> InterfaceOptions -> Property DebianLike
    static' :: Interface
    -> IPAddr
    -> Maybe Gateway
    -> InterfaceOptions
    -> Property DebianLike
    static' Interface
    iface IPAddr
    addr Maybe Gateway
    gateway InterfaceOptions
    options =
    	Interface
    -> [(IPAddr, Maybe Gateway, InterfaceOptions)]
    -> Property DebianLike
    static'' Interface
    iface [(IPAddr
    addr, Maybe Gateway
    gateway, InterfaceOptions
    options)]

    -- | Configures an interface with several stanzas (IPv4 and IPv6 for example).
    static'' :: Interface -> [(IPAddr, Maybe Gateway, InterfaceOptions)] -> Property DebianLike
    static'' :: Interface
    -> [(IPAddr, Maybe Gateway, InterfaceOptions)]
    -> Property DebianLike
    static'' Interface
    iface [(IPAddr, Maybe Gateway, InterfaceOptions)]
    confs =
    	Interface -> [InterfaceStanza] -> Property DebianLike
    interfaceFileContains' (Interface -> Interface
    interfaceDFile Interface
    iface) [InterfaceStanza]
    stanzas
    	Property DebianLike -> Interface -> Property DebianLike
    forall p. IsProp p => p -> Interface -> p
    `describe` (Interface
    "static IP address for " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface)
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    interfacesDEnabled
      where
    	stanzas :: [InterfaceStanza]
    stanzas = ((IPAddr, Maybe Gateway, InterfaceOptions) -> InterfaceStanza)
    -> [(IPAddr, Maybe Gateway, InterfaceOptions)] -> [InterfaceStanza]
    forall a b. (a -> b) -> [a] -> [b]
    map (IPAddr, Maybe Gateway, InterfaceOptions) -> InterfaceStanza
    stanza [(IPAddr, Maybe Gateway, InterfaceOptions)]
    confs
    	stanza :: (IPAddr, Maybe Gateway, InterfaceOptions) -> InterfaceStanza
    stanza (IPAddr
    addr, Maybe Gateway
    gateway, InterfaceOptions
    options) = (IPAddr -> [Interface]
    headerlines IPAddr
    addr, IPAddr -> Maybe Gateway -> InterfaceOptions -> InterfaceOptions
    forall {t}.
    ConfigurableValue t =>
    t -> Maybe Gateway -> InterfaceOptions -> InterfaceOptions
    options' IPAddr
    addr Maybe Gateway
    gateway InterfaceOptions
    options)
    	headerlines :: IPAddr -> [Interface]
    headerlines IPAddr
    addr =
    		[ Interface
    "auto " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface
    		, Interface
    "iface " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    " " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ (IPAddr -> Interface
    inet IPAddr
    addr) Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    " static"
    		]
    	options' :: t -> Maybe Gateway -> InterfaceOptions -> InterfaceOptions
    options' t
    addr Maybe Gateway
    gateway InterfaceOptions
    options = [Maybe (Interface, Interface)] -> InterfaceOptions
    forall a. [Maybe a] -> [a]
    catMaybes
    		[ (Interface, Interface) -> Maybe (Interface, Interface)
    forall a. a -> Maybe a
    Just ((Interface, Interface) -> Maybe (Interface, Interface))
    -> (Interface, Interface) -> Maybe (Interface, Interface)
    forall a b. (a -> b) -> a -> b
    $ (Interface
    "address", t -> Interface
    forall t. ConfigurableValue t => t -> Interface
    val t
    addr)
    		, case Maybe Gateway
    gateway of
    			Just (Gateway IPAddr
    gaddr) ->
    				(Interface, Interface) -> Maybe (Interface, Interface)
    forall a. a -> Maybe a
    Just (Interface
    "gateway", IPAddr -> Interface
    forall t. ConfigurableValue t => t -> Interface
    val IPAddr
    gaddr)
    			Maybe Gateway
    Nothing -> Maybe (Interface, Interface)
    forall a. Maybe a
    Nothing
    		] InterfaceOptions -> InterfaceOptions -> InterfaceOptions
    forall a. [a] -> [a] -> [a]
    ++ InterfaceOptions
    options
    	inet :: IPAddr -> Interface
    inet IPAddr
    addr = case IPAddr
    addr of
    		IPv4 Interface
    _ -> Interface
    "inet"
    		IPv6 Interface
    _ -> Interface
    "inet6"

    -- | Writes a static interface file for the specified interface
    -- to preserve its current configuration.
    --
    -- The interface has to be up already. It could have been brought up by
    -- DHCP, or by other means. The current ipv4 addresses
    -- and routing configuration of the interface are written into the file.
    --
    -- If the interface file already exists, this property does nothing,
    -- no matter its content.
    --
    -- (ipv6 addresses are not included because it's assumed they come up
    -- automatically in most situations.)
    preserveStatic :: Interface -> Property DebianLike
    preserveStatic :: Interface -> Property DebianLike
    preserveStatic Interface
    iface = Property DebianLike -> 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 DebianLike -> Property DebianLike)
    -> Property DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $
    	IO 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) -> IO Bool -> IO Bool
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Interface -> IO Bool
    doesFileExist Interface
    f) Property DebianLike
    setup
    		Property DebianLike -> Interface -> Property DebianLike
    forall p. IsProp p => p -> Interface -> p
    `describe` Interface
    desc
    		Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    interfacesDEnabled
      where
    	f :: Interface
    f = Interface -> Interface
    interfaceDFile Interface
    iface
    	desc :: Interface
    desc = Interface
    "static " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface
    	setup :: Property DebianLike
    	setup :: Property DebianLike
    setup = Interface
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
        -> Propellor Result)
    -> Property DebianLike
    forall {k} (metatypes :: k).
    SingI metatypes =>
    Interface
    -> (OuterMetaTypesWitness metatypes -> Propellor Result)
    -> Property (MetaTypes metatypes)
    property' Interface
    desc ((OuterMetaTypesWitness
        '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
      -> Propellor Result)
     -> Property DebianLike)
    -> (OuterMetaTypesWitness
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
        -> Propellor Result)
    -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ \OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
    o -> do
    		[Interface]
    ls <- IO [Interface] -> Propellor [Interface]
    forall a. IO a -> Propellor a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO [Interface] -> Propellor [Interface])
    -> IO [Interface] -> Propellor [Interface]
    forall a b. (a -> b) -> a -> b
    $ Interface -> [Interface]
    lines (Interface -> [Interface]) -> IO Interface -> IO [Interface]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Interface -> [Interface] -> IO Interface
    readProcess Interface
    "ip"
    			[Interface
    "-o", Interface
    "addr", Interface
    "show", Interface
    iface, Interface
    "scope", Interface
    "global"]
    		[Interface]
    stanzas <- IO [Interface] -> Propellor [Interface]
    forall a. IO a -> Propellor a
    forall (m :: * -> *) a. MonadIO m => IO a -> m a
    liftIO (IO [Interface] -> Propellor [Interface])
    -> IO [Interface] -> Propellor [Interface]
    forall a b. (a -> b) -> a -> b
    $ [[Interface]] -> [Interface]
    forall (t :: * -> *) a. Foldable t => t [a] -> [a]
    concat ([[Interface]] -> [Interface])
    -> IO [[Interface]] -> IO [Interface]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> (Interface -> IO [Interface]) -> [Interface] -> IO [[Interface]]
    forall (t :: * -> *) (m :: * -> *) a b.
    (Traversable t, Monad m) =>
    (a -> m b) -> t a -> m (t b)
    forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
    mapM Interface -> IO [Interface]
    mkstanza [Interface]
    ls
    		OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Propellor Result
    forall (inner :: [MetaType]) (outer :: [MetaType]).
    EnsurePropertyAllowed inner outer =>
    OuterMetaTypesWitness outer
    -> Property (MetaTypes inner) -> Propellor Result
    ensureProperty OuterMetaTypesWitness
      '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]
    o (Property
       (MetaTypes
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
     -> Propellor Result)
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ Interface
    -> [Interface]
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    hasContent Interface
    f ([Interface]
     -> Property
          (MetaTypes
             '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> [Interface]
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $ (Interface
    "auto " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface) Interface -> [Interface] -> [Interface]
    forall a. a -> [a] -> [a]
    : [Interface]
    stanzas
    	mkstanza :: Interface -> IO [Interface]
    mkstanza Interface
    ipline = case Interface -> [Interface]
    words Interface
    ipline of
    		-- Note that the IP address is written CIDR style, so
    		-- the netmask does not need to be specified separately.
    		(Interface
    _:Interface
    iface':Interface
    "inet":Interface
    addr:[Interface]
    _) | Interface
    iface' Interface -> Interface -> Bool
    forall a. Eq a => a -> a -> Bool
    == Interface
    iface -> do
    			Maybe Interface
    gw <- IO (Maybe Interface)
    getgateway
    			[Interface] -> IO [Interface]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ([Interface] -> IO [Interface]) -> [Interface] -> IO [Interface]
    forall a b. (a -> b) -> a -> b
    $ [Maybe Interface] -> [Interface]
    forall a. [Maybe a] -> [a]
    catMaybes
    				[ Interface -> Maybe Interface
    forall a. a -> Maybe a
    Just (Interface -> Maybe Interface) -> Interface -> Maybe Interface
    forall a b. (a -> b) -> a -> b
    $ Interface
    "iface " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    iface Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    " inet static"
    				, Interface -> Maybe Interface
    forall a. a -> Maybe a
    Just (Interface -> Maybe Interface) -> Interface -> Maybe Interface
    forall a b. (a -> b) -> a -> b
    $ Interface
    "\taddress " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    addr
    				, (Interface
    "\tgateway " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++) (Interface -> Interface) -> Maybe Interface -> Maybe Interface
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Maybe Interface
    gw
    				]
    		[Interface]
    _ -> [Interface] -> IO [Interface]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return []
    	getgateway :: IO (Maybe Interface)
    getgateway = do
    		[Interface]
    rs <- Interface -> [Interface]
    lines (Interface -> [Interface]) -> IO Interface -> IO [Interface]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> Interface -> [Interface] -> IO Interface
    readProcess Interface
    "ip"
    			[Interface
    "route", Interface
    "show", Interface
    "scope", Interface
    "global", Interface
    "dev", Interface
    iface]
    		Maybe Interface -> IO (Maybe Interface)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Maybe Interface -> IO (Maybe Interface))
    -> Maybe Interface -> IO (Maybe Interface)
    forall a b. (a -> b) -> a -> b
    $ case Interface -> [Interface]
    words (Interface -> [Interface]) -> Maybe Interface -> Maybe [Interface]
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> [Interface] -> Maybe Interface
    forall a. [a] -> Maybe a
    headMaybe [Interface]
    rs of
    			Just (Interface
    "default":Interface
    "via":Interface
    gw:[Interface]
    _) -> Interface -> Maybe Interface
    forall a. a -> Maybe a
    Just Interface
    gw
    			Maybe [Interface]
    _ -> Maybe Interface
    forall a. Maybe a
    Nothing

    -- | 6to4 ipv6 connection, should work anywhere
    ipv6to4 :: Property DebianLike
    ipv6to4 :: Property DebianLike
    ipv6to4 = Property DebianLike -> 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 DebianLike -> Property DebianLike)
    -> Property DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ Interface -> [Interface] -> InterfaceOptions -> Property DebianLike
    interfaceFileContains (Interface -> Interface
    interfaceDFile Interface
    "sit0")
    	[ Interface
    "auto sit0"
    	, Interface
    "iface sit0 inet6 static"
    	]
    	[ (Interface
    "address", Interface
    "2002:5044:5531::1")
    	, (Interface
    "netmask", Interface
    "64")
    	, (Interface
    "gateway", Interface
    "::192.88.99.1")
    	]
    	Property DebianLike -> Interface -> Property DebianLike
    forall p. IsProp p => p -> Interface -> p
    `describe` Interface
    "ipv6to4"
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    interfacesDEnabled
    	Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Interface -> Property DebianLike
    ifUp Interface
    "sit0"

    interfacesFile :: FilePath
    interfacesFile :: Interface
    interfacesFile = Interface
    "/etc/network/interfaces"

    -- | A file in the interfaces.d directory.
    interfaceDFile :: Interface -> FilePath
    interfaceDFile :: Interface -> Interface
    interfaceDFile Interface
    i = Interface
    "/etc/network/interfaces.d" Interface -> Interface -> Interface
    </> Interface -> Interface
    escapeInterfaceDName Interface
    i

    -- | /etc/network/interfaces.d/ files have to match -- ^[a-zA-Z0-9_-]+$
    -- see "man 5 interfaces"
    escapeInterfaceDName :: Interface -> FilePath
    escapeInterfaceDName :: Interface -> Interface
    escapeInterfaceDName = (Char -> Bool) -> Interface -> Interface
    forall a. (a -> Bool) -> [a] -> [a]
    filter (\Char
    c -> Char -> Bool
    isAscii Char
    c Bool -> Bool -> Bool
    && (Char -> Bool
    isAlphaNum Char
    c Bool -> Bool -> Bool
    || Char
    c Char -> Interface -> Bool
    forall a. Eq a => a -> [a] -> Bool
    forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
    `elem` Interface
    "_-"))

    -- | Ensures that files in the the interfaces.d directory are used.
    -- interfacesDEnabled :: Property DebianLike
    interfacesDEnabled :: Property DebianLike
    interfacesDEnabled :: Property DebianLike
    interfacesDEnabled = 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
    $
    	Interface
    -> Interface
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    containsLine Interface
    interfacesFile Interface
    "source-directory interfaces.d"
    		Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Interface
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall p. IsProp p => p -> Interface -> p
    `describe` Interface
    "interfaces.d directory enabled"

    interfaceFileContains :: FilePath -> [String] -> InterfaceOptions -> Property DebianLike
    interfaceFileContains :: Interface -> [Interface] -> InterfaceOptions -> Property DebianLike
    interfaceFileContains Interface
    f [Interface]
    headerlines InterfaceOptions
    options =
    	Interface -> [InterfaceStanza] -> Property DebianLike
    interfaceFileContains' Interface
    f [([Interface]
    headerlines, InterfaceOptions
    options)]

    interfaceFileContains' :: FilePath -> [InterfaceStanza] -> Property DebianLike
    interfaceFileContains' :: Interface -> [InterfaceStanza] -> Property DebianLike
    interfaceFileContains' Interface
    f [InterfaceStanza]
    stanzas = 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
    $ Interface
    -> [Interface]
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    hasContent Interface
    f ([Interface]
     -> Property
          (MetaTypes
             '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> [Interface]
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $
    	Interface
    warning Interface -> [Interface] -> [Interface]
    forall a. a -> [a] -> [a]
    : (InterfaceStanza -> [Interface])
    -> [InterfaceStanza] -> [Interface]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap InterfaceStanza -> [Interface]
    stanza [InterfaceStanza]
    stanzas
      where
    	stanza :: InterfaceStanza -> [Interface]
    stanza ([Interface]
    headerlines, InterfaceOptions
    options) = [Interface]
    headerlines [Interface] -> [Interface] -> [Interface]
    forall a. [a] -> [a] -> [a]
    ++ ((Interface, Interface) -> Interface)
    -> InterfaceOptions -> [Interface]
    forall a b. (a -> b) -> [a] -> [b]
    map (Interface, Interface) -> Interface
    fmt InterfaceOptions
    options
    	fmt :: (Interface, Interface) -> Interface
    fmt (Interface
    k, Interface
    v) = Interface
    "\t" Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    k Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    " " Interface -> Interface -> Interface
    forall a. [a] -> [a] -> [a]
    ++ Interface
    v
    	warning :: Interface
    warning = Interface
    "# Deployed by propellor, do not edit."