htsn 0.0.4 → 0.0.5
raw patch · 8 files changed
+82/−63 lines, 8 files
Files
- doc/man1/htsn.1 +2/−2
- htsn.cabal +5/−13
- src/Configuration.hs +3/−3
- src/ExitCodes.hs +6/−1
- src/FeedHosts.hs +6/−6
- src/Main.hs +39/−26
- src/Unix.hs +2/−2
- src/Xml.hs +19/−10
doc/man1/htsn.1 view
@@ -72,7 +72,7 @@ .IP \fB\-\-log-file\fR If you specify a file here, logs will be written to it (possibly in addition to syslog). Can be either a relative or absolute path. It-will not be auto-rotated; use something log logrotate for that.+will not be auto-rotated; use something like logrotate for that. Default: none @@ -150,7 +150,7 @@ \(dq.htsnrc\(dq in the user's home directory. The latter will override the former. .P-The user's home directory is simply $HOME on Unix; on Windows its+The user's home directory is simply $HOME on Unix; on Windows it's wherever %APPDATA% points. The system configuration directory is determined by Cabal; the \(dqsysconfdir\(dq parameter during the \(dqconfigure\(dq step is used.
htsn.cabal view
@@ -1,5 +1,5 @@ name: htsn-version: 0.0.4+version: 0.0.5 cabal-version: >= 1.8 author: Michael Orlitzky maintainer: Michael Orlitzky <michael@orlitzky.com>@@ -89,7 +89,7 @@ . If you specify a file here, logs will be written to it (possibly in addition to syslog). Can be either a relative or absolute path. It- will not be auto-rotated; use something log logrotate for that.+ will not be auto-rotated; use something like logrotate for that. . Default: none .@@ -191,7 +191,7 @@ \".htsnrc\" in the user's home directory. The latter will override the former. .- The user's home directory is simply $HOME on Unix; on Windows its+ The user's home directory is simply $HOME on Unix; on Windows it's wherever %APPDATA% points. The system configuration directory is determined by Cabal; the /sysconfdir/ parameter during the \"configure\" step is used.@@ -246,16 +246,12 @@ -fwarn-incomplete-record-updates -fwarn-monomorphism-restriction -fwarn-unused-do-bind- -rtsopts- -threaded- -optc-O3- -optc-march=native -O2 ghc-prof-options: -prof- -auto-all- -caf-all+ -fprof-auto+ -fprof-cafs @@ -291,10 +287,6 @@ -fwarn-incomplete-record-updates -fwarn-monomorphism-restriction -fwarn-unused-do-bind- -rtsopts- -threaded- -optc-O3- -optc-march=native -O2
src/Configuration.hs view
@@ -49,9 +49,9 @@ username = def } --- | Merge a Configuration with an OptionalConfiguration. This is more--- or less the Monoid instance for OptionalConfiguration, but since--- the two types are different, we have to repeat ourselves.+-- | Merge a 'Configuration' with an 'OptionalConfiguration'. This is+-- more or less the Monoid instance for 'OptionalConfiguration', but+-- since the two types are different, we have to repeat ourselves. merge_optional :: Configuration -> OC.OptionalConfiguration -> Configuration
src/ExitCodes.hs view
@@ -1,5 +1,6 @@ -- | All exit codes that the program can return (excepting--- ExitSuccess).+-- 'ExitSuccess').+-- module ExitCodes ( exit_no_feed_hosts, exit_no_password,@@ -8,19 +9,23 @@ where -- | No feed hosts were given on the command line or in the config file.+-- exit_no_feed_hosts :: Int exit_no_feed_hosts = 1 -- | No password was given on the command line or in the config file.+-- exit_no_password :: Int exit_no_password = 2 -- | No username was given on the command line or in the config file.+-- exit_no_username :: Int exit_no_username = 3 -- | When running as a daemon, the existence of a fixed PID file is -- used to determine whether or not the daemon is already -- running. If the PID file already exists, we shouldn't start.+-- exit_pidfile_exists :: Int exit_pidfile_exists = 4
src/FeedHosts.hs view
@@ -2,9 +2,9 @@ -- | A newtype around a list of Strings which represent the feed -- hosts. This is all to avoid an orphan instance of Configured for--- [String] if we had defined one in e.g. OptionalConfiguration.+-- [String] if we had defined one in e.g. 'OptionalConfiguration'. ----- This was placed under the "TSN" namespace because its Default+-- This was placed under the \"TSN\" namespace because its Default -- instance is specific to TSN, even though otherwise it's just a -- list of strings. --@@ -29,11 +29,11 @@ deriving (Data, Show, Typeable) +-- | The default list of feed hosts. These were found by checking+-- PTR records in the neighborhood of the IP address in use. There+-- is a feed4.sportsnetwork.com, but it was not operational when+-- this was written. instance Default FeedHosts where- -- | The default list of feed hosts. These were found by checking- -- PTR records in the neighborhood of the IP address in use. There- -- is a feed4.sportsnetwork.com, but it was not operational when- -- this was written. def = FeedHosts ["feed1.sportsnetwork.com", "feed2.sportsnetwork.com", "feed3.sportsnetwork.com"]
src/Main.hs view
@@ -4,6 +4,7 @@ module Main where +-- System imports. import Control.Concurrent ( threadDelay ) import Control.Exception ( bracket, throw ) import Control.Monad ( when )@@ -13,6 +14,13 @@ import Network ( connectTo, PortID (PortNumber) )+import Network.Services.TSN.Logging ( init_logging )+import Network.Services.TSN.Report (+ report_debug,+ report_info,+ report_warning,+ report_error )+import Network.Services.TSN.Terminal ( display_sent ) import System.Console.CmdArgs ( def ) import System.Directory ( doesFileExist ) import System.Exit ( ExitCode(..), exitWith )@@ -30,6 +38,7 @@ import System.IO.Error ( catchIOError ) import System.Timeout ( timeout ) +-- Local imports. import CommandLine ( get_args ) import Configuration ( Configuration(..), merge_optional ) import ExitCodes (@@ -38,22 +47,15 @@ exit_no_username, exit_pidfile_exists ) import FeedHosts ( FeedHosts(..) )-import Network.Services.TSN.Logging ( init_logging ) import qualified OptionalConfiguration as OC ( OptionalConfiguration(..), from_rc )-import Network.Services.TSN.Report (- report_debug,- report_info,- report_warning,- report_error )-import Network.Services.TSN.Terminal ( display_sent ) import Xml ( parse_xmlfid ) import Unix ( full_daemonize ) --- | Receive a single line of text from a Handle, and send it to the--- debug log.+-- | Receive a single line of text from a 'Handle', and record it for+-- debugging purposes. -- recv_line :: Handle -> IO String recv_line h = do@@ -62,14 +64,16 @@ return line --- | Takes a Configuration, and an XML document (as a String). The XML--- document is written to the output directory, as specified by the--- Configuration.+-- | Takes a 'Configuration', and an XML document (as a 'String'). The+-- XML document is written to the output directory, as specified by+-- the 'Configuration'. -- -- This can fail, but we don't purposefully throw any exceptions. If -- something goes wrong, we would rather log it and keep going. ---save_document :: Configuration -> String -> IO ()+save_document :: Configuration+ -> String -- ^ String representation of an XML document+ -> IO () save_document cfg doc = case either_path of Left err -> report_error err@@ -87,9 +91,9 @@ either_path = fmap ((output_directory cfg) </>) filename --- | Loop forever, writing the buffer to file whenever a </message>--- tag is seen. This is the low-level "loop forever" function that--- we stay in as long as we are connected to one feed.+-- | Loop forever, writing the @buffer@ to file whenever a+-- \</message\> tag is seen. This is the low-level \"loop forever\"+-- function that we stay in as long as we are connected to one feed. -- -- The documentation at -- <http://www.sportsnetworkdata.com/feeds/xml-levels.asp> states@@ -98,7 +102,13 @@ -- for a given document. We therefore rely on this to simplify -- processing. ---loop :: Configuration -> Handle -> [String] -> IO ()+-- The bang pattern at least on @buffer@ is necessary for+-- performance reasons.+--+loop :: Configuration+ -> Handle -- ^ Handle to the feed (network connection)+ -> [String] -- ^ Current XML document buffer, line-by-line, in reverse+ -> IO () loop !cfg !h !buffer = do line <- recv_line h let new_buffer = line : buffer@@ -124,9 +134,9 @@ -- (apparently) successfully guessed it. -- -- The first thing TSN sends once we've connected is the string--- "Username: ", containing 10 ASCII characters. We then send a+-- \"Username: \", containing 10 ASCII characters. We then send a -- username, followed by a newline. If TSN likes the username, the--- second they'll send is the string "Password: ", also containing+-- second they'll send is the string \"Password: \", also containing -- 10 ASCII characters, to which we reply in kind. -- -- Assuming the above will always hold, it is implemented as follows:@@ -139,8 +149,8 @@ -- -- 4. Send password if we got the password prompt ----- If TSN likes the password as well, they send the string "The--- Sports Network" before finally beginning to stream the feed.+-- If TSN likes the password as well, they send the string \"The+-- Sports Network\" before finally beginning to stream the feed. -- log_in :: Configuration -> Handle -> IO () log_in cfg h = do@@ -188,13 +198,15 @@ -- -- Steps: ----- 1. Connect to the host on the XML port+-- 1. Connect to @host@ on the XML feed port. ----- 2. Log in+-- 2. Log in. -- -- 3. Go into the eternal read/save loop. ---connect_and_parse :: Configuration -> String -> IO ()+connect_and_parse :: Configuration+ -> String -- ^ Hostname to connect to+ -> IO () connect_and_parse cfg host = do report_info $ "Connecting to " ++ host ++ "." bracket acquire_handle release_handle action@@ -234,7 +246,8 @@ -- | A wrapper around threadDelay which takes seconds instead of -- microseconds as its argument. ---thread_sleep :: Int -> IO ()+thread_sleep :: Int -- ^ Number of seconds for which to sleep.+ -> IO () thread_sleep seconds = do let microseconds = seconds * (10 ^ (6 :: Int)) threadDelay microseconds@@ -300,7 +313,7 @@ else run_program where- -- | This is the top-level "loop forever" function. If an+ -- | This is the top-level \"loop forever\" function. If an -- exception is thrown, it will propagate up to this point, where -- it will be logged and ignored in style. --
src/Unix.hs view
@@ -92,10 +92,10 @@ create_pid_directory pid_directory uid gid = do it_exists <- doesDirectoryExist pid_directory unless it_exists $ do- report_info $ "Creating PID directory " ++ pid_directory+ report_info $ "Creating PID directory " ++ pid_directory ++ "." createDirectory pid_directory report_info $ "Changing owner/group of " ++ pid_directory ++- " to " ++ (show uid) ++ "/" ++ (show gid)+ " to " ++ (show uid) ++ "/" ++ (show gid) ++ "." setOwnerAndGroup pid_directory uid gid -- | Write a PID file, install a SIGTERM handler, drop privileges, and
src/Xml.hs view
@@ -3,6 +3,7 @@ -- module Xml ( parse_xmlfid,+ -- * Tests xml_tests ) where @@ -20,10 +21,10 @@ xreadDoc ) --- | A tiny parser written in HXT to extract the "XML_File_ID" element--- from a document. If we fail to parse an XML_File_ID, we return--- the reason wrapped in a 'Left' constructor. The reason should be--- one of two things:+-- | A tiny parser written in HXT to extract the \"XML_File_ID\"+-- element from a document. If we fail to parse an XML_File_ID, we+-- return the reason wrapped in a 'Left' constructor. The reason+-- should be one of two things: -- -- 1. No XML_File_ID elements were found. --@@ -32,12 +33,12 @@ -- -- We use an Either rather than a Maybe because we do expect some -- non-integer XML_File_IDs. In the examples, you will see--- NHL_DepthChart_XML.XML with an XML_File_ID of "49618.61" and--- CFL_Boxscore_XML1.xml with an XML_File_ID of "R28916". According--- to Brijesh Patel of TSN, these are special category files and not--- part of the usual feed.+-- NHL_DepthChart_XML.XML with an XML_File_ID of \"49618.61\" and+-- CFL_Boxscore_XML1.xml with an XML_File_ID of+-- \"R28916\". According to Brijesh Patel of TSN, these are special+-- category files and not part of the usual feed. ----- We want to report them differently, "just in case."+-- We want to report them differently, \"just in case.\" -- parse_xmlfid :: String -- ^ The XML Document -> Either String Integer@@ -64,7 +65,12 @@ parse_results = map read_either_integer elements --- * Tasty Tests+--+-- Tasty Tests+--++-- | A list of all tests for this module.+-- xml_tests :: TestTree xml_tests = testGroup@@ -72,6 +78,9 @@ [ xml_file_id_tests ] +-- | Ensure that we parse the correct XML_File_ID out of some known+-- examples.+-- xml_file_id_tests :: TestTree xml_file_id_tests = testCase "XML_File_ID is parsed correctly" $ do