Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(MODULES-1176) FFI module - allow to work on x86 / x64 #55

Merged
merged 5 commits into from
Aug 15, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,23 @@ group :development, :test do
gem 'pry', :require => false
gem 'simplecov', :require => false
gem 'rspec', '~>2.14.0', :require => false
gem 'beaker', :require => false
gem 'beaker', :require => false, :platforms => :ruby
end

is_x64 = Gem::Platform.local.cpu == 'x64'
if is_x64
platform(:x64_mingw) do
gem "win32-dir", "~> 0.4.9", :require => false
gem "win32-process", "~> 0.7.4", :require => false
gem "win32-service", "~> 0.8.4", :require => false
gem "minitar", "~> 0.5.4", :require => false
end
else
platform(:mingw) do
gem "win32-process", "~> 0.6.5", :require => false
gem "win32-service", "~> 0.7.2", :require => false
gem "minitar", "~> 0.5.4", :require => false
end
end

ENV['GEM_PUPPET_VERSION'] ||= ENV['PUPPET_GEM_VERSION']
Expand Down
3 changes: 1 addition & 2 deletions lib/puppet/provider/registry_key/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ def destroy
Puppet.debug("Destroying registry key #{self}")

raise ArgumentError, "Cannot delete root key: #{path}" unless subkey
reg_delete_key_ex = Win32API.new('advapi32', 'RegDeleteKeyEx', 'LPLL', 'L')

# hive.hkey returns an integer value that's like a FD
if reg_delete_key_ex.call(hive.hkey, subkey, access, 0) != 0
if RegDeleteKeyExA(hive.hkey, subkey, access, 0) != 0
raise "Failed to delete registry key: #{self}"
end
end
Expand Down
24 changes: 9 additions & 15 deletions lib/puppet/provider/registry_value/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ def exists?
found = false
begin
hive.open(subkey, Win32::Registry::KEY_READ | access) do |reg|
type = [0].pack('L')
size = [0].pack('L')
found = reg_query_value_ex_a.call(reg.hkey, valuename, 0, type, 0, size) == 0
status = RegQueryValueExA(reg.hkey, valuename,
FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL,
FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL)

found = status == 0
raise Win32::Registry::Error.new(status) if !found
end
rescue Win32::Registry::Error => detail
case detail.code
Expand Down Expand Up @@ -83,10 +86,9 @@ def regvalue
unless @regvalue
@regvalue = {}
hive.open(subkey, Win32::Registry::KEY_READ | access) do |reg|
type = [0].pack('L')
size = [0].pack('L')

if reg_query_value_ex_a.call(reg.hkey, valuename, 0, type, 0, size) == 0
if RegQueryValueExA(reg.hkey, valuename,
FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL,
FFI::MemoryPointer::NULL, FFI::MemoryPointer::NULL) == 0
@regvalue[:type], @regvalue[:data] = from_native(reg.read(valuename))
end
end
Expand Down Expand Up @@ -139,14 +141,6 @@ def from_native(ary)
return [type2name(ntype), pdata.kind_of?(Array) ? pdata : [pdata]]
end

def reg_query_value_ex_a
self.class.reg_query_value_ex_a
end

def self.reg_query_value_ex_a
@reg_query_value_ex_a ||= Win32API.new('advapi32', 'RegQueryValueEx', 'LPLPPP', 'L')
end

private

def write_value
Expand Down
2 changes: 1 addition & 1 deletion lib/puppet_x/puppetlabs/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def filter_path
/hkey_performance_text/,
/hkey_performance_nlstext/,
/hkey_dyn_data/
raise ArgumentError, "Unsupported prefined key: #{path}"
raise ArgumentError, "Unsupported predefined key: #{path}"
else
raise ArgumentError, "Invalid registry key: #{path}"
end
Expand Down
51 changes: 51 additions & 0 deletions lib/puppet_x/puppetlabs/registry/provider_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,46 @@ module PuppetX
module Puppetlabs
module Registry
module ProviderBase
def self.define_ffi
extend FFI::Library

ffi_convention :stdcall

