title: A simple PHP uptime monitor for Windows Servers and Cisco Switches
body: <p>I've written (well cobbled together from other people's code) a very simple uptime monitor for our servers - it's just an ICMP (ping) monitor and it works very well for our limited amount of servers (20 or so), and very fast.
  Here's the code (the actual ping test functions I think are based on Birk Jensen's work ( <a href="http://birk-jensen.dk/2010/09/php-ping/" rel="nofollow">http://birk-jensen.dk/2010/09/php-ping/</a> ), and I've just utilised his functions to display a green circle PNG when everything is up and red ones for each server that's down (if any).</p>
  
  <pre><code>&lt;html&gt;
  &lt;head&gt;
  &lt;style type='text/css'&gt;
  
  *{
      font-family:verdana,tahoma,arial;
      font-size:17px;
  }
  
  .light{width:30px;}
  
  h1{
          font-size:25px;
  }
  &lt;/style&gt;
  
  
  
  &lt;meta http-equiv="refresh" content="30"&gt;
  &lt;/head&gt;
  &lt;body&gt;
  &lt;?php
  
  $time1=date('H:i:s');
  echo "Last Refresh Time = $time1&lt;br/&gt;&lt;hr/&gt;";
  
  error_reporting(0);
  
  /*-----------------------------------------------------------------------------------------*/    
      // Checksum calculation function
      function icmpChecksum($data)
      {
      if (strlen($data)%2)
      $data .= "\x00";
  
      $bit = unpack('n*', $data);
      $sum = array_sum($bit);
  
      while ($sum &gt;&gt; 16)
      $sum = ($sum &gt;&gt; 16) + ($sum &amp; 0xffff);
  
      return pack('n*', ~$sum);
      }
  
  
  /*-----------------------------------------------------------------------------------------*/
      function PingTry1($pingaddress){
      // Making the package
      $type= "\x08";
      $code= "\x00";
      $checksum= "\x00\x00";
      $identifier = "\x00\x00";
      $seqNumber = "\x00\x00";
      $data= "testing123";
      $package = $type.$code.$checksum.$identifier.$seqNumber.$data;
      $checksum = icmpChecksum($package); // Calculate the checksum
      $package = $type.$code.$checksum.$identifier.$seqNumber.$data;
      // And off to the sockets
      $socket = socket_create(AF_INET, SOCK_RAW, 1);
  
      socket_set_option ( $socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=&gt;1, "usec"=&gt;0) );
      socket_connect($socket, $pingaddress, null);
  
      $startTime = microtime(true);
      socket_send($socket, $package, strLen($package), 0);
      if (socket_read($socket, 255)) {
      return true;
      }
      else{
      return false;
      }
  
      socket_close($socket);
  
      }
  /*-----------------------------------------------------------------------------------------*/
     function DoTheCheck($name,$ip){
         global $errors;
         global $j;
         if (PingTry1($ip)==1){
      //do nothing
                        }else{
                        $j++;
                        $errors[$j] = "$name --&gt; $ip";
  
                        }
  
      }
  /*-----------------------------------------------------------------------------------------*/
  
  //READ IN THE INI FILE INTO $filedata Array
  
  $myFile1="hosts.ini";
  $filehandle1 = fopen($myFile1, 'r') or die("Couldn't open file [$myFile1]");
  $number1=count(file($myFile1));;
  $filedata = fread($filehandle1, filesize($myFile1));
  fclose($filehandle1);
  
  // Create an array with each line of the file
  $array1 = explode("\r\n", $filedata);
  
  unset($filedata); //free up a bit of memory
  
  foreach ($array1 as &amp;$line) { // step through the array, line by line
      if (!empty($line)){
  list ($name,$ip)=split(",",$line);
      DoTheCheck($name,$ip);
                   }
  }
  
  
      if ($errors){
  
              echo 'The Following Hosts are down - &lt;br/&gt;&lt;br/&gt;&lt;table&gt;';
  
  foreach ($errors as &amp;$value) {
      $k++;
      echo '&lt;tr&gt;&lt;td&gt;&lt;img class="light" src="red.png" /&gt;&lt;/td&gt;&lt;td&gt;'.$errors[$k].'&lt;/td&gt;&lt;/tr&gt;';
  }
  echo '&lt;/tr&gt;&lt;/table&gt;';
      }
  else{echo '&lt;img class="light" src="green.png" /&gt;&lt;h1&gt;ALL IPS ARE UP!&lt;/h1&gt;';}
  
  
  
  
      ?&gt;
      &lt;/body&gt;
      &lt;/html&gt;
  </code></pre>
  
  <p>The code above works great for servers but it doesn't seem to work at all for Cisco switches - probably something to do with the way it does its 'ping' as such.</p>
  
  <p>I haven't done any work on this script in ages because of college commitments etc but I've gone back to doing as much google research as I can, but admittedly I'm a level 2 or 3 PHP n00b at best.
  Today I found a couple of solutions that work for switches, but they have 5 or 6 second timeout periods, which is unacceptable as I want the system to loop as much as possible and as cleanly as possible, and log downtime for graphing later on.</p>
  
  <p>For Example -  I've tried this:</p>
  
  <pre><code>   function ping($host, $timeout = 1) {
                  /* ICMP ping packet with a pre-calculated checksum */
                  $package = "\x08\x00\x7d\x4b\x00\x00\x00\x00PingHost";
                  $socket  = socket_create(AF_INET, SOCK_RAW, 1);
                  socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' =&gt; $timeout, 'usec' =&gt; 0));
                  socket_connect($socket, $host, null);
  
                  $ts = microtime(true);
                  socket_send($socket, $package, strLen($package), 0);
                  if (socket_read($socket, 255))
                          $result = microtime(true) - $ts;
                  else    $result = false;
                  socket_close($socket);
  
                  return $result;
          }
  </code></pre>
  
  <p>and also this:</p>
  
  <pre><code>    $url         = '192.168.1.1'; 
      $socket       = ( bool )false; 
      $error      = ( bool )false; 
  
      $socket = @fsockopen( $url, 23, $errno, $errstr, 1 ) or $error = ( bool )true; 
  
      if ( ( $socket ) &amp;&amp; ( !$error ) ) 
      { 
          echo "bound";
          /* socket is bound - do something */ 
      } 
      else 
      { 
          echo "not bound , [$errstr]";
          /* socket is dead - errors are in $errno &amp; $errstr */ 
      }  
  
  if ($socket)fclose($socket);
  </code></pre>
  
  <p>And they both seem to work when the host is online, but if I give it an IP that doesn't exist (for testing, as if host was offline), it takes about 5 or more seconds to time out on a single IP, which is just too slow for my needs.</p>
  
  <p>Would it be possible to do this using pcntl_fork or even curl with multi-threading ? or multiple 'exec' calls or AJAX even (I'm willing to try anything at this stage)</p>
  
  <p>or some sort of Data Layer (layer 2) Mac scanning code would be great either - I don't expect anyone to write the full code, but I'm sure somebody that has done this sort of thing before would have a good idea of the pitfalls and how to get around them.</p>
  
  <p>So in summary - a simple and easy fix would be nice ( I'll Keep Dreaming :-D ) but any help or advice at all is much appreciated.</p>
  
  <p>EDIT - after some advice to try Net_Ping in PEAR I've got the following code:</p>
  
  <pre><code>&lt;?php
  
  $time1=date('H:i:s');
  echo "Last Refresh Time = $time1&lt;br/&gt;&lt;hr/&gt;";
  
  //not sure if still needed -       error_reporting(0);
  
  
  require_once "Net/Ping.php";
  $ping = Net_Ping::factory();
  
  $ping-&gt;setArgs(array('count' =&gt; 2, 'ttl' =&gt; 50, 'timeout' =&gt; 1));
  /*---------------------------------------------------------------------*/
  function DoPing($ip)
  {
      global $ping;
  
      $results = $ping-&gt;ping($ip);
      if ($results-&gt;_loss==0) {return true;}else{return false;}
  }
  /*---------------------------------------------------------------------------------*/
     function DoTheCheck($name,$ip){
         global $errors;
         global $j;
  
         if (DoPing($ip)==1){
      //do nothing
                        }else{
                        $j++;
                        $errors[$j] = "$name --&gt; $ip";
                        }
      }
  /*-----------------------------------------------------------------------------------*/
  
  //READ IN THE INI FILE INTO $filedata Array
  
  $myFile1="hosts.ini";
  $filehandle1 = fopen($myFile1, 'r') or die("Couldn't open file [$myFile1]");
  $number1=count(file($myFile1));;
  $filedata = fread($filehandle1, filesize($myFile1));
  fclose($filehandle1);
  
  // Create an array with each line of the file
  $array1 = explode("\r\n", $filedata);
  
  unset($filedata); //free up a bit of memory
  
  foreach ($array1 as &amp;$line) { // step through the array, line by line
      if (  (!empty($line)) &amp;&amp; (!strstr($line,'##'))  ) {
  list ($name,$ip)=split(",",$line);
      DoTheCheck($name,$ip);
                   }
  }
  
      if ($errors){
  
              echo 'The Following Hosts are down - &lt;br/&gt;&lt;br/&gt;&lt;table&gt;';
  
  foreach ($errors as &amp;$value) {
      $k++;
      echo '&lt;tr&gt;&lt;td&gt;&lt;img class="light" src="red.png" /&gt;&lt;/td&gt;&lt;td&gt;'.$errors[$k].'&lt;/td&gt;&lt;/tr&gt;';
  }
  echo '&lt;/tr&gt;&lt;/table&gt;';
      }
  else{echo '&lt;img class="light" src="green.png" /&gt;&lt;h1&gt;ALL IPS ARE UP!&lt;/h1&gt;';}
  
  ?&gt;
  </code></pre>
  
  <p>but that is too slow... takes about a minute or two to check about 20 servers and 10 switches. I need to add about 100 switches so it's only going to get slower. There must be a better way to do this. Again, any help is always very much appreciated. I will probably try Munin but realistically I need something I can integrate into my Company's Intranet (PHP).</p>