-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathCombinatorrent.hs
150 lines (129 loc) · 5.14 KB
/
Combinatorrent.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
module Main (main)
where
import Control.Concurrent
import Control.Concurrent.STM
import Control.Monad
import Control.Monad.State
import Data.Maybe
import Data.List
import System.Environment
import System.Random
import System.Console.GetOpt
import System.Directory (doesDirectoryExist)
import System.FilePath ()
import System.Log.Logger
import System.Log.Handler.Simple
import System.IO as SIO
import qualified Process.Console as Console
import qualified Process.PeerMgr as PeerMgr
import qualified Process.ChokeMgr as ChokeMgr (start)
import qualified Process.Listen as Listen
import qualified Process.DirWatcher as DirWatcher (start)
import qualified Process.Status as Status (start, StatusChannel, PStat)
import qualified Process.TorrentManager as TorrentManager (start, TorrentMgrChan, TorrentManagerMsg(..))
import Supervisor
import Torrent
import Version
import qualified Test
main :: IO ()
main = do args <- getArgs
if "--tests" `elem` args
then Test.runTests
else progOpts args >>= run
-- COMMAND LINE PARSING
data Flag = Version | Debug | LogFile FilePath | WatchDir FilePath | StatFile FilePath
deriving (Eq, Show)
options :: [OptDescr Flag]
options =
[ Option ['V','?'] ["version"] (NoArg Version) "Show version number"
, Option ['D'] ["debug"] (NoArg Debug) "Spew extra debug information"
, Option [] ["logfile"] (ReqArg LogFile "FILE") "Choose a filepath on which to log"
, Option ['W'] ["watchdir"] (ReqArg WatchDir "DIR") "Choose a directory to watch for torrents"
, Option ['S'] ["statfile"] (ReqArg StatFile "FILE") "Choose a file to gather stats into"
]
(~=) :: Flag -> Flag -> Bool
Version ~= Version = True
Debug ~= Debug = True
LogFile _ ~= LogFile _ = True
WatchDir _ ~= WatchDir _ = True
StatFile _ ~= StatFile _ = True
_ ~= _ = False
flag :: Flag -> [Flag] -> Maybe Flag
flag x = find (x ~=)
progOpts :: [String] -> IO ([Flag], [String])
progOpts args = do
case getOpt Permute options args of
(o,n,[] ) -> return (o, n)
(_,_,errs) -> ioError (userError (concat errs ++ usageInfo header options))
where header = "Usage: Combinatorrent [OPTION...] file"
run :: ([Flag], [String]) -> IO ()
run (flags, files) = do
if Version `elem` flags
then progHeader
else case files of
[] | isNothing $ flag (WatchDir "") flags -> putStrLn "No torrentfile input"
names -> progHeader >> download flags names
progHeader :: IO ()
progHeader = putStrLn $ "This is Combinatorrent \x2620 version " ++ version ++ "\n" ++
" For help type 'help'\n"
setupLogging :: [Flag] -> IO ()
setupLogging flags = do
fLog <- case flag (LogFile "") flags of
Nothing -> streamHandler SIO.stdout DEBUG
Just (LogFile fp) -> fileHandler fp DEBUG
Just _ -> error "Impossible match"
when (Debug `elem` flags)
(updateGlobalLogger rootLoggerName
(setHandlers [fLog] . (setLevel DEBUG)))
setupDirWatching :: [Flag] -> TorrentManager.TorrentMgrChan -> IO [Child]
setupDirWatching flags watchC = do
case flag (WatchDir "") flags of
Nothing -> return []
Just (WatchDir dir) -> do
ex <- doesDirectoryExist dir
if ex
then do return [ Worker $ DirWatcher.start dir watchC ]
else do putStrLn $ "Directory does not exist, not watching"
return []
Just _ -> error "Impossible match"
setupStatus :: [Flag] -> Status.StatusChannel -> TVar [Status.PStat] -> Child
setupStatus flags statusC stv =
case flag (StatFile "") flags of
Nothing -> Worker $ Status.start Nothing statusC stv
Just (StatFile fn) -> Worker $ Status.start (Just fn) statusC stv
Just _ -> error "Impossible match"
generatePeerId :: IO PeerId
generatePeerId = do
gen <- getStdGen
return $ mkPeerId gen
download :: [Flag] -> [String] -> IO ()
download flags names = do
setupLogging flags
watchC <- liftIO newTChanIO
workersWatch <- setupDirWatching flags watchC
-- setup channels
statusC <- liftIO $ newTChanIO
waitC <- liftIO $ newEmptyTMVarIO
supC <- liftIO newTChanIO
pmC <- liftIO $ newTChanIO
chokeC <- liftIO $ newTChanIO
rtv <- atomically $ newTVar []
stv <- atomically $ newTVar []
debugM "Main" "Created channels"
pid <- generatePeerId
(tid, _) <- allForOne "MainSup"
(workersWatch ++
[ Worker $ Console.start waitC statusC
, Worker $ TorrentManager.start watchC statusC stv chokeC pid pmC
, setupStatus flags statusC stv
, Worker $ PeerMgr.start pmC pid chokeC rtv
, Worker $ ChokeMgr.start chokeC rtv 100 -- 100 is upload rate in KB
, Worker $ Listen.start defaultPort pmC
]) supC
atomically $ writeTChan watchC (map TorrentManager.AddedTorrent names)
_ <- atomically $ takeTMVar waitC
infoM "Main" "Closing down, giving processes 10 seconds to cool off"
atomically $ writeTChan supC (PleaseDie tid)
threadDelay $ 10*1000000
infoM "Main" "Done..."
return ()