#!/usr/bin/ruby

####################################################################################
# ifetch-tools is a set of tools in ruby that can collect images from ip based cameras,
# monitor collection process, and provide an interface to view collected history.
# Copyright (C) 2005-2021 Richard Nelson
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

##############################################################################################
# Set the current version information.
##############################################################################################
VER = "0.18.5"

##############################################################################################
# The below should daemonize this code.
##############################################################################################
#exit if fork			# Parent exits, child continues.
#Process.setsid			# Become session leader.
#exit if fork			# Zap session leader. See [1].
#Dir.chdir "/"			# Release old working directory.
##File.umask 0000			# Ensure sensible umask. Adjust as needed.
#File.umask 0177			# Ensure sensible umask. Adjust as needed.
#STDIN.reopen "/dev/null"	# Free file descriptors and
#STDOUT.reopen "/dev/null", "a"	# point them somewhere sensible.
#STDERR.reopen STDOUT		# STDOUT/ERR should better go to a logfile.

##############################################################################################
# Set some options to ensure memory management.
##############################################################################################
RMAGICK_ENABLE_MANAGED_MEMORY = true
GC.enable

##############################################################################################
# Do the require and include stuff we need for our operations.
##############################################################################################
require 'English'
require 'drb'
require 'fileutils'
require 'logger'
require 'uri'
require 'net/http'
require 'net/http/digest_auth'
require 'time'
#require "rubygems"
#gem "rmagick", "= 2.15.3"
require 'rmagick'
include Magick

##############################################################################################
# Initialize the global array for the image access info with all nil values.
##############################################################################################
$image_array = Array.new

##############################################################################################
# Initialize the global variables to assist RMagick in memory help.
##############################################################################################
$image_temp = nil
$mdpp = nil

##############################################################################################
# Initialize various variables that we intend to pull in with nil values.
##############################################################################################
data_location=nil
day_image_sleep=nil
image_addr=nil
image_addr_port=nil
image_auth_type=nil
image_count=nil
image_count_on_motion=nil
image_pwd=nil
image_sleep=nil
image_uid=nil
image_url=nil
image_watermark=nil
log_files=nil
log_size=nil
marshal_dump_enable=nil
marshal_dump_sentry=1
motion_enable=nil
motion_mdpp=nil
motion_sleep=nil
sentry_gc_count=0

##############################################################################################
# Define a class for our DRB communications.
##############################################################################################
class DataExchangeServer
	def xchange_array
		$image_array
	end
end

##############################################################################################
# Define the way we pull in an image from a camera.
#
# This def will pull the image from the cam now and return the response.
##############################################################################################
def pullimage(address,port,url,uid,pwd,auth_type)
	# Here we determine if we need to handle as basic auth or digest
	if auth_type == 'digest' then
		uri = URI.parse('http://'+address.to_s+':'+port.to_s+''+url.to_s)
		uri.user = uid
		uri.password = pwd
		response = Net::HTTP.start(address, port, open_timeout: 5, read_timeout: 5) do |http|
			req_img = Net::HTTP::Get.new uri.request_uri
			digest_auth = Net::HTTP::DigestAuth.new
			temp_response = http.request(req_img)
			# response is a 401 response with a WWW-Authenticate header
			auth = digest_auth.auth_header uri, temp_response['www-authenticate'], 'GET'
			# create a new request with the Authorization header
			req_img = Net::HTTP::Get.new uri.request_uri
			req_img.add_field 'Authorization', auth
			# re-issue request with Authorization
			http.request(req_img)
		end
	else
		response = Net::HTTP.start(address, port, open_timeout: 5, read_timeout: 5) do |http|
			req_img = Net::HTTP::Get.new(url)
			req_img.basic_auth uid, pwd
			http.request(req_img)
		end
	end
	# Now send response back
	return response.body
