Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / Runtime.hs @ 9eeb0aa5

History | View | Annotate | Download (5 kB)

1
{-| Implementation of the runtime configuration details.
2

    
3
-}
4

    
5
{-
6

    
7
Copyright (C) 2011, 2012 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
                    deriving (Show, Enum, Bounded, Eq, Ord)
60

    
61
data MiscGroup = DaemonsGroup
62
               | AdminGroup
63
                 deriving (Show, Enum, Bounded, Eq, Ord)
64

    
65
data GanetiGroup = DaemonGroup GanetiDaemon
66
                 | ExtraGroup MiscGroup
67
                   deriving (Show, Eq, Ord)
68

    
69
type RuntimeEnts = (M.Map GanetiDaemon UserID, M.Map GanetiGroup GroupID)
70

    
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
77

    
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
84

    
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
93

    
94
-- | Returns the log file for a daemon.
95
daemonLogFile :: GanetiDaemon -> FilePath
96
daemonLogFile daemon = Path.logDir </> daemonName daemon <.> "log"
97

    
98
-- | Returns the pid file name for a daemon.
99
daemonPidFile :: GanetiDaemon -> FilePath
100
daemonPidFile daemon = Path.runDir </> daemonName daemon <.> "pid"
101

    
102
-- | All groups list. A bit hacking, as we can't enforce it's complete
103
-- at compile time.
104
allGroups :: [GanetiGroup]
105
allGroups = map DaemonGroup [minBound..maxBound] ++
106
            map ExtraGroup  [minBound..maxBound]
107

    
108
ignoreDoesNotExistErrors :: IO a -> IO (Result a)
109
ignoreDoesNotExistErrors value = do
110
  result <- tryJust (\e -> if isDoesNotExistError e
111
                             then Just (show e)
112
                             else Nothing) value
113
  return $ eitherToResult result
114

    
115
-- | Computes the group/user maps.
116
getEnts :: IO (Result RuntimeEnts)
117
getEnts = do
118
  users <- mapM (\daemon -> do
119
                   entry <- ignoreDoesNotExistErrors .
120
                            getUserEntryForName .
121
                            daemonUser $ daemon
122
                   return (entry >>= \e -> return (daemon, userID e))
123
                ) [minBound..maxBound]
124
  groups <- mapM (\group -> do
125
                    entry <- ignoreDoesNotExistErrors .
126
                             getGroupEntryForName .
127
                             daemonGroup $ group
128
                    return (entry >>= \e -> return (group, groupID e))
129
                 ) allGroups
130
  return $ do -- 'Result' monad
131
    users'  <- sequence users
132
    groups' <- sequence groups
133
    let usermap = M.fromList users'
134
        groupmap = M.fromList groups'
135
    return (usermap, groupmap)
136

    
137

    
138
-- | Checks whether a daemon runs as the right user.
139
verifyDaemonUser :: GanetiDaemon -> RuntimeEnts -> IO ()
140
verifyDaemonUser daemon ents = do
141
  myuid <- getEffectiveUserID
142
  -- note: we use directly ! as lookup failues shouldn't happen, due
143
  -- to the above map construction
144
  checkUidMatch (daemonName daemon) ((M.!) (fst ents) daemon) myuid
145

    
146
-- | Check that two UIDs are matching or otherwise exit.
147
checkUidMatch :: String -> UserID -> UserID -> IO ()
148
checkUidMatch name expected actual =
149
  when (expected /= actual) $ do
150
    hPrintf stderr "%s started using wrong user ID (%d), \
151
                   \expected %d\n" name
152
              (fromIntegral actual::Int)
153
              (fromIntegral expected::Int) :: IO ()
154
    exitWith $ ExitFailure C.exitFailure