diff options
author | joe <joe@jerkface.net> | 2014-03-12 21:24:09 -0400 |
---|---|---|
committer | joe <joe@jerkface.net> | 2014-03-12 21:24:09 -0400 |
commit | a778899096731cc087df795d8107bebf10efe59e (patch) | |
tree | e6c262264cb48e111819c1510e9763f5fab71ce1 | |
parent | 7167550ca24975e06e028de7c797612fff82a16d (diff) |
consolation demo program
-rw-r--r-- | Presence/LocalPeerCred.hs | 1 | ||||
-rw-r--r-- | Presence/UTmp.hs | 70 | ||||
-rw-r--r-- | WaitForSignal.hs | 10 | ||||
-rw-r--r-- | consolation.hs | 51 |
4 files changed, 126 insertions, 6 deletions
diff --git a/Presence/LocalPeerCred.hs b/Presence/LocalPeerCred.hs index e5832b47..6f7735dd 100644 --- a/Presence/LocalPeerCred.hs +++ b/Presence/LocalPeerCred.hs | |||
@@ -162,6 +162,7 @@ scanProc uid inode = do | |||
162 | tty <- readSymbolicLink $ "/proc/"++pid++"/fd/0" | 162 | tty <- readSymbolicLink $ "/proc/"++pid++"/fd/0" |
163 | return (Just (pid,tty)) | 163 | return (Just (pid,tty)) |
164 | else loop fds | 164 | else loop fds |
165 | -- requires root (or same user as for pid)... | ||
165 | fds <- getDirectoryContents ("/proc/"++pid++"/fd") `catchIO_` return [] | 166 | fds <- getDirectoryContents ("/proc/"++pid++"/fd") `catchIO_` return [] |
166 | mb <- loop fds | 167 | mb <- loop fds |
167 | maybe (searchPids pids) (return . Just) mb | 168 | maybe (searchPids pids) (return . Just) mb |
diff --git a/Presence/UTmp.hs b/Presence/UTmp.hs index a3adaeee..86a19751 100644 --- a/Presence/UTmp.hs +++ b/Presence/UTmp.hs | |||
@@ -1,10 +1,12 @@ | |||
1 | {-# LANGUAGE TemplateHaskell #-} | 1 | {-# LANGUAGE TemplateHaskell #-} |
2 | module UTmp | 2 | module UTmp |
3 | ( users | 3 | ( users |
4 | , users2 | ||
4 | , utmp_file | 5 | , utmp_file |
5 | , UserName | 6 | , UserName |
6 | , Tty | 7 | , Tty |
7 | , ProcessID | 8 | , ProcessID |
9 | , UtmpRecord(..) | ||
8 | ) where | 10 | ) where |
9 | 11 | ||
10 | import qualified Data.ByteString as S | 12 | import qualified Data.ByteString as S |
@@ -22,6 +24,8 @@ import Data.Int | |||
22 | import Control.Monad.Error.Class | 24 | import Control.Monad.Error.Class |
23 | import System.IO.Error | 25 | import System.IO.Error |
24 | import qualified Paths | 26 | import qualified Paths |
27 | import Data.Text ( Text ) | ||
28 | import qualified Data.Text.Encoding as Text | ||
25 | 29 | ||
26 | 30 | ||
27 | utmp_file = Paths.utmp -- "/var/run/utmp" | 31 | utmp_file = Paths.utmp -- "/var/run/utmp" |
@@ -56,11 +60,12 @@ utmp_records bs = [bs] | |||
56 | 60 | ||
57 | utmp = fmap (map decode_utmp_bytestring . utmp_records) utmp_bs | 61 | utmp = fmap (map decode_utmp_bytestring . utmp_records) utmp_bs |
58 | 62 | ||
63 | toStr = takeWhile (/='\0') . C.unpack | ||
59 | 64 | ||
60 | interp_utmp_record (typ,pid,tty,inittab,user,hostv4,term,exit,session,time,hostv6) = | 65 | interp_utmp_record (typ,pid,tty,inittab,user,hostv4,term,exit,session,time,hostv6) = |
61 | (toStr user, toStr tty, processId pid, (toEnum . fromIntegral) typ :: UT_Type) | 66 | ( (toEnum . fromIntegral) typ :: UT_Type |
67 | , toStr user, toStr tty, processId pid, toStr hostv4 ) | ||
62 | where | 68 | where |
63 | toStr = takeWhile (/='\0') . C.unpack | ||
64 | processId = CPid . coerce | 69 | processId = CPid . coerce |
65 | coerce :: Word32 -> Int32 | 70 | coerce :: Word32 -> Int32 |
66 | coerce = unsafeCoerce | 71 | coerce = unsafeCoerce |
@@ -91,12 +96,65 @@ type UserName = L.ByteString | |||
91 | type Tty = L.ByteString | 96 | type Tty = L.ByteString |
92 | 97 | ||
93 | users :: IO [(UserName, Tty, ProcessID)] | 98 | users :: IO [(UserName, Tty, ProcessID)] |
94 | users = do | 99 | users = fmap (map only3) $ do |
95 | us <- utmp | 100 | us <- utmp |
96 | let us' = map interp_utmp_record us | 101 | let us' = map interp_utmp_record us |
97 | us'' = mapMaybe user_proc us' | 102 | us'' = mapMaybe user_proc us' |
98 | user_proc (u,tty,pid,USER_PROCESS) = Just (L.pack u,L.pack tty,pid) | 103 | user_proc (USER_PROCESS, u,tty,pid, hostv4) |
99 | user_proc _ = Nothing | 104 | = Just (L.pack u,L.pack tty,pid,hostv4) |
100 | onThrd f (_,_,pid) = f pid | 105 | user_proc _ = Nothing |
106 | onThrd f (_,_,pid,_) = f pid | ||
101 | us3 <- filterM (onThrd processAlive) us'' | 107 | us3 <- filterM (onThrd processAlive) us'' |
102 | return us3 | 108 | return us3 |
109 | |||
110 | only3 (a,b,c,_) = (a,b,c) | ||
111 | |||
112 | data UtmpRecord = UtmpRecord | ||
113 | { utmpUser :: Text | ||
114 | , utmpTty :: Text | ||
115 | , utmpPid :: CPid | ||
116 | , utmpHost :: Text | ||
117 | } | ||
118 | deriving ( Show, Read, Eq, Ord ) | ||
119 | |||
120 | toText bs = Text.decodeUtf8 $ C.takeWhile (/='\0') bs | ||
121 | |||
122 | interp_utmp_record2 (typ,pid,tty,inittab,user,hostv4,term,exit,session,time,hostv6) = | ||
123 | ( (toEnum . fromIntegral) typ :: UT_Type | ||
124 | , UtmpRecord | ||
125 | { utmpUser = toText user | ||
126 | , utmpTty = toText tty | ||
127 | , utmpPid = processId pid | ||
128 | , utmpHost = toText hostv4 } | ||
129 | ) | ||
130 | where | ||
131 | processId = CPid . coerce | ||
132 | coerce :: Word32 -> Int32 | ||
133 | coerce = unsafeCoerce | ||
134 | |||
135 | -- users2 :: IO [(UserName, Tty, ProcessID)] | ||
136 | users2 = do | ||
137 | us <- utmp | ||
138 | let us' = map interp_utmp_record2 us | ||
139 | us'' = mapMaybe user_proc us' | ||
140 | user_proc (USER_PROCESS, rec) | ||
141 | = Just rec | ||
142 | user_proc _ = Nothing | ||
143 | us3 <- filterM (processAlive . utmpPid) us'' | ||
144 | return us3 | ||
145 | |||
146 | {- | ||
147 | - This is how the w command reports idle time: | ||
148 | /* stat the device file to get an idle time */ | ||
149 | static time_t idletime(const char *restrict const tty) | ||
150 | { | ||
151 | struct stat sbuf; | ||
152 | if (stat(tty, &sbuf) != 0) | ||
153 | return 0; | ||
154 | return time(NULL) - sbuf.st_atime; | ||
155 | } | ||
156 | - THis might be useful fo rimplementing | ||
157 | - xep-0012 Last Activity | ||
158 | - iq get {jabber:iq:last}query | ||
159 | - | ||
160 | -} | ||
diff --git a/WaitForSignal.hs b/WaitForSignal.hs new file mode 100644 index 00000000..6252eaa6 --- /dev/null +++ b/WaitForSignal.hs | |||
@@ -0,0 +1,10 @@ | |||
1 | module WaitForSignal where | ||
2 | |||
3 | import Control.Concurrent.STM | ||
4 | import System.Posix.Signals | ||
5 | |||
6 | waitForTermSignal = do | ||
7 | quitVar <- newEmptyTMVarIO | ||
8 | installHandler sigTERM (CatchOnce (atomically $ putTMVar quitVar True)) Nothing | ||
9 | installHandler sigINT (CatchOnce (atomically $ putTMVar quitVar True)) Nothing | ||
10 | atomically $ takeTMVar quitVar | ||
diff --git a/consolation.hs b/consolation.hs new file mode 100644 index 00000000..d3c0ef28 --- /dev/null +++ b/consolation.hs | |||
@@ -0,0 +1,51 @@ | |||
1 | |||
2 | import Control.Monad | ||
3 | import Control.Applicative | ||
4 | import Control.Concurrent.STM | ||
5 | import System.INotify ( initINotify, EventVariety(Modify), addWatch ) | ||
6 | import Data.Word ( Word8 ) | ||
7 | import Data.Text ( Text ) | ||
8 | import Data.Map ( Map ) | ||
9 | import Data.List ( foldl' ) | ||
10 | import qualified Data.Map as Map | ||
11 | |||
12 | import WaitForSignal ( waitForTermSignal ) | ||
13 | import UTmp ( users2, utmp_file, UtmpRecord(..) ) | ||
14 | import FGConsole ( monitorTTY ) | ||
15 | |||
16 | data ConsoleState = ConsoleState | ||
17 | { csActiveTTY :: TVar Word8 | ||
18 | , csUtmp :: TVar (Map Text UtmpRecord) | ||
19 | } | ||
20 | |||
21 | newConsoleState = atomically $ | ||
22 | ConsoleState <$> newTVar 0 <*> newTVar Map.empty | ||
23 | |||
24 | onLogin cs e = do | ||
25 | us <- UTmp.users2 | ||
26 | let m = foldl' (\m x -> Map.insert (utmpTty x) x m) Map.empty us | ||
27 | old <- atomically $ swapTVar (csUtmp cs) m | ||
28 | let newborn = m Map.\\ old | ||
29 | dead = old Map.\\ m | ||
30 | putStrLn $ "--------" | ||
31 | forM_ (Map.elems newborn) $ putStrLn . ("new: "++) . show | ||
32 | forM_ (Map.elems dead ) $ putStrLn . ("gone: "++) . show | ||
33 | |||
34 | onTTY cs vtnum = do | ||
35 | prev <- atomically $ swapTVar (csActiveTTY cs) vtnum | ||
36 | putStrLn $ "tty: " ++ show vtnum | ||
37 | |||
38 | main = do | ||
39 | cs <- newConsoleState | ||
40 | inotify <- initINotify | ||
41 | wd <- addWatch | ||
42 | inotify | ||
43 | [Modify] -- [CloseWrite,Open,Close,Access,Modify,Move] | ||
44 | utmp_file | ||
45 | (onLogin cs) | ||
46 | |||
47 | mtty <- monitorTTY (onTTY cs) | ||
48 | |||
49 | waitForTermSignal | ||
50 | |||
51 | putStrLn "goodbye." | ||