@@ -27,16 +27,18 @@ import qualified System.Timeout as Timeout
27
27
import Prelude
28
28
29
29
import Control.Concurrent.Async
30
- import Control.Lens hiding ((.=) )
30
+ import Control.Lens hiding ((.=) , (<.>) )
31
31
import Data.Time
32
32
import Data.Time.Clock.POSIX
33
33
import Network.HTTP.Client (HttpException )
34
34
import Network.HTTP.Client.TLS
35
35
import Network.Socket
36
- import System.FilePath ((</>) )
36
+ import System.Directory (removeFile )
37
+ import System.FileLock
38
+ import System.FilePath ((</>) , (<.>) )
37
39
import System.IO.Temp
38
- import System.Process
39
40
import System.PosixCompat.User (getEffectiveUserName )
41
+ import System.Process
40
42
import qualified Data.ByteString as B
41
43
import qualified Data.ByteString.Lazy as BL
42
44
import qualified Data.ByteString.Lazy.Char8 as BL8
@@ -67,56 +69,72 @@ main = do
67
69
ShowVersion -> do
68
70
putStrLn $ " sensu-run " ++ V. showVersion Paths. version
69
71
exitSuccess
70
- RunOptions {.. } -> withSystemTempFile " sensu-run.XXX" $ \ path hdl -> do
71
- executed <- getCurrentTime
72
- rawStatus <- try $ bracket
73
- (startProcess cmdspec)
74
- (\ (_, _, ph) -> do
75
- terminateProcess ph
76
- killProcessTree ph
77
- waitForProcess ph)
78
- $ \ (out, err, ph) -> do
79
- aout <- async $ redirectOutput out $ if redirect
80
- then [hdl, stdout] else [hdl]
81
- aerr <- async $ redirectOutput err $ if redirect
82
- then [hdl, stderr] else [hdl]
83
- mapM_ waitCatch [aout, aerr]
84
- withTimeout timeout $ waitForProcess ph
85
- hClose hdl
86
- exited <- getCurrentTime
87
- rawOutput <- BL. readFile path
88
- user <- T. pack <$> getEffectiveUserName
89
- let
90
- encoded = encode CheckResult
91
- { command = cmdspec
92
- , output = TL. toLazyText $ mconcat
93
- [ TL. fromLazyText (TL. decodeUtf8With TE. lenientDecode rawOutput)
94
- , case rawStatus of
95
- Left (ioe :: IOException ) -> " \n " <> TL. fromString (show ioe)
96
- Right Nothing -> " \n " <> " sensu-run: timed out"
97
- Right _ -> mempty
98
- ]
99
- , status = case rawStatus of
100
- Right (Just ExitSuccess ) -> OK
101
- Right (Just ExitFailure {}) -> CRITICAL
102
- _ -> UNKNOWN
103
- , duration = diffUTCTime exited executed
104
- , ..
105
- }
106
- if dryRun
107
- then BL8. putStrLn encoded
108
- else case endpoint of
109
- ClientSocketInput port -> sendToClientSocketInput port encoded
110
- SensuServer urls -> sendToSensuServer urls encoded
111
- case rawStatus of
112
- Left ioe -> do
113
- hPutStrLn stderr $ show ioe
114
- exitFailure
115
- Right (Just ExitSuccess ) -> exitSuccess
116
- Right Nothing -> do
117
- hPutStrLn stderr $ showCmdSpec cmdspec ++ " timed out"
118
- exitFailure
119
- Right (Just ExitFailure {}) -> exitFailure
72
+ RunOptions {.. } -> exclusivelyIf lock name $
73
+ withSystemTempFile " sensu-run.XXX" $ \ path hdl -> do
74
+ executed <- getCurrentTime
75
+ rawStatus <- try $ bracket
76
+ (startProcess cmdspec)
77
+ (\ (_, _, ph) -> do
78
+ terminateProcess ph
79
+ killProcessTree ph
80
+ waitForProcess ph)
81
+ $ \ (out, err, ph) -> do
82
+ aout <- async $ redirectOutput out $ if redirect
83
+ then [hdl, stdout] else [hdl]
84
+ aerr <- async $ redirectOutput err $ if redirect
85
+ then [hdl, stderr] else [hdl]
86
+ mapM_ waitCatch [aout, aerr]
87
+ withTimeout timeout $ waitForProcess ph
88
+ hClose hdl
89
+ exited <- getCurrentTime
90
+ rawOutput <- BL. readFile path
91
+ user <- T. pack <$> getEffectiveUserName
92
+ let
93
+ encoded = encode CheckResult
94
+ { command = cmdspec
95
+ , output = TL. toLazyText $ mconcat
96
+ [ TL. fromLazyText (TL. decodeUtf8With TE. lenientDecode rawOutput)
97
+ , case rawStatus of
98
+ Left (ioe :: IOException ) -> " \n " <> TL. fromString (show ioe)
99
+ Right Nothing -> " \n " <> " sensu-run: timed out"
100
+ Right _ -> mempty
101
+ ]
102
+ , status = case rawStatus of
103
+ Right (Just ExitSuccess ) -> OK
104
+ Right (Just ExitFailure {}) -> CRITICAL
105
+ _ -> UNKNOWN
106
+ , duration = diffUTCTime exited executed
107
+ , ..
108
+ }
109
+ if dryRun
110
+ then BL8. putStrLn encoded
111
+ else case endpoint of
112
+ ClientSocketInput port -> sendToClientSocketInput port encoded
113
+ SensuServer urls -> sendToSensuServer urls encoded
114
+ case rawStatus of
115
+ Left ioe -> do
116
+ hPutStrLn stderr $ show ioe
117
+ exitFailure
118
+ Right (Just ExitSuccess ) -> exitSuccess
119
+ Right Nothing -> do
120
+ hPutStrLn stderr $ showCmdSpec cmdspec ++ " timed out"
121
+ exitFailure
122
+ Right (Just ExitFailure {}) -> exitFailure
123
+
124
+ exclusivelyIf :: Bool -> T. Text -> IO a -> IO a
125
+ exclusivelyIf exclusive name io
126
+ | exclusive = do
127
+ tmpDir <- getCanonicalTemporaryDirectory
128
+ let path = tmpDir </> " sensu-run" <.> T. unpack name <.> " lock"
129
+ r <- withTryFileLock path Exclusive (const io)
130
+ case r of
131
+ Nothing -> do
132
+ putStrLn $ path ++ " is aquired by other process"
133
+ exitSuccess
134
+ Just a -> do
135
+ removeFile path
136
+ return a
137
+ | otherwise = io
120
138
121
139
sendToClientSocketInput
122
140
:: PortNumber -- ^ Listening port of Sensu client socket
@@ -215,6 +233,7 @@ data Options
215
233
, handlers :: [T. Text ]
216
234
, endpoint :: Endpoint
217
235
, redirect :: Bool
236
+ , lock :: Bool
218
237
, dryRun :: Bool
219
238
}
220
239
@@ -271,6 +290,10 @@ options = asum
271
290
[ O. long " redirect"
272
291
, O. help " Redirect command output to sensu-run's output"
273
292
]
293
+ (not -> lock) <- O. switch $ mconcat
294
+ [ O. long " no-lock"
295
+ , O. help " Do not create a lock file to allow multiple instances to run"
296
+ ]
274
297
dryRun <- O. switch $ mconcat
275
298
[ O. long " dry-run"
276
299
, O. long " dry"
0 commit comments