summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoe <joe@jerkface.net>2014-03-12 21:24:09 -0400
committerjoe <joe@jerkface.net>2014-03-12 21:24:09 -0400
commita778899096731cc087df795d8107bebf10efe59e (patch)
treee6c262264cb48e111819c1510e9763f5fab71ce1
parent7167550ca24975e06e028de7c797612fff82a16d (diff)
consolation demo program
-rw-r--r--Presence/LocalPeerCred.hs1
-rw-r--r--Presence/UTmp.hs70
-rw-r--r--WaitForSignal.hs10
-rw-r--r--consolation.hs51
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 #-}
2module UTmp 2module 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
10import qualified Data.ByteString as S 12import qualified Data.ByteString as S
@@ -22,6 +24,8 @@ import Data.Int
22import Control.Monad.Error.Class 24import Control.Monad.Error.Class
23import System.IO.Error 25import System.IO.Error
24import qualified Paths 26import qualified Paths
27import Data.Text ( Text )
28import qualified Data.Text.Encoding as Text
25 29
26 30
27utmp_file = Paths.utmp -- "/var/run/utmp" 31utmp_file = Paths.utmp -- "/var/run/utmp"
@@ -56,11 +60,12 @@ utmp_records bs = [bs]
56 60
57utmp = fmap (map decode_utmp_bytestring . utmp_records) utmp_bs 61utmp = fmap (map decode_utmp_bytestring . utmp_records) utmp_bs
58 62
63toStr = takeWhile (/='\0') . C.unpack
59 64
60interp_utmp_record (typ,pid,tty,inittab,user,hostv4,term,exit,session,time,hostv6) = 65interp_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
91type Tty = L.ByteString 96type Tty = L.ByteString
92 97
93users :: IO [(UserName, Tty, ProcessID)] 98users :: IO [(UserName, Tty, ProcessID)]
94users = do 99users = 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
110only3 (a,b,c,_) = (a,b,c)
111
112data UtmpRecord = UtmpRecord
113 { utmpUser :: Text
114 , utmpTty :: Text
115 , utmpPid :: CPid
116 , utmpHost :: Text
117 }
118 deriving ( Show, Read, Eq, Ord )
119
120toText bs = Text.decodeUtf8 $ C.takeWhile (/='\0') bs
121
122interp_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)]
136users2 = 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 */
149static 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 @@
1module WaitForSignal where
2
3import Control.Concurrent.STM
4import System.Posix.Signals
5
6waitForTermSignal = 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
2import Control.Monad
3import Control.Applicative
4import Control.Concurrent.STM
5import System.INotify ( initINotify, EventVariety(Modify), addWatch )
6import Data.Word ( Word8 )
7import Data.Text ( Text )
8import Data.Map ( Map )
9import Data.List ( foldl' )
10import qualified Data.Map as Map
11
12import WaitForSignal ( waitForTermSignal )
13import UTmp ( users2, utmp_file, UtmpRecord(..) )
14import FGConsole ( monitorTTY )
15
16data ConsoleState = ConsoleState
17 { csActiveTTY :: TVar Word8
18 , csUtmp :: TVar (Map Text UtmpRecord)
19 }
20
21newConsoleState = atomically $
22 ConsoleState <$> newTVar 0 <*> newTVar Map.empty
23
24onLogin 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
34onTTY cs vtnum = do
35 prev <- atomically $ swapTVar (csActiveTTY cs) vtnum
36 putStrLn $ "tty: " ++ show vtnum
37
38main = 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."