2e1a67ef6e86e4a39b59f1043db1d507ad815ab3
[ganeti-local] / src / Ganeti / Runtime.hs
1 {-| Implementation of the runtime configuration details.
2
3 -}
4
5 {-
6
7 Copyright (C) 2011, 2012, 2013 Google Inc.
8
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.
13
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.
18
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
22 02110-1301, USA.
23
24 -}
25
26 module Ganeti.Runtime
27   ( GanetiDaemon(..)
28   , MiscGroup(..)
29   , GanetiGroup(..)
30   , RuntimeEnts
31   , daemonName
32   , daemonOnlyOnMaster
33   , daemonUser
34   , daemonGroup
35   , daemonLogFile
36   , daemonPidFile
37   , getEnts
38   , verifyDaemonUser
39   ) where
40
41 import Control.Exception
42 import Control.Monad
43 import qualified Data.Map as M
44 import System.Exit
45 import System.FilePath
46 import System.IO
47 import System.IO.Error
48 import System.Posix.Types
49 import System.Posix.User
50 import Text.Printf
51
52 import qualified Ganeti.Constants as C
53 import qualified Ganeti.Path as Path
54 import Ganeti.BasicTypes
55
56 data GanetiDaemon = GanetiMasterd
57                   | GanetiNoded
58                   | GanetiRapi
59                   | GanetiConfd
60                   | GanetiQueryd
61                   | GanetiMond
62                     deriving (Show, Enum, Bounded, Eq, Ord)
63
64 data MiscGroup = DaemonsGroup
65                | AdminGroup
66                  deriving (Show, Enum, Bounded, Eq, Ord)
67
68 data GanetiGroup = DaemonGroup GanetiDaemon
69                  | ExtraGroup MiscGroup
70                    deriving (Show, Eq, Ord)
71
72 type RuntimeEnts = (M.Map GanetiDaemon UserID, M.Map GanetiGroup GroupID)
73
74 -- | Returns the daemon name for a given daemon.
75 daemonName :: GanetiDaemon -> String
76 daemonName GanetiMasterd = C.masterd
77 daemonName GanetiNoded   = C.noded
78 daemonName GanetiRapi    = C.rapi
79 daemonName GanetiConfd   = C.confd
80 daemonName GanetiQueryd  = C.queryd
81 daemonName GanetiMond    = C.mond
82
83 -- | Returns whether the daemon only runs on the master node.
84 daemonOnlyOnMaster :: GanetiDaemon -> Bool
85 daemonOnlyOnMaster GanetiMasterd = True
86 daemonOnlyOnMaster GanetiNoded   = False
87 daemonOnlyOnMaster GanetiRapi    = False
88 daemonOnlyOnMaster GanetiConfd   = False
89 daemonOnlyOnMaster GanetiQueryd  = True
90 daemonOnlyOnMaster GanetiMond    = False
91
92 -- | Returns the log file base for a daemon.
93 daemonLogBase :: GanetiDaemon -> String
94 daemonLogBase GanetiMasterd = C.daemonsLogbaseGanetiMasterd
95 daemonLogBase GanetiNoded   = C.daemonsLogbaseGanetiNoded
96 daemonLogBase GanetiRapi    = C.daemonsLogbaseGanetiRapi
97 daemonLogBase GanetiConfd   = C.daemonsLogbaseGanetiConfd
98 daemonLogBase GanetiQueryd  = C.daemonsLogbaseGanetiQueryd
99 daemonLogBase GanetiMond    = C.daemonsLogbaseGanetiMond
100
101 -- | Returns the configured user name for a daemon.
102 daemonUser :: GanetiDaemon -> String
103 daemonUser GanetiMasterd = C.masterdUser
104 daemonUser GanetiNoded   = C.nodedUser
105 daemonUser GanetiRapi    = C.rapiUser
106 daemonUser GanetiConfd   = C.confdUser
107 daemonUser GanetiQueryd  = C.querydUser
108 daemonUser GanetiMond    = C.mondUser
109
110 -- | Returns the configured group for a daemon.
111 daemonGroup :: GanetiGroup -> String
112 daemonGroup (DaemonGroup GanetiMasterd) = C.masterdGroup
113 daemonGroup (DaemonGroup GanetiNoded)   = C.nodedGroup
114 daemonGroup (DaemonGroup GanetiRapi)    = C.rapiGroup
115 daemonGroup (DaemonGroup GanetiConfd)   = C.confdGroup
116 daemonGroup (DaemonGroup GanetiQueryd)  = C.querydGroup
117 daemonGroup (DaemonGroup GanetiMond)    = C.mondGroup
118 daemonGroup (ExtraGroup  DaemonsGroup)  = C.daemonsGroup
119 daemonGroup (ExtraGroup  AdminGroup)    = C.adminGroup
120
121 -- | Returns the log file for a daemon.
122 daemonLogFile :: GanetiDaemon -> IO FilePath
123 daemonLogFile daemon = do
124   logDir <- Path.logDir
125   return $ logDir </> daemonLogBase daemon <.> "log"
126
127 -- | Returns the pid file name for a daemon.
128 daemonPidFile :: GanetiDaemon -> IO FilePath
129 daemonPidFile daemon = do
130   runDir <- Path.runDir
131   return $ runDir </> daemonName daemon <.> "pid"
132
133 -- | All groups list. A bit hacking, as we can't enforce it's complete
134 -- at compile time.
135 allGroups :: [GanetiGroup]
136 allGroups = map DaemonGroup [minBound..maxBound] ++
137             map ExtraGroup  [minBound..maxBound]
138
139 ignoreDoesNotExistErrors :: IO a -> IO (Result a)
140 ignoreDoesNotExistErrors value = do
141   result <- tryJust (\e -> if isDoesNotExistError e
142                              then Just (show e)
143                              else Nothing) value
144   return $ eitherToResult result
145
146 -- | Computes the group/user maps.
147 getEnts :: IO (Result RuntimeEnts)
148 getEnts = do
149   users <- mapM (\daemon -> do
150                    entry <- ignoreDoesNotExistErrors .
151                             getUserEntryForName .
152                             daemonUser $ daemon
153                    return (entry >>= \e -> return (daemon, userID e))
154                 ) [minBound..maxBound]
155   groups <- mapM (\group -> do
156                     entry <- ignoreDoesNotExistErrors .
157                              getGroupEntryForName .
158                              daemonGroup $ group
159                     return (entry >>= \e -> return (group, groupID e))
160                  ) allGroups
161   return $ do -- 'Result' monad
162     users'  <- sequence users
163     groups' <- sequence groups
164     let usermap = M.fromList users'
165         groupmap = M.fromList groups'
166     return (usermap, groupmap)
167
168
169 -- | Checks whether a daemon runs as the right user.
170 verifyDaemonUser :: GanetiDaemon -> RuntimeEnts -> IO ()
171 verifyDaemonUser daemon ents = do
172   myuid <- getEffectiveUserID
173   -- note: we use directly ! as lookup failues shouldn't happen, due
174   -- to the above map construction
175   checkUidMatch (daemonName daemon) ((M.!) (fst ents) daemon) myuid
176
177 -- | Check that two UIDs are matching or otherwise exit.
178 checkUidMatch :: String -> UserID -> UserID -> IO ()
179 checkUidMatch name expected actual =
180   when (expected /= actual) $ do
181     hPrintf stderr "%s started using wrong user ID (%d), \
182                    \expected %d\n" name
183               (fromIntegral actual::Int)
184               (fromIntegral expected::Int) :: IO ()
185     exitWith $ ExitFailure C.exitFailure