Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Utils.hs @ e10c4a69

History | View | Annotate | Download (16.5 kB)

1 525bfb36 Iustin Pop
{-| Utility functions. -}
2 e4f08c46 Iustin Pop
3 e2fa2baf Iustin Pop
{-
4 e2fa2baf Iustin Pop
5 a7f0953a Iustin Pop
Copyright (C) 2009, 2010, 2011, 2012, 2013 Google Inc.
6 e2fa2baf Iustin Pop
7 e2fa2baf Iustin Pop
This program is free software; you can redistribute it and/or modify
8 e2fa2baf Iustin Pop
it under the terms of the GNU General Public License as published by
9 e2fa2baf Iustin Pop
the Free Software Foundation; either version 2 of the License, or
10 e2fa2baf Iustin Pop
(at your option) any later version.
11 e2fa2baf Iustin Pop
12 e2fa2baf Iustin Pop
This program is distributed in the hope that it will be useful, but
13 e2fa2baf Iustin Pop
WITHOUT ANY WARRANTY; without even the implied warranty of
14 e2fa2baf Iustin Pop
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 e2fa2baf Iustin Pop
General Public License for more details.
16 e2fa2baf Iustin Pop
17 e2fa2baf Iustin Pop
You should have received a copy of the GNU General Public License
18 e2fa2baf Iustin Pop
along with this program; if not, write to the Free Software
19 e2fa2baf Iustin Pop
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 e2fa2baf Iustin Pop
02110-1301, USA.
21 e2fa2baf Iustin Pop
22 e2fa2baf Iustin Pop
-}
23 e2fa2baf Iustin Pop
24 26d62e4c Iustin Pop
module Ganeti.Utils
25 ebf38064 Iustin Pop
  ( debug
26 ebf38064 Iustin Pop
  , debugFn
27 ebf38064 Iustin Pop
  , debugXy
28 ebf38064 Iustin Pop
  , sepSplit
29 ebf38064 Iustin Pop
  , stdDev
30 ebf38064 Iustin Pop
  , if'
31 ebf38064 Iustin Pop
  , select
32 ebf38064 Iustin Pop
  , applyIf
33 ebf38064 Iustin Pop
  , commaJoin
34 79eef90b Agata Murawska
  , ensureQuoted
35 ebf38064 Iustin Pop
  , tryRead
36 ebf38064 Iustin Pop
  , formatTable
37 c3024b7e René Nussbaumer
  , printTable
38 ebf38064 Iustin Pop
  , parseUnit
39 19e310cc René Nussbaumer
  , plural
40 04edfc99 Iustin Pop
  , niceSort
41 04edfc99 Iustin Pop
  , niceSortKey
42 88a10df5 Iustin Pop
  , exitIfBad
43 88a10df5 Iustin Pop
  , exitErr
44 88a10df5 Iustin Pop
  , exitWhen
45 88a10df5 Iustin Pop
  , exitUnless
46 838393d1 Michele Tartara
  , logWarningIfBad
47 256e28c4 Iustin Pop
  , rStripSpace
48 80a0546b Michele Tartara
  , newUUID
49 ace37e24 Michele Tartara
  , getCurrentTime
50 a6e054a8 Iustin Pop
  , getCurrentTimeUSec
51 b6aeda4a Dato Simó
  , clockTimeToString
52 b009f682 Dato Simó
  , chompPrefix
53 7dbe4c72 Klaus Aehlig
  , warn
54 9fb621af Yiannis Tsiouris
  , wrap
55 9fb621af Yiannis Tsiouris
  , trim
56 72747d91 Iustin Pop
  , defaultHead
57 72747d91 Iustin Pop
  , exitIfEmpty
58 da9e2aff Iustin Pop
  , splitEithers
59 da9e2aff Iustin Pop
  , recombineEithers
60 986a8671 Michele Tartara
  , resolveAddr
61 a39cd547 Michele Tartara
  , setOwnerAndGroupFromNames
62 ebf38064 Iustin Pop
  ) where
