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