1 {-| Implementation of the runtime configuration details.
7 Copyright (C) 2011, 2012 Google Inc.
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
40 import Control.Exception
42 import qualified Data.Map as M
44 import System.FilePath
46 import System.IO.Error
47 import System.Posix.Types
48 import System.Posix.User
51 import qualified Ganeti.Constants as C
52 import qualified Ganeti.Path as Path
53 import Ganeti.BasicTypes
55 data GanetiDaemon = GanetiMasterd
59 deriving (Show, Enum, Bounded, Eq, Ord)
61 data MiscGroup = DaemonsGroup
63 deriving (Show, Enum, Bounded, Eq, Ord)
65 data GanetiGroup = DaemonGroup GanetiDaemon
66 | ExtraGroup MiscGroup
67 deriving (Show, Eq, Ord)
69 type RuntimeEnts = (M.Map GanetiDaemon UserID, M.Map GanetiGroup GroupID)
71 -- | Returns the daemon name for a given daemon.
72 daemonName :: GanetiDaemon -> String
73 daemonName GanetiMasterd = C.masterd
74 daemonName GanetiNoded = C.noded
75 daemonName GanetiRapi = C.rapi
76 daemonName GanetiConfd = C.confd
78 -- | Returns the configured user name for a daemon.
79 daemonUser :: GanetiDaemon -> String
80 daemonUser GanetiMasterd = C.masterdUser
81 daemonUser GanetiNoded = C.nodedUser
82 daemonUser GanetiRapi = C.rapiUser
83 daemonUser GanetiConfd = C.confdUser
85 -- | Returns the configured group for a daemon.
86 daemonGroup :: GanetiGroup -> String
87 daemonGroup (DaemonGroup GanetiMasterd) = C.masterdGroup
88 daemonGroup (DaemonGroup GanetiNoded) = C.nodedGroup
89 daemonGroup (DaemonGroup GanetiRapi) = C.rapiGroup
90 daemonGroup (DaemonGroup GanetiConfd) = C.confdGroup
91 daemonGroup (ExtraGroup DaemonsGroup) = C.daemonsGroup
92 daemonGroup (ExtraGroup AdminGroup) = C.adminGroup
94 -- | Returns the log file for a daemon.
95 daemonLogFile :: GanetiDaemon -> IO FilePath
96 daemonLogFile daemon = do
98 return $ logDir </> daemonName daemon <.> "log"
100 -- | Returns the pid file name for a daemon.
101 daemonPidFile :: GanetiDaemon -> IO FilePath
102 daemonPidFile daemon = do
103 runDir <- Path.runDir
104 return $ runDir </> daemonName daemon <.> "pid"
106 -- | All groups list. A bit hacking, as we can't enforce it's complete
108 allGroups :: [GanetiGroup]
109 allGroups = map DaemonGroup [minBound..maxBound] ++
110 map ExtraGroup [minBound..maxBound]
112 ignoreDoesNotExistErrors :: IO a -> IO (Result a)
113 ignoreDoesNotExistErrors value = do
114 result <- tryJust (\e -> if isDoesNotExistError e
117 return $ eitherToResult result
119 -- | Computes the group/user maps.
120 getEnts :: IO (Result RuntimeEnts)
122 users <- mapM (\daemon -> do
123 entry <- ignoreDoesNotExistErrors .
124 getUserEntryForName .
126 return (entry >>= \e -> return (daemon, userID e))
127 ) [minBound..maxBound]
128 groups <- mapM (\group -> do
129 entry <- ignoreDoesNotExistErrors .
130 getGroupEntryForName .
132 return (entry >>= \e -> return (group, groupID e))
134 return $ do -- 'Result' monad
135 users' <- sequence users
136 groups' <- sequence groups
137 let usermap = M.fromList users'
138 groupmap = M.fromList groups'
139 return (usermap, groupmap)
142 -- | Checks whether a daemon runs as the right user.
143 verifyDaemonUser :: GanetiDaemon -> RuntimeEnts -> IO ()
144 verifyDaemonUser daemon ents = do
145 myuid <- getEffectiveUserID
146 -- note: we use directly ! as lookup failues shouldn't happen, due
147 -- to the above map construction
148 checkUidMatch (daemonName daemon) ((M.!) (fst ents) daemon) myuid
150 -- | Check that two UIDs are matching or otherwise exit.
151 checkUidMatch :: String -> UserID -> UserID -> IO ()
152 checkUidMatch name expected actual =
153 when (expected /= actual) $ do
154 hPrintf stderr "%s started using wrong user ID (%d), \
156 (fromIntegral actual::Int)
157 (fromIntegral expected::Int) :: IO ()
158 exitWith $ ExitFailure C.exitFailure