diff options
author | Joe Crayne <joe@jerkface.net> | 2018-09-08 08:15:21 -0400 |
---|---|---|
committer | Joe Crayne <joe@jerkface.net> | 2018-10-03 07:00:40 -0400 |
commit | 9c02c2816c826d8f40cfaa4409175c88cfc1ea12 (patch) | |
tree | 69d2a079d19b7affa9e2b5f41552ed2b35c68d7d /consolation.hs | |
parent | fbf9890a6bcd4e6212b5947f908bc34f233b279d (diff) |
Move example utilities into examples directory.
Diffstat (limited to 'consolation.hs')
-rw-r--r-- | consolation.hs | 186 |
1 files changed, 0 insertions, 186 deletions
diff --git a/consolation.hs b/consolation.hs deleted file mode 100644 index 0c576dfc..00000000 --- a/consolation.hs +++ /dev/null | |||
@@ -1,186 +0,0 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | ||
2 | module Main where | ||
3 | |||
4 | import Control.Monad | ||
5 | import Control.Applicative | ||
6 | import Control.Concurrent | ||
7 | import Control.Concurrent.STM | ||
8 | import Data.Monoid | ||
9 | import Data.Char | ||
10 | import System.INotify ( initINotify, EventVariety(Modify), addWatch ) | ||
11 | import Data.Word ( Word8 ) | ||
12 | import Data.Text ( Text ) | ||
13 | import Data.Map ( Map ) | ||
14 | import Data.List ( foldl' ) | ||
15 | import qualified Data.Map as Map | ||
16 | import qualified Data.Traversable as Traversable | ||
17 | import qualified Data.Text as Text | ||
18 | import qualified Data.Text.IO as Text | ||
19 | import qualified Network.BSD as BSD | ||
20 | |||
21 | import WaitForSignal ( waitForTermSignal ) | ||
22 | import UTmp ( users2, utmp_file, UtmpRecord(..), UT_Type(..) ) | ||
23 | import FGConsole ( monitorTTY ) | ||
24 | |||
25 | data ConsoleState = ConsoleState | ||
26 | { csActiveTTY :: TVar Word8 | ||
27 | , csUtmp :: TVar (Map Text (TVar (Maybe UtmpRecord))) | ||
28 | } | ||
29 | |||
30 | newConsoleState = atomically $ | ||
31 | ConsoleState <$> newTVar 0 <*> newTVar Map.empty | ||
32 | |||
33 | |||
34 | onLogin cs start = \e -> do | ||
35 | us <- UTmp.users2 | ||
36 | let (m,cruft) = | ||
37 | foldl' (\(m,cruft) x -> | ||
38 | case utmpType x of | ||
39 | USER_PROCESS | ||
40 | -> (Map.insert (utmpTty x) x m,cruft) | ||
41 | DEAD_PROCESS | utmpPid x /= 0 | ||
42 | -> (m,Map.insert (utmpTty x) x cruft) | ||
43 | _ -> (m,cruft)) | ||
44 | (Map.empty,Map.empty) | ||
45 | us | ||
46 | forM_ (Map.elems cruft) $ \c -> do | ||
47 | putStrLn $ "cruft " ++ show (utmpTty c, utmpPid c,utmpHost c, utmpRemoteAddr c) | ||
48 | newborn <- atomically $ do | ||
49 | old <- readTVar (csUtmp cs) -- swapTVar (csUtmp cs) m | ||
50 | newborn <- flip Traversable.mapM (m Map.\\ old) | ||
51 | $ newTVar . Just | ||
52 | updated <- let upd v u = writeTVar v $ Just u | ||
53 | in Traversable.sequence $ Map.intersectionWith upd old m | ||
54 | let dead = old Map.\\ m | ||
55 | Traversable.mapM (flip writeTVar Nothing) dead | ||
56 | writeTVar (csUtmp cs) $ (old `Map.union` newborn) Map.\\ dead | ||
57 | return newborn | ||
58 | let getActive = do | ||
59 | tty <- readTVar $ csActiveTTY cs | ||
60 | utmp <- readTVar $ csUtmp cs | ||
61 | flip (maybe $ return (tty,Nothing)) | ||
62 | (Map.lookup ("tty"<>tshow tty) utmp) | ||
63 | $ \tuvar -> do | ||
64 | tu <- readTVar tuvar | ||
65 | return (tty,tu) | ||
66 | |||
67 | forM_ (Map.elems newborn) $ | ||
68 | forkIO . start getActive | ||
69 | -- forM_ (Map.elems dead ) $ putStrLn . ("gone: "++) . show | ||
70 | |||
71 | onTTY outvar cs vtnum = do | ||
72 | logit outvar $ "switch: " <> tshow vtnum | ||
73 | atomically $ writeTVar (csActiveTTY cs) vtnum | ||
74 | |||
75 | retryWhen var pred = do | ||
76 | value <- var | ||
77 | if pred value then retry | ||
78 | else return value | ||
79 | |||
80 | tshow x = Text.pack . show $ x | ||
81 | |||
82 | resource :: UtmpRecord -> Text | ||
83 | resource u = | ||
84 | case utmpTty u of | ||
85 | s | Text.take 3 s == "tty" -> s | ||
86 | s | Text.take 4 s == "pts/" -> "pty" <> Text.drop 4 s <> ":" <> utmpHost u | ||
87 | s -> escapeR s <> ":" <> utmpHost u | ||
88 | where | ||
89 | escapeR s = s | ||
90 | |||
91 | textHostName = fmap Text.pack BSD.getHostName | ||
92 | |||
93 | ujid u = do | ||
94 | h <- textHostName | ||
95 | return $ utmpUser u <> "@" <> h <> "/" <> resource u | ||
96 | |||
97 | newCon :: (Text -> IO ()) -> STM (Word8,Maybe UtmpRecord) -> TVar (Maybe UtmpRecord) -> IO () | ||
98 | newCon log activeTTY utmp = do | ||
99 | ((tty,tu),u) <- atomically $ | ||
100 | liftM2 (,) activeTTY | ||
101 | (readTVar utmp) | ||
102 | flip (maybe $ return ()) u $ \u -> do | ||
103 | jid <- ujid u | ||
104 | log $ status (resource u) tty tu <> " " <> jid <> " pid=" <> tshow (utmpPid u) | ||
105 | <> (if istty (resource u) | ||
106 | then " host=" <> tshow (utmpHost u) | ||
107 | else "") | ||
108 | <> " session=" <> tshow (utmpSession u) | ||
109 | <> " addr=" <> tshow (utmpRemoteAddr u) | ||
110 | loop tty tu (Just u) | ||
111 | where | ||
112 | bstatus r ttynum mtu | ||
113 | = r == ttystr | ||
114 | || match mtu | ||
115 | where ttystr = "tty" <> tshow ttynum | ||
116 | searchstr mtu = maybe ttystr utmpHost $ do | ||
117 | tu <- mtu | ||
118 | guard (not $ Text.null $ utmpHost tu) | ||
119 | return tu | ||
120 | match mtu = searchstr mtu `Text.isInfixOf` Text.dropWhile (/=':') r | ||
121 | status r ttynum tu = | ||
122 | if bstatus r ttynum tu | ||
123 | then "Available" | ||
124 | else "Away " | ||
125 | |||
126 | istty r = fst3 == "tty" && Text.all isDigit rst | ||
127 | where | ||
128 | (fst3,rst) = Text.splitAt 3 r | ||
129 | |||
130 | loop tty tu u = do | ||
131 | what <- atomically $ foldr1 orElse | ||
132 | [ do (tty',tu') <- retryWhen activeTTY | ||
133 | (\ttyu -> bstatus r tty tu == uncurry (bstatus r) ttyu) | ||
134 | return $ ttyChanged tty' tu' | ||
135 | , do u' <- retryWhen (readTVar utmp) (==u) | ||
136 | return $ utmpChanged u' | ||
137 | ] | ||
138 | what | ||
139 | where | ||
140 | r = maybe "" resource u | ||
141 | |||
142 | ttyChanged tty' tu' = do | ||
143 | jid <- maybe (return "") ujid u | ||
144 | log $ status r tty' tu' <> " " <> jid | ||
145 | loop tty' tu' u | ||
146 | |||
147 | utmpChanged u' = maybe dead changed u' | ||
148 | where | ||
149 | changed u' = do | ||
150 | jid0 <- maybe (return "") ujid u | ||
151 | jid <- ujid u' | ||
152 | log $ "changed: " <> jid0 <> " --> " <> jid | ||
153 | loop tty tu (Just u') | ||
154 | dead = do | ||
155 | jid <- maybe (return "") ujid u | ||
156 | log $ "Offline " <> jid | ||
157 | |||
158 | logit outvar s = do | ||
159 | atomically $ takeTMVar outvar | ||
160 | Text.putStrLn s | ||
161 | atomically $ putTMVar outvar () | ||
162 | |||
163 | |||
164 | main = do | ||
165 | outvar <- atomically $ newTMVar () | ||
166 | |||
167 | cs <- newConsoleState | ||
168 | inotify <- initINotify | ||
169 | |||
170 | -- get active tty | ||
171 | mtty <- monitorTTY (onTTY outvar cs) | ||
172 | atomically $ retryWhen (readTVar $ csActiveTTY cs) (==0) | ||
173 | |||
174 | -- read utmp | ||
175 | onLogin cs (newCon $ logit outvar) Modify | ||
176 | |||
177 | -- monitor utmp | ||
178 | wd <- addWatch | ||
179 | inotify | ||
180 | [Modify] -- [CloseWrite,Open,Close,Access,Modify,Move] | ||
181 | utmp_file | ||
182 | (onLogin cs (newCon $ logit outvar)) | ||
183 | |||
184 | waitForTermSignal | ||
185 | |||
186 | putStrLn "goodbye." | ||