2020-04-01 22:06:00 -04:00
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- VPN plugin
|
2020-04-01 22:06:00 -04:00
|
|
|
--
|
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
|
2022-12-30 14:58:23 -05:00
|
|
|
( VPN (..)
|
2021-06-21 23:41:57 -04:00
|
|
|
, vpnAlias
|
2021-11-24 00:43:58 -05:00
|
|
|
, vpnDep
|
2022-12-30 14:58:23 -05:00
|
|
|
)
|
|
|
|
where
|
|
|
|
|
|
|
|
import DBus
|
|
|
|
import Data.Internal.DBus
|
2023-01-01 18:33:02 -05:00
|
|
|
import Data.Internal.XIO
|
2022-12-30 16:37:52 -05:00
|
|
|
import RIO
|
2022-12-31 19:47:02 -05:00
|
|
|
import qualified RIO.Map as M
|
2022-12-30 14:58:23 -05:00
|
|
|
import qualified RIO.Text as T
|
|
|
|
import XMonad.Internal.Command.Desktop
|
|
|
|
import XMonad.Internal.DBus.Common
|
|
|
|
import Xmobar
|
|
|
|
import Xmobar.Plugins.Common
|
2020-03-21 18:37:26 -04:00
|
|
|
|
2022-12-26 14:45:49 -05:00
|
|
|
newtype VPN = VPN (T.Text, Colors) deriving (Read, Show)
|
2021-06-21 23:41:57 -04:00
|
|
|
|
2021-12-14 00:37:09 -05:00
|
|
|
instance Exec VPN where
|
2022-12-26 14:45:49 -05:00
|
|
|
alias (VPN _) = T.unpack vpnAlias
|
2021-12-14 00:37:09 -05:00
|
|
|
start (VPN (text, colors)) cb =
|
2023-01-03 23:33:08 -05:00
|
|
|
withDBusClientConnection cb (Just "vpn.log") $ \c -> do
|
2023-09-29 23:44:08 -04:00
|
|
|
let dpy = displayMaybe cb formatter . Just =<< readState
|
2023-01-03 22:28:34 -05:00
|
|
|
s <- newEmptyMVar
|
2023-01-03 22:18:55 -05:00
|
|
|
mapRIO (VEnv c s dpy) $ do
|
|
|
|
initState
|
|
|
|
vpnAddedListener addedCallback
|
|
|
|
vpnRemovedListener removedCallback
|
2023-01-02 10:33:04 -05:00
|
|
|
dpy
|
2021-12-14 00:37:09 -05:00
|
|
|
where
|
2023-09-29 23:44:08 -04:00
|
|
|
formatter names = return $ case names of
|
|
|
|
[] -> colorText colors False text
|
2023-09-30 12:22:30 -04:00
|
|
|
xs -> T.unwords [T.intercalate "|" xs, colorText colors True text]
|
2021-12-14 00:37:09 -05:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- VPN State
|
2021-12-14 00:37:09 -05:00
|
|
|
--
|
|
|
|
-- 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.
|
|
|
|
|
2023-01-03 22:18:55 -05:00
|
|
|
data VEnv c = VEnv
|
|
|
|
{ vClient :: !c
|
2023-01-03 22:31:29 -05:00
|
|
|
, vState :: !(MVar VPNState)
|
2023-01-02 10:33:04 -05:00
|
|
|
, vDisplay :: !(VIO ())
|
|
|
|
, vEnv :: !SimpleApp
|
|
|
|
}
|
|
|
|
|
2023-02-12 23:08:05 -05:00
|
|
|
instance HasLogFunc (VEnv c) where
|
2023-01-02 10:33:04 -05:00
|
|
|
logFuncL = lens vEnv (\x y -> x {vEnv = y}) . logFuncL
|
|
|
|
|
2023-01-03 22:18:55 -05:00
|
|
|
instance HasClient VEnv where
|
|
|
|
clientL = lens vClient (\x y -> x {vClient = y})
|
|
|
|
|
|
|
|
type VIO = RIO (VEnv SysClient)
|
2023-01-02 10:33:04 -05:00
|
|
|
|
2023-09-29 23:44:08 -04:00
|
|
|
type VPNState = M.Map ObjectPath T.Text
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2023-01-03 22:18:55 -05:00
|
|
|
initState :: VIO ()
|
|
|
|
initState = do
|
|
|
|
ot <- getVPNObjectTree
|
|
|
|
s <- asks vState
|
|
|
|
putMVar s $ findTunnels ot
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2023-09-29 23:44:08 -04:00
|
|
|
readState :: VIO [T.Text]
|
|
|
|
readState = M.elems <$> (readMVar =<< asks vState)
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2023-09-29 23:44:08 -04:00
|
|
|
insertState :: ObjectPath -> T.Text -> VIO ()
|
|
|
|
insertState op name = do
|
|
|
|
s <- asks vState
|
|
|
|
modifyMVar_ s $ return . M.insert op name
|
|
|
|
|
|
|
|
deleteState :: ObjectPath -> VIO ()
|
|
|
|
deleteState op = do
|
2023-01-02 10:33:04 -05:00
|
|
|
s <- asks vState
|
2023-09-29 23:44:08 -04:00
|
|
|
modifyMVar_ s $ return . M.delete op
|
2023-01-02 10:33:04 -05:00
|
|
|
|
|
|
|
beforeDisplay :: VIO () -> VIO ()
|
|
|
|
beforeDisplay f = f >> join (asks vDisplay)
|
2021-12-14 00:37:09 -05:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- Tunnel Device Detection
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2023-01-01 19:58:23 -05:00
|
|
|
getVPNObjectTree
|
2023-01-03 22:18:55 -05:00
|
|
|
:: ( SafeClient c
|
|
|
|
, HasClient env
|
|
|
|
, MonadReader (env c) m
|
|
|
|
, HasLogFunc (env c)
|
|
|
|
, MonadUnliftIO m
|
|
|
|
)
|
|
|
|
=> m ObjectTree
|
|
|
|
getVPNObjectTree = callGetManagedObjects vpnBus vpnPath
|
2021-12-14 00:37:09 -05:00
|
|
|
|
|
|
|
findTunnels :: ObjectTree -> VPNState
|
2023-09-29 23:44:08 -04:00
|
|
|
findTunnels = M.mapMaybe lookupVPNInterface
|
|
|
|
|
|
|
|
-- | For the interface map underneath a given object path, try to lookup a
|
|
|
|
-- VPN interface, then lookup the ip link name from the parent interface
|
|
|
|
lookupVPNInterface :: M.Map T.Text (M.Map T.Text Variant) -> Maybe T.Text
|
|
|
|
lookupVPNInterface m
|
|
|
|
| isJust $ M.lookup vpnDeviceTun m =
|
|
|
|
fromVariant =<< M.lookup "Interface" =<< M.lookup vpnDeviceParent m
|
|
|
|
| otherwise = Nothing
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2023-01-01 19:41:46 -05:00
|
|
|
vpnAddedListener
|
2023-01-03 22:18:55 -05:00
|
|
|
:: ( SafeClient c
|
|
|
|
, HasClient env
|
|
|
|
, MonadReader (env c) m
|
|
|
|
, HasLogFunc (env c)
|
|
|
|
, MonadUnliftIO m
|
|
|
|
)
|
2023-01-01 19:41:46 -05:00
|
|
|
=> SignalCallback m
|
|
|
|
-> m ()
|
2023-01-03 22:18:55 -05:00
|
|
|
vpnAddedListener cb = void $ addInterfaceAddedListener vpnBus vpnPath cb
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2023-01-01 19:41:46 -05:00
|
|
|
vpnRemovedListener
|
2023-01-03 22:18:55 -05:00
|
|
|
:: ( SafeClient c
|
|
|
|
, HasClient env
|
|
|
|
, MonadReader (env c) m
|
|
|
|
, HasLogFunc (env c)
|
|
|
|
, MonadUnliftIO m
|
|
|
|
)
|
2023-01-01 19:41:46 -05:00
|
|
|
=> SignalCallback m
|
|
|
|
-> m ()
|
2023-01-03 22:18:55 -05:00
|
|
|
vpnRemovedListener cb = void $ addInterfaceRemovedListener vpnBus vpnPath cb
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2023-01-02 10:33:04 -05:00
|
|
|
addedCallback :: SignalCallback VIO
|
|
|
|
addedCallback [device, added] =
|
|
|
|
beforeDisplay $
|
2023-09-29 23:44:08 -04:00
|
|
|
forM_ (fromVariant device) $ \d ->
|
|
|
|
forM_ (lookupVPNInterface =<< fromVariant added) $
|
|
|
|
insertState d
|
2023-01-02 10:33:04 -05:00
|
|
|
addedCallback _ = return ()
|
2021-12-14 00:37:09 -05:00
|
|
|
|
2023-01-02 10:33:04 -05:00
|
|
|
removedCallback :: SignalCallback VIO
|
2023-09-29 23:44:08 -04:00
|
|
|
removedCallback [device, _] =
|
|
|
|
beforeDisplay $ forM_ (fromVariant device) deleteState
|
2023-01-02 10:33:04 -05:00
|
|
|
removedCallback _ = return ()
|
2022-12-30 14:58:23 -05:00
|
|
|
|
2021-12-14 00:37:09 -05:00
|
|
|
--------------------------------------------------------------------------------
|
2022-12-30 14:58:23 -05:00
|
|
|
-- DBus Interface
|
2021-12-14 00:37:09 -05:00
|
|
|
|
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
|
|
|
|
2022-12-26 14:45:49 -05:00
|
|
|
vpnDeviceTun :: T.Text
|
2021-12-14 00:37:09 -05:00
|
|
|
vpnDeviceTun = "org.freedesktop.NetworkManager.Device.Tun"
|
2021-11-09 00:59:17 -05:00
|
|
|
|
2023-09-29 23:44:08 -04:00
|
|
|
vpnDeviceParent :: T.Text
|
|
|
|
vpnDeviceParent = "org.freedesktop.NetworkManager.Device"
|
|
|
|
|
2022-12-26 14:45:49 -05:00
|
|
|
vpnAlias :: T.Text
|
2021-06-21 23:41:57 -04:00
|
|
|
vpnAlias = "vpn"
|
2020-03-21 18:37:26 -04:00
|
|
|
|
2022-07-09 17:08:10 -04:00
|
|
|
vpnDep :: DBusDependency_ SysClient
|
2022-12-30 14:58:23 -05:00
|
|
|
vpnDep =
|
|
|
|
Endpoint networkManagerPkgs networkManagerBus vpnPath omInterface $
|
|
|
|
Method_ getManagedObjects
|