Downgrade mysql cookbook for now
It doesn't play well with our current dev server setup
This commit is contained in:
@@ -1,57 +0,0 @@
|
||||
class Chef
|
||||
class Provider
|
||||
class WindowsFeature
|
||||
module Base
|
||||
def action_install
|
||||
unless installed?
|
||||
install_feature(@new_resource.feature_name)
|
||||
@new_resource.updated_by_last_action(true)
|
||||
Chef::Log.info("#{@new_resource} installed feature")
|
||||
else
|
||||
Chef::Log.debug("#{@new_resource} is already installed - nothing to do")
|
||||
end
|
||||
end
|
||||
|
||||
def action_remove
|
||||
if installed?
|
||||
remove_feature(@new_resource.feature_name)
|
||||
@new_resource.updated_by_last_action(true)
|
||||
Chef::Log.info("#{@new_resource} removed")
|
||||
else
|
||||
Chef::Log.debug("#{@new_resource} feature does not exist - nothing to do")
|
||||
end
|
||||
end
|
||||
|
||||
def action_delete
|
||||
if available?
|
||||
delete_feature(@new_resource.feature_name)
|
||||
@new_resource.updated_by_last_action(true)
|
||||
Chef::Log.info("#{@new_resource} deleted")
|
||||
else
|
||||
Chef::Log.debug("#{@new_resource} feature is not installed - nothing to do")
|
||||
end
|
||||
end
|
||||
|
||||
def install_feature(_name)
|
||||
fail Chef::Exceptions::UnsupportedAction, "#{self} does not support :install"
|
||||
end
|
||||
|
||||
def remove_feature(_name)
|
||||
fail Chef::Exceptions::UnsupportedAction, "#{self} does not support :remove"
|
||||
end
|
||||
|
||||
def delete_feature(_name)
|
||||
fail Chef::Exceptions::UnsupportedAction, "#{self} does not support :delete"
|
||||
end
|
||||
|
||||
def installed?
|
||||
fail Chef::Exceptions::Override, "You must override installed? in #{self}"
|
||||
end
|
||||
|
||||
def available?
|
||||
fail Chef::Exceptions::Override, "You must override available? in #{self}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,53 +1,53 @@
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Cookbook Name:: windows
|
||||
# Library:: helper
|
||||
#
|
||||
# Copyright:: 2011-2015, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/mixin/shell_out'
|
||||
|
||||
module Powershell
|
||||
module Helper
|
||||
include Chef::Mixin::ShellOut
|
||||
|
||||
def powershell_installed?
|
||||
!powershell_version.nil?
|
||||
end
|
||||
|
||||
def interpreter
|
||||
# force 64-bit powershell from 32-bit ruby process
|
||||
if ::File.exist?("#{ENV['WINDIR']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe")
|
||||
"#{ENV['WINDIR']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe"
|
||||
elsif ::File.exist?("#{ENV['WINDIR']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe")
|
||||
"#{ENV['WINDIR']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe"
|
||||
else
|
||||
'powershell.exe'
|
||||
end
|
||||
end
|
||||
|
||||
def powershell_version
|
||||
cmd = shell_out("#{interpreter} -InputFormat none -Command \"& echo $PSVersionTable.psversion.major\"")
|
||||
if cmd.stdout.empty? # PowerShell 1.0 doesn't have a $PSVersionTable
|
||||
1
|
||||
else
|
||||
Regexp.last_match(1).to_i if cmd.stdout =~ /^(\d+)/
|
||||
end
|
||||
rescue Errno::ENOENT
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Cookbook:: windows
|
||||
# Library:: helper
|
||||
#
|
||||
# Copyright:: 2011-2017, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
require 'chef/mixin/shell_out'
|
||||
|
||||
module Powershell
|
||||
module Helper
|
||||
include Chef::Mixin::ShellOut
|
||||
|
||||
def powershell_installed?
|
||||
!powershell_version.nil?
|
||||
end
|
||||
|
||||
def interpreter
|
||||
# force 64-bit powershell from 32-bit ruby process
|
||||
if ::File.exist?("#{ENV['WINDIR']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe")
|
||||
"#{ENV['WINDIR']}\\sysnative\\WindowsPowershell\\v1.0\\powershell.exe"
|
||||
elsif ::File.exist?("#{ENV['WINDIR']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe")
|
||||
"#{ENV['WINDIR']}\\system32\\WindowsPowershell\\v1.0\\powershell.exe"
|
||||
else
|
||||
'powershell.exe'
|
||||
end
|
||||
end
|
||||
|
||||
def powershell_version
|
||||
cmd = shell_out("#{interpreter} -InputFormat none -Command \"& echo $PSVersionTable.psversion.major\"")
|
||||
if cmd.stdout.empty? # PowerShell 1.0 doesn't have a $PSVersionTable
|
||||
1
|
||||
else
|
||||
Regexp.last_match(1).to_i if cmd.stdout =~ /^(\d+)/
|
||||
end
|
||||
rescue Errno::ENOENT
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
|
||||
#
|
||||
# WARNING
|
||||
#
|
||||
# THIS CODE HAS BEEN MOVED TO CORE CHEF. DO NOT SUMBIT PULL REQUESTS AGAINST THIS
|
||||
# CODE. IT WILL BE REMOVED IN THE FUTURE.
|
||||
#
|
||||
|
||||
unless defined? Chef::Mixin::PowershellOut
|
||||
class Chef
|
||||
module Mixin
|
||||
module PowershellOut
|
||||
include Chef::Mixin::ShellOut
|
||||
|
||||
begin
|
||||
include Chef::Mixin::WindowsArchitectureHelper
|
||||
rescue
|
||||
# nothing to do, as the include will happen when windows_architecture_helper.rb
|
||||
# is loaded. This is for ease of removal of that library when either
|
||||
# powershell_out is core chef or powershell cookbook depends upon version
|
||||
# of chef that has Chef::Mixin::WindowsArchitectureHelper in core chef
|
||||
end
|
||||
|
||||
def powershell_out(*command_args)
|
||||
Chef::Log.warn 'The powershell_out library in the windows cookbook is deprecated.'
|
||||
Chef::Log.warn 'Please upgrade to Chef 12.4.0 or later where it is built-in to core chef.'
|
||||
script = command_args.first
|
||||
options = command_args.last.is_a?(Hash) ? command_args.last : nil
|
||||
|
||||
run_command(script, options)
|
||||
end
|
||||
|
||||
def powershell_out!(*command_args)
|
||||
cmd = powershell_out(*command_args)
|
||||
cmd.error!
|
||||
cmd
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_command(script, options)
|
||||
if options && options[:architecture]
|
||||
architecture = options[:architecture]
|
||||
options.delete(:architecture)
|
||||
else
|
||||
architecture = node_windows_architecture(node)
|
||||
end
|
||||
|
||||
disable_redirection = wow64_architecture_override_required?(node, architecture)
|
||||
|
||||
if disable_redirection
|
||||
original_redirection_state = disable_wow64_file_redirection(node)
|
||||
end
|
||||
|
||||
command = build_command(script)
|
||||
|
||||
if options
|
||||
cmd = shell_out(command, options)
|
||||
else
|
||||
cmd = shell_out(command)
|
||||
end
|
||||
|
||||
if disable_redirection
|
||||
restore_wow64_file_redirection(node, original_redirection_state)
|
||||
end
|
||||
|
||||
cmd
|
||||
end
|
||||
|
||||
def build_command(script)
|
||||
flags = [
|
||||
# Hides the copyright banner at startup.
|
||||
'-NoLogo',
|
||||
# Does not present an interactive prompt to the user.
|
||||
'-NonInteractive',
|
||||
# Does not load the Windows PowerShell profile.
|
||||
'-NoProfile',
|
||||
# always set the ExecutionPolicy flag
|
||||
# see http://technet.microsoft.com/en-us/library/ee176961.aspx
|
||||
'-ExecutionPolicy RemoteSigned',
|
||||
# Powershell will hang if STDIN is redirected
|
||||
# http://connect.microsoft.com/PowerShell/feedback/details/572313/powershell-exe-can-hang-if-stdin-is-redirected
|
||||
'-InputFormat None'
|
||||
]
|
||||
|
||||
command = "powershell.exe #{flags.join(' ')} -Command \"#{script}\""
|
||||
command
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,355 +1,356 @@
|
||||
#
|
||||
# Author:: Doug MacEachern (<dougm@vmware.com>)
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Author:: Paul Morton (<pmorton@biaprotect.com>)
|
||||
# Cookbook Name:: windows
|
||||
# Provider:: registry
|
||||
#
|
||||
# Copyright:: 2010, VMware, Inc.
|
||||
# Copyright:: 2011-2015, Chef Software, Inc.
|
||||
# Copyright:: 2011, Business Intelligence Associates, Inc
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
require 'win32/registry'
|
||||
require_relative 'wmi_helper'
|
||||
end
|
||||
|
||||
module Windows
|
||||
module RegistryHelper
|
||||
@@native_registry_constant = ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64' ? 0x0100 : 0x0200
|
||||
|
||||
def get_hive_name(path)
|
||||
Chef::Log.debug('Resolving registry shortcuts to full names')
|
||||
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
|
||||
hkey = {
|
||||
'HKLM' => 'HKEY_LOCAL_MACHINE',
|
||||
'HKCU' => 'HKEY_CURRENT_USER',
|
||||
'HKU' => 'HKEY_USERS'
|
||||
}[hive_name] || hive_name
|
||||
|
||||
Chef::Log.debug("Hive resolved to #{hkey}")
|
||||
hkey
|
||||
end
|
||||
|
||||
def get_hive(path)
|
||||
Chef::Log.debug("Getting hive for #{path}")
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
|
||||
hkey = get_hive_name(path)
|
||||
|
||||
hive = {
|
||||
'HKEY_LOCAL_MACHINE' => ::Win32::Registry::HKEY_LOCAL_MACHINE,
|
||||
'HKEY_USERS' => ::Win32::Registry::HKEY_USERS,
|
||||
'HKEY_CURRENT_USER' => ::Win32::Registry::HKEY_CURRENT_USER
|
||||
}[hkey]
|
||||
|
||||
unless hive
|
||||
Chef::Application.fatal!("Unsupported registry hive '#{hive_name}'")
|
||||
end
|
||||
|
||||
Chef::Log.debug("Registry hive resolved to #{hkey}")
|
||||
hive
|
||||
end
|
||||
|
||||
def unload_hive(path)
|
||||
hive = get_hive(path)
|
||||
if hive == ::Win32::Registry::HKEY_USERS
|
||||
reg_path = path.split('\\')
|
||||
priv = Chef::WindowsPrivileged.new
|
||||
begin
|
||||
priv.reg_unload_key(reg_path[1])
|
||||
rescue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_value(mode, path, values, type = nil)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key_name = reg_path.join('\\')
|
||||
|
||||
Chef::Log.debug("Creating #{path}")
|
||||
|
||||
create_key(path) unless key_exists?(path, true)
|
||||
|
||||
hive.send(mode, key_name, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
|
||||
changed_something = false
|
||||
values.each do |k, val|
|
||||
key = k.to_s # wtf. avoid "can't modify frozen string" in win32/registry.rb
|
||||
cur_val = nil
|
||||
begin
|
||||
cur_val = reg[key]
|
||||
rescue
|
||||
# subkey does not exist (ok)
|
||||
end
|
||||
|
||||
next unless cur_val != val
|
||||
|
||||
Chef::Log.debug("setting #{key}=#{val}")
|
||||
|
||||
type = :string if type.nil?
|
||||
|
||||
reg_type = {
|
||||
binary: ::Win32::Registry::REG_BINARY,
|
||||
string: ::Win32::Registry::REG_SZ,
|
||||
multi_string: ::Win32::Registry::REG_MULTI_SZ,
|
||||
expand_string: ::Win32::Registry::REG_EXPAND_SZ,
|
||||
dword: ::Win32::Registry::REG_DWORD,
|
||||
dword_big_endian: ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
|
||||
qword: ::Win32::Registry::REG_QWORD
|
||||
}[type]
|
||||
|
||||
reg.write(key, reg_type, val)
|
||||
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
|
||||
changed_something = true
|
||||
end
|
||||
return changed_something
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def get_value(path, value)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
|
||||
hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
|
||||
begin
|
||||
return reg[value]
|
||||
rescue
|
||||
return nil
|
||||
ensure
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_values(path)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
|
||||
values = []
|
||||
begin
|
||||
reg.each_value do |name, type, data|
|
||||
values << [name, type, data]
|
||||
end
|
||||
rescue
|
||||
ensure
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
end
|
||||
values
|
||||
end
|
||||
end
|
||||
|
||||
def delete_value(path, values)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
Chef::Log.debug("Deleting values in #{path}")
|
||||
hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
|
||||
values.each_key do |key|
|
||||
name = key.to_s
|
||||
# Ensure delete operation is idempotent.
|
||||
if value_exists?(path, key)
|
||||
Chef::Log.debug("Deleting value #{name} in #{path}")
|
||||
reg.delete_value(name)
|
||||
else
|
||||
Chef::Log.debug("Value #{name} in #{path} does not exist, skipping.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_key(path)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
Chef::Log.debug("Creating registry key #{path}")
|
||||
hive.create(key)
|
||||
end
|
||||
|
||||
def value_exists?(path, value)
|
||||
if key_exists?(path, true)
|
||||
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
|
||||
Chef::Log.debug("Attempting to open #{key}")
|
||||
Chef::Log.debug("Native Constant #{@@native_registry_constant}")
|
||||
Chef::Log.debug("Hive #{hive}")
|
||||
|
||||
hive.open(key, ::Win32::Registry::KEY_READ | @@native_registry_constant) do |reg|
|
||||
begin
|
||||
rtn_value = reg[value]
|
||||
return true
|
||||
rescue
|
||||
return false
|
||||
ensure
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
# TODO: Does not load user registry...
|
||||
def key_exists?(path, load_hive = false)
|
||||
if load_hive
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
else
|
||||
hive = get_hive(path)
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
root_key = reg_path[0]
|
||||
key = reg_path.join('\\')
|
||||
hive_loaded = false
|
||||
end
|
||||
|
||||
begin
|
||||
hive.open(key, ::Win32::Registry::Constants::KEY_READ | @@native_registry_constant)
|
||||
return true
|
||||
rescue
|
||||
return false
|
||||
ensure
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
end
|
||||
end
|
||||
|
||||
def get_user_hive_location(sid)
|
||||
reg_key = "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\#{sid}"
|
||||
Chef::Log.debug("Looking for profile at #{reg_key}")
|
||||
if key_exists?(reg_key)
|
||||
return get_value(reg_key, 'ProfileImagePath')
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_user_to_sid(username)
|
||||
user_query = execute_wmi_query("select * from Win32_UserAccount where Name='#{username}'")
|
||||
sid = nil
|
||||
|
||||
user_query.each do |user|
|
||||
sid = wmi_object_property(user, 'sid')
|
||||
break
|
||||
end
|
||||
|
||||
Chef::Log.debug("Resolved user SID to #{sid}")
|
||||
return sid
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
|
||||
def hive_loaded?(path)
|
||||
hive = get_hive(path)
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
user_hive = path[0]
|
||||
|
||||
if user_hive?(hive)
|
||||
return key_exists?("#{hive_name}\\#{user_hive}")
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def user_hive?(hive)
|
||||
if hive == ::Win32::Registry::HKEY_USERS
|
||||
return true
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def get_reg_path_info(path)
|
||||
hive = get_hive(path)
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
root_key = reg_path[0]
|
||||
hive_loaded = false
|
||||
|
||||
if user_hive?(hive) && !key_exists?("#{hive_name}\\#{root_key}")
|
||||
reg_path, hive_loaded = load_user_hive(hive, reg_path, root_key)
|
||||
root_key = reg_path[0]
|
||||
Chef::Log.debug("Resolved user (#{path}) to (#{reg_path.join('/')})")
|
||||
end
|
||||
|
||||
[hive, reg_path, hive_name, root_key, hive_loaded]
|
||||
end
|
||||
|
||||
def load_user_hive(hive, reg_path, user_hive)
|
||||
Chef::Log.debug("Reg Path #{reg_path}")
|
||||
# See if the hive is loaded. Logged in users will have a key that is named their SID
|
||||
# if the user has specified the a path by SID and the user is logged in, this function
|
||||
# should not be executed.
|
||||
if user_hive?(hive) && !key_exists?("HKU\\#{user_hive}")
|
||||
Chef::Log.debug('The user is not logged in and has not been specified by SID')
|
||||
sid = resolve_user_to_sid(user_hive)
|
||||
Chef::Log.debug("User SID resolved to (#{sid})")
|
||||
# Now that the user has been resolved to a SID, check and see if the hive exists.
|
||||
# If this exists by SID, the user is logged in and we should use that key.
|
||||
# TODO: Replace the username with the sid and send it back because the username
|
||||
# does not exist as the key location.
|
||||
load_reg = false
|
||||
if key_exists?("HKU\\#{sid}")
|
||||
reg_path[0] = sid # use the active profile (user is logged on)
|
||||
Chef::Log.debug("HKEY_USERS Mapped: #{user_hive} -> #{sid}")
|
||||
else
|
||||
Chef::Log.debug('User is not logged in')
|
||||
load_reg = true
|
||||
end
|
||||
|
||||
# The user is not logged in, so we should load the registry from disk
|
||||
if load_reg
|
||||
profile_path = get_user_hive_location(sid)
|
||||
unless profile_path.nil?
|
||||
ntuser_dat = "#{profile_path}\\NTUSER.DAT"
|
||||
if ::File.exist?(ntuser_dat)
|
||||
priv = Chef::WindowsPrivileged.new
|
||||
if priv.reg_load_key(sid, ntuser_dat)
|
||||
Chef::Log.debug("RegLoadKey(#{sid}, #{user_hive}, #{ntuser_dat})")
|
||||
reg_path[0] = sid
|
||||
else
|
||||
Chef::Log.debug("Failed RegLoadKey(#{sid}, #{user_hive}, #{ntuser_dat})")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[reg_path, load_reg]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_hive_unloaded(hive_loaded = false)
|
||||
if hive_loaded
|
||||
Chef::Log.debug('Hive was loaded, we really should unload it')
|
||||
unload_hive(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Registry
|
||||
module_function
|
||||
|
||||
extend Windows::RegistryHelper
|
||||
end
|
||||
#
|
||||
# Author:: Doug MacEachern (<dougm@vmware.com>)
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Author:: Paul Morton (<pmorton@biaprotect.com>)
|
||||
# Cookbook:: windows
|
||||
# Provider:: registry
|
||||
#
|
||||
# Copyright:: 2010-2017, VMware, Inc.
|
||||
# Copyright:: 2011-2017, Chef Software, Inc.
|
||||
# Copyright:: 2011-2017, Business Intelligence Associates, Inc
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
require 'win32/registry'
|
||||
require_relative 'wmi_helper'
|
||||
end
|
||||
|
||||
module Windows
|
||||
module RegistryHelper
|
||||
@@native_registry_constant = if ENV['PROCESSOR_ARCHITECTURE'] == 'AMD64' ||
|
||||
ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64'
|
||||
0x0100
|
||||
else
|
||||
0x0200
|
||||
end
|
||||
|
||||
def get_hive_name(path)
|
||||
Chef::Log.debug('Resolving registry shortcuts to full names')
|
||||
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
|
||||
hkey = {
|
||||
'HKLM' => 'HKEY_LOCAL_MACHINE',
|
||||
'HKCU' => 'HKEY_CURRENT_USER',
|
||||
'HKU' => 'HKEY_USERS',
|
||||
}[hive_name] || hive_name
|
||||
|
||||
Chef::Log.debug("Hive resolved to #{hkey}")
|
||||
hkey
|
||||
end
|
||||
|
||||
def get_hive(path)
|
||||
Chef::Log.debug("Getting hive for #{path}")
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
|
||||
hkey = get_hive_name(path)
|
||||
|
||||
hive = {
|
||||
'HKEY_LOCAL_MACHINE' => ::Win32::Registry::HKEY_LOCAL_MACHINE,
|
||||
'HKEY_USERS' => ::Win32::Registry::HKEY_USERS,
|
||||
'HKEY_CURRENT_USER' => ::Win32::Registry::HKEY_CURRENT_USER,
|
||||
}[hkey]
|
||||
|
||||
unless hive
|
||||
Chef::Application.fatal!("Unsupported registry hive '#{hive_name}'")
|
||||
end
|
||||
|
||||
Chef::Log.debug("Registry hive resolved to #{hkey}")
|
||||
hive
|
||||
end
|
||||
|
||||
def unload_hive(path)
|
||||
hive = get_hive(path)
|
||||
if hive == ::Win32::Registry::HKEY_USERS
|
||||
reg_path = path.split('\\')
|
||||
priv = Chef::WindowsPrivileged.new
|
||||
begin
|
||||
priv.reg_unload_key(reg_path[1])
|
||||
rescue
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_value(mode, path, values, type = nil)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key_name = reg_path.join('\\')
|
||||
|
||||
Chef::Log.debug("Creating #{path}")
|
||||
|
||||
create_key(path) unless key_exists?(path, true)
|
||||
|
||||
hive.send(mode, key_name, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
|
||||
changed_something = false
|
||||
values.each do |k, val|
|
||||
key = k.to_s # wtf. avoid "can't modify frozen string" in win32/registry.rb
|
||||
cur_val = nil
|
||||
begin
|
||||
cur_val = reg[key]
|
||||
rescue
|
||||
# subkey does not exist (ok)
|
||||
end
|
||||
|
||||
next unless cur_val != val
|
||||
|
||||
Chef::Log.debug("setting #{key}=#{val}")
|
||||
|
||||
type = :string if type.nil?
|
||||
|
||||
reg_type = {
|
||||
binary: ::Win32::Registry::REG_BINARY,
|
||||
string: ::Win32::Registry::REG_SZ,
|
||||
multi_string: ::Win32::Registry::REG_MULTI_SZ,
|
||||
expand_string: ::Win32::Registry::REG_EXPAND_SZ,
|
||||
dword: ::Win32::Registry::REG_DWORD,
|
||||
dword_big_endian: ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
|
||||
qword: ::Win32::Registry::REG_QWORD,
|
||||
}[type]
|
||||
|
||||
reg.write(key, reg_type, val)
|
||||
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
|
||||
changed_something = true
|
||||
end
|
||||
return changed_something
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def get_value(path, value)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
|
||||
hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
|
||||
begin
|
||||
return reg[value]
|
||||
rescue
|
||||
return nil
|
||||
ensure
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_values(path)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
|
||||
values = []
|
||||
begin
|
||||
reg.each_value do |name, type, data|
|
||||
values << [name, type, data]
|
||||
end
|
||||
rescue
|
||||
ensure
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
end
|
||||
values
|
||||
end
|
||||
end
|
||||
|
||||
def delete_value(path, values)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
Chef::Log.debug("Deleting values in #{path}")
|
||||
hive.open(key, ::Win32::Registry::KEY_ALL_ACCESS | @@native_registry_constant) do |reg|
|
||||
values.each_key do |key|
|
||||
name = key.to_s
|
||||
# Ensure delete operation is idempotent.
|
||||
if value_exists?(path, key)
|
||||
Chef::Log.debug("Deleting value #{name} in #{path}")
|
||||
reg.delete_value(name)
|
||||
else
|
||||
Chef::Log.debug("Value #{name} in #{path} does not exist, skipping.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_key(path)
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
Chef::Log.debug("Creating registry key #{path}")
|
||||
hive.create(key)
|
||||
end
|
||||
|
||||
def value_exists?(path, value)
|
||||
if key_exists?(path, true)
|
||||
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
|
||||
Chef::Log.debug("Attempting to open #{key}")
|
||||
Chef::Log.debug("Native Constant #{@@native_registry_constant}")
|
||||
Chef::Log.debug("Hive #{hive}")
|
||||
|
||||
hive.open(key, ::Win32::Registry::KEY_READ | @@native_registry_constant) do |reg|
|
||||
begin
|
||||
rtn_value = reg[value]
|
||||
return true
|
||||
rescue
|
||||
return false
|
||||
ensure
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
# TODO: Does not load user registry...
|
||||
def key_exists?(path, load_hive = false)
|
||||
if load_hive
|
||||
hive, reg_path, hive_name, root_key, hive_loaded = get_reg_path_info(path)
|
||||
key = reg_path.join('\\')
|
||||
else
|
||||
hive = get_hive(path)
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
root_key = reg_path[0]
|
||||
key = reg_path.join('\\')
|
||||
hive_loaded = false
|
||||
end
|
||||
|
||||
begin
|
||||
hive.open(key, ::Win32::Registry::Constants::KEY_READ | @@native_registry_constant)
|
||||
return true
|
||||
rescue
|
||||
return false
|
||||
ensure
|
||||
ensure_hive_unloaded(hive_loaded)
|
||||
end
|
||||
end
|
||||
|
||||
def get_user_hive_location(sid)
|
||||
reg_key = "HKLM\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\#{sid}"
|
||||
Chef::Log.debug("Looking for profile at #{reg_key}")
|
||||
if key_exists?(reg_key)
|
||||
return get_value(reg_key, 'ProfileImagePath')
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_user_to_sid(username)
|
||||
user_query = execute_wmi_query("select * from Win32_UserAccount where Name='#{username}'")
|
||||
sid = nil
|
||||
|
||||
user_query.each do |user|
|
||||
sid = wmi_object_property(user, 'sid')
|
||||
break
|
||||
end
|
||||
|
||||
Chef::Log.debug("Resolved user SID to #{sid}")
|
||||
return sid
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
|
||||
def hive_loaded?(path)
|
||||
hive = get_hive(path)
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
user_hive = path[0]
|
||||
|
||||
if user_hive?(hive)
|
||||
return key_exists?("#{hive_name}\\#{user_hive}")
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
def user_hive?(hive)
|
||||
hive == ::Win32::Registry::HKEY_USERS
|
||||
end
|
||||
|
||||
def get_reg_path_info(path)
|
||||
hive = get_hive(path)
|
||||
reg_path = path.split('\\')
|
||||
hive_name = reg_path.shift
|
||||
root_key = reg_path[0]
|
||||
hive_loaded = false
|
||||
|
||||
if user_hive?(hive) && !key_exists?("#{hive_name}\\#{root_key}")
|
||||
reg_path, hive_loaded = load_user_hive(hive, reg_path, root_key)
|
||||
root_key = reg_path[0]
|
||||
Chef::Log.debug("Resolved user (#{path}) to (#{reg_path.join('/')})")
|
||||
end
|
||||
|
||||
[hive, reg_path, hive_name, root_key, hive_loaded]
|
||||
end
|
||||
|
||||
def load_user_hive(hive, reg_path, user_hive)
|
||||
Chef::Log.debug("Reg Path #{reg_path}")
|
||||
# See if the hive is loaded. Logged in users will have a key that is named their SID
|
||||
# if the user has specified the a path by SID and the user is logged in, this function
|
||||
# should not be executed.
|
||||
if user_hive?(hive) && !key_exists?("HKU\\#{user_hive}")
|
||||
Chef::Log.debug('The user is not logged in and has not been specified by SID')
|
||||
sid = resolve_user_to_sid(user_hive)
|
||||
Chef::Log.debug("User SID resolved to (#{sid})")
|
||||
# Now that the user has been resolved to a SID, check and see if the hive exists.
|
||||
# If this exists by SID, the user is logged in and we should use that key.
|
||||
# TODO: Replace the username with the sid and send it back because the username
|
||||
# does not exist as the key location.
|
||||
load_reg = false
|
||||
if key_exists?("HKU\\#{sid}")
|
||||
reg_path[0] = sid # use the active profile (user is logged on)
|
||||
Chef::Log.debug("HKEY_USERS Mapped: #{user_hive} -> #{sid}")
|
||||
else
|
||||
Chef::Log.debug('User is not logged in')
|
||||
load_reg = true
|
||||
end
|
||||
|
||||
# The user is not logged in, so we should load the registry from disk
|
||||
if load_reg
|
||||
profile_path = get_user_hive_location(sid)
|
||||
unless profile_path.nil?
|
||||
ntuser_dat = "#{profile_path}\\NTUSER.DAT"
|
||||
if ::File.exist?(ntuser_dat)
|
||||
priv = Chef::WindowsPrivileged.new
|
||||
if priv.reg_load_key(sid, ntuser_dat)
|
||||
Chef::Log.debug("RegLoadKey(#{sid}, #{user_hive}, #{ntuser_dat})")
|
||||
reg_path[0] = sid
|
||||
else
|
||||
Chef::Log.debug("Failed RegLoadKey(#{sid}, #{user_hive}, #{ntuser_dat})")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[reg_path, load_reg]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_hive_unloaded(hive_loaded = false)
|
||||
if hive_loaded
|
||||
Chef::Log.debug('Hive was loaded, we really should unload it')
|
||||
unload_hive(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module Registry
|
||||
module_function
|
||||
|
||||
extend Windows::RegistryHelper
|
||||
end
|
||||
|
||||
@@ -1,207 +1,207 @@
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Cookbook Name:: windows
|
||||
# Library:: version
|
||||
#
|
||||
# Copyright:: 2011-2015, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
require_relative 'wmi_helper'
|
||||
require 'Win32API'
|
||||
end
|
||||
|
||||
module Windows
|
||||
class Version
|
||||
# http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx
|
||||
|
||||
# Suite Masks
|
||||
# Microsoft BackOffice components are installed.
|
||||
VER_SUITE_BACKOFFICE = 0x00000004 unless defined?(VER_SUITE_BACKOFFICE)
|
||||
# Windows Server 2003, Web Edition is installed.
|
||||
VER_SUITE_BLADE = 0x00000400 unless defined?(VER_SUITE_BLADE)
|
||||
# Windows Server 2003, Compute Cluster Edition is installed.
|
||||
VER_SUITE_COMPUTE_SERVER = 0x00004000 unless defined?(VER_SUITE_COMPUTE_SERVER)
|
||||
# Windows Server 2008 Datacenter, Windows Server 2003, Datacenter Edition, or Windows 2000 Datacenter Server is installed.
|
||||
VER_SUITE_DATACENTER = 0x00000080 unless defined?(VER_SUITE_DATACENTER)
|
||||
# Windows Server 2008 Enterprise, Windows Server 2003, Enterprise Edition, or Windows 2000 Advanced Server is installed. Refer to the Remarks section for more information about this bit flag.
|
||||
VER_SUITE_ENTERPRISE = 0x00000002 unless defined?(VER_SUITE_ENTERPRISE)
|
||||
# Windows XP Embedded is installed.
|
||||
VER_SUITE_EMBEDDEDNT = 0x00000040 unless defined?(VER_SUITE_EMBEDDEDNT)
|
||||
# Windows Vista Home Premium, Windows Vista Home Basic, or Windows XP Home Edition is installed.
|
||||
VER_SUITE_PERSONAL = 0x00000200 unless defined?(VER_SUITE_PERSONAL)
|
||||
# Remote Desktop is supported, but only one interactive session is supported. This value is set unless the system is running in application server mode.
|
||||
VER_SUITE_SINGLEUSERTS = 0x00000100 unless defined?(VER_SUITE_SINGLEUSERTS)
|
||||
# Microsoft Small Business Server was once installed on the system, but may have been upgraded to another version of Windows. Refer to the Remarks section for more information about this bit flag.
|
||||
VER_SUITE_SMALLBUSINESS = 0x00000001 unless defined?(VER_SUITE_SMALLBUSINESS)
|
||||
# Microsoft Small Business Server is installed with the restrictive client license in force. Refer to the Remarks section for more information about this bit flag.
|
||||
VER_SUITE_SMALLBUSINESS_RESTRICTED = 0x00000020 unless defined?(VER_SUITE_SMALLBUSINESS_RESTRICTED)
|
||||
# Windows Storage Server 2003 R2 or Windows Storage Server 2003is installed.
|
||||
VER_SUITE_STORAGE_SERVER = 0x00002000 unless defined?(VER_SUITE_STORAGE_SERVER)
|
||||
# Terminal Services is installed. This value is always set.
|
||||
# If VER_SUITE_TERMINAL is set but VER_SUITE_SINGLEUSERTS is not set, the system is running in application server mode.
|
||||
VER_SUITE_TERMINAL = 0x00000010 unless defined?(VER_SUITE_TERMINAL)
|
||||
# Windows Home Server is installed.
|
||||
VER_SUITE_WH_SERVER = 0x00008000 unless defined?(VER_SUITE_WH_SERVER)
|
||||
|
||||
# Product Type
|
||||
# The system is a domain controller and the operating system is Windows Server 2012, Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, or Windows 2000 Server.
|
||||
VER_NT_DOMAIN_CONTROLLER = 0x0000002 unless defined?(VER_NT_DOMAIN_CONTROLLER)
|
||||
# The operating system is Windows Server 2012, Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, or Windows 2000 Server.
|
||||
# Note that a server that is also a domain controller is reported as VER_NT_DOMAIN_CONTROLLER, not VER_NT_SERVER.
|
||||
VER_NT_SERVER = 0x0000003 unless defined?(VER_NT_SERVER)
|
||||
# The operating system is Windows 7, Windows Vista, Windows XP Professional, Windows XP Home Edition, or Windows 2000 Professional.
|
||||
VER_NT_WORKSTATION = 0x0000001 unless defined?(VER_NT_WORKSTATION)
|
||||
|
||||
# GetSystemMetrics
|
||||
# The build number if the system is Windows Server 2003 R2; otherwise, 0.
|
||||
SM_SERVERR2 = 89 unless defined?(SM_SERVERR2)
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/ms724358(v=vs.85).aspx
|
||||
SKU = {
|
||||
0x00000006 => { ms_const: 'PRODUCT_BUSINESS', name: 'Business' },
|
||||
0x00000010 => { ms_const: 'PRODUCT_BUSINESS_N', name: 'Business N' },
|
||||
0x00000012 => { ms_const: 'PRODUCT_CLUSTER_SERVER', name: 'HPC Edition' },
|
||||
0x00000008 => { ms_const: 'PRODUCT_DATACENTER_SERVER', name: 'Server Datacenter (full installation)' },
|
||||
0x0000000C => { ms_const: 'PRODUCT_DATACENTER_SERVER_CORE', name: 'Server Datacenter (core installation)' },
|
||||
0x00000027 => { ms_const: 'PRODUCT_DATACENTER_SERVER_CORE_V', name: 'Server Datacenter without Hyper-V (core installation)' },
|
||||
0x00000025 => { ms_const: 'PRODUCT_DATACENTER_SERVER_V', name: 'Server Datacenter without Hyper-V (full installation)' },
|
||||
0x00000004 => { ms_const: 'PRODUCT_ENTERPRISE', name: 'Enterprise' },
|
||||
0x00000046 => { ms_const: 'PRODUCT_ENTERPRISE_E', name: 'Not supported' },
|
||||
0x0000001B => { ms_const: 'PRODUCT_ENTERPRISE_N', name: 'Enterprise N' },
|
||||
0x0000000A => { ms_const: 'PRODUCT_ENTERPRISE_SERVER', name: 'Server Enterprise (full installation)' },
|
||||
0x0000000E => { ms_const: 'PRODUCT_ENTERPRISE_SERVER_CORE', name: 'Server Enterprise (core installation)' },
|
||||
0x00000029 => { ms_const: 'PRODUCT_ENTERPRISE_SERVER_CORE_V', name: 'Server Enterprise without Hyper-V (core installation)' },
|
||||
0x0000000F => { ms_const: 'PRODUCT_ENTERPRISE_SERVER_IA64', name: 'Server Enterprise for Itanium-based Systems' },
|
||||
0x00000026 => { ms_const: 'PRODUCT_ENTERPRISE_SERVER_V', name: 'Server Enterprise without Hyper-V (full installation)' },
|
||||
0x00000002 => { ms_const: 'PRODUCT_HOME_BASIC', name: 'Home Basic' },
|
||||
0x00000043 => { ms_const: 'PRODUCT_HOME_BASIC_E', name: 'Not supported' },
|
||||
0x00000005 => { ms_const: 'PRODUCT_HOME_BASIC_N', name: 'Home Basic N' },
|
||||
0x00000003 => { ms_const: 'PRODUCT_HOME_PREMIUM', name: 'Home Premium' },
|
||||
0x00000044 => { ms_const: 'PRODUCT_HOME_PREMIUM_E', name: 'Not supported' },
|
||||
0x0000001A => { ms_const: 'PRODUCT_HOME_PREMIUM_N', name: 'Home Premium N' },
|
||||
0x0000002A => { ms_const: 'PRODUCT_HYPERV', name: 'Microsoft Hyper-V Server' },
|
||||
0x0000001E => { ms_const: 'PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT', name: 'Windows Essential Business Server Management Server' },
|
||||
0x00000020 => { ms_const: 'PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING', name: 'Windows Essential Business Server Messaging Server' },
|
||||
0x0000001F => { ms_const: 'PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY', name: 'Windows Essential Business Server Security Server' },
|
||||
0x00000030 => { ms_const: 'PRODUCT_PROFESSIONAL', name: 'Professional' },
|
||||
0x00000045 => { ms_const: 'PRODUCT_PROFESSIONAL_E', name: 'Not supported' },
|
||||
0x00000031 => { ms_const: 'PRODUCT_PROFESSIONAL_N', name: 'Professional N' },
|
||||
0x00000067 => { ms_const: 'PRODUCT_PROFESSIONAL_WMC', name: 'Professional with Media Center' },
|
||||
0x00000018 => { ms_const: 'PRODUCT_SERVER_FOR_SMALLBUSINESS', name: 'Windows Server 2008 for Windows Essential Server Solutions' },
|
||||
0x00000023 => { ms_const: 'PRODUCT_SERVER_FOR_SMALLBUSINESS_V', name: 'Windows Server 2008 without Hyper-V for Windows Essential Server Solutions' },
|
||||
0x00000021 => { ms_const: 'PRODUCT_SERVER_FOUNDATION', name: 'Server Foundation' },
|
||||
0x00000022 => { ms_const: 'PRODUCT_HOME_PREMIUM_SERVER', name: 'Windows Home Server 2011' },
|
||||
0x00000032 => { ms_const: 'PRODUCT_SB_SOLUTION_SERVER', name: 'Windows Small Business Server 2011 Essentials' },
|
||||
0x00000013 => { ms_const: 'PRODUCT_HOME_SERVER', name: 'Windows Storage Server 2008 R2 Essentials' },
|
||||
0x00000009 => { ms_const: 'PRODUCT_SMALLBUSINESS_SERVER', name: 'Windows Small Business Server' },
|
||||
0x00000038 => { ms_const: 'PRODUCT_SOLUTION_EMBEDDEDSERVER', name: 'Windows MultiPoint Server' },
|
||||
0x00000007 => { ms_const: 'PRODUCT_STANDARD_SERVER', name: 'Server Standard (full installation)' },
|
||||
0x0000000D => { ms_const: 'PRODUCT_STANDARD_SERVER_CORE', name: 'Server Standard (core installation)' },
|
||||
0x00000028 => { ms_const: 'PRODUCT_STANDARD_SERVER_CORE_V', name: 'Server Standard without Hyper-V (core installation)' },
|
||||
0x00000024 => { ms_const: 'PRODUCT_STANDARD_SERVER_V', name: 'Server Standard without Hyper-V (full installation)' },
|
||||
0x0000000B => { ms_const: 'PRODUCT_STARTER', name: 'Starter' },
|
||||
0x00000042 => { ms_const: 'PRODUCT_STARTER_E', name: 'Not supported' },
|
||||
0x0000002F => { ms_const: 'PRODUCT_STARTER_N', name: 'Starter N' },
|
||||
0x00000017 => { ms_const: 'PRODUCT_STORAGE_ENTERPRISE_SERVER', name: 'Storage Server Enterprise' },
|
||||
0x00000014 => { ms_const: 'PRODUCT_STORAGE_EXPRESS_SERVER', name: 'Storage Server Express' },
|
||||
0x00000015 => { ms_const: 'PRODUCT_STORAGE_STANDARD_SERVER', name: 'Storage Server Standard' },
|
||||
0x00000016 => { ms_const: 'PRODUCT_STORAGE_WORKGROUP_SERVER', name: 'Storage Server Workgroup' },
|
||||
0x00000000 => { ms_const: 'PRODUCT_UNDEFINED', name: 'An unknown product' },
|
||||
0x00000001 => { ms_const: 'PRODUCT_ULTIMATE', name: 'Ultimate' },
|
||||
0x00000047 => { ms_const: 'PRODUCT_ULTIMATE_E', name: 'Not supported' },
|
||||
0x0000001C => { ms_const: 'PRODUCT_ULTIMATE_N', name: 'Ultimate N' },
|
||||
0x00000011 => { ms_const: 'PRODUCT_WEB_SERVER', name: 'Web Server (full installation)' },
|
||||
0x0000001D => { ms_const: 'PRODUCT_WEB_SERVER_CORE', name: 'Web Server (core installation)' }
|
||||
}.freeze unless defined?(SKU)
|
||||
|
||||
attr_reader :major_version, :minor_version, :build_number, :service_pack_major_version, :service_pack_minor_version
|
||||
attr_reader :version, :product_type, :product_suite, :sku
|
||||
|
||||
def initialize
|
||||
unless RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
fail NotImplementedError, 'only valid on Windows platform'
|
||||
end
|
||||
@version, @product_type, @product_suite, @sku, @service_pack_major_version, @service_pack_minor_version = get_os_info
|
||||
@major_version, @minor_version, @build_number = version.split('.').map(&:to_i)
|
||||
end
|
||||
|
||||
WIN_VERSIONS = {
|
||||
'Windows Server 2012 R2' => { major: 6, minor: 3, callable: -> { @product_type != VER_NT_WORKSTATION } },
|
||||
'Windows 8' => { major: 6, minor: 2, callable: -> { @product_type == VER_NT_WORKSTATION } },
|
||||
'Windows Server 2012' => { major: 6, minor: 2, callable: -> { @product_type != VER_NT_WORKSTATION } },
|
||||
'Windows 7' => { major: 6, minor: 1, callable: -> { @product_type == VER_NT_WORKSTATION } },
|
||||
'Windows Server 2008 R2' => { major: 6, minor: 1, callable: -> { @product_type != VER_NT_WORKSTATION } },
|
||||
'Windows Server 2008' => { major: 6, minor: 0, callable: -> { @product_type != VER_NT_WORKSTATION } },
|
||||
'Windows Vista' => { major: 6, minor: 0, callable: -> { @product_type == VER_NT_WORKSTATION } },
|
||||
'Windows Server 2003 R2' => { major: 5, minor: 2, callable: -> { Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2) != 0 } },
|
||||
'Windows Home Server' => { major: 5, minor: 2, callable: -> { (@product_suite & VER_SUITE_WH_SERVER) == VER_SUITE_WH_SERVER } },
|
||||
'Windows Server 2003' => { major: 5, minor: 2, callable: -> { Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2) == 0 } },
|
||||
'Windows XP' => { major: 5, minor: 1 },
|
||||
'Windows 2000' => { major: 5, minor: 0 }
|
||||
}.freeze unless defined?(WIN_VERSIONS)
|
||||
|
||||
marketing_names = []
|
||||
|
||||
# General Windows checks
|
||||
WIN_VERSIONS.each do |k, v|
|
||||
method_name = "#{k.gsub(/\s/, '_').downcase}?"
|
||||
define_method(method_name) do
|
||||
(@major_version == v[:major]) &&
|
||||
(@minor_version == v[:minor]) &&
|
||||
(v[:callable] ? v[:callable].call : true)
|
||||
end
|
||||
marketing_names << [k, method_name]
|
||||
end
|
||||
|
||||
define_method(:marketing_name) do
|
||||
marketing_names.each do |mn|
|
||||
break mn[0] if send(mn[1])
|
||||
end
|
||||
end
|
||||
|
||||
# Server Type checks
|
||||
%w( core full datacenter ).each do |m|
|
||||
define_method("server_#{m}?") do
|
||||
if @sku
|
||||
!(SKU[@sku][:name] =~ /#{m}/i).nil?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Win32API call to GetSystemMetrics(SM_SERVERR2)
|
||||
# returns: The build number if the system is Windows Server 2003 R2; otherwise, 0.
|
||||
def sm_serverr2
|
||||
@sm_serverr2 ||= Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2)
|
||||
end
|
||||
|
||||
# query WMI Win32_OperatingSystem for required OS info
|
||||
def get_os_info
|
||||
cols = %w( Version ProductType OSProductSuite OperatingSystemSKU ServicePackMajorVersion ServicePackMinorVersion )
|
||||
os_info = execute_wmi_query('select * from Win32_OperatingSystem').each.next
|
||||
cols.map do |c|
|
||||
begin
|
||||
wmi_object_property(os_info, c)
|
||||
rescue # OperatingSystemSKU doesn't exist in all versions of Windows
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Cookbook:: windows
|
||||
# Library:: version
|
||||
#
|
||||
# Copyright:: 2011-2017, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
require_relative 'wmi_helper'
|
||||
require 'Win32API'
|
||||
end
|
||||
|
||||
module Windows
|
||||
class Version
|
||||
# http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx
|
||||
|
||||
# Suite Masks
|
||||
# Microsoft BackOffice components are installed.
|
||||
VER_SUITE_BACKOFFICE = 0x00000004 unless defined?(VER_SUITE_BACKOFFICE)
|
||||
# Windows Server 2003, Web Edition is installed.
|
||||
VER_SUITE_BLADE = 0x00000400 unless defined?(VER_SUITE_BLADE)
|
||||
# Windows Server 2003, Compute Cluster Edition is installed.
|
||||
VER_SUITE_COMPUTE_SERVER = 0x00004000 unless defined?(VER_SUITE_COMPUTE_SERVER)
|
||||
# Windows Server 2008 Datacenter, Windows Server 2003, Datacenter Edition, or Windows 2000 Datacenter Server is installed.
|
||||
VER_SUITE_DATACENTER = 0x00000080 unless defined?(VER_SUITE_DATACENTER)
|
||||
# Windows Server 2008 Enterprise, Windows Server 2003, Enterprise Edition, or Windows 2000 Advanced Server is installed. Refer to the Remarks section for more information about this bit flag.
|
||||
VER_SUITE_ENTERPRISE = 0x00000002 unless defined?(VER_SUITE_ENTERPRISE)
|
||||
# Windows XP Embedded is installed.
|
||||
VER_SUITE_EMBEDDEDNT = 0x00000040 unless defined?(VER_SUITE_EMBEDDEDNT)
|
||||
# Windows Vista Home Premium, Windows Vista Home Basic, or Windows XP Home Edition is installed.
|
||||
VER_SUITE_PERSONAL = 0x00000200 unless defined?(VER_SUITE_PERSONAL)
|
||||
# Remote Desktop is supported, but only one interactive session is supported. This value is set unless the system is running in application server mode.
|
||||
VER_SUITE_SINGLEUSERTS = 0x00000100 unless defined?(VER_SUITE_SINGLEUSERTS)
|
||||
# Microsoft Small Business Server was once installed on the system, but may have been upgraded to another version of Windows. Refer to the Remarks section for more information about this bit flag.
|
||||
VER_SUITE_SMALLBUSINESS = 0x00000001 unless defined?(VER_SUITE_SMALLBUSINESS)
|
||||
# Microsoft Small Business Server is installed with the restrictive client license in force. Refer to the Remarks section for more information about this bit flag.
|
||||
VER_SUITE_SMALLBUSINESS_RESTRICTED = 0x00000020 unless defined?(VER_SUITE_SMALLBUSINESS_RESTRICTED)
|
||||
# Windows Storage Server 2003 R2 or Windows Storage Server 2003is installed.
|
||||
VER_SUITE_STORAGE_SERVER = 0x00002000 unless defined?(VER_SUITE_STORAGE_SERVER)
|
||||
# Terminal Services is installed. This value is always set.
|
||||
# If VER_SUITE_TERMINAL is set but VER_SUITE_SINGLEUSERTS is not set, the system is running in application server mode.
|
||||
VER_SUITE_TERMINAL = 0x00000010 unless defined?(VER_SUITE_TERMINAL)
|
||||
# Windows Home Server is installed.
|
||||
VER_SUITE_WH_SERVER = 0x00008000 unless defined?(VER_SUITE_WH_SERVER)
|
||||
|
||||
# Product Type
|
||||
# The system is a domain controller and the operating system is Windows Server 2012, Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, or Windows 2000 Server.
|
||||
VER_NT_DOMAIN_CONTROLLER = 0x0000002 unless defined?(VER_NT_DOMAIN_CONTROLLER)
|
||||
# The operating system is Windows Server 2012, Windows Server 2008 R2, Windows Server 2008, Windows Server 2003, or Windows 2000 Server.
|
||||
# Note that a server that is also a domain controller is reported as VER_NT_DOMAIN_CONTROLLER, not VER_NT_SERVER.
|
||||
VER_NT_SERVER = 0x0000003 unless defined?(VER_NT_SERVER)
|
||||
# The operating system is Windows 7, Windows Vista, Windows XP Professional, Windows XP Home Edition, or Windows 2000 Professional.
|
||||
VER_NT_WORKSTATION = 0x0000001 unless defined?(VER_NT_WORKSTATION)
|
||||
|
||||
# GetSystemMetrics
|
||||
# The build number if the system is Windows Server 2003 R2; otherwise, 0.
|
||||
SM_SERVERR2 = 89 unless defined?(SM_SERVERR2)
|
||||
|
||||
# http://msdn.microsoft.com/en-us/library/ms724358(v=vs.85).aspx
|
||||
SKU = {
|
||||
0x00000006 => { ms_const: 'PRODUCT_BUSINESS', name: 'Business' },
|
||||
0x00000010 => { ms_const: 'PRODUCT_BUSINESS_N', name: 'Business N' },
|
||||
0x00000012 => { ms_const: 'PRODUCT_CLUSTER_SERVER', name: 'HPC Edition' },
|
||||
0x00000008 => { ms_const: 'PRODUCT_DATACENTER_SERVER', name: 'Server Datacenter (full installation)' },
|
||||
0x0000000C => { ms_const: 'PRODUCT_DATACENTER_SERVER_CORE', name: 'Server Datacenter (core installation)' },
|
||||
0x00000027 => { ms_const: 'PRODUCT_DATACENTER_SERVER_CORE_V', name: 'Server Datacenter without Hyper-V (core installation)' },
|
||||
0x00000025 => { ms_const: 'PRODUCT_DATACENTER_SERVER_V', name: 'Server Datacenter without Hyper-V (full installation)' },
|
||||
0x00000004 => { ms_const: 'PRODUCT_ENTERPRISE', name: 'Enterprise' },
|
||||
0x00000046 => { ms_const: 'PRODUCT_ENTERPRISE_E', name: 'Not supported' },
|
||||
0x0000001B => { ms_const: 'PRODUCT_ENTERPRISE_N', name: 'Enterprise N' },
|
||||
0x0000000A => { ms_const: 'PRODUCT_ENTERPRISE_SERVER', name: 'Server Enterprise (full installation)' },
|
||||
0x0000000E => { ms_const: 'PRODUCT_ENTERPRISE_SERVER_CORE', name: 'Server Enterprise (core installation)' },
|
||||
0x00000029 => { ms_const: 'PRODUCT_ENTERPRISE_SERVER_CORE_V', name: 'Server Enterprise without Hyper-V (core installation)' },
|
||||
0x0000000F => { ms_const: 'PRODUCT_ENTERPRISE_SERVER_IA64', name: 'Server Enterprise for Itanium-based Systems' },
|
||||
0x00000026 => { ms_const: 'PRODUCT_ENTERPRISE_SERVER_V', name: 'Server Enterprise without Hyper-V (full installation)' },
|
||||
0x00000002 => { ms_const: 'PRODUCT_HOME_BASIC', name: 'Home Basic' },
|
||||
0x00000043 => { ms_const: 'PRODUCT_HOME_BASIC_E', name: 'Not supported' },
|
||||
0x00000005 => { ms_const: 'PRODUCT_HOME_BASIC_N', name: 'Home Basic N' },
|
||||
0x00000003 => { ms_const: 'PRODUCT_HOME_PREMIUM', name: 'Home Premium' },
|
||||
0x00000044 => { ms_const: 'PRODUCT_HOME_PREMIUM_E', name: 'Not supported' },
|
||||
0x0000001A => { ms_const: 'PRODUCT_HOME_PREMIUM_N', name: 'Home Premium N' },
|
||||
0x0000002A => { ms_const: 'PRODUCT_HYPERV', name: 'Microsoft Hyper-V Server' },
|
||||
0x0000001E => { ms_const: 'PRODUCT_MEDIUMBUSINESS_SERVER_MANAGEMENT', name: 'Windows Essential Business Server Management Server' },
|
||||
0x00000020 => { ms_const: 'PRODUCT_MEDIUMBUSINESS_SERVER_MESSAGING', name: 'Windows Essential Business Server Messaging Server' },
|
||||
0x0000001F => { ms_const: 'PRODUCT_MEDIUMBUSINESS_SERVER_SECURITY', name: 'Windows Essential Business Server Security Server' },
|
||||
0x00000030 => { ms_const: 'PRODUCT_PROFESSIONAL', name: 'Professional' },
|
||||
0x00000045 => { ms_const: 'PRODUCT_PROFESSIONAL_E', name: 'Not supported' },
|
||||
0x00000031 => { ms_const: 'PRODUCT_PROFESSIONAL_N', name: 'Professional N' },
|
||||
0x00000067 => { ms_const: 'PRODUCT_PROFESSIONAL_WMC', name: 'Professional with Media Center' },
|
||||
0x00000018 => { ms_const: 'PRODUCT_SERVER_FOR_SMALLBUSINESS', name: 'Windows Server 2008 for Windows Essential Server Solutions' },
|
||||
0x00000023 => { ms_const: 'PRODUCT_SERVER_FOR_SMALLBUSINESS_V', name: 'Windows Server 2008 without Hyper-V for Windows Essential Server Solutions' },
|
||||
0x00000021 => { ms_const: 'PRODUCT_SERVER_FOUNDATION', name: 'Server Foundation' },
|
||||
0x00000022 => { ms_const: 'PRODUCT_HOME_PREMIUM_SERVER', name: 'Windows Home Server 2011' },
|
||||
0x00000032 => { ms_const: 'PRODUCT_SB_SOLUTION_SERVER', name: 'Windows Small Business Server 2011 Essentials' },
|
||||
0x00000013 => { ms_const: 'PRODUCT_HOME_SERVER', name: 'Windows Storage Server 2008 R2 Essentials' },
|
||||
0x00000009 => { ms_const: 'PRODUCT_SMALLBUSINESS_SERVER', name: 'Windows Small Business Server' },
|
||||
0x00000038 => { ms_const: 'PRODUCT_SOLUTION_EMBEDDEDSERVER', name: 'Windows MultiPoint Server' },
|
||||
0x00000007 => { ms_const: 'PRODUCT_STANDARD_SERVER', name: 'Server Standard (full installation)' },
|
||||
0x0000000D => { ms_const: 'PRODUCT_STANDARD_SERVER_CORE', name: 'Server Standard (core installation)' },
|
||||
0x00000028 => { ms_const: 'PRODUCT_STANDARD_SERVER_CORE_V', name: 'Server Standard without Hyper-V (core installation)' },
|
||||
0x00000024 => { ms_const: 'PRODUCT_STANDARD_SERVER_V', name: 'Server Standard without Hyper-V (full installation)' },
|
||||
0x0000000B => { ms_const: 'PRODUCT_STARTER', name: 'Starter' },
|
||||
0x00000042 => { ms_const: 'PRODUCT_STARTER_E', name: 'Not supported' },
|
||||
0x0000002F => { ms_const: 'PRODUCT_STARTER_N', name: 'Starter N' },
|
||||
0x00000017 => { ms_const: 'PRODUCT_STORAGE_ENTERPRISE_SERVER', name: 'Storage Server Enterprise' },
|
||||
0x00000014 => { ms_const: 'PRODUCT_STORAGE_EXPRESS_SERVER', name: 'Storage Server Express' },
|
||||
0x00000015 => { ms_const: 'PRODUCT_STORAGE_STANDARD_SERVER', name: 'Storage Server Standard' },
|
||||
0x00000016 => { ms_const: 'PRODUCT_STORAGE_WORKGROUP_SERVER', name: 'Storage Server Workgroup' },
|
||||
0x00000000 => { ms_const: 'PRODUCT_UNDEFINED', name: 'An unknown product' },
|
||||
0x00000001 => { ms_const: 'PRODUCT_ULTIMATE', name: 'Ultimate' },
|
||||
0x00000047 => { ms_const: 'PRODUCT_ULTIMATE_E', name: 'Not supported' },
|
||||
0x0000001C => { ms_const: 'PRODUCT_ULTIMATE_N', name: 'Ultimate N' },
|
||||
0x00000011 => { ms_const: 'PRODUCT_WEB_SERVER', name: 'Web Server (full installation)' },
|
||||
0x0000001D => { ms_const: 'PRODUCT_WEB_SERVER_CORE', name: 'Web Server (core installation)' },
|
||||
}.freeze unless defined?(SKU)
|
||||
|
||||
attr_reader :major_version, :minor_version, :build_number, :service_pack_major_version, :service_pack_minor_version
|
||||
attr_reader :version, :product_type, :product_suite, :sku
|
||||
|
||||
def initialize
|
||||
unless RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
raise NotImplementedError, 'only valid on Windows platform'
|
||||
end
|
||||
@version, @product_type, @product_suite, @sku, @service_pack_major_version, @service_pack_minor_version = get_os_info
|
||||
@major_version, @minor_version, @build_number = version.split('.').map(&:to_i)
|
||||
end
|
||||
|
||||
WIN_VERSIONS = {
|
||||
'Windows Server 2012 R2' => { major: 6, minor: 3, callable: -> { @product_type != VER_NT_WORKSTATION } },
|
||||
'Windows 8' => { major: 6, minor: 2, callable: -> { @product_type == VER_NT_WORKSTATION } },
|
||||
'Windows Server 2012' => { major: 6, minor: 2, callable: -> { @product_type != VER_NT_WORKSTATION } },
|
||||
'Windows 7' => { major: 6, minor: 1, callable: -> { @product_type == VER_NT_WORKSTATION } },
|
||||
'Windows Server 2008 R2' => { major: 6, minor: 1, callable: -> { @product_type != VER_NT_WORKSTATION } },
|
||||
'Windows Server 2008' => { major: 6, minor: 0, callable: -> { @product_type != VER_NT_WORKSTATION } },
|
||||
'Windows Vista' => { major: 6, minor: 0, callable: -> { @product_type == VER_NT_WORKSTATION } },
|
||||
'Windows Server 2003 R2' => { major: 5, minor: 2, callable: -> { Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2) != 0 } },
|
||||
'Windows Home Server' => { major: 5, minor: 2, callable: -> { (@product_suite & VER_SUITE_WH_SERVER) == VER_SUITE_WH_SERVER } },
|
||||
'Windows Server 2003' => { major: 5, minor: 2, callable: -> { Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2) == 0 } },
|
||||
'Windows XP' => { major: 5, minor: 1 },
|
||||
'Windows 2000' => { major: 5, minor: 0 },
|
||||
}.freeze unless defined?(WIN_VERSIONS)
|
||||
|
||||
marketing_names = []
|
||||
|
||||
# General Windows checks
|
||||
WIN_VERSIONS.each do |k, v|
|
||||
method_name = "#{k.gsub(/\s/, '_').downcase}?"
|
||||
define_method(method_name) do
|
||||
(@major_version == v[:major]) &&
|
||||
(@minor_version == v[:minor]) &&
|
||||
(v[:callable] ? v[:callable].call : true)
|
||||
end
|
||||
marketing_names << [k, method_name]
|
||||
end
|
||||
|
||||
define_method(:marketing_name) do
|
||||
marketing_names.each do |mn|
|
||||
break mn[0] if send(mn[1])
|
||||
end
|
||||
end
|
||||
|
||||
# Server Type checks
|
||||
%w( core full datacenter ).each do |m|
|
||||
define_method("server_#{m}?") do
|
||||
if @sku
|
||||
!(SKU[@sku][:name] =~ /#{m}/i).nil?
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Win32API call to GetSystemMetrics(SM_SERVERR2)
|
||||
# returns: The build number if the system is Windows Server 2003 R2; otherwise, 0.
|
||||
def sm_serverr2
|
||||
@sm_serverr2 ||= Win32API.new('user32', 'GetSystemMetrics', 'I', 'I').call(SM_SERVERR2)
|
||||
end
|
||||
|
||||
# query WMI Win32_OperatingSystem for required OS info
|
||||
def get_os_info
|
||||
cols = %w( Version ProductType OSProductSuite OperatingSystemSKU ServicePackMajorVersion ServicePackMinorVersion )
|
||||
os_info = execute_wmi_query('select * from Win32_OperatingSystem').each.next
|
||||
cols.map do |c|
|
||||
begin
|
||||
wmi_object_property(os_info, c)
|
||||
rescue # OperatingSystemSKU doesn't exist in all versions of Windows
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
79
cookbooks/windows/libraries/version_helper.rb
Normal file
79
cookbooks/windows/libraries/version_helper.rb
Normal file
@@ -0,0 +1,79 @@
|
||||
#
|
||||
# Cookbook:: windows
|
||||
# Library:: version_helper
|
||||
# Author:: Baptiste Courtois (<b.courtois@criteo.com>)
|
||||
#
|
||||
# Copyright:: 2015-2017, Criteo
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
module Windows
|
||||
# Module based on windows ohai kernel.cs_info providing version helpers
|
||||
module VersionHelper
|
||||
# Module referencing CORE SKU contants from product type
|
||||
# see. https://msdn.microsoft.com/windows/desktop/ms724358#PRODUCT_DATACENTER_SERVER_CORE
|
||||
# n.b. Prefix - PRODUCT_ - and suffix - _CORE- have been removed
|
||||
module CoreSKU
|
||||
# Server Datacenter Core
|
||||
DATACENTER_SERVER = 0x0C unless constants.include?(:DATACENTER_SERVER)
|
||||
# Server Datacenter without Hyper-V Core
|
||||
DATACENTER_SERVER_V = 0x27 unless constants.include?(:DATACENTER_SERVER_V)
|
||||
# Server Enterprise Core
|
||||
ENTERPRISE_SERVER = 0x0E unless constants.include?(:ENTERPRISE_SERVER)
|
||||
# Server Enterprise without Hyper-V Core
|
||||
ENTERPRISE_SERVER_V = 0x29 unless constants.include?(:ENTERPRISE_SERVER_V)
|
||||
# Server Standard Core
|
||||
STANDARD_SERVER = 0x0D unless constants.include?(:STANDARD_SERVER)
|
||||
# Server Standard without Hyper-V Core
|
||||
STANDARD_SERVER_V = 0x28 unless constants.include?(:STANDARD_SERVER_V)
|
||||
end
|
||||
|
||||
# Module referencing product type contants
|
||||
# see. https://msdn.microsoft.com/windows/desktop/ms724833#VER_NT_SERVER
|
||||
# n.b. Prefix - VER_NT_ - has been removed
|
||||
module ProductType
|
||||
WORKSTATION = 0x1 unless constants.include?(:WORKSTATION)
|
||||
DOMAIN_CONTROLLER = 0x2 unless constants.include?(:DOMAIN_CONTROLLER)
|
||||
SERVER = 0x3 unless constants.include?(:SERVER)
|
||||
end
|
||||
|
||||
# Determines whether current node is running a windows Core version
|
||||
def self.core_version?(node)
|
||||
validate_platform node
|
||||
|
||||
CoreSKU.constants.any? { |c| CoreSKU.const_get(c) == node['kernel']['os_info']['operating_system_sku'] }
|
||||
end
|
||||
|
||||
# Determines whether current node is a workstation version
|
||||
def self.workstation_version?(node)
|
||||
validate_platform node
|
||||
node['kernel']['os_info']['product_type'] == ProductType::WORKSTATION
|
||||
end
|
||||
|
||||
# Determines whether current node is a server version
|
||||
def self.server_version?(node)
|
||||
!workstation_version?(node)
|
||||
end
|
||||
|
||||
# Determines NT version of the current node
|
||||
def self.nt_version(node)
|
||||
validate_platform node
|
||||
|
||||
node['platform_version'].to_f
|
||||
end
|
||||
|
||||
def self.validate_platform(node)
|
||||
raise 'Windows helper are only supported on windows platform!' if node['platform'] != 'windows'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,86 +0,0 @@
|
||||
# Try to include from core chef, if error then monkey patch it in.
|
||||
|
||||
begin
|
||||
include Chef::Mixin::WindowsArchitectureHelper
|
||||
rescue
|
||||
Chef::Log.debug('Chef::Mixin::WindowsArchitectureHelper not in core version, Monkey patching in.')
|
||||
|
||||
require 'chef/exceptions'
|
||||
require 'win32/api' if Chef::Platform.windows?
|
||||
|
||||
class Chef
|
||||
module Mixin
|
||||
module WindowsArchitectureHelper
|
||||
def node_windows_architecture(node)
|
||||
node['kernel']['machine'].to_sym
|
||||
end
|
||||
|
||||
def wow64_architecture_override_required?(node, desired_architecture)
|
||||
i386_windows_process? &&
|
||||
node_windows_architecture(node) == :x86_64 &&
|
||||
desired_architecture == :x86_64
|
||||
end
|
||||
|
||||
def node_supports_windows_architecture?(node, desired_architecture)
|
||||
assert_valid_windows_architecture!(desired_architecture)
|
||||
(node_windows_architecture(node) == :x86_64 ||
|
||||
desired_architecture == :i386) ? true : false
|
||||
end
|
||||
|
||||
def valid_windows_architecture?(architecture)
|
||||
(architecture == :x86_64) || (architecture == :i386)
|
||||
end
|
||||
|
||||
def assert_valid_windows_architecture!(architecture)
|
||||
unless valid_windows_architecture?(architecture)
|
||||
raise Chef::Exceptions::Win32ArchitectureIncorrect,
|
||||
'The specified architecture was not valid. It must be one of :i386 or :x86_64'
|
||||
end
|
||||
end
|
||||
|
||||
def i386_windows_process?
|
||||
Chef::Platform.windows? && 'X86'.casecmp(ENV['PROCESSOR_ARCHITECTURE']) == 0
|
||||
end
|
||||
|
||||
def disable_wow64_file_redirection(node)
|
||||
original_redirection_state = ['0'].pack('P')
|
||||
|
||||
if (node_windows_architecture(node) == :x86_64) && ::Chef::Platform.windows?
|
||||
win32_wow_64_disable_wow_64_fs_redirection =
|
||||
::Win32::API.new('Wow64DisableWow64FsRedirection', 'P', 'L', 'kernel32')
|
||||
|
||||
succeeded = win32_wow_64_disable_wow_64_fs_redirection.call(original_redirection_state)
|
||||
|
||||
if succeeded == 0
|
||||
raise Win32APIError 'Failed to disable Wow64 file redirection'
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
original_redirection_state
|
||||
end
|
||||
|
||||
def restore_wow64_file_redirection(node, original_redirection_state)
|
||||
if (node_windows_architecture(node) == :x86_64) && ::Chef::Platform.windows?
|
||||
win32_wow_64_revert_wow_64_fs_redirection =
|
||||
::Win32::API.new('Wow64RevertWow64FsRedirection', 'P', 'L', 'kernel32')
|
||||
|
||||
succeeded = win32_wow_64_revert_wow_64_fs_redirection.call(original_redirection_state)
|
||||
|
||||
if succeeded == 0
|
||||
raise Win32APIError 'Failed to revert Wow64 file redirection'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Making sure this library is available to Chef::Mixin::PowershellOut
|
||||
# Required for clients that don't have Chef::Mixin::WindowsArchitectureHelper in
|
||||
# core chef.
|
||||
if ::Chef::Platform.windows?
|
||||
require_relative 'powershell_out'
|
||||
Chef::Mixin::PowershellOut.send(:include, Chef::Mixin::WindowsArchitectureHelper)
|
||||
end
|
||||
@@ -1,168 +1,174 @@
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Cookbook Name:: windows
|
||||
# Library:: helper
|
||||
#
|
||||
# Copyright:: 2011-2015, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'uri'
|
||||
require 'Win32API' if Chef::Platform.windows?
|
||||
require 'chef/exceptions'
|
||||
|
||||
module Windows
|
||||
module Helper
|
||||
AUTO_RUN_KEY = 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'.freeze unless defined?(AUTO_RUN_KEY)
|
||||
ENV_KEY = 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'.freeze unless defined?(ENV_KEY)
|
||||
ExpandEnvironmentStrings = Win32API.new('kernel32', 'ExpandEnvironmentStrings', %w(P P L), 'L') if Chef::Platform.windows? && !defined?(ExpandEnvironmentStrings)
|
||||
|
||||
# returns windows friendly version of the provided path,
|
||||
# ensures backslashes are used everywhere
|
||||
def win_friendly_path(path)
|
||||
path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR || '\\') if path
|
||||
end
|
||||
|
||||
# account for Window's wacky File System Redirector
|
||||
# http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx
|
||||
# especially important for 32-bit processes (like Ruby) on a
|
||||
# 64-bit instance of Windows.
|
||||
def locate_sysnative_cmd(cmd)
|
||||
if ::File.exist?("#{ENV['WINDIR']}\\sysnative\\#{cmd}")
|
||||
"#{ENV['WINDIR']}\\sysnative\\#{cmd}"
|
||||
elsif ::File.exist?("#{ENV['WINDIR']}\\system32\\#{cmd}")
|
||||
"#{ENV['WINDIR']}\\system32\\#{cmd}"
|
||||
else
|
||||
cmd
|
||||
end
|
||||
end
|
||||
|
||||
# Create a feature provider dependent value object.
|
||||
# mainly created becasue Windows Feature names are
|
||||
# different based on whether dism.exe or servicemanagercmd.exe
|
||||
# is used for installation
|
||||
def value_for_feature_provider(provider_hash)
|
||||
p = Chef::Platform.find_provider_for_node(node, :windows_feature)
|
||||
key = p.to_s.downcase.split('::').last
|
||||
provider_hash[key] || provider_hash[key.to_sym]
|
||||
end
|
||||
|
||||
# singleton instance of the Windows Version checker
|
||||
def win_version
|
||||
@win_version ||= Windows::Version.new
|
||||
end
|
||||
|
||||
# Helper function to properly parse a URI
|
||||
def as_uri(source)
|
||||
URI.parse(source)
|
||||
rescue URI::InvalidURIError
|
||||
Chef::Log.warn("#{source} was an invalid URI. Trying to escape invalid characters")
|
||||
URI.parse(URI.escape(source))
|
||||
end
|
||||
|
||||
# if a file is local it returns a windows friendly path version
|
||||
# if a file is remote it caches it locally
|
||||
def cached_file(source, checksum = nil, windows_path = true)
|
||||
@installer_file_path ||= begin
|
||||
|
||||
if source =~ /^(file|ftp|http|https):\/\//
|
||||
uri = as_uri(source)
|
||||
cache_file_path = "#{Chef::Config[:file_cache_path]}/#{::File.basename(::URI.unescape(uri.path))}"
|
||||
Chef::Log.debug("Caching a copy of file #{source} at #{cache_file_path}")
|
||||
r = Chef::Resource::RemoteFile.new(cache_file_path, run_context)
|
||||
r.source(source)
|
||||
r.backup(false)
|
||||
r.checksum(checksum) if checksum
|
||||
r.run_action(:create)
|
||||
else
|
||||
cache_file_path = source
|
||||
end
|
||||
|
||||
windows_path ? win_friendly_path(cache_file_path) : cache_file_path
|
||||
end
|
||||
end
|
||||
|
||||
# Expands the environment variables
|
||||
def expand_env_vars(path)
|
||||
# We pick 32k because that is the largest it could be:
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms724265%28v=vs.85%29.aspx
|
||||
buf = 0.chr * 32 * 1024 # 32k
|
||||
if ExpandEnvironmentStrings.call(path.dup, buf, buf.length) == 0
|
||||
fail Chef::Exceptions::Win32APIError, 'Failed calling ExpandEnvironmentStrings (received 0)'
|
||||
end
|
||||
buf.strip
|
||||
end
|
||||
|
||||
def is_package_installed?(package_name) # rubocop:disable Style/PredicateName
|
||||
installed_packages.include?(package_name)
|
||||
end
|
||||
|
||||
def installed_packages
|
||||
@installed_packages || begin
|
||||
installed_packages = {}
|
||||
# Computer\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall
|
||||
installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE)) # rescue nil
|
||||
# 64-bit registry view
|
||||
# Computer\HKEY_LOCAL_MACHINE\Software\Wow6464Node\Microsoft\Windows\CurrentVersion\Uninstall
|
||||
installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0100))) # rescue nil
|
||||
# 32-bit registry view
|
||||
# Computer\HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
|
||||
installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0200))) # rescue nil
|
||||
# Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall
|
||||
installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_CURRENT_USER)) # rescue nil
|
||||
installed_packages
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_installed_packages_from_key(hkey = ::Win32::Registry::HKEY_LOCAL_MACHINE, desired = ::Win32::Registry::Constants::KEY_READ)
|
||||
uninstall_subkey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall'
|
||||
packages = {}
|
||||
begin
|
||||
::Win32::Registry.open(hkey, uninstall_subkey, desired) do |reg|
|
||||
reg.each_key do |key, _wtime|
|
||||
begin
|
||||
k = reg.open(key, desired)
|
||||
display_name = begin
|
||||
k['DisplayName']
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
version = begin
|
||||
k['DisplayVersion']
|
||||
rescue
|
||||
'NO VERSION'
|
||||
end
|
||||
uninstall_string = begin
|
||||
k['UninstallString']
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
if display_name
|
||||
packages[display_name] = { name: display_name,
|
||||
version: version,
|
||||
uninstall_string: uninstall_string }
|
||||
end
|
||||
rescue ::Win32::Registry::Error
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue ::Win32::Registry::Error
|
||||
end
|
||||
packages
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Chef::Recipe.send(:include, Windows::Helper)
|
||||
#
|
||||
# Author:: Seth Chisamore (<schisamo@chef.io>)
|
||||
# Cookbook:: windows
|
||||
# Library:: helper
|
||||
#
|
||||
# Copyright:: 2011-2017, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
require 'uri'
|
||||
require 'Win32API' if Chef::Platform.windows?
|
||||
require 'chef/exceptions'
|
||||
|
||||
module Windows
|
||||
module Helper
|
||||
AUTO_RUN_KEY = 'HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run'.freeze unless defined?(AUTO_RUN_KEY)
|
||||
ENV_KEY = 'HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment'.freeze unless defined?(ENV_KEY)
|
||||
ExpandEnvironmentStrings = Win32API.new('kernel32', 'ExpandEnvironmentStrings', %w(P P L), 'L') if Chef::Platform.windows? && !defined?(ExpandEnvironmentStrings)
|
||||
|
||||
# returns windows friendly version of the provided path,
|
||||
# ensures backslashes are used everywhere
|
||||
def win_friendly_path(path)
|
||||
path.gsub(::File::SEPARATOR, ::File::ALT_SEPARATOR || '\\') if path
|
||||
end
|
||||
|
||||
# account for Window's wacky File System Redirector
|
||||
# http://msdn.microsoft.com/en-us/library/aa384187(v=vs.85).aspx
|
||||
# especially important for 32-bit processes (like Ruby) on a
|
||||
# 64-bit instance of Windows.
|
||||
def locate_sysnative_cmd(cmd)
|
||||
if ::File.exist?("#{ENV['WINDIR']}\\sysnative\\#{cmd}")
|
||||
"#{ENV['WINDIR']}\\sysnative\\#{cmd}"
|
||||
elsif ::File.exist?("#{ENV['WINDIR']}\\system32\\#{cmd}")
|
||||
"#{ENV['WINDIR']}\\system32\\#{cmd}"
|
||||
else
|
||||
cmd
|
||||
end
|
||||
end
|
||||
|
||||
# Create a feature provider dependent value object.
|
||||
# mainly created becasue Windows Feature names are
|
||||
# different based on whether dism.exe or servicemanagercmd.exe
|
||||
# is used for installation
|
||||
def value_for_feature_provider(provider_hash)
|
||||
p = Chef::Platform.find_provider_for_node(node, :windows_feature)
|
||||
key = p.to_s.downcase.split('::').last
|
||||
provider_hash[key] || provider_hash[key.to_sym]
|
||||
end
|
||||
|
||||
# singleton instance of the Windows Version checker
|
||||
def win_version
|
||||
@win_version ||= Windows::Version.new
|
||||
end
|
||||
|
||||
# Helper function to properly parse a URI
|
||||
def as_uri(source)
|
||||
URI.parse(source)
|
||||
rescue URI::InvalidURIError
|
||||
Chef::Log.warn("#{source} was an invalid URI. Trying to escape invalid characters")
|
||||
URI.parse(URI.escape(source))
|
||||
end
|
||||
|
||||
# if a file is local it returns a windows friendly path version
|
||||
# if a file is remote it caches it locally
|
||||
def cached_file(source, checksum = nil, windows_path = true)
|
||||
@installer_file_path ||= begin
|
||||
|
||||
if source =~ %r{^(file|ftp|http|https):\/\/}
|
||||
uri = as_uri(source)
|
||||
cache_file_path = "#{Chef::Config[:file_cache_path]}/#{::File.basename(::URI.unescape(uri.path))}"
|
||||
Chef::Log.debug("Caching a copy of file #{source} at #{cache_file_path}")
|
||||
remote_file cache_file_path do
|
||||
source source
|
||||
backup false
|
||||
checksum checksum unless checksum.nil?
|
||||
end.run_action(:create)
|
||||
else
|
||||
cache_file_path = source
|
||||
end
|
||||
|
||||
windows_path ? win_friendly_path(cache_file_path) : cache_file_path
|
||||
end
|
||||
end
|
||||
|
||||
# Expands the environment variables
|
||||
def expand_env_vars(path)
|
||||
# We pick 32k because that is the largest it could be:
|
||||
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms724265%28v=vs.85%29.aspx
|
||||
buf = 0.chr * 32 * 1024 # 32k
|
||||
if ExpandEnvironmentStrings.call(path.dup, buf, buf.length) == 0
|
||||
raise Chef::Exceptions::Win32APIError, 'Failed calling ExpandEnvironmentStrings (received 0)'
|
||||
end
|
||||
buf.strip
|
||||
end
|
||||
|
||||
def is_package_installed?(package_name) # rubocop:disable Style/PredicateName
|
||||
installed_packages.include?(package_name)
|
||||
end
|
||||
|
||||
def installed_packages
|
||||
@installed_packages || begin
|
||||
installed_packages = {}
|
||||
# Computer\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall
|
||||
installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE)) # rescue nil
|
||||
# 64-bit registry view
|
||||
# Computer\HKEY_LOCAL_MACHINE\Software\Wow6464Node\Microsoft\Windows\CurrentVersion\Uninstall
|
||||
installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0100))) # rescue nil
|
||||
# 32-bit registry view
|
||||
# Computer\HKEY_LOCAL_MACHINE\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
|
||||
installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_LOCAL_MACHINE, (::Win32::Registry::Constants::KEY_READ | 0x0200))) # rescue nil
|
||||
# Computer\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Uninstall
|
||||
installed_packages.merge!(extract_installed_packages_from_key(::Win32::Registry::HKEY_CURRENT_USER)) # rescue nil
|
||||
installed_packages
|
||||
end
|
||||
end
|
||||
|
||||
# Returns an array
|
||||
def to_array(var)
|
||||
var = var.is_a?(Array) ? var : [var]
|
||||
var.reject(&:nil?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_installed_packages_from_key(hkey = ::Win32::Registry::HKEY_LOCAL_MACHINE, desired = ::Win32::Registry::Constants::KEY_READ)
|
||||
uninstall_subkey = 'Software\Microsoft\Windows\CurrentVersion\Uninstall'
|
||||
packages = {}
|
||||
begin
|
||||
::Win32::Registry.open(hkey, uninstall_subkey, desired) do |reg|
|
||||
reg.each_key do |key, _wtime|
|
||||
begin
|
||||
k = reg.open(key, desired)
|
||||
display_name = begin
|
||||
k['DisplayName']
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
version = begin
|
||||
k['DisplayVersion']
|
||||
rescue
|
||||
'NO VERSION'
|
||||
end
|
||||
uninstall_string = begin
|
||||
k['UninstallString']
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
if display_name
|
||||
packages[display_name] = { name: display_name,
|
||||
version: version,
|
||||
uninstall_string: uninstall_string }
|
||||
end
|
||||
rescue ::Win32::Registry::Error
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue ::Win32::Registry::Error
|
||||
end
|
||||
packages
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Chef::Recipe.send(:include, Windows::Helper)
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
require 'chef/resource/lwrp_base'
|
||||
require 'chef/provider/lwrp_base'
|
||||
|
||||
require 'win32/registry' if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
|
||||
require 'chef/mixin/shell_out'
|
||||
require 'chef/mixin/language'
|
||||
class Chef
|
||||
class Provider
|
||||
class WindowsCookbookPackage < Chef::Provider::LWRPBase
|
||||
include Chef::Mixin::ShellOut
|
||||
include Windows::Helper
|
||||
|
||||
# the logic in all action methods mirror that of
|
||||
# the Chef::Provider::Package which will make
|
||||
# refactoring into core chef easy
|
||||
|
||||
action :install do
|
||||
# If we specified a version, and it's not the current version, move to the specified version
|
||||
if !@new_resource.version.nil? && @new_resource.version != @current_resource.version
|
||||
install_version = @new_resource.version
|
||||
# If it's not installed at all, install it
|
||||
elsif @current_resource.version.nil?
|
||||
install_version = candidate_version
|
||||
end
|
||||
|
||||
if install_version
|
||||
Chef::Log.info("Installing #{@new_resource} version #{install_version}")
|
||||
status = install_package(@new_resource.package_name, install_version)
|
||||
new_resource.updated_by_last_action(true) if status
|
||||
end
|
||||
end
|
||||
|
||||
action :upgrade do
|
||||
if @current_resource.version != candidate_version
|
||||
orig_version = @current_resource.version || 'uninstalled'
|
||||
Chef::Log.info("Upgrading #{@new_resource} version from #{orig_version} to #{candidate_version}")
|
||||
status = upgrade_package(@new_resource.package_name, candidate_version)
|
||||
new_resource.updated_by_last_action(true) if status
|
||||
end
|
||||
end
|
||||
|
||||
action :remove do
|
||||
if removing_package?
|
||||
Chef::Log.info("Removing #{@new_resource}")
|
||||
remove_package(@current_resource.package_name, @new_resource.version)
|
||||
new_resource.updated_by_last_action(true)
|
||||
end
|
||||
end
|
||||
|
||||
def removing_package?
|
||||
if @current_resource.version.nil?
|
||||
false # nothing to remove
|
||||
elsif @new_resource.version.nil?
|
||||
true # remove any version of a package
|
||||
elsif @new_resource.version == @current_resource.version
|
||||
true # remove the version we have
|
||||
else
|
||||
false # we don't have the version we want to remove
|
||||
end
|
||||
end
|
||||
|
||||
def expand_options(options)
|
||||
options ? " #{options}" : ''
|
||||
end
|
||||
|
||||
# these methods are the required overrides of
|
||||
# a provider that extends from Chef::Provider::Package
|
||||
# so refactoring into core Chef should be easy
|
||||
|
||||
def load_current_resource
|
||||
@current_resource = Chef::Resource::WindowsPackage.new(@new_resource.name)
|
||||
@current_resource.package_name(@new_resource.package_name)
|
||||
@current_resource.version(nil)
|
||||
|
||||
unless current_installed_version.nil?
|
||||
@current_resource.version(current_installed_version)
|
||||
end
|
||||
|
||||
@current_resource
|
||||
end
|
||||
|
||||
def current_installed_version
|
||||
@current_installed_version ||= begin
|
||||
if installed_packages.include?(@new_resource.package_name)
|
||||
installed_packages[@new_resource.package_name][:version]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def candidate_version
|
||||
@candidate_version ||= begin
|
||||
@new_resource.version || 'latest'
|
||||
end
|
||||
end
|
||||
|
||||
def install_package(_name, _version)
|
||||
Chef::Log.debug("Processing #{@new_resource} as a #{installer_type} installer.")
|
||||
install_args = [cached_file(@new_resource.source, @new_resource.checksum), expand_options(unattended_installation_flags), expand_options(@new_resource.options)]
|
||||
Chef::Log.info('Starting installation...this could take awhile.')
|
||||
Chef::Log.debug "Install command: #{sprintf(install_command_template, *install_args)}"
|
||||
shell_out!(sprintf(install_command_template, *install_args), timeout: @new_resource.timeout, returns: @new_resource.success_codes)
|
||||
end
|
||||
|
||||
def remove_package(_name, _version)
|
||||
uninstall_string = installed_packages[@new_resource.package_name][:uninstall_string]
|
||||
Chef::Log.info("Registry provided uninstall string for #{@new_resource} is '#{uninstall_string}'")
|
||||
uninstall_command = begin
|
||||
if uninstall_string =~ /msiexec/i
|
||||
"#{uninstall_string} /qn"
|
||||
else
|
||||
uninstall_string.delete!('"')
|
||||
"start \"\" /wait /d\"#{::File.dirname(uninstall_string)}\" #{::File.basename(uninstall_string)}#{expand_options(@new_resource.options)} /S & exit %%%%ERRORLEVEL%%%%"
|
||||
end
|
||||
end
|
||||
Chef::Log.info("Removing #{@new_resource} with uninstall command '#{uninstall_command}'")
|
||||
shell_out!(uninstall_command, { returns: @new_resource.success_codes })
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def install_command_template
|
||||
case installer_type
|
||||
when :msi
|
||||
"msiexec%2$s \"%1$s\"%3$s"
|
||||
else
|
||||
"start \"\" /wait \"%1$s\"%2$s%3$s & exit %%%%ERRORLEVEL%%%%"
|
||||
end
|
||||
end
|
||||
|
||||
# http://unattended.sourceforge.net/installers.php
|
||||
def unattended_installation_flags
|
||||
case installer_type
|
||||
when :msi
|
||||
# this is no-ui
|
||||
'/qn /i'
|
||||
when :installshield
|
||||
'/s /sms'
|
||||
when :nsis
|
||||
'/S /NCRC'
|
||||
when :inno
|
||||
# "/sp- /silent /norestart"
|
||||
'/verysilent /norestart'
|
||||
when :wise
|
||||
'/s'
|
||||
end
|
||||
end
|
||||
|
||||
def installer_type
|
||||
@installer_type || begin
|
||||
if @new_resource.installer_type
|
||||
@new_resource.installer_type
|
||||
else
|
||||
basename = ::File.basename(cached_file(@new_resource.source, @new_resource.checksum))
|
||||
if basename.split('.').last.downcase == 'msi' # Microsoft MSI
|
||||
:msi
|
||||
else
|
||||
# search the binary file for installer type
|
||||
contents = ::Kernel.open(::File.expand_path(cached_file(@new_resource.source)), 'rb', &:read) # TODO: limit data read in
|
||||
case contents
|
||||
when /inno/i # Inno Setup
|
||||
:inno
|
||||
when /wise/i # Wise InstallMaster
|
||||
:wise
|
||||
when /nsis/i # Nullsoft Scriptable Install System
|
||||
:nsis
|
||||
else
|
||||
# if file is named 'setup.exe' assume installshield
|
||||
if basename == 'setup.exe'
|
||||
:installshield
|
||||
else
|
||||
fail Chef::Exceptions::AttributeNotFound, 'installer_type could not be determined, please set manually'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Chef
|
||||
class Resource
|
||||
class WindowsCookbookPackage < Chef::Resource::LWRPBase
|
||||
if Gem::Version.new(Chef::VERSION) >= Gem::Version.new('12.4.0')
|
||||
provides :windows_package, os: 'windows', override: true
|
||||
elsif Gem::Version.new(Chef::VERSION) >= Gem::Version.new('12')
|
||||
provides :windows_package, os: 'windows'
|
||||
end
|
||||
actions :install, :remove
|
||||
|
||||
default_action :install
|
||||
|
||||
attribute :package_name, kind_of: String, name_attribute: true
|
||||
attribute :source, kind_of: String, required: true
|
||||
attribute :version, kind_of: String
|
||||
attribute :options, kind_of: String
|
||||
attribute :installer_type, kind_of: Symbol, default: nil, equal_to: [:msi, :inno, :nsis, :wise, :installshield, :custom]
|
||||
attribute :checksum, kind_of: String
|
||||
attribute :timeout, kind_of: Integer, default: 600
|
||||
attribute :success_codes, kind_of: Array, default: [0, 42, 127]
|
||||
|
||||
self.resource_name = 'windows_package'
|
||||
def initialize(*args)
|
||||
super
|
||||
@provider = Chef::Provider::WindowsCookbookPackage
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Gem::Version.new(Chef::VERSION) < Gem::Version.new('12')
|
||||
# this wires up the cookbook version of the windows_package resource as Chef::Resource::WindowsPackage,
|
||||
# which is kinda hella janky
|
||||
Chef::Resource.send(:remove_const, :WindowsPackage) if defined? Chef::Resource::WindowsPackage
|
||||
Chef::Resource.const_set('WindowsPackage', Chef::Resource::WindowsCookbookPackage)
|
||||
else
|
||||
if Chef.respond_to?(:set_resource_priority_array)
|
||||
# this wires up the dynamic resource resolver to favor the cookbook version of windows_package over
|
||||
# the internal version (but the internal Chef::Resource::WindowsPackage is still the internal version
|
||||
# and a wrapper cookbook can override this e.g. for users that want to use the windows cookbook but
|
||||
# want the internal windows_package resource)
|
||||
Chef.set_resource_priority_array(:windows_package, [Chef::Resource::WindowsCookbookPackage], platform: 'windows')
|
||||
end
|
||||
end
|
||||
@@ -1,103 +1,103 @@
|
||||
#
|
||||
# Author:: Doug MacEachern <dougm@vmware.com>
|
||||
# Author:: Paul Morton (<pmorton@biaprotect.com>)
|
||||
# Cookbook Name:: windows
|
||||
# Library:: windows_privileged
|
||||
#
|
||||
# Copyright:: 2010, VMware, Inc.
|
||||
# Copyright:: 2011, Business Intelligence Associates, Inc
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# helpers for Windows API calls that require privilege adjustments
|
||||
class Chef
|
||||
class WindowsPrivileged
|
||||
# File -> Load Hive... in regedit.exe
|
||||
def reg_load_key(name, file)
|
||||
load_deps
|
||||
|
||||
run(SE_BACKUP_NAME, SE_RESTORE_NAME) do
|
||||
rc = RegLoadKey(HKEY_USERS, name.to_s, file)
|
||||
if rc == ERROR_SUCCESS
|
||||
return true
|
||||
elsif rc == ERROR_SHARING_VIOLATION
|
||||
return false
|
||||
else
|
||||
fail get_last_error(rc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# File -> Unload Hive... in regedit.exe
|
||||
def reg_unload_key(name)
|
||||
load_deps
|
||||
|
||||
run(SE_BACKUP_NAME, SE_RESTORE_NAME) do
|
||||
rc = RegUnLoadKey(HKEY_USERS, name.to_s)
|
||||
fail get_last_error(rc) if rc != ERROR_SUCCESS
|
||||
end
|
||||
end
|
||||
|
||||
def run(*privileges)
|
||||
load_deps
|
||||
|
||||
token = [0].pack('L')
|
||||
|
||||
unless OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, token)
|
||||
fail get_last_error
|
||||
end
|
||||
token = token.unpack('L')[0]
|
||||
|
||||
privileges.each do |name|
|
||||
unless adjust_privilege(token, name, SE_PRIVILEGE_ENABLED)
|
||||
fail get_last_error
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
yield
|
||||
ensure # disable privs
|
||||
privileges.each do |name|
|
||||
adjust_privilege(token, name, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def adjust_privilege(token, priv, attr = 0)
|
||||
load_deps
|
||||
|
||||
luid = [0, 0].pack('Ll')
|
||||
if LookupPrivilegeValue(nil, priv, luid)
|
||||
new_state = [1, luid.unpack('Ll'), attr].flatten.pack('LLlL')
|
||||
AdjustTokenPrivileges(token, 0, new_state, new_state.size, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_deps
|
||||
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
require 'windows/error'
|
||||
require 'windows/registry'
|
||||
require 'windows/process'
|
||||
require 'windows/security'
|
||||
|
||||
include Windows::Error
|
||||
include Windows::Registry
|
||||
include Windows::Process
|
||||
include Windows::Security
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
#
|
||||
# Author:: Doug MacEachern <dougm@vmware.com>
|
||||
# Author:: Paul Morton (<pmorton@biaprotect.com>)
|
||||
# Cookbook:: windows
|
||||
# Library:: windows_privileged
|
||||
#
|
||||
# Copyright:: 2010-2017, VMware, Inc.
|
||||
# Copyright:: 2011-2017, Business Intelligence Associates, Inc
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
# helpers for Windows API calls that require privilege adjustments
|
||||
class Chef
|
||||
class WindowsPrivileged
|
||||
# File -> Load Hive... in regedit.exe
|
||||
def reg_load_key(name, file)
|
||||
load_deps
|
||||
|
||||
run(SE_BACKUP_NAME, SE_RESTORE_NAME) do
|
||||
rc = RegLoadKey(HKEY_USERS, name.to_s, file)
|
||||
if rc == ERROR_SUCCESS
|
||||
return true
|
||||
elsif rc == ERROR_SHARING_VIOLATION
|
||||
return false
|
||||
else
|
||||
raise get_last_error(rc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# File -> Unload Hive... in regedit.exe
|
||||
def reg_unload_key(name)
|
||||
load_deps
|
||||
|
||||
run(SE_BACKUP_NAME, SE_RESTORE_NAME) do
|
||||
rc = RegUnLoadKey(HKEY_USERS, name.to_s)
|
||||
raise get_last_error(rc) if rc != ERROR_SUCCESS
|
||||
end
|
||||
end
|
||||
|
||||
def run(*privileges)
|
||||
load_deps
|
||||
|
||||
token = [0].pack('L')
|
||||
|
||||
unless OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, token)
|
||||
raise get_last_error
|
||||
end
|
||||
token = token.unpack('L')[0]
|
||||
|
||||
privileges.each do |name|
|
||||
unless adjust_privilege(token, name, SE_PRIVILEGE_ENABLED)
|
||||
raise get_last_error
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
yield
|
||||
ensure # disable privs
|
||||
privileges.each do |name|
|
||||
adjust_privilege(token, name, 0)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def adjust_privilege(token, priv, attr = 0)
|
||||
load_deps
|
||||
|
||||
luid = [0, 0].pack('Ll')
|
||||
if LookupPrivilegeValue(nil, priv, luid)
|
||||
new_state = [1, luid.unpack('Ll'), attr].flatten.pack('LLlL')
|
||||
AdjustTokenPrivileges(token, 0, new_state, new_state.size, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_deps
|
||||
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
require 'windows/error'
|
||||
require 'windows/registry'
|
||||
require 'windows/process'
|
||||
require 'windows/security'
|
||||
|
||||
include Windows::Error
|
||||
include Windows::Registry
|
||||
include Windows::Process
|
||||
include Windows::Security
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
#
|
||||
# Author:: Adam Edwards (<adamed@chef.io>)
|
||||
#
|
||||
# Copyright:: 2014-2015, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
require 'win32ole'
|
||||
|
||||
def execute_wmi_query(wmi_query)
|
||||
wmi = ::WIN32OLE.connect('winmgmts://')
|
||||
result = wmi.ExecQuery(wmi_query)
|
||||
return nil unless result.each.count > 0
|
||||
result
|
||||
end
|
||||
|
||||
def wmi_object_property(wmi_object, wmi_property)
|
||||
wmi_object.send(wmi_property)
|
||||
end
|
||||
end
|
||||
#
|
||||
# Author:: Adam Edwards (<adamed@chef.io>)
|
||||
#
|
||||
# Copyright:: 2014-2017, Chef Software, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
if RUBY_PLATFORM =~ /mswin|mingw32|windows/
|
||||
require 'win32ole'
|
||||
|
||||
def execute_wmi_query(wmi_query)
|
||||
wmi = ::WIN32OLE.connect('winmgmts://')
|
||||
result = wmi.ExecQuery(wmi_query)
|
||||
return nil unless result.each.count > 0
|
||||
result
|
||||
end
|
||||
|
||||
def wmi_object_property(wmi_object, wmi_property)
|
||||
wmi_object.send(wmi_property)
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user