2020-04-01 22:06:00 -04:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | VPN plugin
|
|
|
|
--
|
2021-12-14 00:37:09 -05:00
|
|
|
-- Use the networkmanager to detect when a VPN interface is added or removed.
|
|
|
|
-- Specifically, monitor the object tree to detect paths with the interface
|
|
|
|
-- "org.freedesktop.NetworkManager.Device.Tun".
|
2020-04-01 22:06:00 -04:00
|
|
|
|
2021-06-21 23:41:57 -04:00
|
|
|
module Xmobar.Plugins.VPN
|
|
|
|
( VPN(..)
|
|
|
|
, vpnAlias
|
2021-11-24 00:43:58 -05:00
|
|
|
, vpnDep
|
2021-06-21 23:41:57 -04:00
|
|
|
) where
|
2020-03-21 18:37:26 -04:00
|
|
|
|
2021-12-14 00:37:09 -05:00
|
|
|
import Control.Concurrent.MVar
|
2021-11-27 13:24:13 -05:00
|
|
|
import Control.Monad
|
|
|
|
|
2022-07-09 01:02:37 -04:00
|
|
|
import qualified Data.Map as M
|
2021-12-14 00:37:09 -05:00
|
|
|
import Data.Maybe
|
2022-07-09 01:02:37 -04:00
|
|
|
import qualified Data.Set as S
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2020-03-25 18:55:52 -04:00
|
|
|
import DBus
|
2021-12-14 00:37:09 -05:00
|
|
|
import DBus.Client
|
2021-11-27 01:02:22 -05:00
|
|
|
import DBus.Internal
|
2020-03-21 18:37:26 -04:00
|
|
|
|
2022-07-09 01:02:37 -04:00
|
|
|
import XMonad.Internal.Command.Desktop
|
2021-11-24 00:43:58 -05:00
|
|
|
import XMonad.Internal.Dependency
|
2021-06-19 00:54:01 -04:00
|
|
|
import Xmobar
|
2021-11-24 00:43:58 -05:00
|
|
|
import Xmobar.Plugins.Common
|
2020-03-21 18:37:26 -04:00
|
|
|
|
2021-11-27 17:33:02 -05:00
|
|
|
newtype VPN = VPN (String, Colors) deriving (Read, Show)
|
2021-06-21 23:41:57 -04:00
|
|
|
|
2021-12-14 00:37:09 -05:00
|
|
|
instance Exec VPN where
|
|
|
|
alias (VPN _) = vpnAlias
|
|
|
|
start (VPN (text, colors)) cb =
|
|
|
|
withDBusClientConnection True cb $ \c -> do
|
|
|
|
state <- initState c
|
|
|
|
let display = displayMaybe cb iconFormatter . Just =<< readState state
|
|
|
|
let signalCallback' f = f state display
|
|
|
|
vpnAddedListener (signalCallback' addedCallback) c
|
|
|
|
vpnRemovedListener (signalCallback' removedCallback) c
|
|
|
|
display
|
|
|
|
where
|
|
|
|
iconFormatter b = return $ colorText colors b text
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | VPN State
|
|
|
|
--
|
|
|
|
-- Maintain a set of paths which are the currently active VPNs. Most of the time
|
|
|
|
-- this will be a null or singleton set, but this setup could handle the edge
|
|
|
|
-- case of multiple VPNs being active at once without puking.
|
|
|
|
|
|
|
|
type VPNState = S.Set ObjectPath
|
|
|
|
|
|
|
|
type MutableVPNState = MVar VPNState
|
|
|
|
|
|
|
|
initState :: Client -> IO MutableVPNState
|
|
|
|
initState client = do
|
|
|
|
ot <- getVPNObjectTree client
|
|
|
|
newMVar $ findTunnels ot
|
|
|
|
|
|
|
|
readState :: MutableVPNState -> IO Bool
|
|
|
|
readState = fmap (not . null) . readMVar
|
|
|
|
|
|
|
|
updateState :: (ObjectPath -> VPNState -> VPNState) -> MutableVPNState
|
|
|
|
-> ObjectPath -> IO ()
|
|
|
|
updateState f state op = modifyMVar_ state $ return . f op
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Tunnel Device Detection
|
|
|
|
--
|
|
|
|
|
|
|
|
getVPNObjectTree :: Client -> IO ObjectTree
|
|
|
|
getVPNObjectTree client = callGetManagedObjects client vpnBus vpnPath
|
|
|
|
|
|
|
|
findTunnels :: ObjectTree -> VPNState
|
|
|
|
findTunnels = S.fromList . M.keys . M.filter (elem vpnDeviceTun . M.keys)
|
|
|
|
|
|
|
|
vpnAddedListener :: SignalCallback -> Client -> IO ()
|
|
|
|
vpnAddedListener = fmap void . addInterfaceAddedListener vpnBus vpnPath
|
|
|
|
|
|
|
|
vpnRemovedListener :: SignalCallback -> Client -> IO ()
|
|
|
|
vpnRemovedListener = fmap void . addInterfaceRemovedListener vpnBus vpnPath
|
|
|
|
|
|
|
|
addedCallback :: MutableVPNState -> IO () -> SignalCallback
|
|
|
|
addedCallback state display [device, added] = update >> display
|
|
|
|
where
|
|
|
|
added' = fromVariant added :: Maybe (M.Map String (M.Map String Variant))
|
|
|
|
is = M.keys $ fromMaybe M.empty added'
|
|
|
|
update = updateDevice S.insert state device is
|
|
|
|
addedCallback _ _ _ = return ()
|
|
|
|
|
|
|
|
removedCallback :: MutableVPNState -> IO () -> SignalCallback
|
|
|
|
removedCallback state display [device, interfaces] = update >> display
|
|
|
|
where
|
|
|
|
is = fromMaybe [] $ fromVariant interfaces :: [String]
|
|
|
|
update = updateDevice S.delete state device is
|
|
|
|
removedCallback _ _ _ = return ()
|
|
|
|
|
|
|
|
updateDevice :: (ObjectPath -> VPNState -> VPNState) -> MutableVPNState
|
|
|
|
-> Variant -> [String] -> IO ()
|
|
|
|
updateDevice f state device interfaces = when (vpnDeviceTun `elem` interfaces) $
|
|
|
|
forM_ d $ updateState f state
|
|
|
|
where
|
|
|
|
d = fromVariant device :: Maybe ObjectPath
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | DBus Interface
|
|
|
|
--
|
|
|
|
|
2021-06-21 23:41:57 -04:00
|
|
|
vpnBus :: BusName
|
2021-11-24 00:43:58 -05:00
|
|
|
vpnBus = busName_ "org.freedesktop.NetworkManager"
|
2021-06-21 23:41:57 -04:00
|
|
|
|
|
|
|
vpnPath :: ObjectPath
|
2021-12-14 00:37:09 -05:00
|
|
|
vpnPath = objectPath_ "/org/freedesktop"
|
2021-11-09 00:59:17 -05:00
|
|
|
|
2021-12-14 00:37:09 -05:00
|
|
|
vpnDeviceTun :: String
|
|
|
|
vpnDeviceTun = "org.freedesktop.NetworkManager.Device.Tun"
|
2021-11-09 00:59:17 -05:00
|
|
|
|
2021-06-21 23:41:57 -04:00
|
|
|
vpnAlias :: String
|
|
|
|
vpnAlias = "vpn"
|
2020-03-21 18:37:26 -04:00
|
|
|
|
2022-06-26 19:05:25 -04:00
|
|
|
vpnDep :: DBusDependency_
|
2022-07-09 01:02:37 -04:00
|
|
|
vpnDep = Endpoint networkManagerPkgs vpnBus vpnPath omInterface
|
|
|
|
$ Method_ getManagedObjects
|