Skip to content
This repository has been archived by the owner on Mar 4, 2023. It is now read-only.

Commit

Permalink
Merge branch 'master' into multi_proto_server
Browse files Browse the repository at this point in the history
Merged the TCP port scanning from my master branch into the Main module.
Also merged the somewhat updated README.markdown file.

Conflicts:
	server/Main.hs
	server/scion-server.cabal
  • Loading branch information
ttencate committed Jun 23, 2009
2 parents 1aaa014 + ea218cc commit 3d2d972
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 24 deletions.
23 changes: 10 additions & 13 deletions README.markdown
Expand Up @@ -20,9 +20,9 @@ Installation


Scion requires [GHC 6.10.1][ghc] or later. All other dependencies Scion requires [GHC 6.10.1][ghc] or later. All other dependencies
should be on [Hackage][hackage] and can be installed using should be on [Hackage][hackage] and can be installed using
[cabal-install][ci]: [cabal-install][ci] in the lib directory:


$ cd dir/to/scion $ cd dir/to/scion/lib
$ cabal install $ cabal install


Scion supports various configuration flags which are useful when Scion supports various configuration flags which are useful when
Expand All @@ -41,31 +41,28 @@ Since Scion is a library, you should consult the haddock documentation
for how to use it. However, you may look at the Emacs frontend for for how to use it. However, you may look at the Emacs frontend for
inspiration. inspiration.


The Emacs frontend is implemented as a Haskell server The Emacs frontend is implemented as a Haskell server. The server is a
separate package, scion-server, which depends on the main scion package.


Emacs Emacs
----- -----


Install Scion with Emacs support, either via Install Scion with Emacs support:


$ cabal install scion -femacs $ cd dir/to/scion/server

$ cabal install
or, if you have a locally copy of Scion

$ cd <scion>
$ cabal install -femacs


You'll end up with a binary called "emacs-server". You'll end up with a binary called "scion_server".


$ ./.cabal/bin/emacs_server $ ~/.cabal/bin/scion_server


Add the following to your emacs configuration (typically "~/.emacs"): Add the following to your emacs configuration (typically "~/.emacs"):


(add-to-list 'load-path "<scion>/emacs") (add-to-list 'load-path "<scion>/emacs")
(require 'scion) (require 'scion)


;; if ./cabal/bin is not in your $PATH ;; if ./cabal/bin is not in your $PATH
(setq scion-program "~/.cabal/bin/emacs_server") (setq scion-program "~/.cabal/bin/scion_server")


