Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Runtime.hs @ b12a6a00

History | View | Annotate | Download (7.1 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
                  | GanetiLuxid
67
                  | GanetiMond
68
                    deriving (Show, Enum, Bounded, Eq, Ord)
69

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

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

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

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

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

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

    
107
-- | Returns the configured user name for a daemon.
108
daemonUser :: GanetiDaemon -> String
109
daemonUser GanetiMasterd = AutoConf.masterdUser
110
daemonUser GanetiNoded   = AutoConf.nodedUser
111
daemonUser GanetiRapi    = AutoConf.rapiUser
112
daemonUser GanetiConfd   = AutoConf.confdUser
113
daemonUser GanetiLuxid   = AutoConf.luxidUser
114
daemonUser GanetiMond    = AutoConf.mondUser
115

    
116
-- | Returns the configured group for a daemon.
117
daemonGroup :: GanetiGroup -> String
118
daemonGroup (DaemonGroup GanetiMasterd) = AutoConf.masterdGroup
119
daemonGroup (DaemonGroup GanetiNoded)   = AutoConf.nodedGroup
120
daemonGroup (DaemonGroup GanetiRapi)    = AutoConf.rapiGroup
121
daemonGroup (DaemonGroup GanetiConfd)   = AutoConf.confdGroup
122
daemonGroup (DaemonGroup GanetiLuxid)   = AutoConf.luxidGroup
123
daemonGroup (DaemonGroup GanetiMond)    = AutoConf.mondGroup
124
daemonGroup (ExtraGroup  DaemonsGroup)  = AutoConf.daemonsGroup
125
daemonGroup (ExtraGroup  AdminGroup)    = AutoConf.adminGroup
126

    
127
data ExtraLogReason = AccessLog | ErrorLog
128

    
129
-- | Some daemons might require more than one logfile.  Specifically,
130
-- right now only the Haskell http library "snap", used by the
131
-- monitoring daemon, requires multiple log files.
132
daemonsExtraLogbase :: GanetiDaemon -> ExtraLogReason -> String
133
daemonsExtraLogbase daemon AccessLog = daemonLogBase daemon ++ "-access"
134
daemonsExtraLogbase daemon ErrorLog = daemonLogBase daemon ++ "-error"
135

    
136
-- | Returns the log file for a daemon.
137
daemonLogFile :: GanetiDaemon -> IO FilePath
138
daemonLogFile daemon = do
139
  logDir <- Path.logDir
140
  return $ logDir </> daemonLogBase daemon <.> "log"
141

    
142
-- | Returns the extra log files for a daemon.
143
daemonsExtraLogFile :: GanetiDaemon -> ExtraLogReason -> IO FilePath
144
daemonsExtraLogFile daemon logreason = do
145
  logDir <- Path.logDir
146
  return $ logDir </> daemonsExtraLogbase daemon logreason <.> "log"
147

    
148
-- | Returns the pid file name for a daemon.
149
daemonPidFile :: GanetiDaemon -> IO FilePath
150
daemonPidFile daemon = do
151
  runDir <- Path.runDir
152
  return $ runDir </> daemonName daemon <.> "pid"
153

    
154
-- | All groups list. A bit hacking, as we can't enforce it's complete
155
-- at compile time.
156
allGroups :: [GanetiGroup]
157
allGroups = map DaemonGroup [minBound..maxBound] ++
158
            map ExtraGroup  [minBound..maxBound]
159

    
160
ignoreDoesNotExistErrors :: IO a -> IO (Result a)
161
ignoreDoesNotExistErrors value = do
162
  result <- tryJust (\e -> if isDoesNotExistError e
163
                             then Just (show e)
164
                             else Nothing) value
165
  return $ eitherToResult result
166

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

    
189

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

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