# -*- coding: binary -*-
require 'msf/core/plugin'

=begin
require 'active_record'
#
# This monkeypatch can help to diagnose errors involving connection pool
# exhaustion and other strange ActiveRecord including errors like:
#
#   DEPRECATION WARNING: Database connections will not be closed automatically, please close your
#   database connection at the end of the thread by calling `close` on your
#   connection.  For example: ActiveRecord::Base.connection.close
#
# and
#
#   ActiveRecord::StatementInvalid NoMethodError: undefined method `fields' for nil:NilClass: SELECT  "workspaces".* FROM "workspaces"  WHERE "workspaces"."id" = 24 LIMIT 1
#
#
# Based on this code: https://gist.github.com/1364551 linked here:
# http://bibwild.wordpress.com/2011/11/14/multi-threading-in-rails-activerecord-3-0-3-1/
module ActiveRecord
  class Base
    class << self
      def connection
        unless connection_pool.active_connection?
          $stdout.puts("AR::B.connection implicit checkout")
          $stdout.puts(caller.join("\n"))
          raise ImplicitConnectionForbiddenError.new("Implicit ActiveRecord checkout attempted!")
        end
        retrieve_connection
      end
    end
  end
  class ImplicitConnectionForbiddenError < ActiveRecord::ConnectionTimeoutError ; end
end
=end


module Msf

###
#
# This class manages the threads spawned by the framework object, this provides some additional
# features over standard ruby threads.
#
###
class ThreadManager < Array

	include Framework::Offspring

	attr_accessor :monitor

	#
	# Initializes the thread manager.
	#
	def initialize(framework)
		self.framework = framework
		self.monitor   = spawn_monitor
	end

	#
	# Spawns a monitor thread for removing dead threads
	#
	def spawn_monitor
		::Thread.new do
			begin

			::Thread.current[:tm_name] = "Thread Monitor"
			::Thread.current[:tm_crit] = true

			while true
				::IO.select(nil, nil, nil, 1.0)
				self.each_index do |i|
					state = self[i].alive? rescue false
					self[i] = nil if not state
				end
				self.delete(nil)
			end

			rescue ::Exception => e
				elog("thread monitor: #{e} #{e.backtrace} source:#{self[:tm_call].inspect}")
			end
		end
	end

	#
	# Spawns a new thread
	#
	def spawn(name, crit, *args, &block)
		t = nil

		if block
			t = ::Thread.new(name, crit, caller, block, *args) do |*argv|
				::Thread.current[:tm_name] = argv.shift.to_s
				::Thread.current[:tm_crit] = argv.shift
				::Thread.current[:tm_call] = argv.shift
				::Thread.current[:tm_time] = Time.now

				begin
					argv.shift.call(*argv)
				rescue ::Exception => e
					elog("thread exception: #{::Thread.current[:tm_name]}  critical=#{::Thread.current[:tm_crit]}  error:#{e.class} #{e} source:#{::Thread.current[:tm_call].inspect}")
					elog("Call Stack\n#{e.backtrace.join("\n")}")
					raise e
				ensure
					if framework.db and framework.db.active
						# NOTE: despite the Deprecation Warning's advice, this should *NOT*
						# be ActiveRecord::Base.connection.close which causes unrelated
						# threads to raise ActiveRecord::StatementInvalid exceptions at
						# some point in the future, presumably due to the pool manager
						# believing that the connection is still usable and handing it out
						# to another thread.
						::ActiveRecord::Base.connection_pool.release_connection
					end
				end
			end
		else
			t = ::Thread.new(name, crit, caller, *args) do |*argv|
				::Thread.current[:tm_name] = argv.shift
				::Thread.current[:tm_crit] = argv.shift
				::Thread.current[:tm_call] = argv.shift
				::Thread.current[:tm_time] = Time.now
				# Calling spawn without a block means we cannot force a database
				# connection release when the thread completes, so doing so can
				# potentially use up all database resources and starve all subsequent
				# threads that make use of the database. Log a warning so we can track
				# down this kind of usage.
				dlog("Thread spawned without a block!")
				dlog("Call stack: \n#{::Thread.current[:tm_call].join("\n")}")
			end
		end

		self << t
		t
	end

	#
	# Registers an existing thread
	#
	def register(t, name, crit)
		t[:tm_name] = name
		t[:tm_crit] = crit
		t[:tm_call] = caller
		t[:tm_time] = Time.now
		self << t
		t
	end

	#
	# Updates an existing thread
	#
	def update(ut, name, crit)
		ti = nil
		self.each_index do |i|
			tt = self[i]
			next if not tt
			if ut.__id__ == tt.__id__
				ti = i
				break
			end
		end

		t = self[ti]
		if not t
			raise RuntimeError, "Thread not found"
		end

		t[:tm_name] = name
		t[:tm_crit] = crit
		t
	end

	#
	# Kills a thread by index
	#
	def kill(idx)
		self[idx].kill rescue false
	end

end

end
