using System; using System.Media; using System.Net; using System.Threading; namespace RobvanderWoude { class PingSite { static bool beep = false; static int Main( string[] args ) { try { // Default values bool varSpeed = false; bool verbose = true; bool changes = false; int interval = 0; int fastInt = 0; int slowInt = 0; int previous = 0; string expected = string.Empty; string url = string.Empty; #region Command Line Parsing // Custom error message string msgInvalid = "Invalid command line argument" + ( ( args.Length == 1 ) ? string.Empty : "(s)" ); // No command line arguments? Display help if ( args.Length == 0 ) { return WriteError( string.Empty ); } foreach ( string arg in args ) { // Check switches first switch ( arg.ToLower( ).Substring( 0, 2 ) ) { case "/?": case "-?": case "/h": case "-h": // Display help return WriteError( string.Empty ); case "--": if ( arg.ToLower( ) == "--help" ) { // Display help return WriteError( string.Empty ); } else { // Display error message return WriteError( msgInvalid ); } case "/b": beep = true; break; case "/c": verbose = false; changes = true; break; case "/i": if ( arg.ToLower( ).StartsWith( "/i:" ) || arg.ToLower( ).StartsWith( "/interval:" ) ) { slowInt = System.Convert.ToInt32( arg.Substring( arg.IndexOf( ":" ) + 1 ) ); if ( slowInt < 1 ) { return WriteError( "Interval must be 1 or greater" ); } } else { return WriteError( msgInvalid ); } break; case "/q": verbose = false; changes = false; break; case "/s": { if ( arg.IndexOf( ":" ) > 0 ) { expected = arg.Substring( arg.IndexOf( ":" ) + 1 ).ToUpper( ); } else { expected = "OK"; } if ( expected == "OK" ) { expected = "200,301,302,303,304,305,306,307"; } } break; case "/v": varSpeed = true; break; default: url = arg; break; } } if ( string.IsNullOrEmpty( url ) ) { return WriteError( "An URL must be specified" ); } if ( slowInt < 1 && changes ) { return WriteError( "To display status changes only, an interval must be specified" ); } if ( slowInt > 0 && !( verbose || changes ) ) { return WriteError( "/Quiet switch not allowed if an interval is specified" ); } if ( varSpeed && ( string.IsNullOrEmpty( expected ) || slowInt < 1 ) ) { return WriteError( "/Variable requires both /Status and /Interval" ); } interval = slowInt; if ( slowInt >= 25 ) { fastInt = slowInt / 5; } else if ( slowInt < 5 ) { fastInt = slowInt; } else { fastInt = 5; } #endregion Command Line Parsing // Loop will be aborted if interval is less than 1 while ( true ) { HttpStatusCode status = GetResponse( url ); string statusText = status.ToString( ); int statusCode = (int) status; // Display result if ( ( ( previous != statusCode ) && changes ) || verbose ) { ConsoleColor statusColor; if ( string.IsNullOrEmpty( expected ) || expected.Contains( statusCode.ToString( ) ) ) { statusColor = ConsoleColor.White; interval = slowInt; } else { statusColor = ConsoleColor.Red; if ( varSpeed ) { interval = fastInt; } } Console.Write( "[{0}]\t", DateTime.Now ); Console.ForegroundColor = statusColor; Console.Write( "{0}\t{1}", statusCode, statusText ); Console.ResetColor( ); Console.WriteLine( "\t{0}", url ); Console.Title = "PingSite: " + statusCode.ToString( ); // Beep on changes, if requested if ( beep ) { bool beepNow = false; if ( changes ) { beepNow = true; } if ( string.IsNullOrEmpty( expected ) == false ) { if ( expected.Contains( statusCode.ToString( ) ) == false ) { beepNow = true; } } if ( beepNow ) { Console.Beep( ); } } } // Save the last status code to compare it in the next iteration of the loop previous = statusCode; // Break the loop if interval is less than 1 if ( interval < 1 ) { if ( string.IsNullOrEmpty( expected ) ) { // Return the actual status code return statusCode; } else { // If we were monitoring for an expected code, return either 0 (match) or 2 (no match) return ( ( expected.Contains( statusCode.ToString( ) ) ) ? 0 : 2 ); } } // Wait for the specified number of seconds Thread.Sleep( interval * 1000 ); } } catch ( Exception e ) { return WriteError( e.Message ); } } public static HttpStatusCode GetResponse( string url ) { // Returns the HTTP Status Code for the specified URL, or 0 on errors try { HttpWebRequest req = (HttpWebRequest) WebRequest.Create( url ); req.AllowAutoRedirect = false; HttpWebResponse resp = (HttpWebResponse) req.GetResponse( ); HttpStatusCode stat = resp.StatusCode; req.Abort( ); return stat; } catch ( WebException e ) { // Handle exceptions caused by the server response, e.g. 404 try { HttpWebResponse httpResponse = (HttpWebResponse) e.Response; return httpResponse.StatusCode; } // Handle the "real" exceptions catch { WriteError( e.Message, false ); return 0; } } catch ( Exception e ) { WriteError( e.Message ); return 0; } } #region Error Handling public static int WriteError( Exception e ) { return WriteError( e == null ? null : e.Message ); } public static int WriteError( string errorMessage, bool showHelp = true ) { /* PingSite, Version 1.21 Check if a website is up and running Usage: PINGSITE url [ /Status[:expected] ] [ /Interval:seconds ] [ /Variable ] [ /Beep ] [ /Changes | /Quiet ] Where: url is the URL of the site or page to check (including http://) /Beep beep on changes or if response does not match expected /Changes display status changes only (requires /Interval) /Interval defines the optional interval in seconds between checks (default if not specified: terminate after 1 check) /Quiet no screen output, 'errorlevel' only /Status is either the expected numeric HTTP response code, or 'OK' for 200, 301..307 (default) /Variable shorter interval on mismatch (requires /Interval & /Status) Notes: If /Status was used, 'errorlevel' 0 is returned if status matches expected, 2 if not; otherwise the actual status code is returned as 'errorlevel'; in case of (command line) errors 1 is returned. Switches /Interval and /Quiet are mutually exclusive. Switches may be abbreviated, e.g. /I instead of /Interval. Written by Rob van der Woude http://www.robvanderwoude.com */ if ( string.IsNullOrEmpty( errorMessage ) == false ) { Console.Error.WriteLine( ); Console.ForegroundColor = ConsoleColor.Red; Console.Error.Write( "ERROR: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( errorMessage ); Console.ResetColor( ); if ( beep ) { Console.Beep( ); } } if ( showHelp ) { Console.Error.WriteLine( ); Console.Error.WriteLine( "PingSite, Version 1.21" ); Console.Error.WriteLine( "Check if a website is up and running" ); Console.Error.WriteLine( ); Console.Error.Write( "Usage: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "PINGSITE url [ /Status[:expected] ] [ /Interval:seconds ]" ); Console.Error.WriteLine( " [ /Variable ] [ /Beep ] [ /Changes | /Quiet ]" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Where: url is the URL of the site or page to check (including http://)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /B" ); Console.ResetColor( ); Console.Error.WriteLine( "eep beep on changes or if response does not match expected" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /C" ); Console.ResetColor( ); Console.Error.Write( "hanges display status changes only (requires " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/I" ); Console.ResetColor( ); Console.Error.WriteLine( "nterval)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /I" ); Console.ResetColor( ); Console.Error.WriteLine( "nterval defines the optional interval in seconds between checks" ); Console.Error.WriteLine( " (default if not specified: terminate after 1 check)" ); Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /Q" ); Console.ResetColor( ); Console.Error.WriteLine( "uiet no screen output, 'errorlevel' only" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /S" ); Console.ResetColor( ); Console.Error.WriteLine( "tatus is either the expected numeric HTTP response code, or" ); Console.Error.WriteLine( " 'OK' for 200, 301..307 (default)" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " /V" ); Console.ResetColor( ); Console.Error.Write( "ariable shorter interval on mismatch (requires " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/I" ); Console.ResetColor( ); Console.Error.Write( "nterval & " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/S" ); Console.ResetColor( ); Console.Error.WriteLine( "tatus)" ); Console.Error.WriteLine( ); Console.Error.Write( "Notes: If " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/S" ); Console.ResetColor( ); Console.Error.WriteLine( "tatus was used, 'errorlevel' 0 is returned if status matches" ); Console.Error.WriteLine( " expected, 2 if not; otherwise the actual status code is returned as" ); Console.Error.WriteLine( " 'errorlevel'; in case of (command line) errors 1 is returned." ); Console.Error.Write( " Switches " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/I" ); Console.ResetColor( ); Console.Error.Write( "nterval and " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/Q" ); Console.ResetColor( ); Console.Error.WriteLine( "uiet are mutually exclusive." ); Console.Error.Write( " Switches may be abbreviated, e.g. " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/I" ); Console.ResetColor( ); Console.Error.Write( " instead of " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "/I" ); Console.ResetColor( ); Console.Error.WriteLine( "nterval." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Written by Rob van der Woude" ); Console.Error.Write( "http://www.robvanderwoude.com" ); } return 1; } #endregion Error Handling } }