(defun my-haskell-hook () (defun my-haskell-hook ()
;; Whenever we open a file in Haskell mode, also activate Scion ;; Whenever we open a file in Haskell mode, also activate Scion
Expand Down
1 change: 1 addition & 0 deletions lib/.gitignore
@@ -0,0 +1 @@
dist
1 change: 1 addition & 0 deletions server/.gitignore
@@ -0,0 +1 @@
dist
42 changes: 31 additions & 11 deletions server/Main.hs
Expand Up @@ -45,7 +45,7 @@ import Network.Socket.ByteString
import Data.List (isPrefixOf, break) import Data.List (isPrefixOf, break)
import Data.Foldable (foldrM) import Data.Foldable (foldrM)
import qualified Control.Exception as E import qualified Control.Exception as E
import Control.Monad ( when, forever ) import Control.Monad ( when, forever, liftM )
import System.Console.GetOpt import System.Console.GetOpt




Expand All @@ -58,7 +58,7 @@ logError = log HL.ERROR
-- if you're paranoid about your code Socketfile or StdInOut -- if you're paranoid about your code Socketfile or StdInOut
-- will be the most secure choice.. (Everyone can connect via TCP/IP at the -- will be the most secure choice.. (Everyone can connect via TCP/IP at the
-- moment) -- moment)
data ConnectionMode = TCPIP PortNumber data ConnectionMode = TCPIP Bool PortNumber -- the Bool indicates whether to scan
| StdInOut | StdInOut
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
| Socketfile FilePath | Socketfile FilePath
Expand All @@ -67,15 +67,19 @@ data ConnectionMode = TCPIP PortNumber


data StartupConfig = StartupConfig { data StartupConfig = StartupConfig {
connectionMode :: ConnectionMode, connectionMode :: ConnectionMode,
autoPort :: Bool,
showHelp :: Bool showHelp :: Bool
} deriving Show } deriving Show
defaultStartupConfig = StartupConfig ( TCPIP (fromInteger 4005)) False defaultStartupConfig = StartupConfig (TCPIP False (fromInteger 4005)) False False


-- options :: [OptDescr (Options -> Options)] -- options :: [OptDescr (Options -> Options)]
options = options =
[ Option ['p'] ["port"] [ Option ['p'] ["port"]
(ReqArg (\o opts -> return $ opts { connectionMode = (TCPIP . fromInteger) (read o) }) "8010") (ReqArg (\o opts -> return $ opts { connectionMode = (TCPIP False . fromInteger) (read o) }) "8010")
"listen on this TCP port" "listen on this TCP port"
, Option ['a'] ["autoport"]
(NoArg (\opts -> return $ opts { autoPort = True }))
"scan until a free TCP port is found"
, Option ['i'] ["stdinout"] , Option ['i'] ["stdinout"]
(NoArg (\opts -> return $ opts { connectionMode = StdInOut})) (NoArg (\opts -> return $ opts { connectionMode = StdInOut}))
"client must connect to stdin and stdout" "client must connect to stdin and stdout"
Expand Down Expand Up @@ -103,10 +107,24 @@ helpText = do
let header = unlines [ "usage of scion server (executable :" ++ pN ++ ")" ] let header = unlines [ "usage of scion server (executable :" ++ pN ++ ")" ]
return $ usageInfo header options return $ usageInfo header options


-- attempts to listen on each port in the list in turn, and returns the first successful
listenOnOneOf :: [PortID] -> IO Socket
listenOnOneOf (p:ps) = catch
(listenOn p)
(\(ex :: IOError) -> if null ps then E.throwIO ex else listenOnOneOf ps)

-- this way, we can iterate until we find a free port number
instance Bounded PortNumber where
minBound = 0
maxBound = 0xFFFF

serve :: ConnectionMode -> IO () serve :: ConnectionMode -> IO ()
serve (TCPIP nr) = do serve (TCPIP auto nr) = do
sock <- liftIO $ listenOn (PortNumber nr) sock <- liftIO $ if auto
putStrLn $ "=== Listening on port: " ++ show nr then listenOnOneOf (map PortNumber [nr..maxBound])
else listenOn (PortNumber nr)
realNr <- liftIO $ socketPort sock
putStrLn $ "=== Listening on port: " ++ show realNr
forever $ E.handle (\(e::E.IOException) -> logInfo ("caught :" ++ (show e) ++ "\n\nwaiting for next client")) $ do forever $ E.handle (\(e::E.IOException) -> logInfo ("caught :" ++ (show e) ++ "\n\nwaiting for next client")) $ do
(sock', _addr) <- liftIO $ accept sock (sock', _addr) <- liftIO $ accept sock
sock_conn <- CIO.mkSocketConnection sock' sock_conn <- CIO.mkSocketConnection sock'
Expand All @@ -132,6 +150,11 @@ handleClient :: (CIO.ConnectionIO con) => con -> IO ()
handleClient con = do handleClient con = do
runScion $ Gen.handle con 0 runScion $ Gen.handle con 0


fixConfig :: StartupConfig -> StartupConfig
fixConfig conf = case connectionMode conf of
TCPIP _ nr -> conf { connectionMode = TCPIP (autoPort conf) nr }
otherwise -> conf

main :: IO () main :: IO ()
main = do main = do


Expand All @@ -144,7 +167,7 @@ main = do
when ((not . null) nonOpts) $ when ((not . null) nonOpts) $
logError $ "no additional arguments expected, got: " ++ (show nonOpts) logError $ "no additional arguments expected, got: " ++ (show nonOpts)


startupConfig <- foldrM ($) defaultStartupConfig opts startupConfig <- return . fixConfig =<< foldrM ($) defaultStartupConfig opts


-- help -- help
when (showHelp startupConfig) $ helpText >>= putStrLn >> exitSuccess when (showHelp startupConfig) $ helpText >>= putStrLn >> exitSuccess
Expand All @@ -155,6 +178,3 @@ main = do
do do
log HL.DEBUG $ "opts: " ++ (show startupConfig) log HL.DEBUG $ "opts: " ++ (show startupConfig)
serve (connectionMode startupConfig) serve (connectionMode startupConfig)



0 comments on commit 3d2d972

Please sign in to comment.