1{-# LANGUAGE CPP #-} 2#ifdef USE_NL80211 3{-# LANGUAGE TypeApplications #-} 4#endif 5----------------------------------------------------------------------------- 6-- | 7-- Module : Plugins.Monitors.Wireless 8-- Copyright : (c) Jose Antonio Ortega Ruiz 9-- License : BSD-style (see LICENSE) 10-- 11-- Maintainer : Jose Antonio Ortega Ruiz 12-- Stability : unstable 13-- Portability : unportable 14-- 15-- A monitor reporting SSID and signal level for wireless interfaces 16-- 17----------------------------------------------------------------------------- 18 19module Xmobar.Plugins.Monitors.Wireless (wirelessConfig, runWireless) where 20 21import System.Console.GetOpt 22 23import Xmobar.Plugins.Monitors.Common 24 25#ifdef IWLIB 26import Network.IWlib 27#elif defined USE_NL80211 28import Control.Exception (bracket) 29import qualified Data.Map as M 30import GHC.Int (Int8) 31import Data.Maybe (listToMaybe, fromMaybe) 32import Control.Monad.IO.Class (liftIO) 33import Control.Monad.Trans.Maybe (MaybeT(..), runMaybeT) 34import Data.ByteString.Char8 (unpack) 35import Data.Serialize.Put (runPut, putWord32host, putByteString) 36import Data.Serialize.Get (runGet) 37 38import System.Linux.Netlink hiding (query) 39import System.Linux.Netlink.GeNetlink.NL80211 40import System.Linux.Netlink.GeNetlink.NL80211.StaInfo 41import System.Linux.Netlink.GeNetlink.NL80211.Constants 42import System.Posix.IO (closeFd) 43 44data IwData = IwData { wiEssid :: String, wiSignal :: Maybe Int, wiQuality :: Int } 45 46getWirelessInfo :: String -> IO IwData 47getWirelessInfo ifname = 48 bracket makeNL80211Socket (closeFd . getFd) (\s -> do 49 iflist <- getInterfaceList s 50 iwdata <- runMaybeT $ do 51 ifidx <- MaybeT . return $ foldr (\(n, i) z -> 52 if ifname == "" || ifname == n then Just i else z) 53 Nothing 54 iflist 55 scanp <- liftIO (getConnectedWifi s ifidx) >>= 56 MaybeT . return . listToMaybe 57 bssid <- MaybeT . return $ M.lookup eNL80211_ATTR_BSS (packetAttributes scanp) >>= 58 rightToMaybe . runGet getAttributes >>= 59 M.lookup eNL80211_BSS_BSSID 60 stap <- liftIO (query s eNL80211_CMD_GET_STATION True $ M.fromList 61 [(eNL80211_ATTR_IFINDEX, runPut $ putWord32host ifidx), 62 (eNL80211_ATTR_MAC, runPut $ putByteString bssid)]) >>= 63 MaybeT . return . listToMaybe 64 let ssid = fromMaybe "" $ getWifiAttributes scanp >>= M.lookup eWLAN_EID_SSID >>= 65 return . unpack 66 signal = staInfoFromPacket stap >>= staSignalMBM >>= 67 return . fromIntegral @Int8 . fromIntegral 68 qlty = maybe (-1) (round @Float . (/ 0.7) . (+ 110) . 69 clamp (-110) (-40) . fromIntegral) signal 70 MaybeT . return $ Just $ IwData ssid signal qlty 71 return $ fromMaybe (IwData "" Nothing (-1)) iwdata) 72 where 73 rightToMaybe = either (const Nothing) Just 74 clamp lb up v 75 | v < lb = lb 76 | v > up = up 77 | otherwise = v 78#endif 79 80newtype WirelessOpts = WirelessOpts 81 { qualityIconPattern :: Maybe IconPattern 82 } 83 84defaultOpts :: WirelessOpts 85defaultOpts = WirelessOpts 86 { qualityIconPattern = Nothing 87 } 88 89options :: [OptDescr (WirelessOpts -> WirelessOpts)] 90options = 91 [ Option "" ["quality-icon-pattern"] (ReqArg (\d opts -> 92 opts { qualityIconPattern = Just $ parseIconPattern d }) "") "" 93 ] 94 95wirelessConfig :: IO MConfig 96wirelessConfig = 97 mkMConfig "<ssid> <quality>" 98 ["ssid", "essid", "signal", "quality", "qualitybar", "qualityvbar", "qualityipat"] 99 100runWireless :: String -> [String] -> Monitor String 101runWireless iface args = do 102 opts <- io $ parseOptsWith options defaultOpts args 103#ifdef IWLIB 104 iface' <- if "" == iface then io findInterface else return iface 105#else 106 let iface' = iface 107#endif 108 wi <- io $ getWirelessInfo iface' 109 na <- getConfigValue naString 110 let essid = wiEssid wi 111 qlty = fromIntegral $ wiQuality wi 112 e = if essid == "" then na else essid 113 ep <- showWithPadding e 114#ifdef USE_NL80211 115 let s = wiSignal wi 116#else 117 let s = if qlty >= 0 then Just (qlty * 0.7 - 110) else Nothing 118#endif 119 sp <- showWithPadding $ maybe "" show s 120 q <- if qlty >= 0 121 then showPercentWithColors (qlty / 100) 122 else showWithPadding "" 123 qb <- showPercentBar qlty (qlty / 100) 124 qvb <- showVerticalBar qlty (qlty / 100) 125 qipat <- showIconPattern (qualityIconPattern opts) (qlty / 100) 126 parseTemplate [ep, ep, sp, q, qb, qvb, qipat] 127 128#ifdef IWLIB 129findInterface :: IO String 130findInterface = do 131 c <- readFile "/proc/net/wireless" 132 let nds = lines c 133 return $ if length nds > 2 then takeWhile (/= 'c') (nds!!2) else [] 134#endif 135