-----------------------------------------------------------------------------
-- |
-- Module      :  Plugins.Monitors.Net.Linux
-- Copyright   :  (c) 2011, 2012, 2013, 2014, 2017, 2020 Jose Antonio Ortega Ruiz
--                (c) 2007-2010 Andrea Rossato
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  Jose A. Ortega Ruiz <jao@gnu.org>
-- Stability   :  unstable
-- Portability :  unportable
--
-- A net device monitor for Xmobar
--

-----------------------------------------------------------------------------

{-# LANGUAGE OverloadedStrings #-}

module Xmobar.Plugins.Monitors.Net.Linux (
  existingDevs
  , findNetDev
  ) where

import Xmobar.Plugins.Monitors.Net.Common (NetDevRawTotal, NetDev(..), NetDevInfo(..))

import Control.Monad (filterM)
import System.Directory (getDirectoryContents, doesFileExist)
import System.FilePath ((</>))
import System.IO.Error (catchIOError)
import System.IO.Unsafe (unsafeInterleaveIO)

import qualified Data.ByteString.Char8 as B


operstateDir :: String -> FilePath
operstateDir :: String -> String
operstateDir String
d = String
"/sys/class/net" String -> String -> String
</> String
d String -> String -> String
</> String
"operstate"

existingDevs :: IO [String]
existingDevs :: IO [String]
existingDevs = String -> IO [String]
getDirectoryContents String
"/sys/class/net" IO [String] -> ([String] -> IO [String]) -> IO [String]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (String -> IO Bool) -> [String] -> IO [String]
forall (m :: * -> *) a.
Applicative m =>
(a -> m Bool) -> [a] -> m [a]
filterM String -> IO Bool
isDev
  where isDev :: String -> IO Bool
isDev String
d | String
d String -> [String] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
excludes = Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False
                | Bool
otherwise = String -> IO Bool
doesFileExist (String -> String
operstateDir String
d)
        excludes :: [String]
excludes = [String
".", String
"..", String
"lo"]

isUp :: String -> IO Bool
isUp :: String -> IO Bool
isUp String
d = (IO Bool -> (IOError -> IO Bool) -> IO Bool)
-> (IOError -> IO Bool) -> IO Bool -> IO Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip IO Bool -> (IOError -> IO Bool) -> IO Bool
forall a. IO a -> (IOError -> IO a) -> IO a
catchIOError (IO Bool -> IOError -> IO Bool
forall a b. a -> b -> a
const (IO Bool -> IOError -> IO Bool) -> IO Bool -> IOError -> IO Bool
forall a b. (a -> b) -> a -> b
$ Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False) (IO Bool -> IO Bool) -> IO Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ do
  ByteString
operstate <- String -> IO ByteString
B.readFile (String -> String
operstateDir String
d)
  Bool -> IO Bool
forall (m :: * -> *) a. Monad m => a -> m a
return (Bool -> IO Bool) -> Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$! ([ByteString] -> ByteString
forall a. [a] -> a
head ([ByteString] -> ByteString)
-> (ByteString -> [ByteString]) -> ByteString -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
B.lines) ByteString
operstate ByteString -> [ByteString] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ByteString
"up", ByteString
"unknown"]

readNetDev :: [String] -> IO NetDevRawTotal
readNetDev :: [String] -> IO NetDevRawTotal
readNetDev ~[String
d, String
x, String
y] = do
  Bool
up <- IO Bool -> IO Bool
forall a. IO a -> IO a
unsafeInterleaveIO (IO Bool -> IO Bool) -> IO Bool -> IO Bool
forall a b. (a -> b) -> a -> b
$ String -> IO Bool
isUp String
d
  NetDevRawTotal -> IO NetDevRawTotal