# uintptr_t is defined in an FFI conf as platform specific, either
# ulong_long on x64 or just ulong on x86
typedef :uintptr_t, :handle
# any time LONG / ULONG is in a win32 API definition DO NOT USE platform specific width
# which is what FFI uses by default
# instead create new aliases for these very special cases
typedef :int32, :win32_long
typedef :uint32, :win32_ulong
typedef :uint32, :dword

# http://msdn.microsoft.com/en-us/library/windows/desktop/ms724911(v=vs.85).aspx
# LONG WINAPI RegQueryValueEx(
# _In_ HKEY hKey,
# _In_opt_ LPCTSTR lpValueName,
# _Reserved_ LPDWORD lpReserved,
# _Out_opt_ LPDWORD lpType,
# _Out_opt_ LPBYTE lpData,
# _Inout_opt_ LPDWORD lpcbData
# );
ffi_lib :advapi32
attach_function :RegQueryValueExA,
[:handle, :pointer, :pointer, :pointer, :pointer, :pointer], :win32_long

# http://msdn.microsoft.com/en-us/library/windows/desktop/ms724847(v=vs.85).aspx
# LONG WINAPI RegDeleteKeyEx(
# _In_ HKEY hKey,
# _In_ LPCTSTR lpSubKey,
# _In_ REGSAM samDesired,
# _Reserved_ DWORD Reserved
# );
ffi_lib :advapi32
attach_function :RegDeleteKeyExA,
[:handle, :pointer, :win32_ulong, :dword], :win32_long
end

# This is a class method in order to be easily mocked in the spec tests.
def self.initialize_system_api
if Puppet.features.microsoft_windows?
Expand All @@ -15,6 +55,17 @@ def self.initialize_system_api
error.set_backtrace exc.backtrace
raise error
end

begin
require 'ffi'
define_ffi
rescue LoadError => exc
msg = "Could not load the required ffi library [#{exc.message}]"
Puppet.err msg
error = Puppet::Error.new(msg)
error.set_backtrace exc.backtrace
raise error
end
end
end

Expand Down
2 changes: 1 addition & 1 deletion metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
},
{
"name": "puppet",
"version_requirement": "3.x"
"version_requirement": ">= 3.3.0"
}
],
"description": "This module provides a native type and provider to manage keys and values in the Windows Registry",
Expand Down
29 changes: 29 additions & 0 deletions spec/unit/puppet/provider/registry_key_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#! /usr/bin/env ruby

require 'spec_helper'
require 'puppet/type/registry_key'

describe Puppet::Type.type(:registry_key).provider(:registry), :if => Puppet.features.microsoft_windows? do
let (:catalog) do Puppet::Resource::Catalog.new end
let (:type) { Puppet::Type.type(:registry_key) }

describe "#destroy" do
it "can destroy a randomly created key" do

guid = SecureRandom.uuid
reg_key = type.new(:path => "hklm\\SOFTWARE\\Puppet Labs\\PuppetRegProviderTest\\#{guid}", :provider => described_class.name)
already_exists = reg_key.provider.exists?
already_exists.should be_false

# something has gone terribly wrong here, pull the ripcord
break if already_exists

reg_key.provider.create
reg_key.provider.exists?.should be_true

# test FFI code
reg_key.provider.destroy
reg_key.provider.exists?.should be_false
end
end
end
29 changes: 29 additions & 0 deletions spec/unit/puppet/provider/registry_value_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#! /usr/bin/env ruby

require 'spec_helper'
require 'puppet/type/registry_value'

describe Puppet::Type.type(:registry_value).provider(:registry), :if => Puppet.features.microsoft_windows? do
let (:catalog) do Puppet::Resource::Catalog.new end
let (:type) { Puppet::Type.type(:registry_value) }

describe "#exists?" do
it "should return true for a well known hive" do
reg_value = type.new(:path => 'hklm\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareType', :provider => described_class.name)
reg_value.provider.exists?.should be_true
end

it "should return false for a bogus hive/path" do
reg_value = type.new(:path => 'hklm\foobar5000', :catalog => catalog, :provider => described_class.name)
reg_value.provider.exists?.should be_false
end
end

describe "#regvalue" do
it "should return a valid string for a well known key" do
reg_value = type.new(:path => 'hklm\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareType', :provider => described_class.name)
reg_value.provider.data.should eq ['System']
reg_value.provider.type.should eq :string
end
end
end