end
##############################################################################################
# The two def below are modified from gpl code at:
# http://spodzone.org.uk/packages/hawking-HNC230G-motion-detect.txt
# Copyright Tim Haynes <tim+hawking@spodzone.org.uk> 2006-
# Distributable under the terms of the Gnu Public Licence (GPL) - see
# <http://www.gnu.org/copyleft/gpl.html>.
##############################################################################################
##############################################################################################
# Define process(image)
# massage an image to be suitable for comparison
# here we compute the luminosity channel since rmagick's builtin doesn't work
# and scale it down to half/quarter-size to remove tiny transient errors.
##############################################################################################
#def process(image)
#	#image.colorize(0.25, 0.6, 0.15, 1, Magick::Pixel::from_color("grey")).modulate(1.25,0.01,1).scale(0.5)
#	#image_temp=Magick::Image.from_blob(image)[0]
#	#image_temp.colorize(0.25, 0.6, 0.15, 1, Magick::Pixel::from_color("grey")).modulate(1.25,0.01,1).scale(0.5)
#	#image_temp.modulate(1.25,0.01,1).scale(0.5)
#	#image_temp.modulate.scale(0.25)
#	Magick::Image.from_blob(image)[0]
#end
def difference(a,b)
	#a.write("a.jpeg")
	#b.write("b.jpeg")
	# see "mean difference per pixel" in the RMagick docs
	a=Magick::Image.from_blob(a)[0]
	b=Magick::Image.from_blob(b)[0]
	$mdpp = a.difference(b)[1]
	a.destroy!
	b.destroy!
	return $mdpp
end

##############################################################################################
# timeStamp images with RMagick.
##############################################################################################
def timeStamp(image,textInfo)
	mark = Magick::Image.new(300, 30) do
		self.background_color = 'black'
	end

	gc = Magick::Draw.new
	gc.annotate(mark, 0, 0, 0, 0, textInfo) do
		self.gravity = Magick::CenterGravity
		self.pointsize = 32
		self.font_family = "Times"
		self.fill = "white"
		self.stroke = "none"
	end

	#image_temp.write("/tmp/annotatewatermark.jpg")
	# Now return the image
	#image.to_blob

	# Convert to from_blob
	image = Magick::Image.from_blob(image)[0]
	# Watermark and to_blob
	$image_temp = image.watermark(mark, 0.15, 0, Magick::NorthWestGravity).to_blob
	# Try to cleanup things
	mark.destroy!
	image.destroy!
	gc = nil
	GC.start(full_mark: true, immediate_sweep: true)
	return $image_temp
end

###############################################################
# Eval in the ifetch-tools.conf file with error trap on this operation.
begin
	eval(File.open("/etc/ifetch-tools/ifetch-tools.conf") {|fh| fh.read})
rescue
	puts "Error encountered reading the ifetch-tools.conf file of: "+$!.to_s
	# Stop after the error feedback.
	exit
end

###############################################################
# Eval in the conf file for the camera with error trap on this operation.
begin
	eval(File.open(ARGV[0]) {|fh| fh.read})
rescue
	puts "Encountered reading the camera.conf file of: "+$!.to_s
	# Stop after the error feedback.
	exit
end

###############################################################
# Set the prefix to be the name of the conf file. Also the prefix is now expected to be a  numeric value for port operations on drb exchange.
prefix=File.basename(ARGV[0], ".conf")

###############################################################
# Set on varialbe names for file operations.
log_name = "/var/log/ifetch-tools/#{prefix}.txt"
pid_name = "/var/run/ifetch-tools/#{prefix}.pid"

