Statistics
| Branch: | Tag: | Revision:

root / htools / Ganeti / Runtime.hs @ 5cefb2b2

History | View | Annotate | Download (5.1 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 GanetiConfd = C.daemonsLogfilesGanetiConfd
96
daemonLogFile _           = error "Unimplemented"
97

    
98
-- | Returns the pid file name for a daemon.
99
daemonPidFile :: GanetiDaemon -> FilePath
100
daemonPidFile daemon = C.runGanetiDir </> 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