-----------------------------------------------------------------------------
-- |
-- 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" forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= 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 forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
excludes = 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 = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. IO a -> (IOError -> IO a) -> IO a
catchIOError (forall a b. a -> b -> a
const forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False) forall a b. (a -> b) -> a -> b
$ do
  ByteString
operstate <- String -> IO ByteString
B.readFile (String -> String
operstateDir String
d)
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$! (forall a. [a] -> a
head forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
B.lines) ByteString
operstate 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 <- forall a. IO a -> IO a
unsafeInterleaveIO forall a b. (a -> b) -> a -> b
$ String -> IO Bool
isUp String
d
  forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall num. String -> NetDevInfo num -> NetDev num
N String
d (if Bool
up then forall num. num -> num -> NetDevInfo num
ND (forall {a}. (Num a, Read a) => String -> a
r String
x) (forall {a}. (Num a, Read a) => String -> a
r String
y) else forall num. NetDevInfo num
NI)
    where r :: String -> a
r String
s | String
s forall a. Eq a => a -> a -> Bool
== String
"" = a
0
              | Bool
otherwise = forall a. Read a => String -> a
read String
s

netParser :: B.ByteString -> IO [NetDevRawTotal]
netParser :: ByteString -> IO [NetDevRawTotal]
netParser = forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ([String] -> IO NetDevRawTotal
readNetDev forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [String]
splitDevLine) forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
readDevLines
  where readDevLines :: ByteString -> [ByteString]
readDevLines = forall a. Int -> [a] -> [a]
drop Int
2 forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString]
B.lines
        splitDevLine :: ByteString -> [String]
splitDevLine = forall a b. (a -> b) -> [a] -> [b]
map ByteString -> String
B.unpack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {b}. [b] -> [b]
selectCols forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Bool
B.null) forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> ByteString -> [ByteString]
B.splitWith (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
' ',Char
':'])
        selectCols :: [b] -> [b]
selectCols [b]
cols = forall a b. (a -> b) -> [a] -> [b]
map ([b]
colsforall 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" forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= ByteString -> IO [NetDevRawTotal]
netParser
  case forall a. (a -> Bool) -> [a] -> [a]
filter forall {num}. NetDev num -> Bool
isDev [NetDevRawTotal]
nds of
    NetDevRawTotal
x:[NetDevRawTotal]
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return NetDevRawTotal
x
    [NetDevRawTotal]
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return forall num. NetDev num
NA
  where isDev :: NetDev num -> Bool
isDev (N String
d NetDevInfo num
_) = String
d forall a. Eq a => a -> a -> Bool
== String
dev
        isDev NetDev num
NA = Bool
False