Logo Search packages:      
Sourcecode: condor version File versions  Download package

time_offset.cpp

/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

  
#include "condor_common.h"
#include "condor_debug.h"
#include "condor_io.h"
#include "time_offset.h"
#include <math.h>

// --------------------------------------------
// CEDAR STUBS
// --------------------------------------------

/**
 * Given a Stream, this stub will send & receive the TimeOffsetPackets
 * over the Stream and populate the min/max ranges passed in by reference
 * If we are unable to establish the offset, either by invalid information
 * or network failure, we will return false
 * 
 * @param s - the Stream connection to the other daemon
 * @param offset - the theoritical offset between ourselves and the other daemon
 * @return true if the offset was succesfully calculated between 2 daemons
 **/
bool
time_offset_cedar_stub( Stream* s, long &offset ) {
      bool ret = false;
            //
            // The init method should put in our local timestamp
            // for when the packet is leaving us on the local side
            //
      TimeOffsetPacket packet = time_offset_initPacket();
      TimeOffsetPacket rPacket;
      
            //
            // Use the base Cedar sending function to populate our
            // packet information
            //
      if ( time_offset_send_cedar_stub( s, packet, rPacket ) ) {
            ret = time_offset_calculate( packet, rPacket, offset );
      }
      return ( ret );
}

/**
 * Given a Stream, this stub will send & receive the TimeOffsetPackets
 * over the Stream and populate the min/max ranges passed in by reference
 * If we are unable to establish the range, either by invalid information
 * or network failure, we will return false
 * 
 * @param s - the Stream connection to the other daemon
 * @param min_range - the minimum range value for the time offset
 * @param max_range - the maximum range value for the time offset
 * @return true if the range was succesfully calculated between 2 daemons
 **/
bool
time_offset_range_cedar_stub( Stream* s, long &min_range, long &max_range ) {
      bool ret = false;
            //
            // The init method should put in our local timestamp
            // for when the packet is leaving us on the local side
            //
      TimeOffsetPacket packet = time_offset_initPacket();
      TimeOffsetPacket rPacket;
      
            //
            // Use the base Cedar sending function to populate our
            // packet information
            //
      if ( time_offset_send_cedar_stub( s, packet, rPacket ) ) {
            ret = time_offset_range_calculate( packet, rPacket, min_range, max_range );
      }
      return ( ret );
}

/**
 * Given a Stream, this stub will send & receive the TimeOffsetPackets
 * over the Stream. If we fail to communiucate with the remote daemon
 * we will return false
 * 
 * @param s - the Stream connection to the other daemon
 * @param packet - the packet we are sending to the remote side
 * @param rPacket - the packet we will get back from the remote daemon
 * @return true we were able to communicate with the other daemon
 **/
bool
time_offset_send_cedar_stub( Stream* s,
                                           TimeOffsetPacket &packet, TimeOffsetPacket &rPacket ) {
            //
            // Construct a time offset packet and shove it over
            // the wire. We add in our local time for when the packet
            // was sent
            //
      s->encode();
      if ( ! time_offset_codePacket_cedar( packet, s ) ) {
            dprintf( D_FULLDEBUG, "time_offset_send_cedar() failed to send inital packet "
                                            "to remote daemon\n" );
            return ( false );
      }
      s->end_of_message();
      
            //
            // We will now get the response from the remote entity
            // This will have the time that the packet arrived at their
            // end and the time that it was sent back out.
            // We will need to set the local arrival time on the packet right away
            //
      s->decode();
      if (! time_offset_codePacket_cedar( rPacket, s ) ) {
            dprintf( D_FULLDEBUG, "time_offset_send_cedar() failed to receive response "
                                            "packet from remote daemon\n" );
            return ( false );
      }
      s->end_of_message();
      
            //
            // Stuff in the time that we received the packet back
            //
      rPacket.localArrive = time( NULL );
            //
            // Our packets have been updated, so we can return true
            //
      return ( true );
}

/**
 * This is the connection code for the other end of a time offset range
 * routine. The other connection will have sent us a DC_TIME_OFFSET
 * command which will have take us into this function
 * 
 * @param Service - not used
 * @param cmd - the daemon command that we are being asked to execute
 * @param s -the Stream connection to the other daemon
 * @return true if
 **/
bool
time_offset_receive_cedar_stub( Service*, int /* cmd */, Stream* s ) {
            //
            // Get the TimeOffsetPacket information off the wire
            //
      s->decode();
      TimeOffsetPacket packet;
      if ( ! time_offset_codePacket_cedar( packet, s ) ) {
            dprintf( D_FULLDEBUG, "time_offset_receive_cedar_stub() failed to "
                                            "receive intial packet from remote daemon\n" );
            return ( false );
      }
            //
            // Close our connection
            // The other end will be waiting for our response but we need
            // to check a few conditions before we can send it out to them
            //
      s->end_of_message();    
      dprintf( D_FULLDEBUG, "time_offset_receive_cedar_stub() got the intial "
                                    "packet!\n");
      
            //
            // Call to the command handler that does all the logic
            // for the remote side
            //
      if ( time_offset_receive( packet ) ){
            s->encode();
            if (! time_offset_codePacket_cedar( packet, s ) ) {
                  dprintf( D_FULLDEBUG, "time_offset_receive_cedar_stub() failed to "
                                                  "send response packet to remote daemon\n" );
                  return ( false );
            }
            s->end_of_message();
            dprintf( D_FULLDEBUG, "time_offset_receive_cedar_stub() sent back response "
                                          "packet!\n");
      }
      return ( true );
}

