Commit 7946c25d authored by Iustin Pop's avatar Iustin Pop

htools: add runtime functionality

This duplicates the lib/runtime.py functionality, allowing us to check
for runtime users and groups consistency.
Signed-off-by: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarGuido Trotter <ultrotter@google.com>
parent 21dc8694
......@@ -402,6 +402,7 @@ HS_LIB_SRCS = \
htools/Ganeti/Luxi.hs \
htools/Ganeti/Objects.hs \
htools/Ganeti/OpCodes.hs \
htools/Ganeti/Runtime.hs \
htools/Ganeti/THH.hs
HS_BUILT_SRCS = htools/Ganeti/HTools/Version.hs htools/Ganeti/Constants.hs
......
{-| Implementation of the runtime configuration details.
-}
{-
Copyright (C) 2011, 2012 Google Inc.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
-}
module Ganeti.Runtime
( GanetiDaemon(..)
, MiscGroup(..)
, GanetiGroup(..)
, RuntimeEnts
, daemonName
, daemonUser
, daemonGroup
, daemonLogFile
, daemonPidFile
, getEnts
, verifyDaemonUser
) where
import Control.Exception
import Control.Monad
import qualified Data.Map as M
import System.Exit
import System.FilePath
import System.IO
import System.IO.Error
import System.Posix.Types
import System.Posix.User
import Text.Printf
import qualified Ganeti.Constants as C
import Ganeti.BasicTypes
data GanetiDaemon = GanetiMasterd
| GanetiNoded
| GanetiRapi
| GanetiConfd
deriving (Show, Enum, Bounded, Eq, Ord)
data MiscGroup = DaemonsGroup
| AdminGroup
deriving (Show, Enum, Bounded, Eq, Ord)
data GanetiGroup = DaemonGroup GanetiDaemon
| ExtraGroup MiscGroup
deriving (Show, Eq, Ord)
type RuntimeEnts = (M.Map GanetiDaemon UserID, M.Map GanetiGroup GroupID)
-- | Returns the daemon name for a given daemon.
daemonName :: GanetiDaemon -> String
daemonName GanetiMasterd = C.masterd
daemonName GanetiNoded = C.noded
daemonName GanetiRapi = C.rapi
daemonName GanetiConfd = C.confd
-- | Returns the configured user name for a daemon.
daemonUser :: GanetiDaemon -> String
daemonUser GanetiMasterd = C.masterdUser
daemonUser GanetiNoded = C.nodedUser
daemonUser GanetiRapi = C.rapiUser
daemonUser GanetiConfd = C.confdUser
-- | Returns the configured group for a daemon.
daemonGroup :: GanetiGroup -> String
daemonGroup (DaemonGroup GanetiMasterd) = C.masterdGroup
daemonGroup (DaemonGroup GanetiNoded) = C.nodedGroup
daemonGroup (DaemonGroup GanetiRapi) = C.rapiGroup
daemonGroup (DaemonGroup GanetiConfd) = C.confdGroup
daemonGroup (ExtraGroup DaemonsGroup) = C.daemonsGroup
daemonGroup (ExtraGroup AdminGroup) = C.adminGroup
-- | Returns the log file for a daemon.
daemonLogFile :: GanetiDaemon -> FilePath
daemonLogFile GanetiConfd = C.daemonsLogfilesGanetiConfd
daemonLogFile _ = error "Unimplemented"
-- | Returns the pid file name for a daemon.
daemonPidFile :: GanetiDaemon -> FilePath
daemonPidFile daemon = C.runGanetiDir </> daemonName daemon <.> "pid"
-- | All groups list. A bit hacking, as we can't enforce it's complete
-- at compile time.
allGroups :: [GanetiGroup]
allGroups = map DaemonGroup [minBound..maxBound] ++
map ExtraGroup [minBound..maxBound]
ignoreDoesNotExistErrors :: IO a -> IO (Result a)
ignoreDoesNotExistErrors value = do
result <- tryJust (\e -> if isDoesNotExistError e
then Just (show e)
else Nothing) value
return $ eitherToResult result
-- | Computes the group/user maps.
getEnts :: IO (Result RuntimeEnts)
getEnts = do
users <- mapM (\daemon -> do
entry <- ignoreDoesNotExistErrors .
getUserEntryForName .
daemonUser $ daemon
return (entry >>= \e -> return (daemon, userID e))
) [minBound..maxBound]
groups <- mapM (\group -> do
entry <- ignoreDoesNotExistErrors .
getGroupEntryForName .
daemonGroup $ group
return (entry >>= \e -> return (group, groupID e))
) allGroups
return $ do -- 'Result' monad
users' <- sequence users
groups' <- sequence groups
let usermap = M.fromList users'
groupmap = M.fromList groups'
return (usermap, groupmap)
-- | Checks whether a daemon runs as the right user.
verifyDaemonUser :: GanetiDaemon -> RuntimeEnts -> IO ()
verifyDaemonUser daemon ents = do
myuid <- getEffectiveUserID
-- note: we use directly ! as lookup failues shouldn't happen, due
-- to the above map construction
checkUidMatch (daemonName daemon) ((M.!) (fst ents) daemon) myuid
-- | Check that two UIDs are matching or otherwise exit.
checkUidMatch :: String -> UserID -> UserID -> IO ()
checkUidMatch name expected actual =
when (expected /= actual) $ do
hPrintf stderr "%s started using wrong user ID (%d), \
\expected %d\n" name
(fromIntegral actual::Int)
(fromIntegral expected::Int) :: IO ()
exitWith $ ExitFailure C.exitFailure
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment