packages feed

mptcpanalyzer-0.0.2.0: src/MptcpAnalyzer/Plots/Live.hs

{-
Module:  MptcpAnalyzer.Commands.Plots.Live
Description :  Description
Maintainer  : matt
Portability : Linux
-}
module MptcpAnalyzer.Plots.Live
where

import qualified Data.Map as Map
import qualified Data.Text as T
import MptcpAnalyzer.ArtificialFields
import MptcpAnalyzer.Cache
import MptcpAnalyzer.Plots.Types
import MptcpAnalyzer.Prelude
import MptcpAnalyzer.Utils.Text
import Net.Tcp
import qualified Polysemy.Embed as P
import qualified Polysemy.Log as Log
import System.Process hiding (runCommand)
import Tshark.Live
import Tshark.Main

import Polysemy (Final, Members, Sem, runFinal)
import qualified Polysemy as P
import qualified Polysemy.Embed as P
import qualified Polysemy.IO as P
import qualified Polysemy.Internal as P
import Polysemy.Log (Log)
import qualified Polysemy.Log as Log
import Polysemy.Log.Colog (interpretLogStdout)
import qualified Polysemy.State as P
import Polysemy.Trace (trace)
import qualified Polysemy.Trace as P

-- import MptcpAnalyzer.Commands.Definitions as CMD
import Control.Monad.State (execStateT)
import Data.Maybe (fromMaybe, fromJust)
import GHC.IO.Handle
import Pipes (runEffect)
import System.Exit
import System.IO (stdout)
import Tshark.Fields
import Tshark.Capture
import Net.Mptcp.Connection
import Control.Lens ((^.))

configureLivePlotTcp :: Members '[Log, P.Trace, P.Embed IO] r
  => LivePlotTcpSettings
  -> Sem r LiveStatsTcp
configureLivePlotTcp (LivePlotTcpSettings connectionFilter mbFake mbConnectionRole ifname) = do
    let
      fields = Map.elems $ Map.map tfieldFullname baseFields

      -- stats/packetCount/Frame
      -- keeping it light for now
      destination = fromMaybe RoleServer mbConnectionRole
      lsConfig = LiveStatsConfig connectionFilter destination

      -- initialLiveStats :: LiveStatsTcp = LiveStats mempty 0 mempty
      toLoad = case mbFake of
        Just filename -> Right filename
        Nothing -> Left ifname

      --capture-comment
      tsharkPrefs = defaultTsharkPrefs {
        tsharkReadFilter = Just $ genReadFilterFromTcpConnection connectionFilter (Just destination)
        }
      (RawCommand bin genArgs) = generateCsvCommand fields toLoad tsharkPrefs
      -- args = genArgs ++ ["--capture-comment='Generated by mptcpanalyzer'"]
      args = genArgs ++ [ "-l"]
      createProc :: CreateProcess
      createProc = (proc bin args) {
            std_err = CreatePipe
            -- Inherit,
            , std_out = CreatePipe
            -- lets the child handle Ctrl-c
            , delegate_ctlc = True
          }

    Log.info $ "Looking at destination " <> tshow destination

    trace $ "Command run: " ++ show (RawCommand bin args)
    trace $ "Command run: " ++ showCommandForUser bin args
    -- Log.info $ "Starting " <> tshow bin <> tshow args
    ls <- P.embed $ startLivePlot lsConfig createProc
    pure ls

startLivePlot :: LiveStatsConfig -> CreateProcess -> IO LiveStatsTcp
startLivePlot lsConfig createProc = do
  let initialLiveStats :: LiveStatsTcp = LiveStats mempty mempty 0 mempty False

  (_, Just hout, Just herr, ph) <-  createProcess_ "error when creating process" createProc
  -- hSetBuffering stdout NoBuffering
  hSetBuffering stdout LineBuffering

  -- non blocking
  exitCode <- getProcessExitCode ph
  case exitCode of
    Just code -> putStrLn "Finished" >> pure initialLiveStats
    _ -> do
      -- hSetBuffering hout LineBuffering
      -- hSetBuffering herr NoBuffering
      putStrLn $ "Starting live TCP plot with initial stats: (before): " ++ show (lsPackets initialLiveStats)
      liveStats <- execStateT (runEffect (tsharkLoopTcp lsConfig hout)) initialLiveStats
      putStrLn $ "Live stats (after): " ++ show (lsPackets liveStats)
      -- putStrLn $ "Live stats (after): " ++ (T.unpack . showLiveStatsTcp) liveStats
      -- blocking
      putStrLn $ "Live stats (after): " ++ (T.unpack . showLiveStatsTcp) liveStats
      putStrLn $ "Waiting for process"
      exitCode2 <- waitForProcess ph
      case exitCode2 of
        ExitSuccess -> putStrLn "Success" >> pure liveStats
        _  -> do
          putStrLn "hGetContents"
          hGetContents herr >>= putStrLn >> pure initialLiveStats
  -- putStrLn $ "final exitCode"