/**
 * Given a TimeOffsetPacket & Stream, get all the data over the wire
 * I realize that this could probably be in the Stream code, but 
 * I didn't want ot muddy it up and I didn't want to mess around
 * with the circular dependencies
 * 
 * @param p - the container to update
 * @param s - the Stream connection to the other daemon
 * @return true if we ere able to update the packet successfully
 **/
bool 
time_offset_codePacket_cedar( TimeOffsetPacket &p, Stream *s )
{
      if (!s->code(p.localDepart))  return (false);
      if (!s->code(p.remoteArrive)) return (false);
      if (!s->code(p.remoteDepart)) return (false);
      if (!s->code(p.localArrive))  return (false);
      return (true);
}

// --------------------------------------------
// LOGIC METHODS
// --------------------------------------------

/**
 * This method receives a packet, stuffs its own arrival
 * time and departure time into it, then returns the packet.
 * We may want to use a more precise timestamp, but this works
 * for now
 * 
 * @param packet - the packet to add our timestamps to
 * @return true if we were able to update the packet for our reply
 **/
bool
time_offset_receive( TimeOffsetPacket &packet ) {
            //
            // Set the remote arrival time on the packet right away
            // We will check for other conditions but it's important
            // that we set this as soon as possible
            //
      packet.remoteArrive = time( NULL );
            //
            // Make sure that it has the client's departure timestamp
            //
      if ( packet.localArrive == 0 ) {
            dprintf( D_FULLDEBUG, "Received a time offset request but the "
                                      "local departure time was empty." );
            return (false);
      }
            //
            // We'll add our timestamp of when the packet is being sent out
            //
      packet.remoteDepart = time( NULL );
      return (true);
}

/**
 * Validates that the packets have matching information
 * If this function returns false, we should not use these packets
 * to calculate offset information
 * 
 * @param packet - the packet we originally sent to the remote side
 * @param rPacket - the packet sent back to use from the remote daemon
 * @return true if the packets have the expected information
 **/
bool
time_offset_validate( TimeOffsetPacket &packet, TimeOffsetPacket &rPacket )
{
            //
            // Make sure that we have the remoteArrive & remoteDepart times
            //
      if ( ! rPacket.remoteArrive ) {
            dprintf( D_FULLDEBUG, "The time offset response does not have the "
                                            "remote arrival time. Offset will default to %d\n",
                                            TIME_OFFSET_DEFAULT );
            return ( false );
      }
      if ( ! rPacket.remoteDepart ) {
            dprintf( D_FULLDEBUG, "The time offset response does not have the "
                                            "remote departure time. Offset will default to %d\n",
                                            TIME_OFFSET_DEFAULT );
            return ( false );
      }
            //
            // Make sure that the remote packet and the original packet
            // have the same local departure time
            //
      if ( packet.localDepart != rPacket.localArrive ) {
                  //
                  // It didn't, which is odd
                  // Because we don't know what's going on, we'll just return
                  // that the offset is zero
                  //
            dprintf( D_FULLDEBUG, "The time offset response has a different local "
                                            "departure timestamp. Offset will default to %d\n",
                                            TIME_OFFSET_DEFAULT );
            return ( false );
      }
      return ( true );
}
 
 
/**
 * Given the packet we sent to the remote daemon, and the packet
 * we got in the reply, we will calculate the time offset range
 * between ourselves and them. We do some simple validation checks,
 * and if the information isn't what we expect, we will return false
 * 
 * @param packet - the packet we originally sent to the remote side
 * @param rPacket - the packet sent back to use from the remote daemon
 * @param min_range - the minimum range value for the time offset
 * @param max_range - the maximum range value for the time offset
 * @return true if the range was succesfully calculated between 2 daemons
 **/
bool
time_offset_range_calculate( TimeOffsetPacket &packet, TimeOffsetPacket &rPacket,
                                           long &min_range, long &max_range )
{
            //
            // Validate the packets first
            //
      if ( ! time_offset_validate( packet, rPacket ) ) {
            return ( false );
      }
            //
            // Now use the basic formula from NTP to formulate the offset range
            //
      long a = rPacket.remoteArrive - rPacket.localDepart;
      long b = rPacket.remoteDepart - rPacket.localArrive;
      long theta = (long)rint( ( a + b ) / 2 );
      long sigma = (long)rint( ( a - b ) / 2 );
      min_range = theta - sigma;
      max_range = theta + sigma;
      return ( true );
}

/**
 * Given the packet we sent to the remote daemon, and the packet
 * we got in the reply, we will calculate the time offset
 * between ourselves and them. We do some simple validation checks,
 * and if the information isn't what we expect, we will return false
 * Note that this is assuming that the network connection is symmetrical
 * 
 * @param packet - the packet we originally sent to the remote side
 * @param rPacket - the packet sent back to use from the remote daemon
 * @param offset - the offset that will calculated between the daemons
 * @return true if we were able to calculate the offset 
 **/
bool
time_offset_calculate( TimeOffsetPacket &packet, TimeOffsetPacket &rPacket,
                                 long &offset )
{
            //
            // Validate the packets first
            //
      if ( ! time_offset_validate( packet, rPacket ) ) {
            return ( false );
      }
            //
            // This is the theoritical offset if you assume that the network
            // connection is symmetrical. I'm keeping it here for posterity
            //
      offset = (long)rint( ( (rPacket.remoteArrive - rPacket.localDepart) +
                                       (rPacket.remoteDepart - rPacket.localArrive) ) / 2);
      return ( true );
}

/**
 * Initializes a TimeOffsetPacket
 * We simply stuff our current time into the localDepart field
 * 
 * @return a new initialized TimeOffsetPacket struct
 **/
TimeOffsetPacket
time_offset_initPacket() {
      TimeOffsetPacket packet;
      packet.localDepart = time( NULL );  
      return ( packet );
}

Generated by  Doxygen 1.6.0   Back to index