Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Runtime.hs @ b9202225

History | View | Annotate | Download (7.3 kB)

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
  , daemonLogBase
34
  , daemonUser
35
  , daemonGroup
36
  , ExtraLogReason(..)
37
  , daemonLogFile
38
  , daemonsExtraLogbase
39
  , daemonsExtraLogFile
40
  , daemonPidFile
41
  , getEnts
42
  , verifyDaemonUser
43
  ) where
44

    
45
import Control.Exception
46
import Control.Monad
47
import qualified Data.Map as M
48
import System.Exit
49
import System.FilePath
50
import System.IO
51
import System.IO.Error
52
import System.Posix.Types
53
import System.Posix.User
54
import Text.Printf
55

    
56
import qualified Ganeti.ConstantUtils as ConstantUtils
57
import qualified Ganeti.Path as Path
58
import Ganeti.BasicTypes
59

    
60
import AutoConf
61

    
62
data GanetiDaemon = GanetiMasterd
63
                  | GanetiNoded
64
                  | GanetiRapi
65
                  | GanetiConfd
66
                  | GanetiKvmd
67
                  | GanetiLuxid
68
                  | GanetiMond
69
                    deriving (Show, Enum, Bounded, Eq, Ord)
70

    
71
data MiscGroup = DaemonsGroup
72
               | AdminGroup
73
                 deriving (Show, Enum, Bounded, Eq, Ord)
74

    
75
data GanetiGroup = DaemonGroup GanetiDaemon
76
                 | ExtraGroup MiscGroup
77
                   deriving (Show, Eq, Ord)
78

    
79
type RuntimeEnts = (M.Map GanetiDaemon UserID, M.Map GanetiGroup GroupID)
80

    
81
-- | Returns the daemon name for a given daemon.
82
daemonName :: GanetiDaemon -> String
83
daemonName GanetiMasterd = "ganeti-masterd"
84
daemonName GanetiNoded   = "ganeti-noded"
85
daemonName GanetiRapi    = "ganeti-rapi"
86
daemonName GanetiConfd   = "ganeti-confd"
87
daemonName GanetiKvmd    = "ganeti-kvmd"
88
daemonName GanetiLuxid   = "ganeti-luxid"
89
daemonName GanetiMond    = "ganeti-mond"
90

    
91
-- | Returns whether the daemon only runs on the master node.
92
daemonOnlyOnMaster :: GanetiDaemon -> Bool
93
daemonOnlyOnMaster GanetiMasterd = True
94
daemonOnlyOnMaster GanetiNoded   = False
95
daemonOnlyOnMaster GanetiRapi    = False
96
daemonOnlyOnMaster GanetiConfd   = False
97
daemonOnlyOnMaster GanetiKvmd    = False
98
daemonOnlyOnMaster GanetiLuxid   = True
99
daemonOnlyOnMaster GanetiMond    = False
100

    
101
-- | Returns the log file base for a daemon.
102
daemonLogBase :: GanetiDaemon -> String
103
daemonLogBase GanetiMasterd = "master-daemon"
104
daemonLogBase GanetiNoded   = "node-daemon"
105
daemonLogBase GanetiRapi    = "rapi-daemon"
106
daemonLogBase GanetiConfd   = "conf-daemon"
107
daemonLogBase GanetiKvmd    = "kvm-daemon"
108
daemonLogBase GanetiLuxid   = "luxi-daemon"
109
daemonLogBase GanetiMond    = "monitoring-daemon"
110

    
111
-- | Returns the configured user name for a daemon.
112
daemonUser :: GanetiDaemon -> String
113
daemonUser GanetiMasterd = AutoConf.masterdUser
114
daemonUser GanetiNoded   = AutoConf.nodedUser
115
daemonUser GanetiRapi    = AutoConf.rapiUser
116
daemonUser GanetiConfd   = AutoConf.confdUser
117
daemonUser GanetiKvmd    = AutoConf.kvmdUser
118
daemonUser GanetiLuxid   = AutoConf.luxidUser
119
daemonUser GanetiMond    = AutoConf.mondUser
120

    
121
-- | Returns the configured group for a daemon.
122
daemonGroup :: GanetiGroup -> String
123
daemonGroup (DaemonGroup GanetiMasterd) = AutoConf.masterdGroup
124
daemonGroup (DaemonGroup GanetiNoded)   = AutoConf.nodedGroup
125
daemonGroup (DaemonGroup GanetiRapi)    = AutoConf.rapiGroup
126
daemonGroup (DaemonGroup GanetiConfd)   = AutoConf.confdGroup
127
daemonGroup (DaemonGroup GanetiLuxid)   = AutoConf.luxidGroup
128
daemonGroup (DaemonGroup GanetiKvmd)    = AutoConf.kvmdGroup
129
daemonGroup (DaemonGroup GanetiMond)    = AutoConf.mondGroup
130
daemonGroup (ExtraGroup  DaemonsGroup)  = AutoConf.daemonsGroup
131
daemonGroup (ExtraGroup  AdminGroup)    = AutoConf.adminGroup
132

    
133
data ExtraLogReason = AccessLog | ErrorLog
134

    
135
-- | Some daemons might require more than one logfile.  Specifically,
136
-- right now only the Haskell http library "snap", used by the
137
-- monitoring daemon, requires multiple log files.
138
daemonsExtraLogbase :: GanetiDaemon -> ExtraLogReason -> String
139
daemonsExtraLogbase daemon AccessLog = daemonLogBase daemon ++ "-access"
140
daemonsExtraLogbase daemon ErrorLog = daemonLogBase daemon ++ "-error"
141

    
142
-- | Returns the log file for a daemon.
143
daemonLogFile :: GanetiDaemon -> IO FilePath
144
daemonLogFile daemon = do
145
  logDir <- Path.logDir