################################################################
# Set the singleton so we can be the only one with this config.
lock_file = File::open("/var/lock/ifetch-tools/#{prefix}.lock", 'w')
if lock_file.flock(File::LOCK_EX|File::LOCK_NB) == 0 then
	# Do stuff on singleton if clean singleton established.
	###############################################################
	# Just drop our PID out for multi camera monitoring and operations.
	File.open(pid_name,'w') do |f|
		f.write(Process.pid)
	end

	###############################################################
	# Setup for logging general output.
	log = Logger.new(log_name, log_files, log_size*1024)
	log.info("RMagick memory management status: "+Magick::MANAGED_MEMORY.to_s)

	###############################################################
	# Lets trap here to ensure we can close clean with the signal of INT
	# Funny on the rescue where this shows up. Please see the rescue where we
	# are watching the HTTP:: stuff that I seem to have to make a clause for
	# exit.
	trap("INT"){
		puts "Caught INT so attempting to exit!"
		log.warn("Caught INT so attempting to exit!")
		exit
	}

	###############################################################
	# First thing and let the log file know we are alive.
	log.info("ifetch version - #{VER} starting.")

	###############################################################
	## 20070203 Nelson - After a bit of consideration and talking with a user on history
	## operations I need a way to offer greater history than what I had designed for so
	## the trick we shall try is to use the mod of the image count and splice out the images
	## to sub folders. This may take some time and to do so we will glob in still.

	###############################################################
	# 20090122 Nelson - tesing for /var/lib/ifetch-tools which is the default location that
	# data is to be stored in. Just create it if it does not exists.
	if File.exist?("/var/lib/ifetch-tools") then
		log.info("/var/lib/ifetch-tools directory exists.")
	else
		log.info("No /var/lib/ifetch-tools data directory found so creating one.")
		Dir.mkdir("/var/lib/ifetch-tools")
		# This is a simple step on directory structures to override File.umaks setting.
		File.chmod(0700, "/var/lib/ifetch-tools")
	end

	###############################################################
	# 20080224 Nelson - modified for dynamic symlink via variable data_location that can now
	# be included in the camera conf file.
	#
	# Below is the code to generate the folder structure for history work.
	# If the MODPRIME is changed in the ifetch.conf file then the history will need to be smoked!
	log.info("Testing for directory structure for camera #{prefix}!")
	# Fist check for the camera directory.
	if File.exist?("/var/lib/ifetch-tools/#{prefix}") then
		log.info("Camera #{prefix} parent directory exists.")
		# This is a simple step on directory structures to override File.umaks setting.
		File.chmod(0700, "/var/lib/ifetch-tools/#{prefix}")
	else
		log.info("Camera #{prefix} NO htdocs folder starting data directory generation.")
		if data_location!=nil then
			log.info("Camera #{prefix} data directory is being generated for symlink operations at #{data_location}.")
			if File.exist?(data_location+"/#{prefix}") then
				log.info("Camera #{prefix} symlink parent directory exists.")
				log.info("Camera #{prefix} only creating symlink.")
				File.symlink(data_location+"/#{prefix}", "/var/lib/ifetch-tools/#{prefix}")
			else
				log.info("Camera #{prefix} NO symlink parent directory exists.")
				log.info("Camera #{prefix} creating parent and symlink.")
				Dir.mkdir(data_location+"/#{prefix}")
				log.info("Camera #{prefix} data directory is being symlinked from #{data_location}/#{prefix} to /var/lib/ifetch-tools/#{prefix}.")
				File.symlink(data_location+"/#{prefix}", "/var/lib/ifetch-tools/#{prefix}")
			end
			# This is a simple step on directory structures to override File.umaks setting.
			File.chmod(0700, data_location+"/#{prefix}")
		else
			log.info("Camera #{prefix} data directory is being generated and NOT symlinked.")
			Dir.mkdir("/var/lib/ifetch-tools/#{prefix}")
			# This is a simple step on directory structures to override File.umaks setting.
			File.chmod(0700, "/var/lib/ifetch-tools/#{prefix}")
		end
	end
	# Now handle the mod directory.
	0.upto(MODPRIME-1) do |count|
		if File.exist?("/var/lib/ifetch-tools/#{prefix}/#{count}") then
			log.info("Camera #{prefix} mod #{count} directory exists.")
		else
			log.info("Camera #{prefix} mod #{count} directory is being generated.")
			Dir.mkdir("/var/lib/ifetch-tools/#{prefix}/#{count}")
		end
		# This is a simple step on directory structures to override File.umaks setting.
		File.chmod(0700, "/var/lib/ifetch-tools/#{prefix}/#{count}")
	end

	###############################################################
	# We will use info here because we will sniff for all ok later with entry on the log
	# and a simple sentry on success after trouble.
	log.info("Please wait for history to initialize before attempting to access the history!")

	###############################################################
	# This is the area where we collect the timestamps are on any history.
	# 20170205 Feature add to attempt to improve indexing on large storages utilizing Marshaling.
	# Set a temp file name for the tstamp path.
	temp_mdump_path = ["/var/lib/ifetch-tools",prefix,prefix+".marshal"].join('/')

	# Load the contents of last Marshal dump first in to the global array.
	if File.exist?(temp_mdump_path) then
		log.info("Camera .marshal file found. Attempting to load...")
		# If we are here then we have found a .marshal file for the camera.
		# First let us load the image_array dump.
		$image_array = Marshal.load File.read(temp_mdump_path)
		log.info("Camera .marshal file loaded!")
		# Second let us pull in all the .tstamp files for the images that we need to pull in
		# to make our image_array up to day.
		log.info("Attempting to load camera .tstamp files...")
		temp_tstamp_files = Dir["/var/lib/ifetch-tools/#{prefix}/*.tstamp"]
		0.upto(temp_tstamp_files.length - 1) do |count|
			temp_fn = File.basename(temp_tstamp_files[count], ".tstamp").split(/_/)[1].to_i
			begin
				$image_array[temp_fn] = Marshal.load File.read(temp_tstamp_files[count])
			rescue Exception
				log.warn("Encountered an error during .tstamp import of: "+$!.to_s)
			end
		end
		log.info("Loading of camera .tstamp files complete!")
	else
		# If we are here then we must brute force time stamps for all files.
		log.info("No .marshal file found to  switching to brute force dating of history!")
		# Get a listing of all the images in the history
		history_files = Dir["/var/lib/ifetch-tools/#{prefix}/*/*.jpg"]
		# Unremark the below lines for diagnostic
		#puts history_files
		#puts history_files.length
		0.upto(history_files.length - 1) do |count|
			# Here we step through the files and pop off the file name/number and put it in the image_array in
			# the correct position.
			temp_fn = File.basename(history_files[count], ".jpg").split(/_/)[1].to_i
			$image_array[temp_fn] = Time.parse(File.mtime(history_files[count]).to_s).strftime("%Y-%m-%d %H:%M:%S")+","+[prefix,temp_fn.divmod(MODPRIME)[1].to_s,File.basename(history_files[count])].join('/')
			#Unremark for below for diag on the actual number being split out.
			#puts File.basename(history_files[count], ".jpg").split(/_/)[1]
		end
	end

	###############################################################
	# Here we adjust the array if need be to be the size requested by the configs.
	if image_count >= $image_array.length then
		log.info("Starting history generation with history count normal!")
		# Make sure no entries are nil
		0.upto($image_array.length - 1) do |count|
			if $image_array[count] == nil then
				$image_array[count] = "19000101 00:00:01,images/missed.jpg"
			end
		end
	else
		#puts "History is more than desired count."
		log.info("Starting history generation with history count over set collection!")
		0.upto(image_count - 1) do |count|
			# This is the same as the above logic for images missing from the array.
			if $image_array[count] == nil then
				$image_array[count] = "19000101 00:00:01,images/missed.jpg"
			end
		end
		log.info("Dumping history count over set collection from disk!")
		image_count.upto($image_array.length - 1) do |count|
			#puts "Deleting "+$image_array[count].split(/,/)[1]+"!"
			if File.exist?("/var/lib/ifetch-tools/"+$image_array[count].split(/,/)[1]) then
				File.delete("/var/lib/ifetch-tools/"+$image_array[count].split(/,/)[1])
			else
				# FIXME test the below logging.
				log.warn("File /var/lib/ifetch-tools/"+$image_array[count].split(/,/)[1]+" was not found to delete.")
			end
		end
		# Drop the elements out of the array now.
		$image_array = $image_array.take image_count
	end

	# 20070108 Nelson: We need to bump the image count to the next correct number.
	# Set the counter up for starting at correct location.
	count = $image_array.index($image_array.max)
	if count == nil
		count = -1
	end
	history_count = $image_array.length

	log.info("History generation complete with #{history_count} images in the history and start image of #{count+1}. Collection count set at #{image_count}. History access is now online.")

	# Here we clear out things we want to like unused array space and others.
	#history_files = nil
	temp_mdump_path = nil
	temp_tstamp_files = nil

	###############################################################
	# Put in service 20061011 test code.
	# Setup the communication model with DRB for the servlet to talk to us.
	#DRb::DRbServer.default_load_limit LOADLIMIT
	#DRb.start_service("druby://:7777", SentenceWrapper.new, {:load_limit => 90214400})
	#DRb.start_service('druby://localhost:9000', aServerObject)
	drb_port = "druby://localhost:"+(BASEPORT+prefix.to_i).to_s
	aServerObject = DataExchangeServer.new
	DRb.start_service(drb_port, aServerObject, {:load_limit => LOADLIMIT})
	#puts "Hello!"
	#DRb.thread.join # Don't exit just yet!

	# Set a sentry to a value of 0 to skip clean operation logging. This is the
	# default behavior.
	sentry_check = 0


	###############################################################
	# Here is where we add a couple of things for the motion detection operations.
	# Set a sentry for skip checking that will count down until images without test are done.
	motion_sentry = 0
	# We need a reference image to test with and we will not write it so we do not need to time stamp it.
	if motion_enable == 0 then
		begin
			log.info("Motion detection enabled. Pulling first image!")
			image1=pullimage(image_addr,image_addr_port,image_url,image_uid,image_pwd,image_auth_type)
		rescue Exception
			log.warn("Encountered an error during image1 initial pull of: "+$!.to_s)
		end

	end
	###############################################################
	# Now start the loop that monitors via image_count.
	until count>image_count
		# Test here to ensure once we get to image_count just start over.
		if count < image_count-1
			# puts "I am adding 1 to count check!"
			count=count+1
		else
			#puts "I am here at count check!"
			#puts count
			count=0
		end
		begin
			# Keep the GC in order
			sentry_gc_count=sentry_gc_count+1
			if sentry_gc_count%100 == 0
				# For sanity run the GC in an effort to keep RMagick memory in check.
				memory_usage_before = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes
				GC.start(full_mark: true, immediate_sweep: true)
				sentry_gc_count = 0
				memory_usage_after = `ps -o rss= -p #{Process.pid}`.to_i # in kilobytes
				log.info("After garbage collection: before=#{memory_usage_before} after=#{memory_usage_after} !")
			end

			# Get the current image
			image2=pullimage(image_addr,image_addr_port,image_url,image_uid,image_pwd,image_auth_type)
			# Adding pullTime to timeStamp images.
			pullTime = Time.now.strftime("%Y-%m-%d %H:%M:%S")
			image_name_DRB_exchange = [prefix,count.divmod(MODPRIME)[1].to_s,prefix+"_"+count.to_s+".jpg"].join('/')
			image_name = ["/var/lib/ifetch-tools",image_name_DRB_exchange].join('/')

			################################################################################
			# Now we test to see if we are doing motion detection and if so then we need two
			# images and we will now compare for difference unless asked to skip
			# Just to be safe run the Garbage Collection
			if motion_enable == 0 then
				#puts "Starting sentry_motion test.\n"
				if motion_sentry < 1 then
					# Here we are asked to listen for testing again.
					#puts "Before"
					mdpp = difference(image2,image1)
					#puts "Images tested resulted with mdpp of #{mdpp}"
					# Log out all mdpp tests to log file.
					if mdpp.is_a?(Numeric) then
						if mdpp == 0 then
							#puts "Zero mdpp detected!"
							log.warn("Zero mdpp image difference detected: mdpp=#{mdpp} and motion_mdpp=#{motion_mdpp}!")
						elsif mdpp > motion_mdpp then
							#puts "Motion detected!"
							log.info("Motion detected: mdpp=#{mdpp} and motion_mdpp=#{motion_mdpp}!")
							motion_sentry = image_count_on_motion
						else
							log.info("Insufficient motion: mdpp=#{mdpp} and motion_mdpp=#{motion_mdpp}.")
							# Update image1 since we compare latest of image2
							image1 = image2
						end
					else
						log.info("The mdpp call did not return a number: mdpp=#{mdpp}!")
					end
				else
					# Here we are taking the number of images without testing.
					# Drop one of the count of untested images.
					motion_sentry = motion_sentry-1
				end
				#puts "Ending sentry_motion test.\n"
			end

			###############################################################
			# Here we test to determine if motion is detected or if motion
			# is off. If either case write file to disk.
			if motion_sentry > 0 || motion_enable == 1 then
				# Update image1 since we compare latest of image2
				image1 = image2
				# Add to the image_array for DRB exchange
				$image_array[count] = pullTime+","+image_name_DRB_exchange
				#puts $image_array.max
				#puts $image_array.index($image_array.max)
				#log.info("The $image_array.max="+$image_array.max.to_s+" and the $image_array.index($image_array.max)="+$image_array.index($image_array.max).to_s+"!")
				###############################################################
				# Now put image in a file. Also let us do a quick error trap here.
				#puts count
				#puts "#{image_name} - Image Name"
				#puts $image_array[count]
				begin
					File.open(image_name,'w') do |f|
						# Here is where we watermark the image we are going to write if enabled.
						if image_watermark == 0 then
							f.write(timeStamp(image2,pullTime))
						else
							# No watermark
							f.write(image2)
						end
					end
				rescue Exception
					log.warn("Encountered an error during image file output of: "+$!.to_s)
				end
				###############################################################
				# To improve large data stores we want to marshal dump every 1000 images.
				# Do marshal_dump operations if true and it is the last image recorded on montion detection.
				if count.divmod(1000)[1] == 0 then
					marshal_dump_sentry = 0
				end
				if marshal_dump_enable == 0 then
					if marshal_dump_sentry == 0 then
						###############################################################
						# To improve large data stores we want to marshal dump image_array every 1000 images.
						begin
							# First marshal dump the image array.
							image_array_name_mdump = ["/var/lib/ifetch-tools",prefix,prefix+".marshal"].join('/')
							image_array_dump = Marshal.dump($image_array)
							File.open(image_array_name_mdump, 'w') {|f| f.write(image_array_dump) }
							# Second clean up the tstamp dumps.
							Dir.glob("/var/lib/ifetch-tools/#{prefix}/*.tstamp").each { |f| File.delete(f) }
						rescue Exception
							log.warn("Encountered an error during marshal dump of: "+$!.to_s)
						end
						# Reset the sentry for the next time.
						marshal_dump_sentry = 1
					else
						###############################################################
						# Now only dump single images one per file for 1000
						begin
							image_name_dump = ["/var/lib/ifetch-tools",prefix,prefix+"_"+count.to_s+".tstamp"].join('/')
							image_dump = Marshal.dump($image_array[count])
							File.open(image_name_dump, 'w') {|f| f.write(image_dump) }
						rescue Exception
							log.warn("Encountered an error during marshal dump of: "+$!.to_s)
						end
					end
				end
			else
				# Here drop the count back and just keep writing out the same image number
				# until we detect sufficient mdpp.
				# I suppose count could equal 0.
				if count >= 0 then
					count=count-1
				end
			end

			###############################################################
			# Now throttle the program back if need be.
			# First test to see if which sleep we need to use.
			# Use the motion burst as first option if triggered.
			if motion_sentry > 0 then
				#puts "Performing motion_sleep of #{motion_sleep}."
				sleep motion_sleep
			else
				# Set the daily sleep modification specified in each camera conf.
				sleep_test_data = day_image_sleep[Time.now.strftime("%w").to_i].split(",")
				if Time.now.strftime("%H:%M") >= sleep_test_data[0] && Time.now.strftime("%H:%M") <= sleep_test_data[1]
					# If we are here we are in a modified time in the day for sleep change
					#puts true
					sleep sleep_test_data[2].to_f
				else
					#puts false
					sleep image_sleep
				end
			end

			###############################################################
			# If we encounter an issue that raised the rescue Exception and set the sentry_check = 1
			# then we are just listenting here if we get to this point to let the log file know that
			# we are ok again. This is used to determine the health of a running script to a camera for
			# for problems. I am sure there are better ways but this seems KISS.
			if sentry_check != 0
				log.info("Sentry clear hit and appears to be operating again ok! ifetch version - #{VER}!")
				sentry_check = 0
			end
		###############################################################
		# Rescue out for all errors.
		rescue Exception
			###############################################################
			# First things is notify there is an error.
			#puts "Encountered an error: "+$!.to_s
			#log.warn("Encountered an error from the Net::HTTP work of: "+$!.to_s)
			log.warn("Encountered an error in collection operations of: "+$!.to_s)
			# Sleep a bit when error is raised for this process.
			sleep ERRORSLEEP

			###############################################################
			# Try to setup catching a clean interrupt. This seems to be odd that I have to
			# do this. It is for the trap("INT") form comman line testing.
			if ""+$!.to_s == 'exit'
				log.warn('Exiting now!')
				# Try to be nice and close the lock file.
				lock_file.close
				exit
			end
			###############################################################
			# If we encounter an issue lets set a sentry to let the program know that
			# we are still alive and well. Setting the sentry to 1 will force an information
			# output to logging that we are back to ok status.
			sentry_check = 1

			# Write image2 to image1, since image1 is what we write and image2 is latest.
			# We swap here in case we get a bad image as image1 and this should flush out.
			image1 = image2

			# If we encounter a bad image we need to dump the bad pull and pull again.
			log.warn("Backing image count sequence down by 1 in an attempt to pull image again after rescue!")
			if count < image_count-1 && count > 0
				count=count-1
			else
				count=0
			end
		end
	end

	###############################################################
	# Try to close the lock file and the Logger.
	lock_file.close
	log.close
	DRb.stop_service() # Stop the DRb on the ifetch session
else
	###############################################################
	# If we get here we already have a running ifetch.rb
	puts "According to lock file the program for the conf file is already running!"
end
