Main Page | Class List | File List | Class Members | File Members

main.cpp

Go to the documentation of this file.
00001 // main.cpp
00002 
00003 #if _MSC_VER == 1200
00004 #pragma warning(disable : 4786)
00005 #endif // MSC ver 6.0
00006 
00007 #include <winsock2.h>
00008 #include <iostream>
00009 #include <iomanip>
00010 
00011 #include "icmp.h"
00012 #include "PingTarget.h"
00013 #include "format_system_message.h"
00014 #include "format_system_time.h"
00015 #include "PerformanceCounter.h"
00016 #include "VersionInfo.h"
00017 #include "console.h"
00018 #include "ProgramOptions.h"
00019 #include "format_paragraph.h"
00020 
00021 const char* pszLicense =
00022   "\"Ping' em\" copyright Erik Frankenfeld - http://www.frankenfeld.dk/pingem\n"
00023   "\n"
00024   "This program and the source code is free software. "
00025   "You may use, modify and redistribute it at your own risk.";
00026 
00027 const char* pszHelpIntro = 
00028   "Usage: pingem [options] target(s)\n\n"
00029   "pingem pings each target, waits for responses and then pauses.\n"
00030   "Statistics are shown for each cycle of send and receive.\n\n"
00031   "Available options:";
00032 
00033 const char* pszShortHelpIntro = 
00034   "Usage: pingem [options] target(s)\n\n"
00035   "pingem -h shows available options and pingem -license displays the license.";
00036 
00037 const char* pszMainReturnValues =
00038   "Program exit codes\n\n"
00039   "  0 : No errors occurred.\n"
00040   "  1 : Error in options or parameters.\n"
00041   "  2 : Runtime error occurred in network initialization or usage.\n"
00042   "  3 : One or more target names could not be resolved.\n"
00043   "  4 : Error occurred while creating or writing to the log file.";
00044 
00045 #define MAIN_OK 0
00046 #define MAIN_OPTION_ERROR 1
00047 #define MAIN_NETWORK_ERROR 2
00048 #define MAIN_TARGET_UNRESOLVED 3
00049 #define MAIN_LOG_FILE_ERROR 4
00050 
00051 PingTargets pingtargets;
00052 ProgramOptions pingoptions;
00053 
00054 int main(int argc, char* argv[])
00055 {
00056   std::cout.imbue(std::locale(""));
00057   PerformanceCounter::Init();
00058 
00059   // Define options and parameters
00060   pingoptions.AddLong(
00061     'w', "timeout",
00062     1000, 0, LONG_MAX,
00063     false,
00064     "ms",
00065     "Time in milliseconds to wait while sending to and receiving from targets.\n"
00066     "Default value is 1000 ms."
00067   );
00068   pingoptions.AddLong(
00069     'p', "pause",
00070     1000, 0, LONG_MAX,
00071     false,
00072     "ms", 
00073     "Time in milliseconds to pause between sending to and receiving from all targets.\n"
00074     "Default value is 1000 ms."
00075   );
00076   pingoptions.AddLong(
00077     'n', "count",
00078     0, 0, LONG_MAX,
00079     false,
00080     "n",
00081     "Number of pings to send to each target. "
00082     "The default value 0 sets no limit to the number of pings."
00083   );
00084   pingoptions.AddString(
00085     'g', "targetsfile",
00086     0, false,
00087     "filename",
00088     "Reads targets from the specified file. One target name or ip per line. "
00089     "Targets in file are added to targets specified as parameters."
00090   );
00091   pingoptions.AddString(
00092     'o', "logfile",
00093     0, false,
00094     "filename",
00095     "Sends output of each ping to the specified file.\n"
00096     "First line is a header with names of targets.\n"
00097     "Remaining lines contains date and time formatted according to the current "
00098     "locale and the response time for each target.\n"
00099     "Columns are separated by the tab character."
00100   );
00101   pingoptions.AddBoolean(
00102     'd', "daily",
00103     0, false,
00104     "Creates a log file for every day using the filename specified for logfile as a pattern: "
00105     "filenameYYYYMMDD.ext\n"
00106     "YYYY is the 4 digit year, MM is the month and DD is the date."
00107   );
00108   pingoptions.AddBoolean(
00109     'u', "hourly",
00110     0, false,
00111     "Creates a log file for every hour using the specified filename for logfile as a pattern: "
00112     "filenameYYYYMMDD_HH.ext\n"
00113     "YYYY is the 4 digit year, MM is the month, DD is the date and HH is the hour 00-23."
00114   );
00115   pingoptions.AddBoolean(
00116     'q', "quiet",
00117     false, false,
00118     "Quiet operation. Only errors terminating the application and their messages are sent to console."
00119   );
00120   pingoptions.AddBoolean(
00121     'h', "help",
00122     false, false,
00123     "Show this option help text and exit."
00124   );
00125   pingoptions.AddBoolean(
00126     '?', 0,
00127     false, false,
00128     "Show this option help text and exit."
00129   );
00130   pingoptions.AddBoolean(
00131     '\0', "exitcodes",
00132     false, false,
00133     "Show available exit codes."
00134   );
00135   pingoptions.AddBoolean(
00136     '\0', "license",
00137     false, false,
00138     "Show license information."
00139   );
00140 
00141   if (!pingoptions.Parse(argc, argv, std::cout.getloc()))
00142   {
00143     std::cerr << std::endl << GetProductInfo() << std::endl << std::endl;
00144     std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00145     return MAIN_OPTION_ERROR;
00146   }
00147   if (pingoptions.GetBoolValue("help") || pingoptions.GetBoolValue('?'))
00148   {
00149     std::cerr << GetProductInfo() << std::endl << std::endl;
00150     std::cerr << format_paragraph(pszHelpIntro, 0, GetStdOutWidth()-2);
00151     pingoptions.PrintHelp(std::cerr, GetStdOutWidth()-2);
00152     return MAIN_OK;
00153   }
00154   if (pingoptions.GetBoolValue("exitcodes"))
00155   {
00156     std::cerr << GetProductInfo() << std::endl << std::endl;
00157     std::cerr << format_paragraph(pszMainReturnValues, 0, GetStdOutWidth()-2) << std::endl;
00158     return MAIN_OK;
00159   }
00160   if (pingoptions.GetBoolValue("license"))
00161   {
00162     std::cerr << format_paragraph(pszLicense, 0, GetStdOutWidth()-2) << std::endl;
00163     return MAIN_OK;
00164   }
00165 
00166   WSADATA wsa;
00167   if (WSAStartup(MAKEWORD(2,0), &wsa) != 0)
00168   {
00169     std::cerr << "Unable to initialize Winsock2 version 2.0" << std::endl;
00170     std::cerr << format_system_message(WSAGetLastError()) << std::endl;
00171     WSACleanup();
00172     return MAIN_NETWORK_ERROR;
00173   }
00174 
00175   bool bAllTargetsResolved = true;
00176   std::string sTargetsFilename = pingoptions.GetStringValue("targetsfile");
00177   if (!sTargetsFilename.empty())
00178   {
00179     bAllTargetsResolved = pingtargets.AddTargets(sTargetsFilename.c_str());
00180   }
00181   for (ProgramOptions::const_parameter_iterator iParam = pingoptions.parameter_begin();
00182     iParam != pingoptions.parameter_end() && bAllTargetsResolved; ++iParam)
00183   {
00184     std::string s = *iParam;
00185     if (!pingtargets.AddTarget(s.c_str()))
00186       bAllTargetsResolved = false;
00187   }
00188   if (!bAllTargetsResolved)
00189   {
00190     std::cerr << std::endl << GetProductInfo() << std::endl << std::endl;
00191     std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00192     return MAIN_TARGET_UNRESOLVED;
00193   }
00194   if (pingtargets.size() == 0)
00195   {
00196     std::cerr << "No available targets." << std::endl << std::endl;
00197     std::cerr << GetProductInfo() << std::endl << std::endl;
00198     std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00199     return MAIN_OPTION_ERROR;
00200   }
00201 
00202   long lTimeout = pingoptions.GetLongValue("timeout");
00203   long lPause = pingoptions.GetLongValue("pause");
00204   long lPingCount = pingoptions.GetLongValue("count");
00205   std::string sLogFilename = pingoptions.GetStringValue("logfile");
00206   bool bDaily = pingoptions.GetBoolValue("daily");
00207   bool bHourly = pingoptions.GetBoolValue("hourly");
00208   bool bQuiet = pingoptions.GetBoolValue("quiet");
00209   
00210   PingTargets::LOG_FILENAME_TYPE LogFilenameType = PingTargets::LOG_FILENAME_PLAIN;
00211   if (bDaily && !bHourly)
00212     LogFilenameType = PingTargets::LOG_FILENAME_DAILY;
00213   else if (bHourly && !bDaily)
00214     LogFilenameType = PingTargets::LOG_FILENAME_HOURLY;
00215   else if (!bHourly && !bDaily)
00216     LogFilenameType = PingTargets::LOG_FILENAME_PLAIN;
00217   else
00218   {
00219     std::cerr << "Options daily and hourly cannot both be specified." << std::endl << std::endl;
00220     std::cerr << GetProductInfo() << std::endl << std::endl;
00221     std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00222     return MAIN_OPTION_ERROR;
00223   }
00224   if (sLogFilename.empty() && (bDaily || bHourly))
00225   {
00226     std::cerr << "Options daily and hourly requires logfile to be specified." << std::endl << std::endl;
00227     std::cerr << GetProductInfo() << std::endl << std::endl;
00228     std::cerr << format_paragraph(pszShortHelpIntro, 0, GetStdOutWidth()-2);
00229     return MAIN_OPTION_ERROR;
00230   }
00231   
00232 
00233   SOCKET sockRaw = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
00234   if (sockRaw == INVALID_SOCKET)
00235   {
00236     std::cerr << "Unable to create raw socket" << std::endl;
00237     std::cerr << format_system_message(WSAGetLastError()) << std::endl;
00238     WSACleanup();
00239     return MAIN_NETWORK_ERROR;
00240   }
00241 
00242   int iDataSize = 32;
00243   int iPacketSize = sizeof(ICMP_HEADER) + iDataSize;
00244   char* pPacket = new char[iPacketSize];
00245   int iRecvSize = iPacketSize + sizeof(IP_HEADER);
00246   char *pRecvBuf = new char[iRecvSize];
00247 
00248   long lSequence = 1;
00249   long lPings = 0;
00250   SYSTEMTIME systemtimeStarted;
00251   GetLocalTime(&systemtimeStarted);
00252   PerformanceCounter tickStarted;
00253 
00254   COORD coordStart;
00255   coordStart.X = 0;
00256   coordStart.Y = 0;
00257   if (!bQuiet)
00258   {
00259     // Erase screen window
00260     ClearStdOutBuffer();
00261   }
00262 
00263   do
00264   {
00265     PingTargets::const_iterator itTarget = pingtargets.begin();
00266     pingtargets.ClearResponseFlags();
00267 
00268     PerformanceCounter endtick;
00269     endtick += PerformanceCounter(0, 0, 0, 0, lTimeout, 0);
00270 
00271     SYSTEMTIME systemtimeStartPing;
00272     GetLocalTime(&systemtimeStartPing);
00273     do
00274     {
00275       if (!bQuiet)
00276       {
00277         ClearStdOutLine();
00278         std::cout << "Sending and receiving...\r";
00279       }
00280       // Time to wait for replies
00281       timeval tmv;
00282       tmv.tv_sec = 0;
00283       tmv.tv_usec = 0;
00284       // Send to all targets one by loop
00285       if (itTarget != pingtargets.end())
00286       {
00287         const PingTarget& target = **itTarget;
00288         EncodePacket(pPacket, iDataSize, target.MakeSequence(lSequence));
00289         sockaddr_in sockaddrSend;
00290         memset(&sockaddrSend, 0, sizeof(sockaddrSend));
00291         sockaddrSend.sin_addr = target.GetInetAddr();
00292         sockaddrSend.sin_family = AF_INET;
00293         int iSent = sendto(sockRaw, pPacket, iPacketSize, 0, (sockaddr*)&sockaddrSend, sizeof(sockaddrSend));
00294         if (iSent == SOCKET_ERROR)
00295         {
00296           // Ignore network error here.
00297           // Logging or debug option showing them could also be an option.
00298           int err = WSAGetLastError();
00299         }
00300         itTarget++;
00301       }
00302       else
00303       {
00304         // Nothing to send. Calculate remaining time to wait for responses
00305         PerformanceCounter tick;
00306         PerformanceCounter diff = endtick - tick;
00307         diff.GetAsSecondsAndMicroseconds(tmv.tv_sec, tmv.tv_usec);
00308       }
00309       // Anything to receive?
00310       fd_set fds;
00311       FD_ZERO(&fds);
00312       #pragma warning(push)
00313       #pragma warning(disable : 4127)
00314       FD_SET(sockRaw, &fds);
00315       #pragma warning(pop)
00316       int iStatus = select(0, &fds, NULL, NULL, &tmv);
00317       if (iStatus == SOCKET_ERROR)
00318       {
00319         // Ignore network error here.
00320         // Logging or debug option showing them could also be an option.
00321         int err = WSAGetLastError();
00322       }
00323       if (iStatus > 0)
00324       {
00325         // Socket is ready
00326         sockaddr_in sockaddrRecv;
00327         int iRecvLen = sizeof(sockaddrRecv);
00328         memset(&sockaddrRecv, 0, sizeof(sockaddrRecv));
00329         int iRead = recvfrom(sockRaw, pRecvBuf, iRecvSize, 0, (sockaddr*)&sockaddrRecv, &iRecvLen);
00330         if (iRead == SOCKET_ERROR)
00331         {
00332           int err = WSAGetLastError();
00333           if (err == WSAEMSGSIZE) // buffer was to small
00334             iRead = iRecvSize;
00335         }
00336         USHORT uRecvSeq;
00337         LONGLONG llRecvTick;
00338         PerformanceCounter tick;
00339         if (DecodePacket(pRecvBuf, iRead, uRecvSeq, llRecvTick))
00340         {
00341           long lRecvId = pingtargets.GetTargetId(uRecvSeq);
00342           long lRecvSeq = pingtargets.GetSequence(uRecvSeq);
00343           if (lRecvSeq == lSequence)
00344           {
00345             // Received the expected sequence
00346             PerformanceCounter diff = tick - PerformanceCounter(llRecvTick);
00347             pingtargets.AddResponse(lRecvId, diff.GetAsMilliseconds());
00348           }
00349         }
00350       }
00351     } while (PerformanceCounter() <= endtick);
00352     pingtargets.AddUnreachableResponses();
00353     lPings++;
00354     if (!bQuiet)
00355     {
00356       ClearStdOutLine();
00357       SetStdOutCoord(coordStart);
00358       std::cout << format_system_time(format_system_time::show_seconds) << " : ";
00359       std::cout << lPings << " pings sent to " << (long)pingtargets.size() << " targets in ";
00360       std::cout << format_performance_counter(
00361         PerformanceCounter()- tickStarted,
00362         format_performance_counter::show_seconds
00363         )
00364         << std::endl;
00365       pingtargets.PrintStatisticsHeader(std::cout, GetStdOutWidth());
00366       std::cout << std::endl;
00367       pingtargets.PrintStatistics(std::cout, GetStdOutWidth());
00368       std::cout << std::endl;
00369     }
00370     if (!sLogFilename.empty())
00371     {
00372       // Print header if first ping
00373       if (!pingtargets.PrintLogCurrent(sLogFilename.c_str(),
00374         systemtimeStartPing, LogFilenameType))
00375         return MAIN_LOG_FILE_ERROR;
00376     }
00377     lSequence++;
00378     if (lSequence > pingtargets.GetMaxSequence())
00379       lSequence = 1;
00380     // Only wait when not finished pinging
00381     if (!lPingCount || lPings < lPingCount)
00382     {
00383       // Show status while waiting
00384       long lTimeWait = lPause;
00385       long lSec = 1;
00386       while (lTimeWait >= 1000)
00387       {
00388         if (!bQuiet)
00389           std::cout << "Waiting " << lSec << "/" << lPause/1000 << " seconds\r";
00390         Sleep(1000);
00391         lTimeWait -= 1000;
00392         lSec++;
00393       }
00394       if (!bQuiet)
00395       {
00396         ClearStdOutLine();
00397         std::cout << "Waiting less than a second\r";
00398       }
00399       Sleep(lTimeWait);
00400       if (!bQuiet)
00401         ClearStdOutLine();
00402     }
00403   } while (!lPingCount || lPings < lPingCount);
00404 
00405   closesocket(sockRaw);
00406   delete []pPacket;
00407   delete []pRecvBuf;
00408   return MAIN_OK;
00409 }

Generated on Sun Jun 26 13:43:46 2005 for pingem by  doxygen 1.4.3