module Propellor.Ssh where
import Propellor.Base
import Utility.UserInfo
import Utility.FileSystemEncoding
import System.Posix
import Data.Time.Clock.POSIX
import Data.Hashable
sshCachingParams :: HostName -> IO [CommandParam]
sshCachingParams :: HostName -> IO [CommandParam]
sshCachingParams HostName
hn = do
HostName
home <- IO HostName
myHomeDir
let socketfile :: HostName
socketfile = HostName -> HostName -> HostName
socketFile HostName
home HostName
hn
Bool -> HostName -> IO ()
createDirectoryIfMissing Bool
True (HostName -> HostName
takeDirectory HostName
socketfile)
let ps :: [CommandParam]
ps =
[ HostName -> CommandParam
Param HostName
"-o"
, HostName -> CommandParam
Param (HostName
"ControlPath=" forall a. [a] -> [a] -> [a]
++ HostName
socketfile)
, HostName -> CommandParam
Param HostName
"-o", HostName -> CommandParam
Param HostName
"ControlMaster=auto"
, HostName -> CommandParam
Param HostName
"-o", HostName -> CommandParam
Param HostName
"ControlPersist=yes"
]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall (m :: * -> *). Monad m => m ()
noop ([CommandParam] -> HostName -> FileStatus -> IO ()
expireold [CommandParam]
ps HostName
socketfile)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< forall (m :: * -> *) a. MonadCatch m => m a -> m (Maybe a)
catchMaybeIO (HostName -> IO FileStatus
getFileStatus HostName
socketfile)
forall (m :: * -> *) a. Monad m => a -> m a
return [CommandParam]
ps
where
expireold :: [CommandParam] -> HostName -> FileStatus -> IO ()
expireold [CommandParam]
ps HostName
f FileStatus
s = do
Integer
now <- forall a b. (RealFrac a, Integral b) => a -> b
truncate forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO POSIXTime
getPOSIXTime :: IO Integer
if FileStatus -> EpochTime
modificationTime FileStatus
s forall a. Ord a => a -> a -> Bool
> forall a b. (Integral a, Num b) => a -> b
fromIntegral Integer
now forall a. Num a => a -> a -> a
- EpochTime
tenminutes
then HostName -> IO ()
touchFile HostName
f
else do
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall a b. (a -> b) -> a -> b
$ HostName -> [CommandParam] -> IO Bool
boolSystem HostName
"ssh" forall a b. (a -> b) -> a -> b
$
[ HostName -> CommandParam
Param HostName
"-O", HostName -> CommandParam
Param HostName
"stop" ] forall a. [a] -> [a] -> [a]
++ [CommandParam]
ps forall a. [a] -> [a] -> [a]
++
[ HostName -> CommandParam
Param HostName
"localhost" ]
HostName -> IO ()
nukeFile HostName
f
tenminutes :: EpochTime
tenminutes = EpochTime
600
socketFile :: FilePath -> HostName -> FilePath
socketFile :: HostName -> HostName -> HostName
socketFile HostName
home HostName
hn = [HostName] -> HostName -> HostName
selectSocketFile
[ HostName
sshdir HostName -> HostName -> HostName
</> HostName
hn forall a. [a] -> [a] -> [a]
++ HostName
".sock"
, HostName
sshdir HostName -> HostName -> HostName
</> HostName
hn
, HostName
sshdir HostName -> HostName -> HostName
</> forall a. Int -> [a] -> [a]
take Int
10 HostName
hn forall a. [a] -> [a] -> [a]
++ HostName
"-" forall a. [a] -> [a] -> [a]
++ HostName
checksum
, HostName
sshdir HostName -> HostName -> HostName
</> HostName
checksum
]
(HostName
home HostName -> HostName -> HostName
</> HostName
".propellor-" forall a. [a] -> [a] -> [a]
++ HostName
checksum)
where
sshdir :: HostName
sshdir = HostName
home HostName -> HostName -> HostName
</> HostName
".ssh" HostName -> HostName -> HostName
</> HostName
"propellor"
checksum :: HostName
checksum = forall a. Int -> [a] -> [a]
take Int
9 forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> HostName
show forall a b. (a -> b) -> a -> b
$ forall a. Num a => a -> a
abs forall a b. (a -> b) -> a -> b
$ forall a. Hashable a => a -> Int
hash HostName
hn
selectSocketFile :: [FilePath] -> FilePath -> FilePath
selectSocketFile :: [HostName] -> HostName -> HostName
selectSocketFile [] HostName
d = HostName
d
selectSocketFile (HostName
f:[HostName]
fs) HostName
d
| HostName -> Bool
valid_unix_socket_path HostName
f = HostName
f
| Bool
otherwise = [HostName] -> HostName -> HostName
selectSocketFile [HostName]
fs HostName
d
valid_unix_socket_path :: FilePath -> Bool
valid_unix_socket_path :: HostName -> Bool
valid_unix_socket_path HostName
f = forall (t :: * -> *) a. Foldable t => t a -> Int
length (HostName -> [Word8]
decodeW8 HostName
f) forall a. Ord a => a -> a -> Bool
< Int
100 forall a. Num a => a -> a -> a
- Int
reservedbyssh
where
reservedbyssh :: Int
reservedbyssh = Int
18