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