starting to move shared classes into rex

git-svn-id: file:///home/svn/incoming/trunk@2559 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Matt Miller 2005-06-04 08:15:10 +00:00
parent d369ef3737
commit 675dbe37b0
6 changed files with 480 additions and 0 deletions

18
lib/rex/constants.rb Normal file
View File

@ -0,0 +1,18 @@
#
# Log severities
#
LOG_ERROR = 'error'
LOG_DEBUG = 'debug'
LOG_INFO = 'info'
LOG_WARN = 'warn'
LOG_RAW = 'raw'
#
# Log levels
#
# LEV_0 errors and warnings may be displayed to the user by default.
#
LEV_0 = 0
LEV_1 = 1
LEV_2 = 2
LEV_3 = 3

View File

@ -0,0 +1,126 @@
require 'Rex'
module Rex
module Logging
###
#
# LogDispatcher
# -------------
#
# The log dispatcher associates log sources with log sinks. A log source
# is a unique identity that is associated with one and only one log sink.
# For instance, the framework-core registers the 'core'
#
###
class LogDispatcher
def initialize()
self.log_sinks = {}
self.log_sinks_rwlock = ReadWriteLock.new
end
# Returns the sink that is associated with the supplied source
def [](src)
sink = nil
log_sinks_rwlock.synchronize_read {
sink = log_sinks[src]
}
return sink
end
# Calls the source association routnie
def []=(src, sink)
store(src, sink)
end
# Associates the supplied source with the supplied sink
def store(src, sink)
log_sinks_rwlock.synchronize_write {
if (log_sinks[src] == nil)
log_sinks[src] = sink
else
raise(
RuntimeError,
"The supplied log source #{src} is already registered.",
caller)
end
}
end
# Removes a source association if one exists
def delete(src)
sink = nil
log_sinks_rwlock.synchronize_write {
sink = log_sinks[src]
log_sinks.delete(src)
}
if (sink)
sink.cleanup
return true
end
return false
end
# Performs the actual log operation against the supplied source
def log(sev, src, level, msg, from)
log_sinks_rwlock.synchronize_read {
if ((sink = log_sinks[src]))
sink.log(sev, src, level, msg, from)
end
}
end
attr_accessor :log_sinks, :log_sinks_rwlock
end
end
end
###
#
# An instance of the log dispatcher exists in the global namespace, along
# with stubs for many of the common logging methods. Various sources can
# register themselves as a log sink such that logs can be directed at
# various targets depending on where they're sourced from. By doing it
# this way, things like sessions can use the global logging stubs and
# still be directed at the correct log file.
#
###
def dlog(msg, src = 'core', level = 0, from = caller)
$dispatcher.log(LOG_DEBUG, src, level, msg, from)
end
def elog(msg, src = 'core', level = 0, from = caller)
$dispatcher.log(LOG_ERROR, src, level, msg, from)
end
def wlog(msg, src = 'core', level = 0, from = caller)
$dispatcher.log(LOG_WARN, src, level, msg, from)
end
def ilog(msg, src = 'core', level = 0, from = caller)
$dispatcher.log(LOG_INFO, src, level, msg, from)
end
def rlog(msg, src = 'core', level = 0, from = caller)
$dispatcher.log(LOG_RAW, src, level, msg, from)
end
def register_log_source(src, sink)
$dispatcher[src] = sink
end
def deregister_log_source(src, sink)
$dispatcher.delete(src)
end
# Creates the global log dispatcher
$dispatcher = Rex::Logging::LogDispatcher.new

View File

@ -0,0 +1,36 @@
require 'Rex/Constants'
module Rex
module Logging
###
#
# LogSink
# -------
#
# This abstract interface is what must be implemented by any class
# that would like to register as a log sink on a given LogDispatcher
# instance, such as the Framework object.
#
###
module LogSink
def cleanup
end
def log(sev, src, level, msg, from)
raise NotImplementedError
end
protected
def get_current_timestamp
return Time.now.strftime("%m/%d/%Y %H:%M:%S")
end
end
end
end
require 'Rex/Logging/Sinks/Flatfile'

View File

@ -0,0 +1,50 @@
module Rex
module Logging
module Sinks
###
#
# Flatfile
# --------
#
# This class implements the LogSink interface and backs it against a
# file on disk.
#
###
class Flatfile
include Rex::Logging::LogSink
def initialize(file)
self.fd = File.new(file, "a")
end
def cleanup
fd.close
end
def log(sev, src, level, msg, from)
if (sev == LOG_RAW)
fd.write(msg)
else
code = 'i'
case sev
when LOG_DEBUG
code = 'd'
when LOG_ERROR
code = 'e'
when LOG_INFO
code = 'i'
end
fd.write("[#{get_current_timestamp}] [#{code}(#{level})] #{src}: #{msg}\n")
end
end
protected
attr_accessor :fd
end
end end end

163
lib/rex/read_write_lock.rb Normal file
View File

