From 7946c25d27d94529afd084df4530ee4d37be732d Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Mon, 21 Nov 2011 16:33:00 +0100 Subject: [PATCH] htools: add runtime functionality This duplicates the lib/runtime.py functionality, allowing us to check for runtime users and groups consistency. Signed-off-by: Iustin Pop Reviewed-by: Guido Trotter --- Makefile.am | 1 + htools/Ganeti/Runtime.hs | 154 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 htools/Ganeti/Runtime.hs diff --git a/Makefile.am b/Makefile.am index e2f05b8..d92139c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/htools/Ganeti/Runtime.hs b/htools/Ganeti/Runtime.hs new file mode 100644 index 0000000..b6371d3 --- /dev/null +++ b/htools/Ganeti/Runtime.hs @@ -0,0 +1,154 @@ +{-| 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 -- 1.7.10.4