63 e4f08c46 Iustin Pop
64 256e28c4 Iustin Pop
import Data.Char (toUpper, isAlphaNum, isDigit, isSpace)
65 04edfc99 Iustin Pop
import Data.Function (on)
66 29ac5975 Iustin Pop
import Data.List
67 a39cd547 Michele Tartara
import qualified Data.Map as M
68 da9e2aff Iustin Pop
import Control.Monad (foldM)
69 e4f08c46 Iustin Pop
70 e4f08c46 Iustin Pop
import Debug.Trace
71 986a8671 Michele Tartara
import Network.Socket
72 e4f08c46 Iustin Pop
73 88a10df5 Iustin Pop
import Ganeti.BasicTypes
74 06fd57e5 Jose A. Lopes
import qualified Ganeti.ConstantUtils as ConstantUtils
75 838393d1 Michele Tartara
import Ganeti.Logging
76 a39cd547 Michele Tartara
import Ganeti.Runtime
77 88a10df5 Iustin Pop
import System.IO
78 88a10df5 Iustin Pop
import System.Exit
79 a39cd547 Michele Tartara
import System.Posix.Files
80 b6aeda4a Dato Simó
import System.Time
81 88a10df5 Iustin Pop
82 9188aeef Iustin Pop
-- * Debug functions
83 9188aeef Iustin Pop
84 e4f08c46 Iustin Pop
-- | To be used only for debugging, breaks referential integrity.
85 e4f08c46 Iustin Pop
debug :: Show a => a -> a
86 e4f08c46 Iustin Pop
debug x = trace (show x) x
87 e4f08c46 Iustin Pop
88 525bfb36 Iustin Pop
-- | Displays a modified form of the second parameter before returning
89 525bfb36 Iustin Pop
-- it.
90 adc5c176 Iustin Pop
debugFn :: Show b => (a -> b) -> a -> a
91 adc5c176 Iustin Pop
debugFn fn x = debug (fn x) `seq` x
92 adc5c176 Iustin Pop
93 525bfb36 Iustin Pop
-- | Show the first parameter before returning the second one.
94 adc5c176 Iustin Pop
debugXy :: Show a => a -> b -> b
95 05ff7a00 Agata Murawska
debugXy = seq . debug
96 adc5c176 Iustin Pop
97 525bfb36 Iustin Pop
-- * Miscellaneous
98 1b7cf8ca Iustin Pop
99 61bbbed7 Agata Murawska
-- | Apply the function if condition holds, otherwise use default value.
100 61bbbed7 Agata Murawska
applyIf :: Bool -> (a -> a) -> a -> a
101 61bbbed7 Agata Murawska
applyIf b f x = if b then f x else x
102 61bbbed7 Agata Murawska
103 e4f08c46 Iustin Pop
-- | Comma-join a string list.
104 e4f08c46 Iustin Pop
commaJoin :: [String] -> String
105 e4f08c46 Iustin Pop
commaJoin = intercalate ","
106 e4f08c46 Iustin Pop
107 748d5d50 Iustin Pop
-- | Split a list on a separator and return an array.
108 748d5d50 Iustin Pop
sepSplit :: Eq a => a -> [a] -> [[a]]
109 e4f08c46 Iustin Pop
sepSplit sep s
110 ebf38064 Iustin Pop
  | null s    = []
111 ebf38064 Iustin Pop
  | null xs   = [x]
112 ebf38064 Iustin Pop
  | null ys   = [x,[]]
113 ebf38064 Iustin Pop
  | otherwise = x:sepSplit sep ys
114 ebf38064 Iustin Pop
  where (x, xs) = break (== sep) s
115 ebf38064 Iustin Pop
        ys = drop 1 xs
116 e4f08c46 Iustin Pop
117 19e310cc René Nussbaumer
-- | Simple pluralize helper
118 19e310cc René Nussbaumer
plural :: Int -> String -> String -> String
119 19e310cc René Nussbaumer
plural 1 s _ = s
120 19e310cc René Nussbaumer
plural _ _ p = p
121 19e310cc René Nussbaumer
122 79eef90b Agata Murawska
-- | Ensure a value is quoted if needed.
123 79eef90b Agata Murawska
ensureQuoted :: String -> String
124 79eef90b Agata Murawska
ensureQuoted v = if not (all (\c -> isAlphaNum c || c == '.') v)
125 79eef90b Agata Murawska
                 then '\'':v ++ "'"
126 79eef90b Agata Murawska
                 else v
127 79eef90b Agata Murawska
128 9188aeef Iustin Pop
-- * Mathematical functions
129 9188aeef Iustin Pop
130 185297fa Iustin Pop
-- Simple and slow statistical functions, please replace with better
131 185297fa Iustin Pop
-- versions
132 e4f08c46 Iustin Pop
133 525bfb36 Iustin Pop
-- | Standard deviation function.
134 4715711d Iustin Pop
stdDev :: [Double] -> Double
135 4715711d Iustin Pop
stdDev lst =
136 7570569e Iustin Pop
  -- first, calculate the list length and sum lst in a single step,
137 7570569e Iustin Pop
  -- for performance reasons
