{-# LANGUAGE GeneralizedNewtypeDeriving, TypeSynonymInstances, FlexibleInstances #-}
module Propellor.Property.Mount where
import Propellor.Base
import Utility.Path
import Data.List
import qualified Data.Semigroup as Sem
type FsType = String
type Source = String
type MountPoint = FilePath
newtype MountOpts = MountOpts [String]
deriving (b -> MountOpts -> MountOpts
NonEmpty MountOpts -> MountOpts
MountOpts -> MountOpts -> MountOpts
(MountOpts -> MountOpts -> MountOpts)
-> (NonEmpty MountOpts -> MountOpts)
-> (forall b. Integral b => b -> MountOpts -> MountOpts)
-> Semigroup MountOpts
forall b. Integral b => b -> MountOpts -> MountOpts
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
stimes :: b -> MountOpts -> MountOpts
$cstimes :: forall b. Integral b => b -> MountOpts -> MountOpts
sconcat :: NonEmpty MountOpts -> MountOpts
$csconcat :: NonEmpty MountOpts -> MountOpts
<> :: MountOpts -> MountOpts -> MountOpts
$c<> :: MountOpts -> MountOpts -> MountOpts
Sem.Semigroup, Semigroup MountOpts
MountOpts
Semigroup MountOpts
-> MountOpts
-> (MountOpts -> MountOpts -> MountOpts)
-> ([MountOpts] -> MountOpts)
-> Monoid MountOpts
[MountOpts] -> MountOpts
MountOpts -> MountOpts -> MountOpts
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
mconcat :: [MountOpts] -> MountOpts
$cmconcat :: [MountOpts] -> MountOpts
mappend :: MountOpts -> MountOpts -> MountOpts
$cmappend :: MountOpts -> MountOpts -> MountOpts
mempty :: MountOpts
$cmempty :: MountOpts
$cp1Monoid :: Semigroup MountOpts
Monoid)
class ToMountOpts a where
toMountOpts :: a -> MountOpts
instance ToMountOpts MountOpts where
toMountOpts :: MountOpts -> MountOpts
toMountOpts = MountOpts -> MountOpts
forall a. a -> a
id
instance ToMountOpts String where
toMountOpts :: String -> MountOpts
toMountOpts String
s = [String] -> MountOpts
MountOpts [String
s]
formatMountOpts :: MountOpts -> String
formatMountOpts :: MountOpts -> String
formatMountOpts (MountOpts []) = String
"defaults"
formatMountOpts (MountOpts [String]
l) = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"," [String]
l
mounted :: FsType -> Source -> MountPoint -> MountOpts -> Property UnixLike
mounted :: String -> String -> String -> MountOpts -> Property UnixLike
mounted String
fs String
src String
mnt MountOpts
opts = String -> Propellor Result -> Property UnixLike
forall k (metatypes :: k).
SingI metatypes =>
String -> Propellor Result -> Property (MetaTypes metatypes)
property (String
mnt String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" mounted") (Propellor Result -> Property UnixLike)
-> Propellor Result -> Property UnixLike
forall a b. (a -> b) -> a -> b
$
Bool -> Result
forall t. ToResult t => t -> Result
toResult (Bool -> Result) -> Propellor Bool -> Propellor Result
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO Bool -> Propellor Bool
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> String -> String -> MountOpts -> IO Bool
mount String
fs String
src String
mnt MountOpts
opts)
bindMount :: FilePath -> FilePath -> Property Linux
bindMount :: String -> String -> Property Linux
bindMount String
src String
dest = Property UnixLike -> Property Linux
forall (p :: * -> *) (untightened :: [MetaType])
(tightened :: [MetaType]).
(TightenTargets p, TightenTargetsAllowed untightened tightened,
SingI tightened) =>
p (MetaTypes untightened) -> p (MetaTypes tightened)
tightenTargets (Property UnixLike -> Property Linux)
-> Property UnixLike -> Property Linux
forall a b. (a -> b) -> a -> b
$
String -> [String] -> UncheckedProperty UnixLike
cmdProperty String
"mount" [String
"--bind", String
src, String
dest]
UncheckedProperty UnixLike -> Result -> Property UnixLike
forall (p :: * -> *) i.
Checkable p i =>
p i -> Result -> Property i
`assume` Result
MadeChange
Property UnixLike -> String -> Property UnixLike
forall p. IsProp p => p -> String -> p
`describe` (String
"bind mounted " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
src String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" to " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
dest)
swapOn :: Source -> RevertableProperty Linux Linux
swapOn :: String -> RevertableProperty Linux Linux
swapOn String
mnt = Property UnixLike -> Property Linux
forall (p :: * -> *) (untightened :: [MetaType])
(tightened :: [MetaType]).
(TightenTargets p, TightenTargetsAllowed untightened tightened,
SingI tightened) =>
p (MetaTypes untightened) -> p (MetaTypes tightened)
tightenTargets Property UnixLike
doswapon Property Linux -> Property Linux -> RevertableProperty Linux Linux
forall setupmetatypes undometatypes.
Property setupmetatypes
-> Property undometatypes
-> RevertableProperty setupmetatypes undometatypes
<!> Property UnixLike -> Property Linux
forall (p :: * -> *) (untightened :: [MetaType])
(tightened :: [MetaType]).
(TightenTargets p, TightenTargetsAllowed untightened tightened,
SingI tightened) =>
p (MetaTypes untightened) -> p (MetaTypes tightened)
tightenTargets Property UnixLike
doswapoff
where
swaps :: IO [String]
swaps = String -> [String]
lines (String -> [String]) -> IO String -> IO [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String] -> IO String
readProcess String
"swapon" [String
"--show=NAME"]
doswapon :: Property UnixLike
doswapon = IO Bool -> UncheckedProperty UnixLike -> Property UnixLike
forall (p :: * -> *) i (m :: * -> *).
(Checkable p i, LiftPropellor m) =>
m Bool -> p i -> Property i
check (String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
notElem String
mnt ([String] -> Bool) -> IO [String] -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [String]
swaps) (UncheckedProperty UnixLike -> Property UnixLike)
-> UncheckedProperty UnixLike -> Property UnixLike
forall a b. (a -> b) -> a -> b
$
String -> [String] -> UncheckedProperty UnixLike
cmdProperty String
"swapon" [String
mnt]
doswapoff :: Property UnixLike
doswapoff = IO Bool -> UncheckedProperty UnixLike -> Property UnixLike
forall (p :: * -> *) i (m :: * -> *).
(Checkable p i, LiftPropellor m) =>
m Bool -> p i -> Property i
check (String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
elem String
mnt ([String] -> Bool) -> IO [String] -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [String]
swaps) (UncheckedProperty UnixLike -> Property UnixLike)
-> UncheckedProperty UnixLike -> Property UnixLike
forall a b. (a -> b) -> a -> b
$
String -> [String] -> UncheckedProperty UnixLike
cmdProperty String
"swapoff" [String
mnt]
mount :: FsType -> Source -> MountPoint -> MountOpts -> IO Bool
mount :: String -> String -> String -> MountOpts -> IO Bool
mount String
fs String
src String
mnt MountOpts
opts = String -> [CommandParam] -> IO Bool
boolSystem String
"mount" ([CommandParam] -> IO Bool) -> [CommandParam] -> IO Bool
forall a b. (a -> b) -> a -> b
$
[ String -> CommandParam
Param String
"-t", String -> CommandParam
Param String
fs
, String -> CommandParam
Param String
"-o", String -> CommandParam
Param (MountOpts -> String
formatMountOpts MountOpts
opts)
, String -> CommandParam
Param String
src
, String -> CommandParam
Param String
mnt
]
mountPoints :: IO [MountPoint]
mountPoints :: IO [String]
mountPoints = String -> [String]
lines (String -> [String]) -> IO String -> IO [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String] -> IO String
readProcess String
"findmnt" [String
"-rn", String
"--output", String
"target"]
isMounted :: MountPoint -> IO Bool
isMounted :: String -> IO Bool
isMounted String
mnt = Maybe String -> Bool
forall a. Maybe a -> Bool
isJust (Maybe String -> Bool) -> IO (Maybe String) -> IO Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO (Maybe String)
getFsType String
mnt
mountPointsBelow :: FilePath -> IO [MountPoint]
mountPointsBelow :: String -> IO [String]
mountPointsBelow String
target = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (\String
p -> String -> String
simplifyPath String
p String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String -> String
simplifyPath String
target)
([String] -> [String])
-> ([String] -> [String]) -> [String] -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (String -> String -> Bool
dirContains String
target)
([String] -> [String]) -> IO [String] -> IO [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO [String]
mountPoints
partialBindMountsOf :: FilePath -> IO [MountPoint]
partialBindMountsOf :: String -> IO [String]
partialBindMountsOf String
sourceDir =
(String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> String -> String
forall a. Int -> [a] -> [a]
drop Int
2 (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
']')) ([String] -> [String])
-> (String -> [String]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter String -> Bool
getThem ([String] -> [String])
-> (String -> [String]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines
(String -> [String]) -> IO String -> IO [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String] -> IO String
readProcess String
"findmnt" [String
"-rn", String
"--output", String
"source,target"]
where
getThem :: String -> Bool
getThem String
l = String
bracketed String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` ((Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
takeWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
' ') String
l)
bracketed :: String
bracketed = String
"[" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
sourceDir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"]"
getFsType :: MountPoint -> IO (Maybe FsType)
getFsType :: String -> IO (Maybe String)
getFsType String
p = String -> [String] -> IO (Maybe String)
findmntField String
"fstype" [String
p]
getFsMountOpts :: MountPoint -> IO MountOpts
getFsMountOpts :: String -> IO MountOpts
getFsMountOpts String
p = MountOpts -> (String -> MountOpts) -> Maybe String -> MountOpts
forall b a. b -> (a -> b) -> Maybe a -> b
maybe MountOpts
forall a. Monoid a => a
mempty String -> MountOpts
forall a. ToMountOpts a => a -> MountOpts
toMountOpts
(Maybe String -> MountOpts) -> IO (Maybe String) -> IO MountOpts
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String] -> IO (Maybe String)
findmntField String
"fs-options" [String
p]
type UUID = String
getMountUUID :: MountPoint -> IO (Maybe UUID)
getMountUUID :: String -> IO (Maybe String)
getMountUUID String
p = String -> [String] -> IO (Maybe String)
findmntField String
"uuid" [String
p]
getSourceUUID :: Source -> IO (Maybe UUID)
getSourceUUID :: String -> IO (Maybe String)
getSourceUUID = String -> String -> IO (Maybe String)
blkidTag String
"UUID"
type Label = String
getMountLabel :: MountPoint -> IO (Maybe Label)
getMountLabel :: String -> IO (Maybe String)
getMountLabel String
p = String -> [String] -> IO (Maybe String)
findmntField String
"label" [String
p]
getSourceLabel :: Source -> IO (Maybe UUID)
getSourceLabel :: String -> IO (Maybe String)
getSourceLabel = String -> String -> IO (Maybe String)
blkidTag String
"LABEL"
getMountSource :: MountPoint -> IO (Maybe Source)
getMountSource :: String -> IO (Maybe String)
getMountSource String
p = String -> [String] -> IO (Maybe String)
findmntField String
"source" [String
p]
getMountContaining :: FilePath -> IO (Maybe Source)
getMountContaining :: String -> IO (Maybe String)
getMountContaining String
p = String -> [String] -> IO (Maybe String)
findmntField String
"source" [String
"-T", String
p]
findmntField :: String -> [String] -> IO (Maybe String)
findmntField :: String -> [String] -> IO (Maybe String)
findmntField String
field [String]
ps = Maybe String -> IO (Maybe String) -> IO (Maybe String)
forall (m :: * -> *) a. MonadCatch m => a -> m a -> m a
catchDefaultIO Maybe String
forall a. Maybe a
Nothing (IO (Maybe String) -> IO (Maybe String))
-> IO (Maybe String) -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$
[String] -> Maybe String
forall a. [a] -> Maybe a
headMaybe ([String] -> Maybe String)
-> (String -> [String]) -> String -> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null) ([String] -> [String])
-> (String -> [String]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines
(String -> Maybe String) -> IO String -> IO (Maybe String)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String] -> IO String
readProcess String
"findmnt" (String
"-n" String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
ps [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
"--output", String
field])
blkidTag :: String -> Source -> IO (Maybe String)
blkidTag :: String -> String -> IO (Maybe String)
blkidTag String
tag String
dev = Maybe String -> IO (Maybe String) -> IO (Maybe String)
forall (m :: * -> *) a. MonadCatch m => a -> m a -> m a
catchDefaultIO Maybe String
forall a. Maybe a
Nothing (IO (Maybe String) -> IO (Maybe String))
-> IO (Maybe String) -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$
[String] -> Maybe String
forall a. [a] -> Maybe a
headMaybe ([String] -> Maybe String)
-> (String -> [String]) -> String -> Maybe String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null) ([String] -> [String])
-> (String -> [String]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines
(String -> Maybe String) -> IO String -> IO (Maybe String)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> [String] -> IO String
readProcess String
"blkid" [String
dev, String
"-s", String
tag, String
"-o", String
"value"]
umountLazy :: FilePath -> IO ()
umountLazy :: String -> IO ()
umountLazy String
mnt =
IO Bool -> IO () -> IO ()
forall (m :: * -> *). Monad m => m Bool -> m () -> m ()
unlessM (String -> [CommandParam] -> IO Bool
boolSystem String
"umount" [ String -> CommandParam
Param String
"-l", String -> CommandParam
Param String
mnt ]) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
String -> IO ()
forall (m :: * -> *) a. MonadIO m => String -> m a
stopPropellorMessage (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"failed unmounting " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
mnt
unmountBelow :: FilePath -> IO ()
unmountBelow :: String -> IO ()
unmountBelow String
d = do
[String]
submnts <- String -> IO [String]
mountPointsBelow String
d
[String] -> (String -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ ([String] -> [String]
forall a. [a] -> [a]
reverse ([String] -> [String]
forall a. Ord a => [a] -> [a]
sort [String]
submnts)) String -> IO ()
umountLazy