2020-05-27 02:01:03 +03:00
|
|
|
{-# OPTIONS_GHC -Wwarn #-}
|
|
|
|
|
2020-01-23 07:16:09 +03:00
|
|
|
{-|
|
|
|
|
Serf Interface
|
|
|
|
|
|
|
|
TODO: `recvLen` is not big-endian safe.
|
2019-07-17 02:14:46 +03:00
|
|
|
-}
|
|
|
|
|
2020-05-27 02:01:03 +03:00
|
|
|
module Urbit.Vere.Serf
|
|
|
|
( module Urbit.Vere.Serf.IPC
|
|
|
|
, withSerf
|
|
|
|
, execReplay
|
|
|
|
, execSnapshot
|
|
|
|
, execShutdown
|
|
|
|
)
|
|
|
|
where
|
2019-06-01 01:55:21 +03:00
|
|
|
|
2020-01-24 08:28:38 +03:00
|
|
|
import Urbit.Prelude
|
2019-06-01 01:55:21 +03:00
|
|
|
|
2019-07-24 04:34:16 +03:00
|
|
|
import Data.Conduit
|
2019-06-01 01:55:21 +03:00
|
|
|
import System.Process
|
2019-09-12 22:49:27 +03:00
|
|
|
import System.ProgressBar
|
2020-01-24 08:28:38 +03:00
|
|
|
import Urbit.Arvo
|
|
|
|
import Urbit.Vere.Pier.Types
|
2020-05-27 02:01:03 +03:00
|
|
|
import Urbit.Vere.Serf.IPC
|
|
|
|
import System.Posix.Signals
|
2019-06-01 01:55:21 +03:00
|
|
|
|
2019-07-22 00:24:07 +03:00
|
|
|
import Data.Bits (setBit)
|
2019-07-12 04:16:40 +03:00
|
|
|
import Data.ByteString (hGet)
|
2019-06-18 02:47:20 +03:00
|
|
|
import Data.ByteString.Unsafe (unsafeUseAsCString)
|
2019-07-12 04:16:40 +03:00
|
|
|
import Foreign.Marshal.Alloc (alloca)
|
|
|
|
import Foreign.Ptr (castPtr)
|
2019-07-12 22:24:44 +03:00
|
|
|
import Foreign.Storable (peek, poke)
|
2019-07-12 04:16:40 +03:00
|
|
|
import System.Exit (ExitCode)
|
2020-02-04 04:27:16 +03:00
|
|
|
import Urbit.King.App (HasStderrLogFunc(..))
|
2019-06-25 23:58:07 +03:00
|
|
|
|
2020-01-24 05:57:22 +03:00
|
|
|
import qualified Data.ByteString.Unsafe as BS
|
|
|
|
import qualified Data.Conduit.Combinators as CC
|
|
|
|
import qualified Data.Text as T
|
2020-01-11 01:07:29 +03:00
|
|
|
import qualified System.IO as IO
|
2020-01-24 05:57:22 +03:00
|
|
|
import qualified System.IO.Error as IO
|
|
|
|
import qualified Urbit.Ob as Ob
|
2020-01-24 08:47:06 +03:00
|
|
|
import qualified Urbit.Time as Time
|
|
|
|
import qualified Urbit.Vere.Log as Log
|
2019-06-18 02:47:20 +03:00
|
|
|
|
|
|
|
|
2019-07-19 03:52:53 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2020-05-27 02:01:03 +03:00
|
|
|
-- TODO XXX HACK FIXME
|
|
|
|
data MissingBootEventsInEventLog = MissingBootEventsInEventLog Word Word
|
|
|
|
deriving (Show, Exception)
|
2019-09-17 21:19:53 +03:00
|
|
|
|
2019-07-19 03:52:53 +03:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2020-05-27 02:01:03 +03:00
|
|
|
bytesNouns :: MonadIO m => ConduitT ByteString Noun m ()
|
|
|
|
bytesNouns = await >>= \case
|
|
|
|
Nothing -> pure ()
|
|
|
|
Just bs -> do
|
|
|
|
noun <- cueBSExn bs
|
|
|
|
(mug :: Noun, bod) <- fromNounExn noun
|
|
|
|
yield bod
|
|
|
|
bytesNouns
|
|
|
|
|
|
|
|
withSerf :: HasLogFunc e => Config -> RAcquire e (Serf, SerfInfo)
|
|
|
|
withSerf config = mkRAcquire (io $ start config) kill
|
|
|
|
where
|
|
|
|
kill (serf, _) = do
|
|
|
|
void $ rio $ execShutdown serf
|
|
|
|
|
|
|
|
{-
|
|
|
|
TODO This needs to be thought through carfully once the callsites
|
|
|
|
have stabilized.
|
2019-07-17 02:14:46 +03:00
|
|
|
-}
|
2020-05-27 02:01:03 +03:00
|
|
|
execShutdown :: HasLogFunc e => Serf -> RIO e ()
|
|
|
|
execShutdown serf = do
|
|
|
|
race_ (wait2sec >> forceKill) $ do
|
|
|
|
logTrace "Getting current serf state (taking lock, might block if in use)."
|
|
|
|
finalState <- takeMVar (serfLock serf)
|
|
|
|
logTrace "Got serf state (and took lock). Requesting shutdown."
|
|
|
|
io (shutdown serf 0)
|
|
|
|
logTrace "Sent shutdown request. Waiting for process to die."
|
|
|
|
io $ waitForProcess (serfProc serf)
|
|
|
|
logTrace "RIP Serf process."
|
|
|
|
where
|
|
|
|
wait2sec = threadDelay 5_000_000
|
|
|
|
forceKill = do
|
|
|
|
logTrace "Serf taking too long to go down, kill with fire (SIGTERM)."
|
|
|
|
io (getPid $ serfProc serf) >>= \case
|
|
|
|
Nothing -> do
|
|
|
|
logTrace "Serf process already dead."
|
|
|
|
Just pid -> do
|
|
|
|
io $ signalProcess sigKILL pid
|
|
|
|
io $ waitForProcess (serfProc serf)
|
|
|
|
logTrace "Finished killing serf process with fire."
|
|
|
|
|
|
|
|
execSnapshot :: forall e . HasLogFunc e => Serf -> RIO e ()
|
|
|
|
execSnapshot serf = do
|
|
|
|
logTrace "execSnapshot: taking lock"
|
|
|
|
serfState <- takeMVar (serfLock serf)
|
|
|
|
io (sendSnapshotRequest serf (ssLast serfState))
|
|
|
|
logTrace "execSnapshot: releasing lock"
|
|
|
|
putMVar (serfLock serf) serfState
|
|
|
|
|
|
|
|
execReplay
|
|
|
|
:: forall e
|
|
|
|
. HasLogFunc e
|
|
|
|
=> Serf
|
|
|
|
-> Log.EventLog
|
|
|
|
-> Maybe Word64
|
|
|
|
-> RIO e (Maybe PlayBail)
|
|
|
|
execReplay serf log last = do
|
|
|
|
lastEventInSnap <- io (ssLast <$> serfCurrentStateBlocking serf)
|
|
|
|
if lastEventInSnap == 0 then doBoot else doReplay
|
|
|
|
where
|
|
|
|
doBoot :: RIO e (Maybe PlayBail)
|
|
|
|
doBoot = do
|
|
|
|
let bootSeqLen = lifecycleLen (Log.identity log)
|
|
|
|
|
|
|
|
evs <- runConduit $ Log.streamEvents log 1
|
|
|
|
.| CC.take (fromIntegral bootSeqLen)
|
|
|
|
.| bytesNouns
|
|
|
|
.| CC.sinkList
|
|
|
|
|
|
|
|
let numEvs = fromIntegral (length evs)
|
|
|
|
let bootLn = bootSeqLen
|
|
|
|
|
|
|
|
when (numEvs /= bootLn) $ do
|
|
|
|
throwIO (MissingBootEventsInEventLog numEvs bootLn)
|
|
|
|
|
|
|
|
io (bootSeq serf evs) >>= \case
|
|
|
|
Just err -> pure (Just err)
|
|
|
|
Nothing -> doReplay
|
|
|
|
|
|
|
|
doReplay :: RIO e (Maybe PlayBail)
|
|
|
|
doReplay = do
|
2020-02-06 02:20:32 +03:00
|
|
|
logTrace "Beginning event log replay"
|
|
|
|
|
2020-05-27 02:01:03 +03:00
|
|
|
lastEventInSnap <- io (ssLast <$> serfCurrentStateBlocking serf)
|
|
|
|
|
2020-02-06 02:20:32 +03:00
|
|
|
last & \case
|
|
|
|
Nothing -> pure ()
|
|
|
|
Just lt -> logTrace $ display $
|
|
|
|
"User requested to replay up to event #" <> tshow lt
|
|
|
|
|
|
|
|
logLastEv :: Word64 <- fromIntegral <$> Log.lastEv log
|
|
|
|
|
|
|
|
logTrace $ display $ "Last event in event log is #" <> tshow logLastEv
|
|
|
|
|
2020-05-27 02:01:03 +03:00
|
|
|
let replayUpTo = min (fromMaybe logLastEv last) logLastEv
|
2020-02-06 02:20:32 +03:00
|
|
|
|
|
|
|
let numEvs :: Int = fromIntegral replayUpTo - fromIntegral lastEventInSnap
|
2020-01-11 01:07:29 +03:00
|
|
|
|
2020-05-27 02:01:03 +03:00
|
|
|
when (numEvs < 0) $ do
|
|
|
|
error "impossible"
|
|
|
|
|
2020-02-06 02:20:32 +03:00
|
|
|
logTrace $ display $ "Replaying up to event #" <> tshow replayUpTo
|
|
|
|
logTrace $ display $ "Will replay " <> tshow numEvs <> " in total."
|
2020-01-11 01:07:29 +03:00
|
|
|
|
2020-05-27 02:01:03 +03:00
|
|
|
runConduit $ Log.streamEvents log (lastEventInSnap + 1)
|
2020-02-06 02:20:32 +03:00
|
|
|
.| CC.take (fromIntegral numEvs)
|
2020-05-27 02:01:03 +03:00
|
|
|
.| bytesNouns
|
|
|
|
.| replay serf
|