-- LiveStatsMptcp
startMptcpCapture :: 
-- Members '[Log, P.Trace, P.Embed IO] r =>
   LiveStatsConfig 
   -> LiveStatsMptcp
   -> CreateProcess
   -> IO LiveStatsMptcp

startMptcpCapture lsConfig initialLiveStats createProc = do
  (_, Just hout, Just herr, ph) <- createProcess_ "error when creating process" createProc
  hSetBuffering stdout LineBuffering
  -- non blocking
  exitCode <- getProcessExitCode ph
  case exitCode of
    Just code -> putStrLn "Finished" >> pure initialLiveStats
    _ -> do
      -- hSetBuffering hout LineBuffering
      -- hSetBuffering herr NoBuffering
      putStrLn $ "Starting live mptcp plotting stats (before): "
      liveStats <- execStateT (runEffect (tsharkLoopMptcp lsConfig hout)) initialLiveStats
      case liveStats ^. lsmMaster of
        Nothing -> putStrLn "Could not detect the mptcp connection"
        Just master -> putStrLn $ "Live stats (after): " ++ (T.unpack $ showMptcpConnectionText master)
      -- putStrLn $ "Live stats (after): " ++ (T.unpack $ showMptcpConnectionText (fromJust $ liveStats ^. lsmMaster))
      putStrLn $ "Live stats (after): " ++ (T.unpack . showLiveStatsMptcp) liveStats
      -- putStrLn $ "Live stats (after): " ++ show (lsPackets liveStats)
      -- blocking
      exitCode2 <- waitForProcess ph
      case exitCode2 of
        ExitSuccess -> putStrLn "Success" >> pure liveStats
        _  -> do
          putStrLn "hGetContents"
          hGetContents herr >>= putStrLn >> pure liveStats
  -- putStrLn $ "final exitCode"

{- | Starts live analysis of an MPTCP flow
One needs to filter
-}
configureLivePlotMptcp :: Members '[Log, Cache, P.Trace, P.Embed IO] r =>
       LivePlotTcpSettings
    -- -> CaptureSettingsMptcp
    -> Sem r LiveStatsMptcp

configureLivePlotMptcp (LivePlotTcpSettings connectionFilter mbFake mbConnectionRole ifname) = do
    let
      destination = fromMaybe RoleServer mbConnectionRole

      lpConfig = LiveStatsConfig connectionFilter destination
      initialLiveStats = mkLiveStatsMptcp
      fields = Map.elems $ Map.map tfieldFullname baseFields

      -- stats/packetCount/Frame
      -- keeping it light for now
      toLoad = case mbFake of
        Just filename -> Right filename
        Nothing -> Left ifname

      --capture-comment
      tsharkPrefs = defaultTsharkPrefs {
        tsharkReadFilter = Just $ genReadFilterMptcpFromMptcpConnection
        }
      (RawCommand bin genArgs) = generateCsvCommand fields toLoad tsharkPrefs
      -- args = genArgs ++ ["--capture-comment='Generated by mptcpanalyzer'"]
      args = genArgs ++ [ "-l"]
      createProc :: CreateProcess
      createProc = (proc bin args) {
            std_err = CreatePipe
            -- Inherit,
            , std_out = CreatePipe
            -- lets the child handle Ctrl-c
            , delegate_ctlc = True
          }

    Log.info $ "Looking at destination " <> tshow destination

    trace $ "Command run: " ++ show (RawCommand bin args)
    trace $ "Command run: " ++ showCommandForUser bin args
    -- Log.info $ "Starting " <> tshow bin <> tshow args
    ls <- P.embed $ startMptcpCapture lpConfig initialLiveStats createProc
    pure ls