@ -0,0 +1,163 @@
require 'thread'
module Rex
###
#
# ReadWriteLock
# -------------
#
# This class implements a read/write lock synchronization
# primitive. It is meant to allow for more efficient access to
# resources that are more often read from than written to and many
# times can have concurrent reader threads. By allowing the reader
# threads to lock the resource concurrently rather than serially,
# a large performance boost can be seen. Acquiring a write lock
# results in exclusive access to the resource and thereby prevents
# any read operations during the time that a write lock is acquired.
# Only one write lock may be acquired at a time.
#
###
class ReadWriteLock
def initialize
@read_sync_mutex = Mutex.new
@write_sync_mutex = Mutex.new
@exclusive_mutex = Mutex.new
@readers = 0
@writer = false
end
# Acquires the read lock for the calling thread
def lock_read
read_sync_mutex.lock
begin
# If there are a non-zero number of readers and a
# writer is waiting to acquire the exclusive lock,
# free up the sync mutex temporarily and lock/unlock
# the exclusive lock. This is to give the writer
# thread a chance to acquire the lock and prevents
# it from being constantly starved.
if ((@readers > 0) and
(@writer))
read_sync_mutex.unlock
exclusive_mutex.lock
exclusive_mutex.unlock
read_sync_mutex.lock
end
# Increment the active reader count
@readers += 1
# If we now have just one reader, acquire the exclusive
# lock. Track the thread owner so that we release the
# lock from within the same thread context later on.
if (@readers == 1)
exclusive_mutex.lock
@owner = Thread.current
end
ensure
read_sync_mutex.unlock
end
end
# Releases the read lock for the calling thread
def unlock_read
read_sync_mutex.lock
begin
unlocked = false
# Keep looping until we've lost this thread's reader
# lock
while (!unlocked)
# If there are no more readers left after this one
if (@readers - 1 == 0)
# If the calling thread is the owner of the exclusive
# reader lock, then let's release that shit!
if (Thread.current == @owner)
@owner = nil
exclusive_mutex.unlock
end
# If there is more than one reader left and this thread is
# the owner of the exclusive lock, then keep looping so that
# we can eventually unlock the exclusive mutex in this thread's
# context
elsif (Thread.current == @owner)
read_sync_mutex.unlock
next
end
# Unlocked!
unlocked = true
# Decrement the active reader count
@readers -= 1
end
ensure
read_sync_mutex.unlock
end
end
# Acquire the exclusive write lock
def lock_write
write_sync_mutex.lock
begin
@writer = true
exclusive_mutex.lock
@owner = Thread.current
ensure
write_sync_mutex.unlock
end
end
# Release the exclusive write lock
def unlock_write
# If the caller is not the owner of the write lock, then someone is
# doing something broken, let's let them know.
if (Thread.current != @owner)
raise RuntimeError, "Non-owner calling thread attempted to release write lock", caller
end
# Otherwise, release the exclusive write lock
@writer = false
exclusive_mutex.unlock
end
# Synchronize a block for read access
def synchronize_read
lock_read
begin
yield
ensure
unlock_read
end
end
# Synchronize a block for write access
def synchronize_write
lock_write
begin
yield
ensure
unlock_write
end
end
protected
attr_accessor :read_sync_mutex
attr_accessor :write_sync_mutex
attr_accessor :exclusive_mutex
end
end

87
lib/rex/transformer.rb Normal file
View File

@ -0,0 +1,87 @@
module Rex
###
#
# Transformer - more than meets the eye!
# -----------
#
# This class, aside from having a kickass name, is responsible for translating
# object instances of one or more types into a single list instance of one or
# more types. This is useful for translating object instances that be can
# either strings or an array of strings into an array of strings, for
# instance. It lets you make things take a uniform structure in an abstract
# manner.
#
###
class Transformer
# Translates the object instance supplied in src_instance to an instance of
# dst_class. The dst_class parameter's instance must support the <<
# operator. An example call to this method looks something like:
#
# Transformer.transform(string, Array, [ String ], target)
def Transformer.transform(src_instance, dst_class, supported_classes,
target = nil)
dst_instance = dst_class.new
if (src_instance.kind_of?(Array))
src_instance.each { |src_inst|
Transformer.transform_single(src_inst, dst_instance,
supported_classes, target)
}
elsif (!src_instance.kind_of?(NilClass))
Transformer.transform_single(src_instance, dst_instance,
supported_classes, target)
end
return dst_instance
end
protected
# Transform a single source instance.
def Transformer.transform_single(src_instance, dst_instance,
supported_classes, target)
# If the src instance's class is supported, just add it to the dst
# instance
if (supported_classes.include?(src_instance.class))
dst_instance << src_instance
# If the source instance is a string, query each of the supported
# classes to see if they can serialize it to their particular data
# type.
elsif (src_instance.kind_of?(String))
new_src_instance = nil
# Walk each supported class calling from_s if exported
supported_classes.each { |sup_class|
new_src_instance = sup_class.from_s(src_instance)
if (new_src_instance != nil)
dst_instance << new_src_instance
break
end
}
# If we don't have a valid new src instance, then we suck
if (new_src_instance == nil)
bomb_translation(src_instance, target)
end
# Otherwise, bomb translation
else
bomb_translation(src_instance, target)
end
end
def Transformer.bomb_translation(src_instance, target)
error = "Invalid source class (#{src_instance.class})"
if (target != nil)
error += " for #{target}"
end
raise ArgumentError, error, caller
end
end
end