Skip to content

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
Original file line number Diff line number Diff line change
Expand Up @@ -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
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
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"):

(add-to-list 'load-path "<scion>/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
Expand Down
1 change: 1 addition & 0 deletions lib/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
1 change: 1 addition & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
42 changes: 31 additions & 11 deletions server/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand All @@ -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
Expand All @@ -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"
Expand Down Expand Up @@ -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'
Expand All @@ -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

Expand All @@ -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
Expand All @@ -155,6 +178,3 @@ main = do
do
log HL.DEBUG $ "opts: " ++ (show startupConfig)
serve (connectionMode startupConfig)



0 comments on commit 3d2d972

Please sign in to comment.