Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / Runtime.hs @ 3329f4de

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 Ganeti.BasicTypes
53

    
54
data GanetiDaemon = GanetiMasterd
55
                  | GanetiNoded
56
                  | GanetiRapi
57
                  | GanetiConfd
58
                    deriving (Show, Enum, Bounded, Eq, Ord)
59

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

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

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

    
70
-- | Returns the daemon name for a given daemon.
71
daemonName :: GanetiDaemon -> String
72
daemonName GanetiMasterd = C.masterd
73
daemonName GanetiNoded   = C.noded
74
daemonName GanetiRapi    = C.rapi
75
daemonName GanetiConfd   = C.confd
76

    
77
-- | Returns the configured user name for a daemon.
78
daemonUser :: GanetiDaemon -> String
79
daemonUser GanetiMasterd = C.masterdUser
80
daemonUser GanetiNoded   = C.nodedUser
81
daemonUser GanetiRapi    = C.rapiUser
82
daemonUser GanetiConfd   = C.confdUser
83

    
84
-- | Returns the configured group for a daemon.
85
daemonGroup :: GanetiGroup -> String
86
daemonGroup (DaemonGroup GanetiMasterd) = C.masterdGroup
87
daemonGroup (DaemonGroup GanetiNoded)   = C.nodedGroup
88
daemonGroup (DaemonGroup GanetiRapi)    = C.rapiGroup
89
daemonGroup (DaemonGroup GanetiConfd)   = C.confdGroup
90
daemonGroup (ExtraGroup  DaemonsGroup)  = C.daemonsGroup
91
daemonGroup (ExtraGroup  AdminGroup)    = C.adminGroup
92

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

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

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

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

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

    
136

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

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