mirror of
https://github.com/rapid7/metasploitable3.git
synced 2024-09-23 00:00:26 +02:00
332 lines
10 KiB
Ruby
332 lines
10 KiB
Ruby
require 'win32/registry'
|
|
require 'yaml'
|
|
require 'fileutils'
|
|
|
|
module DevKitInstaller
|
|
|
|
DEVKIT_ROOT = File.expand_path(File.dirname(__FILE__))
|
|
DEVKIT_START = ':DK-BEG:'
|
|
DEVKIT_END = ':DK-END:'
|
|
|
|
# TODO add JRuby installer registry key
|
|
REG_KEYS = [
|
|
'Software\RubyInstaller\MRI',
|
|
'Software\RubyInstaller\Rubinius',
|
|
'Software\Wow6432Node\RubyInstaller\MRI'
|
|
]
|
|
|
|
CONFIG_FILE = 'config.yml'
|
|
|
|
def self.usage
|
|
<<-EOT
|
|
|
|
Configures an MSYS/MinGW based Development Kit (DevKit) for
|
|
each of the Ruby installations on your Windows system. The
|
|
DevKit enables you to build many of the available native
|
|
RubyGems that don't yet have a binary gem.
|
|
|
|
Usage: ruby dk.rb COMMAND [options]
|
|
|
|
where COMMAND is one of:
|
|
|
|
init prepare DevKit for installation
|
|
review review DevKit install plan
|
|
install install required DevKit executables
|
|
|
|
and 'install' [options] are:
|
|
|
|
-f, --force overwrite existing helper scripts
|
|
EOT
|
|
end
|
|
|
|
def self.timestamp
|
|
Time.now.strftime('%Y%m%d%H%M%S')
|
|
end
|
|
private_class_method :timestamp
|
|
|
|
def self.gem_override(dk_root=DEVKIT_ROOT)
|
|
d = dk_root.gsub('/', '\\\\\\')
|
|
<<-EOT
|
|
# #{DEVKIT_START} override 'gem install' to enable RubyInstaller DevKit usage
|
|
Gem.pre_install do |gem_installer|
|
|
load 'devkit.rb' unless gem_installer.spec.extensions.empty?
|
|
end
|
|
# #{DEVKIT_END}
|
|
EOT
|
|
end
|
|
private_class_method :gem_override
|
|
|
|
def self.devkit_lib(dk_root=DEVKIT_ROOT)
|
|
d = dk_root.gsub('/', '\\\\\\')
|
|
<<-EOT
|
|
# enable RubyInstaller DevKit usage as a vendorable helper library
|
|
unless ENV['PATH'].include?('#{d}\\\\mingw\\\\bin') then
|
|
phrase = 'Temporarily enhancing PATH to include DevKit...'
|
|
if defined?(Gem)
|
|
Gem.ui.say(phrase) if Gem.configuration.verbose
|
|
else
|
|
puts phrase
|
|
end
|
|
puts "Prepending ENV['PATH'] to include DevKit..." if $DEBUG
|
|
ENV['PATH'] = '#{d}\\\\bin;#{d}\\\\mingw\\\\bin;' + ENV['PATH']
|
|
end
|
|
ENV['RI_DEVKIT'] = '#{d}'
|
|
ENV['CC'] = 'gcc'
|
|
ENV['CXX'] = 'g++'
|
|
ENV['CPP'] = 'cpp'
|
|
EOT
|
|
end
|
|
private_class_method :devkit_lib
|
|
|
|
def self.update_gem_override(target)
|
|
in_devkit = false
|
|
bkup = "#{target}.#{timestamp}"
|
|
File.rename(target, bkup)
|
|
|
|
# copy existing gem override except for old DevKit content
|
|
begin
|
|
File.open(bkup, 'r') do |src|
|
|
File.open(target, 'w') do |tgt|
|
|
src.each_line do |src_line|
|
|
case src_line
|
|
when /^# #{DEVKIT_START}/
|
|
in_devkit = true
|
|
when /^# #{DEVKIT_END}/
|
|
in_devkit = false
|
|
next
|
|
end
|
|
tgt.puts(src_line) unless in_devkit
|
|
end
|
|
|
|
# append new DevKit content
|
|
tgt.write(gem_override)
|
|
end
|
|
end
|
|
rescue
|
|
# restore backup if anything went wrong
|
|
FileUtils.cp(bkup, target)
|
|
end
|
|
end
|
|
private_class_method :update_gem_override
|
|
|
|
def self.scan_for(key)
|
|
ris = []
|
|
[Win32::Registry::HKEY_LOCAL_MACHINE, Win32::Registry::HKEY_CURRENT_USER].each do |hive|
|
|
begin
|
|
hive.open(key) do |ri_key|
|
|
ri_key.each_key do |skey, wtime|
|
|
# read the install location if a version subkey
|
|
if skey =~ /\d\.\d\.\d/
|
|
ri_key.open(skey) do |ver_key|
|
|
ri_root = ver_key['InstallLocation'].gsub('\\', '/')
|
|
puts '[INFO] found RubyInstaller v%s at %s' % [ skey, ri_root ]
|
|
ris << ri_root
|
|
end
|
|
end
|
|
end
|
|
end
|
|
rescue Win32::Registry::Error
|
|
end
|
|
end
|
|
ris
|
|
end
|
|
private_class_method :scan_for
|
|
|
|
def self.installed_rubies
|
|
rubies = REG_KEYS.collect { |key| scan_for(key) }
|
|
rubies.flatten.uniq
|
|
end
|
|
private_class_method :installed_rubies
|
|
|
|
def self.init
|
|
# get all known installed Ruby root dirs and write the root dirs
|
|
# to 'config.yml', overwriting any existing config file.
|
|
ir = installed_rubies
|
|
puts <<-EOT
|
|
|
|
Initialization complete! Please review and modify the auto-generated
|
|
'config.yml' file to ensure it contains the root directories to all
|
|
of the installed Rubies you want enhanced by the DevKit.
|
|
EOT
|
|
|
|
File.open(CONFIG_FILE, 'w') do |f|
|
|
f.write <<-EOT
|
|
# This configuration file contains the absolute path locations of all
|
|
# installed Rubies to be enhanced to work with the DevKit. This config
|
|
# file is generated by the 'ruby dk.rb init' step and may be modified
|
|
# before running the 'ruby dk.rb install' step. To include any installed
|
|
# Rubies that were not automagically discovered, simply add a line below
|
|
# the triple hyphens with the absolute path to the Ruby root directory.
|
|
#
|
|
# Example:
|
|
#
|
|
# ---
|
|
# - C:/ruby19trunk
|
|
# - C:/ruby192dev
|
|
#
|
|
EOT
|
|
unless ir.empty? then f.write(ir.to_yaml) else f.write("---\n") end
|
|
end
|
|
end
|
|
private_class_method :init
|
|
|
|
def self.review
|
|
if File.exists?(File.expand_path(CONFIG_FILE))
|
|
File.open(CONFIG_FILE, 'r') do |f|
|
|
puts <<-EOT
|
|
Based upon the settings in the '#{CONFIG_FILE}' file generated
|
|
from running 'ruby dk.rb init' and any of your customizations,
|
|
DevKit functionality will be injected into the following Rubies
|
|
when you run 'ruby dk.rb install'.
|
|
|
|
EOT
|
|
rubies = YAML.load(f.read)
|
|
if rubies.is_a?(Array)
|
|
rubies.each { |i| puts File.expand_path(i) }
|
|
else
|
|
puts "Invalid configuration. Please fix '#{CONFIG_FILE}.'"
|
|
exit(-2)
|
|
end
|
|
end
|
|
else
|
|
puts <<-EOT
|
|
Unable to find '#{CONFIG_FILE}'. Have you run 'ruby dk.rb init' yet?
|
|
EOT
|
|
exit(-2)
|
|
end
|
|
end
|
|
|
|
def self.install
|
|
begin
|
|
rubies = YAML.load_file(CONFIG_FILE)
|
|
rescue
|
|
puts <<-EOT
|
|
Error loading '#{CONFIG_FILE}'. Have you run 'ruby dk.rb init' yet?
|
|
EOT
|
|
exit(-2)
|
|
end
|
|
|
|
unless rubies.is_a?(Array) && !rubies.empty?
|
|
puts <<-EOT
|
|
Invalid configuration or no Rubies listed. Please fix '#{CONFIG_FILE}'
|
|
and rerun 'ruby dk.rb install'
|
|
EOT
|
|
exit(-2)
|
|
end
|
|
|
|
rubies.each do |path|
|
|
path = File.expand_path(path)
|
|
|
|
unless File.directory?(path)
|
|
puts "[ERROR] Skipping invalid directory '#{path}'"
|
|
next
|
|
end
|
|
|
|
site_ruby = Dir.glob("#{path}/lib/ruby/site_ruby")
|
|
site_rubygems = Dir.glob("#{path}/lib/ruby/site_ruby/[1-9].*/rubygems")
|
|
core_rubygems = Dir.glob("#{path}/lib/ruby/[1-9].*/rubygems")
|
|
|
|
# Warn and exit if unable to find a RubyGems installation
|
|
if site_rubygems.empty? && core_rubygems.empty?
|
|
puts <<-EOT
|
|
[ERROR] Unable to find RubyGems in site_ruby or core Ruby. Please
|
|
install RubyGems and rerun 'ruby dk.rb install'.
|
|
EOT
|
|
exit(-2)
|
|
else
|
|
# either (or both) site_rubygems or core_rubygems contains RubyGems;
|
|
# favor injecting override into site_rubygems over core_rubygems
|
|
target_ruby = site_rubygems.empty? ? core_rubygems : site_rubygems
|
|
|
|
# inject RubyGems override file into proper site_ruby location
|
|
# appending an existing override file if it doesn't already contain
|
|
# DevKit specific code.
|
|
target_ruby.each do |folder|
|
|
target = File.join(folder, 'defaults', 'operating_system.rb')
|
|
FileUtils.mkdir_p File.dirname(target)
|
|
|
|
if File.exist?(target)
|
|
content = File.read(target)
|
|
case
|
|
when content !~ /^#.*DevKit/o
|
|
# handle original and new token-based comments
|
|
puts "[INFO] Updating existing gem override for '#{path}'"
|
|
File.open(target, 'a') { |f| f.write(gem_override) }
|
|
when content =~ /^# #{DEVKIT_START} missing DevKit/o
|
|
# replace missing DevKit/build tool convenience notice
|
|
puts "[INFO] Updating convenience notice gem override for '#{path}'"
|
|
update_gem_override(target)
|
|
else
|
|
puts "[INFO] Skipping existing gem override for '#{path}'" unless $options[:force]
|
|
|
|
if $options[:force]
|
|
puts "[WARN] Updating (with backup) existing gem override for '#{path}'"
|
|
update_gem_override(target)
|
|
end
|
|
end
|
|
|
|
else
|
|
puts "[INFO] Installing '#{target}'"
|
|
File.open(target, 'w') { |f| f.write(gem_override) }
|
|
end
|
|
end
|
|
end
|
|
|
|
# inject DevKit PATH helper into site_ruby (allows for overriding)
|
|
# for the 'ruby -rdevkit extconf.rb' use case.
|
|
# TODO more robust JRuby check since can't assume JRuby is running
|
|
# this script?
|
|
jruby_site_shared = File.join(site_ruby, 'shared')
|
|
if File.directory?(jruby_site_shared) && File.exist?(File.join(path, 'bin', 'jruby.bat'))
|
|
site_ruby = jruby_site_shared
|
|
end
|
|
|
|
target = File.join(site_ruby, 'devkit.rb')
|
|
if File.exist?(target)
|
|
# Be paranoid about our 'site_ruby/devkit.rb' namespace. Either
|
|
# someone else has collided with it, or we've already written the
|
|
# helper lib. Warn the developer and skip rather than overwriting
|
|
# or appending.
|
|
puts "[WARN] Skipping existing DevKit helper library for '#{path}'" unless $options[:force]
|
|
|
|
if $options[:force]
|
|
puts "[WARN] Updating (with backup) DevKit helper library for '#{path}'"
|
|
File.rename(target, "#{target}.#{timestamp}")
|
|
File.open(target, 'w') { |f| f.write(devkit_lib) }
|
|
end
|
|
else
|
|
puts "[INFO] Installing '#{target}'"
|
|
File.open(target, 'w') { |f| f.write(devkit_lib) }
|
|
end
|
|
end
|
|
end
|
|
private_class_method :install
|
|
|
|
def self.usage_and_exit
|
|
$stderr.puts usage
|
|
exit(-1)
|
|
end
|
|
|
|
def self.run(*args)
|
|
send(args.first)
|
|
end
|
|
|
|
end
|
|
|
|
if __FILE__ == $0
|
|
if ARGV.empty? || ARGV.delete('--help') || ARGV.delete('-h')
|
|
DevKitInstaller.usage_and_exit
|
|
end
|
|
|
|
cmd = ARGV.delete('init') ||
|
|
ARGV.delete('review') ||
|
|
ARGV.delete('install')
|
|
|
|
$options ||= {}
|
|
$options[:force] = ARGV.delete('--force') || ARGV.delete('-f')
|
|
|
|
DevKitInstaller.usage_and_exit unless ARGV.empty?
|
|
|
|
DevKitInstaller.run(cmd)
|
|
end
|