Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' into multi_proto_server

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...
commit 3d2d972f2715c01a161b4b05b1e9e6d5ab41e46d 2 parents 1aaa014 + ea218cc
Thomas ten Cate ttencate authored
23 README.markdown
View
@@ -20,9 +20,9 @@ Installation
Scion requires [GHC 6.10.1][ghc] or later. All other dependencies
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
Scion supports various configuration flags which are useful when
@@ -41,23 +41,20 @@ 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
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
-----
-Install Scion with Emacs support, either via
+Install Scion with Emacs support:
- $ cabal install scion -femacs
-
-or, if you have a locally copy of Scion
-
- $ cd <scion>
- $ cabal install -femacs
+ $ cd dir/to/scion/server
+ $ cabal install
-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"):
@@ -65,7 +62,7 @@ Add the following to your emacs configuration (typically "~/.emacs"):
(require 'scion)
;; 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 ()
;; Whenever we open a file in Haskell mode, also activate Scion
1  lib/.gitignore
View
@@ -0,0 +1 @@
+dist
1  server/.gitignore
View
@@ -0,0 +1 @@
+dist
42 server/Main.hs
View
@@ -45,7 +45,7 @@ import Network.Socket.ByteString
import Data.List (isPrefixOf, break)
import Data.Foldable (foldrM)
import qualified Control.Exception as E
-import Control.Monad ( when, forever )
+import Control.Monad ( when, forever, liftM )
import System.Console.GetOpt
@@ -58,7 +58,7 @@ logError = log HL.ERROR
-- if you're paranoid about your code Socketfile or StdInOut
-- will be the most secure choice.. (Everyone can connect via TCP/IP at the
-- moment)
-data ConnectionMode = TCPIP PortNumber
+data ConnectionMode = TCPIP Bool PortNumber -- the Bool indicates whether to scan
| StdInOut
#ifndef mingw32_HOST_OS
| Socketfile FilePath
@@ -67,15 +67,19 @@ data ConnectionMode = TCPIP PortNumber
data StartupConfig = StartupConfig {
connectionMode :: ConnectionMode,
+ autoPort :: Bool,
showHelp :: Bool
} deriving Show
-defaultStartupConfig = StartupConfig ( TCPIP (fromInteger 4005)) False
+defaultStartupConfig = StartupConfig (TCPIP False (fromInteger 4005)) False False
-- options :: [OptDescr (Options -> Options)]
options =
[ 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"
+ , Option ['a'] ["autoport"]
+ (NoArg (\opts -> return $ opts { autoPort = True }))
+ "scan until a free TCP port is found"
, Option ['i'] ["stdinout"]
(NoArg (\opts -> return $ opts { connectionMode = StdInOut}))
"client must connect to stdin and stdout"
@@ -103,10 +107,24 @@ helpText = do
let header = unlines [ "usage of scion server (executable :" ++ pN ++ ")" ]
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 (TCPIP nr) = do
- sock <- liftIO $ listenOn (PortNumber nr)
- putStrLn $ "=== Listening on port: " ++ show nr
+serve (TCPIP auto nr) = do
+ sock <- liftIO $ if auto
+ 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
(sock', _addr) <- liftIO $ accept sock
sock_conn <- CIO.mkSocketConnection sock'
@@ -132,6 +150,11 @@ handleClient :: (CIO.ConnectionIO con) => con -> IO ()
handleClient con = do
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 = do
@@ -144,7 +167,7 @@ main = do
when ((not . null) nonOpts) $
logError $ "no additional arguments expected, got: " ++ (show nonOpts)
- startupConfig <- foldrM ($) defaultStartupConfig opts
+ startupConfig <- return . fixConfig =<< foldrM ($) defaultStartupConfig opts
-- help
when (showHelp startupConfig) $ helpText >>= putStrLn >> exitSuccess
@@ -155,6 +178,3 @@ main = do
do
log HL.DEBUG $ "opts: " ++ (show startupConfig)
serve (connectionMode startupConfig)
-
-
-
Please sign in to comment.
Something went wrong with that request. Please try again.