146
  return $ logDir </> daemonLogBase daemon <.> "log"
147

    
148
-- | Returns the extra log files for a daemon.
149
daemonsExtraLogFile :: GanetiDaemon -> ExtraLogReason -> IO FilePath
150
daemonsExtraLogFile daemon logreason = do
151
  logDir <- Path.logDir
152
  return $ logDir </> daemonsExtraLogbase daemon logreason <.> "log"
153

    
154
-- | Returns the pid file name for a daemon.
155
daemonPidFile :: GanetiDaemon -> IO FilePath
156
daemonPidFile daemon = do
157
  runDir <- Path.runDir
158
  return $ runDir </> daemonName daemon <.> "pid"
159

    
160
-- | All groups list. A bit hacking, as we can't enforce it's complete
161
-- at compile time.
162
allGroups :: [GanetiGroup]
163
allGroups = map DaemonGroup [minBound..maxBound] ++
164
            map ExtraGroup  [minBound..maxBound]
165

    
166
ignoreDoesNotExistErrors :: IO a -> IO (Result a)
167
ignoreDoesNotExistErrors value = do
168
  result <- tryJust (\e -> if isDoesNotExistError e
169
                             then Just (show e)
170
                             else Nothing) value
171
  return $ eitherToResult result
172

    
173
-- | Computes the group/user maps.
174
getEnts :: IO (Result RuntimeEnts)
175
getEnts = do
176
  users <- mapM (\daemon -> do
177
                   entry <- ignoreDoesNotExistErrors .
178
                            getUserEntryForName .
179
                            daemonUser $ daemon
180
                   return (entry >>= \e -> return (daemon, userID e))
181
                ) [minBound..maxBound]
182
  groups <- mapM (\group -> do
183
                    entry <- ignoreDoesNotExistErrors .
184
                             getGroupEntryForName .
185
                             daemonGroup $ group
186
                    return (entry >>= \e -> return (group, groupID e))
187
                 ) allGroups
188
  return $ do -- 'Result' monad
189
    users'  <- sequence users
190
    groups' <- sequence groups
191
    let usermap = M.fromList users'
192
        groupmap = M.fromList groups'
193
    return (usermap, groupmap)
194

    
195

    
196
-- | Checks whether a daemon runs as the right user.
197
verifyDaemonUser :: GanetiDaemon -> RuntimeEnts -> IO ()
198
verifyDaemonUser daemon ents = do
199
  myuid <- getEffectiveUserID
200
  -- note: we use directly ! as lookup failues shouldn't happen, due
201
  -- to the above map construction
202
  checkUidMatch (daemonName daemon) ((M.!) (fst ents) daemon) myuid
203

    
204
-- | Check that two UIDs are matching or otherwise exit.
205
checkUidMatch :: String -> UserID -> UserID -> IO ()
206
checkUidMatch name expected actual =
207
  when (expected /= actual) $ do
208
    hPrintf stderr "%s started using wrong user ID (%d), \
209
                   \expected %d\n" name
210
              (fromIntegral actual::Int)
211
              (fromIntegral expected::Int) :: IO ()
212
    exitWith $ ExitFailure ConstantUtils.exitFailure