138 7570569e Iustin Pop
  let (ll', sx) = foldl' (\(rl, rs) e ->
139 7570569e Iustin Pop
                           let rl' = rl + 1
140 7570569e Iustin Pop
                               rs' = rs + e
141 7570569e Iustin Pop
                           in rl' `seq` rs' `seq` (rl', rs')) (0::Int, 0) lst
142 7570569e Iustin Pop
      ll = fromIntegral ll'::Double
143 7570569e Iustin Pop
      mv = sx / ll
144 7570569e Iustin Pop
      av = foldl' (\accu em -> let d = em - mv in accu + d * d) 0.0 lst
145 4715711d Iustin Pop
  in sqrt (av / ll) -- stddev
146 dd4c56ed Iustin Pop
147 bfe6c954 Guido Trotter
-- *  Logical functions
148 bfe6c954 Guido Trotter
149 bfe6c954 Guido Trotter
-- Avoid syntactic sugar and enhance readability. These functions are proposed
150 bfe6c954 Guido Trotter
-- by some for inclusion in the Prelude, and at the moment they are present
151 bfe6c954 Guido Trotter
-- (with various definitions) in the utility-ht package. Some rationale and
152 bfe6c954 Guido Trotter
-- discussion is available at <http://www.haskell.org/haskellwiki/If-then-else>
153 bfe6c954 Guido Trotter
154 bfe6c954 Guido Trotter
-- | \"if\" as a function, rather than as syntactic sugar.
155 bfe6c954 Guido Trotter
if' :: Bool -- ^ condition
156 bfe6c954 Guido Trotter
    -> a    -- ^ \"then\" result
157 bfe6c954 Guido Trotter
    -> a    -- ^ \"else\" result
158 bfe6c954 Guido Trotter
    -> a    -- ^ \"then\" or "else" result depending on the condition
159 bfe6c954 Guido Trotter
if' True x _ = x
160 bfe6c954 Guido Trotter
if' _    _ y = y
161 bfe6c954 Guido Trotter
162 5b763470 Iustin Pop
-- * Parsing utility functions
163 5b763470 Iustin Pop
164 525bfb36 Iustin Pop
-- | Parse results from readsPrec.
165 5b763470 Iustin Pop
parseChoices :: (Monad m, Read a) => String -> String -> [(a, String)] -> m a
166 5b763470 Iustin Pop
parseChoices _ _ ((v, ""):[]) = return v
167 5b763470 Iustin Pop
parseChoices name s ((_, e):[]) =
168 5b763470 Iustin Pop
    fail $ name ++ ": leftover characters when parsing '"
169 5b763470 Iustin Pop
           ++ s ++ "': '" ++ e ++ "'"
170 5b763470 Iustin Pop
parseChoices name s _ = fail $ name ++ ": cannot parse string '" ++ s ++ "'"
171 5b763470 Iustin Pop
172 5b763470 Iustin Pop
-- | Safe 'read' function returning data encapsulated in a Result.
173 5b763470 Iustin Pop
tryRead :: (Monad m, Read a) => String -> String -> m a
174 5b763470 Iustin Pop
tryRead name s = parseChoices name s $ reads s
175 c5f7412e Iustin Pop
176 525bfb36 Iustin Pop
-- | Format a table of strings to maintain consistent length.
177 c5f7412e Iustin Pop
formatTable :: [[String]] -> [Bool] -> [[String]]
178 c5f7412e Iustin Pop
formatTable vals numpos =
179 c5f7412e Iustin Pop
    let vtrans = transpose vals  -- transpose, so that we work on rows
180 c5f7412e Iustin Pop
                                 -- rather than columns
181 c5f7412e Iustin Pop
        mlens = map (maximum . map length) vtrans
182 c5f7412e Iustin Pop
        expnd = map (\(flds, isnum, ml) ->
183 c5f7412e Iustin Pop
                         map (\val ->
184 c5f7412e Iustin Pop
                                  let delta = ml - length val
185 c5f7412e Iustin Pop
                                      filler = replicate delta ' '
186 c5f7412e Iustin Pop
                                  in if delta > 0
187 c5f7412e Iustin Pop
                                     then if isnum
188 c5f7412e Iustin Pop
                                          then filler ++ val
189 c5f7412e Iustin Pop
                                          else val ++ filler
190 c5f7412e Iustin Pop
                                     else val
191 c5f7412e Iustin Pop
                             ) flds
192 c5f7412e Iustin Pop
                    ) (zip3 vtrans numpos mlens)
193 c5f7412e Iustin Pop
   in transpose expnd
194 9b9da389 Iustin Pop
195 c3024b7e René Nussbaumer
-- | Constructs a printable table from given header and rows
196 c3024b7e René Nussbaumer
printTable :: String -> [String] -> [[String]] -> [Bool] -> String
197 c3024b7e René Nussbaumer
printTable lp header rows isnum =
198 2cdaf225 Iustin Pop
  unlines . map ((++) lp . (:) ' ' . unwords) $
199 c3024b7e René Nussbaumer
  formatTable (header:rows) isnum
200 c3024b7e René Nussbaumer
201 1cdcf8f3 Iustin Pop
-- | Converts a unit (e.g. m or GB) into a scaling factor.
202 1cdcf8f3 Iustin Pop
parseUnitValue :: (Monad m) => String -> m Rational
203 1cdcf8f3 Iustin Pop
parseUnitValue unit
204 1cdcf8f3 Iustin Pop
  -- binary conversions first
205 1cdcf8f3 Iustin Pop
  | null unit                     = return 1
206 1cdcf8f3 Iustin Pop
  | unit == "m" || upper == "MIB" = return 1
207 1cdcf8f3 Iustin Pop
  | unit == "g" || upper == "GIB" = return kbBinary
208 1cdcf8f3 Iustin Pop
  | unit == "t" || upper == "TIB" = return $ kbBinary * kbBinary
209 1cdcf8f3 Iustin Pop
  -- SI conversions
210 1cdcf8f3 Iustin Pop
  | unit == "M" || upper == "MB"  = return mbFactor
211 1cdcf8f3 Iustin Pop
  | unit == "G" || upper == "GB"  = return $ mbFactor * kbDecimal
212 1cdcf8f3 Iustin Pop
  | unit == "T" || upper == "TB"  = return $ mbFactor * kbDecimal * kbDecimal
213 1cdcf8f3 Iustin Pop
  | otherwise = fail $ "Unknown unit '" ++ unit ++ "'"
214 1cdcf8f3 Iustin Pop
  where upper = map toUpper unit
215 5850e990 Iustin Pop
        kbBinary = 1024 :: Rational
216 5850e990 Iustin Pop
        kbDecimal = 1000 :: Rational
217 1cdcf8f3 Iustin Pop
        decToBin = kbDecimal / kbBinary -- factor for 1K conversion
218 1cdcf8f3 Iustin Pop
        mbFactor = decToBin * decToBin -- twice the factor for just 1K
219 1cdcf8f3 Iustin Pop
220 1cb92fac Iustin Pop
-- | Tries to extract number and scale from the given string.
221 1cb92fac Iustin Pop
--
222 1cb92fac Iustin Pop
-- Input must be in the format NUMBER+ SPACE* [UNIT]. If no unit is
223 1cb92fac Iustin Pop
-- specified, it defaults to MiB. Return value is always an integral
224 1cb92fac Iustin Pop
-- value in MiB.
225 1cb92fac Iustin Pop
parseUnit :: (Monad m, Integral a, Read a) => String -> m a
226 1cb92fac Iustin Pop
parseUnit str =
227 ebf38064 Iustin Pop
  -- TODO: enhance this by splitting the unit parsing code out and
228 ebf38064 Iustin Pop
  -- accepting floating-point numbers
229 1cdcf8f3 Iustin Pop
  case (reads str::[(Int, String)]) of
230 ebf38064 Iustin Pop
    [(v, suffix)] ->
231 ebf38064 Iustin Pop
      let unit = dropWhile (== ' ') suffix
232 1cdcf8f3 Iustin Pop
      in do
233 1cdcf8f3 Iustin Pop
        scaling <- parseUnitValue unit
234 1cdcf8f3 Iustin Pop
        return $ truncate (fromIntegral v * scaling)
235 ebf38064 Iustin Pop
    _ -> fail $ "Can't parse string '" ++ str ++ "'"
236 88a10df5 Iustin Pop
237 88a10df5 Iustin Pop
-- | Unwraps a 'Result', exiting the program if it is a 'Bad' value,
238 88a10df5 Iustin Pop
-- otherwise returning the actual contained value.
239 88a10df5 Iustin Pop
exitIfBad :: String -> Result a -> IO a
240 707cd3d7 Helga Velroyen
exitIfBad msg (Bad s) = exitErr (msg ++ ": " ++ s)
241 88a10df5 Iustin Pop
exitIfBad _ (Ok v) = return v
242 88a10df5 Iustin Pop
243 88a10df5 Iustin Pop
-- | Exits immediately with an error message.
244 88a10df5 Iustin Pop
exitErr :: String -> IO a
245 88a10df5 Iustin Pop
exitErr errmsg = do
246 707cd3d7 Helga Velroyen
  hPutStrLn stderr $ "Error: " ++ errmsg
247 88a10df5 Iustin Pop
  exitWith (ExitFailure 1)
248 88a10df5 Iustin Pop
249 88a10df5 Iustin Pop
-- | Exits with an error message if the given boolean condition if true.
250 88a10df5 Iustin Pop
exitWhen :: Bool -> String -> IO ()
251 88a10df5 Iustin Pop
exitWhen True msg = exitErr msg
252 88a10df5 Iustin Pop
exitWhen False _  = return ()
253 88a10df5 Iustin Pop
254 88a10df5 Iustin Pop
-- | Exits with an error message /unless/ the given boolean condition
255 88a10df5 Iustin Pop
-- if true, the opposite of 'exitWhen'.
256 88a10df5 Iustin Pop
exitUnless :: Bool -> String -> IO ()
257 88a10df5 Iustin Pop
exitUnless cond = exitWhen (not cond)
258 04edfc99 Iustin Pop
259 838393d1 Michele Tartara
-- | Unwraps a 'Result', logging a warning message and then returning a default
260 838393d1 Michele Tartara
-- value if it is a 'Bad' value, otherwise returning the actual contained value.
261 838393d1 Michele Tartara
logWarningIfBad :: String -> a -> Result a -> IO a
262 838393d1 Michele Tartara
logWarningIfBad msg defVal (Bad s) = do
263 838393d1 Michele Tartara
  logWarning $ msg ++ ": " ++ s
264 838393d1 Michele Tartara
  return defVal
265 838393d1 Michele Tartara
logWarningIfBad _ _ (Ok v) = return v
266 838393d1 Michele Tartara
267 7dbe4c72 Klaus Aehlig
-- | Print a warning, but do not exit.
268 7dbe4c72 Klaus Aehlig
warn :: String -> IO ()
269 7dbe4c72 Klaus Aehlig
warn = hPutStrLn stderr . (++) "Warning: "
270 7dbe4c72 Klaus Aehlig
271 04edfc99 Iustin Pop
-- | Helper for 'niceSort'. Computes the key element for a given string.
272 04edfc99 Iustin Pop
extractKey :: [Either Integer String]  -- ^ Current (partial) key, reversed
273 04edfc99 Iustin Pop
           -> String                   -- ^ Remaining string
274 04edfc99 Iustin Pop
           -> ([Either Integer String], String)
275 04edfc99 Iustin Pop
extractKey ek [] = (reverse ek, [])
276 04edfc99 Iustin Pop
extractKey ek xs@(x:_) =
277 04edfc99 Iustin Pop
  let (span_fn, conv_fn) = if isDigit x
278 04edfc99 Iustin Pop
                             then (isDigit, Left . read)
279 04edfc99 Iustin Pop
                             else (not . isDigit, Right)
280 04edfc99 Iustin Pop
      (k, rest) = span span_fn xs
281 04edfc99 Iustin Pop
  in extractKey (conv_fn k:ek) rest
282 04edfc99 Iustin Pop
283 04edfc99 Iustin Pop
{-| Sort a list of strings based on digit and non-digit groupings.
284 04edfc99 Iustin Pop
285 04edfc99 Iustin Pop
Given a list of names @['a1', 'a10', 'a11', 'a2']@ this function
286 04edfc99 Iustin Pop
will sort the list in the logical order @['a1', 'a2', 'a10', 'a11']@.
287 04edfc99 Iustin Pop
288 04edfc99 Iustin Pop
The sort algorithm breaks each name in groups of either only-digits or
289 04edfc99 Iustin Pop
no-digits, and sorts based on each group.
290 04edfc99 Iustin Pop
291 04edfc99 Iustin Pop
Internally, this is not implemented via regexes (like the Python
292 04edfc99 Iustin Pop
version), but via actual splitting of the string in sequences of
293 04edfc99 Iustin Pop
either digits or everything else, and converting the digit sequences
294 04edfc99 Iustin Pop
in /Left Integer/ and the non-digit ones in /Right String/, at which
295 04edfc99 Iustin Pop
point sorting becomes trivial due to the built-in 'Either' ordering;
296 04edfc99 Iustin Pop
we only need one extra step of dropping the key at the end.
297 04edfc99 Iustin Pop
298 04edfc99 Iustin Pop
-}
299 04edfc99 Iustin Pop
niceSort :: [String] -> [String]
300 a7f0953a Iustin Pop
niceSort = niceSortKey id
301 04edfc99 Iustin Pop
302 04edfc99 Iustin Pop
-- | Key-version of 'niceSort'. We use 'sortBy' and @compare `on` fst@
303 04edfc99 Iustin Pop
-- since we don't want to add an ordering constraint on the /a/ type,
304 04edfc99 Iustin Pop
-- hence the need to only compare the first element of the /(key, a)/
305 04edfc99 Iustin Pop
-- tuple.
306 04edfc99 Iustin Pop
niceSortKey :: (a -> String) -> [a] -> [a]
307 04edfc99 Iustin Pop
niceSortKey keyfn =
308 04edfc99 Iustin Pop
  map snd . sortBy (compare `on` fst) .
309 04edfc99 Iustin Pop
  map (\s -> (fst . extractKey [] $ keyfn s, s))
310 256e28c4 Iustin Pop
311 256e28c4 Iustin Pop
-- | Strip space characthers (including newline). As this is
312 256e28c4 Iustin Pop
-- expensive, should only be run on small strings.
313 256e28c4 Iustin Pop
rStripSpace :: String -> String
314 256e28c4 Iustin Pop
rStripSpace = reverse . dropWhile isSpace . reverse
315 80a0546b Michele Tartara
316 80a0546b Michele Tartara
-- | Returns a random UUID.
317 80a0546b Michele Tartara
-- This is a Linux-specific method as it uses the /proc filesystem.
318 80a0546b Michele Tartara
newUUID :: IO String
319 80a0546b Michele Tartara
newUUID = do
320 06fd57e5 Jose A. Lopes
  contents <- readFile ConstantUtils.randomUuidFile
321 37dfcacb Iustin Pop
  return $! rStripSpace $ take 128 contents
322 b6aeda4a Dato Simó
323 a6e054a8 Iustin Pop
-- | Returns the current time as an 'Integer' representing the number
324 a6e054a8 Iustin Pop
-- of seconds from the Unix epoch.
325 ace37e24 Michele Tartara
getCurrentTime :: IO Integer
326 ace37e24 Michele Tartara
getCurrentTime = do
327 ace37e24 Michele Tartara
  TOD ctime _ <- getClockTime
328 ace37e24 Michele Tartara
  return ctime
329 ace37e24 Michele Tartara
330 a6e054a8 Iustin Pop
-- | Returns the current time as an 'Integer' representing the number
331 a6e054a8 Iustin Pop
-- of microseconds from the Unix epoch (hence the need for 'Integer').
332 a6e054a8 Iustin Pop
getCurrentTimeUSec :: IO Integer
333 a6e054a8 Iustin Pop
getCurrentTimeUSec = do
334 a6e054a8 Iustin Pop
  TOD ctime pico <- getClockTime
335 a6e054a8 Iustin Pop
  -- pico: 10^-12, micro: 10^-6, so we have to shift seconds left and
336 a6e054a8 Iustin Pop
  -- picoseconds right
337 a6e054a8 Iustin Pop
  return $ ctime * 1000000 + pico `div` 1000000
338 a6e054a8 Iustin Pop
339 b6aeda4a Dato Simó
-- | Convert a ClockTime into a (seconds-only) timestamp.
340 b6aeda4a Dato Simó
clockTimeToString :: ClockTime -> String
341 b6aeda4a Dato Simó
clockTimeToString (TOD t _) = show t
342 b009f682 Dato Simó
343 b009f682 Dato Simó
{-| Strip a prefix from a string, allowing the last character of the prefix
344 b009f682 Dato Simó
(which is assumed to be a separator) to be absent from the string if the string
345 b009f682 Dato Simó
terminates there.
346 b009f682 Dato Simó
347 94042ae4 Michele Tartara
\>>> chompPrefix \"foo:bar:\" \"a:b:c\"
348 b009f682 Dato Simó
Nothing
349 b009f682 Dato Simó
350 94042ae4 Michele Tartara
\>>> chompPrefix \"foo:bar:\" \"foo:bar:baz\"
351 94042ae4 Michele Tartara
Just \"baz\"
352 b009f682 Dato Simó
353 94042ae4 Michele Tartara
\>>> chompPrefix \"foo:bar:\" \"foo:bar:\"
354 94042ae4 Michele Tartara
Just \"\"
355 b009f682 Dato Simó
356 94042ae4 Michele Tartara
\>>> chompPrefix \"foo:bar:\" \"foo:bar\"
357 94042ae4 Michele Tartara
Just \"\"
358 b009f682 Dato Simó
359 94042ae4 Michele Tartara
\>>> chompPrefix \"foo:bar:\" \"foo:barbaz\"
360 b009f682 Dato Simó
Nothing
361 b009f682 Dato Simó
-}
362 b009f682 Dato Simó
chompPrefix :: String -> String -> Maybe String
363 b009f682 Dato Simó
chompPrefix pfx str =
364 b009f682 Dato Simó
  if pfx `isPrefixOf` str || str == init pfx
365 b009f682 Dato Simó
    then Just $ drop (length pfx) str
366 b009f682 Dato Simó
    else Nothing
367 9fb621af Yiannis Tsiouris
368 9fb621af Yiannis Tsiouris
-- | Breaks a string in lines with length \<= maxWidth.
369 9fb621af Yiannis Tsiouris
--
370 9fb621af Yiannis Tsiouris
-- NOTE: The split is OK if:
371 9fb621af Yiannis Tsiouris
--
372 9fb621af Yiannis Tsiouris
-- * It doesn't break a word, i.e. the next line begins with space
373 9fb621af Yiannis Tsiouris
--   (@isSpace . head $ rest@) or the current line ends with space
374 9fb621af Yiannis Tsiouris
--   (@null revExtra@);
375 9fb621af Yiannis Tsiouris
--
376 9fb621af Yiannis Tsiouris
-- * It breaks a very big word that doesn't fit anyway (@null revLine@).
377 9fb621af Yiannis Tsiouris
wrap :: Int      -- ^ maxWidth
378 9fb621af Yiannis Tsiouris
     -> String   -- ^ string that needs wrapping
379 9fb621af Yiannis Tsiouris
     -> [String] -- ^ string \"broken\" in lines
380 9fb621af Yiannis Tsiouris
wrap maxWidth = filter (not . null) . map trim . wrap0
381 9fb621af Yiannis Tsiouris
  where wrap0 :: String -> [String]
382 9fb621af Yiannis Tsiouris
        wrap0 text
383 9fb621af Yiannis Tsiouris
          | length text <= maxWidth = [text]
384 9fb621af Yiannis Tsiouris
          | isSplitOK               = line : wrap0 rest
385 9fb621af Yiannis Tsiouris
          | otherwise               = line' : wrap0 rest'
386 9fb621af Yiannis Tsiouris
          where (line, rest) = splitAt maxWidth text
387 9fb621af Yiannis Tsiouris
                (revExtra, revLine) = break isSpace . reverse $ line
388 9fb621af Yiannis Tsiouris
                (line', rest') = (reverse revLine, reverse revExtra ++ rest)
389 9fb621af Yiannis Tsiouris
                isSplitOK =
390 9fb621af Yiannis Tsiouris
                  null revLine || null revExtra || startsWithSpace rest
391 9fb621af Yiannis Tsiouris
                startsWithSpace (x:_) = isSpace x
392 9fb621af Yiannis Tsiouris
                startsWithSpace _     = False
393 9fb621af Yiannis Tsiouris
394 9fb621af Yiannis Tsiouris
-- | Removes surrounding whitespace. Should only be used in small
395 9fb621af Yiannis Tsiouris
-- strings.
396 9fb621af Yiannis Tsiouris
trim :: String -> String
397 9fb621af Yiannis Tsiouris
trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace
398 72747d91 Iustin Pop
399 72747d91 Iustin Pop
-- | A safer head version, with a default value.
400 72747d91 Iustin Pop
defaultHead :: a -> [a] -> a
401 72747d91 Iustin Pop
defaultHead def []    = def
402 72747d91 Iustin Pop
defaultHead _   (x:_) = x
403 72747d91 Iustin Pop
404 72747d91 Iustin Pop
-- | A 'head' version in the I/O monad, for validating parameters
405 72747d91 Iustin Pop
-- without which we cannot continue.
406 72747d91 Iustin Pop
exitIfEmpty :: String -> [a] -> IO a
407 72747d91 Iustin Pop
exitIfEmpty _ (x:_) = return x
408 72747d91 Iustin Pop
exitIfEmpty s []    = exitErr s
409 da9e2aff Iustin Pop
410 da9e2aff Iustin Pop
-- | Split an 'Either' list into two separate lists (containing the
411 da9e2aff Iustin Pop
-- 'Left' and 'Right' elements, plus a \"trail\" list that allows
412 da9e2aff Iustin Pop
-- recombination later.
413 da9e2aff Iustin Pop
--
414 da9e2aff Iustin Pop
-- This is splitter; for recombination, look at 'recombineEithers'.
415 da9e2aff Iustin Pop
-- The sum of \"left\" and \"right\" lists should be equal to the
416 da9e2aff Iustin Pop
-- original list length, and the trail list should be the same length
417 da9e2aff Iustin Pop
-- as well. The entries in the resulting lists are reversed in
418 da9e2aff Iustin Pop
-- comparison with the original list.
419 da9e2aff Iustin Pop
splitEithers :: [Either a b] -> ([a], [b], [Bool])
420 da9e2aff Iustin Pop
splitEithers = foldl' splitter ([], [], [])
421 da9e2aff Iustin Pop
  where splitter (l, r, t) e =
422 da9e2aff Iustin Pop
          case e of
423 da9e2aff Iustin Pop
            Left  v -> (v:l, r, False:t)
424 da9e2aff Iustin Pop
            Right v -> (l, v:r, True:t)
425 da9e2aff Iustin Pop
426 da9e2aff Iustin Pop
-- | Recombines two \"left\" and \"right\" lists using a \"trail\"
427 da9e2aff Iustin Pop
-- list into a single 'Either' list.
428 da9e2aff Iustin Pop
--
429 da9e2aff Iustin Pop
-- This is the counterpart to 'splitEithers'. It does the opposite
430 da9e2aff Iustin Pop
-- transformation, and the output list will be the reverse of the
431 da9e2aff Iustin Pop
-- input lists. Since 'splitEithers' also reverses the lists, calling
432 da9e2aff Iustin Pop
-- these together will result in the original list.
433 da9e2aff Iustin Pop
--
434 da9e2aff Iustin Pop
-- Mismatches in the structure of the lists (e.g. inconsistent
435 da9e2aff Iustin Pop
-- lengths) are represented via 'Bad'; normally this function should
436 da9e2aff Iustin Pop
-- not fail, if lists are passed as generated by 'splitEithers'.
437 da9e2aff Iustin Pop
recombineEithers :: (Show a, Show b) =>
438 da9e2aff Iustin Pop
                    [a] -> [b] -> [Bool] -> Result [Either a b]
439 da9e2aff Iustin Pop
recombineEithers lefts rights trail =
440 da9e2aff Iustin Pop
  foldM recombiner ([], lefts, rights) trail >>= checker
441 da9e2aff Iustin Pop
    where checker (eithers, [], []) = Ok eithers
442 da9e2aff Iustin Pop
          checker (_, lefts', rights') =
443 da9e2aff Iustin Pop
            Bad $ "Inconsistent results after recombination, l'=" ++
444 da9e2aff Iustin Pop
                show lefts' ++ ", r'=" ++ show rights'
445 da9e2aff Iustin Pop
          recombiner (es, l:ls, rs) False = Ok (Left l:es,  ls, rs)
446 da9e2aff Iustin Pop
          recombiner (es, ls, r:rs) True  = Ok (Right r:es, ls, rs)
447 da9e2aff Iustin Pop
          recombiner (_,  ls, rs) t = Bad $ "Inconsistent trail log: l=" ++
448 da9e2aff Iustin Pop
                                      show ls ++ ", r=" ++ show rs ++ ",t=" ++
449 da9e2aff Iustin Pop
                                      show t
450 986a8671 Michele Tartara
451 986a8671 Michele Tartara
-- | Default hints for the resolver
452 986a8671 Michele Tartara
resolveAddrHints :: Maybe AddrInfo
453 986a8671 Michele Tartara
resolveAddrHints =
454 986a8671 Michele Tartara
  Just defaultHints { addrFlags = [AI_NUMERICHOST, AI_NUMERICSERV] }
455 986a8671 Michele Tartara
456 986a8671 Michele Tartara
-- | Resolves a numeric address.
457 986a8671 Michele Tartara
resolveAddr :: Int -> String -> IO (Result (Family, SockAddr))
458 986a8671 Michele Tartara
resolveAddr port str = do
459 986a8671 Michele Tartara
  resolved <- getAddrInfo resolveAddrHints (Just str) (Just (show port))
460 986a8671 Michele Tartara
  return $ case resolved of
461 986a8671 Michele Tartara
             [] -> Bad "Invalid results from lookup?"
462 986a8671 Michele Tartara
             best:_ -> Ok (addrFamily best, addrAddress best)
463 858ecf2b Klaus Aehlig
464 a39cd547 Michele Tartara
-- | Set the owner and the group of a file (given as names, not numeric id).
465 a39cd547 Michele Tartara
setOwnerAndGroupFromNames :: FilePath -> GanetiDaemon -> GanetiGroup -> IO ()
466 a39cd547 Michele Tartara
setOwnerAndGroupFromNames filename daemon dGroup = do
467 a39cd547 Michele Tartara
  -- TODO: it would be nice to rework this (or getEnts) so that runtimeEnts
468 a39cd547 Michele Tartara
  -- is read only once per daemon startup, and then cached for further usage.
469 a39cd547 Michele Tartara
  runtimeEnts <- getEnts
470 a39cd547 Michele Tartara
  ents <- exitIfBad "Can't find required user/groups" runtimeEnts
471 a39cd547 Michele Tartara
  -- note: we use directly ! as lookup failures shouldn't happen, due
472 a39cd547 Michele Tartara
  -- to the map construction
473 a39cd547 Michele Tartara
  let uid = fst ents M.! daemon
474 a39cd547 Michele Tartara
  let gid = snd ents M.! dGroup
475 a39cd547 Michele Tartara
  setOwnerAndGroup filename uid gid