forall (m :: * -> *) a. Monad m => a -> m a
return (NetDevRawTotal -> IO NetDevRawTotal)
-> NetDevRawTotal -> IO NetDevRawTotal
forall a b. (a -> b) -> a -> b
$ String -> NetDevInfo Word64 -> NetDevRawTotal
forall num. String -> NetDevInfo num -> NetDev num
N String
d (if Bool
up then Word64 -> Word64 -> NetDevInfo Word64
forall num. num -> num -> NetDevInfo num
ND (String -> Word64
forall p. (Num p, Read p) => String -> p
r String
x) (String -> Word64
forall p. (Num p, Read p) => String -> p
r String
y) else NetDevInfo Word64
forall num. NetDevInfo num
NI)
    where r :: String -> p
r String
s | String
s String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"" = p
0
              | Bool
otherwise = String -> p
forall a. Read a => String -> a
read String
s

netParser :: B.ByteString -> IO [NetDevRawTotal]
netParser :: ByteString -> IO [NetDevRawTotal]
netParser = (ByteString -> IO NetDevRawTotal)
-> [ByteString] -> IO [NetDevRawTotal]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ([String] -> IO NetDevRawTotal
readNetDev ([String] -> IO NetDevRawTotal)
-> (ByteString -> [String]) -> ByteString -> IO NetDevRawTotal
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [String]
splitDevLine) ([ByteString] -> IO [NetDevRawTotal])
-> (ByteString -> [ByteString])
-> ByteString
-> IO [NetDevRawTotal]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
readDevLines
  where readDevLines :: ByteString -> [ByteString]
readDevLines = Int -> [ByteString] -> [ByteString]
forall a. Int -> [a] -> [a]
drop Int
2 ([ByteString] -> [ByteString])
-> (ByteString -> [ByteString]) -> ByteString -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
B.lines
        splitDevLine :: ByteString -> [String]
splitDevLine = (ByteString -> String) -> [ByteString] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ByteString -> String
B.unpack ([ByteString] -> [String])
-> (ByteString -> [ByteString]) -> ByteString -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ByteString] -> [ByteString]
forall b. [b] -> [b]
selectCols ([ByteString] -> [ByteString])
-> (ByteString -> [ByteString]) -> ByteString -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (ByteString -> Bool) -> [ByteString] -> [ByteString]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (ByteString -> Bool) -> ByteString -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Bool
B.null) ([ByteString] -> [ByteString])
-> (ByteString -> [ByteString]) -> ByteString -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> ByteString -> [ByteString]
B.splitWith (Char -> String -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
' ',Char
':'])
        selectCols :: [b] -> [b]
selectCols [b]
cols = (Int -> b) -> [Int] -> [b]
forall a b. (a -> b) -> [a] -> [b]
map ([b]
cols[b] -> Int -> b
forall a. [a] -> Int -> a
!!) [Int
0,Int
1,Int
9]

findNetDev :: String -> IO NetDevRawTotal
findNetDev :: String -> IO NetDevRawTotal
findNetDev String
dev = do
  [NetDevRawTotal]
nds <- String -> IO ByteString
B.readFile String
"/proc/net/dev" IO ByteString
-> (ByteString -> IO [NetDevRawTotal]) -> IO [NetDevRawTotal]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ByteString -> IO [NetDevRawTotal]
netParser
  case (NetDevRawTotal -> Bool) -> [NetDevRawTotal] -> [NetDevRawTotal]
forall a. (a -> Bool) -> [a] -> [a]
filter NetDevRawTotal -> Bool
forall num. NetDev num -> Bool
isDev [NetDevRawTotal]
nds of
    NetDevRawTotal
x:[NetDevRawTotal]
_ -> NetDevRawTotal -> IO NetDevRawTotal
forall (m :: * -> *) a. Monad m => a -> m a
return NetDevRawTotal
x
    [NetDevRawTotal]
_ -> NetDevRawTotal -> IO NetDevRawTotal
forall (m :: * -> *) a. Monad m => a -> m a
return NetDevRawTotal
forall num. NetDev num
NA
  where isDev :: NetDev num -> Bool
isDev (N String
d NetDevInfo num
_) = String
d String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
dev
        isDev NetDev num
NA = Bool
False