diff options
author | Daniel Steen <dsteen@millennialmedia.com> | 2016-01-19 18:34:46 -0500 |
---|---|---|
committer | Daniel Steen <dsteen@millennialmedia.com> | 2016-01-19 18:34:46 -0500 |
commit | 7ec34e35657ca1f0330667da53bec1366ba5bdf8 (patch) | |
tree | 8f9df4bd48111b36baf5f8045be6c99035bc340c /lib/chef/util | |
parent | 3c6bc501334880f01ff41dc3cc8d32122adcaeba (diff) | |
parent | 25b2c0559caaf29872d6cfe3ab146cb63c496d00 (diff) | |
download | chef-7ec34e35657ca1f0330667da53bec1366ba5bdf8.tar.gz |
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'lib/chef/util')
-rw-r--r-- | lib/chef/util/backup.rb | 14 | ||||
-rw-r--r-- | lib/chef/util/diff.rb | 14 | ||||
-rw-r--r-- | lib/chef/util/dsc/configuration_generator.rb | 14 | ||||
-rw-r--r-- | lib/chef/util/dsc/lcm_output_parser.rb | 8 | ||||
-rw-r--r-- | lib/chef/util/dsc/local_configuration_manager.rb | 14 | ||||
-rw-r--r-- | lib/chef/util/dsc/resource_store.rb | 14 | ||||
-rw-r--r-- | lib/chef/util/file_edit.rb | 6 | ||||
-rw-r--r-- | lib/chef/util/path_helper.rb | 208 | ||||
-rw-r--r-- | lib/chef/util/powershell/cmdlet.rb | 28 | ||||
-rw-r--r-- | lib/chef/util/powershell/cmdlet_result.rb | 2 | ||||
-rw-r--r-- | lib/chef/util/powershell/ps_credential.rb | 5 | ||||
-rw-r--r-- | lib/chef/util/selinux.rb | 4 | ||||
-rw-r--r-- | lib/chef/util/threaded_job_queue.rb | 2 | ||||
-rw-r--r-- | lib/chef/util/windows.rb | 32 | ||||
-rw-r--r-- | lib/chef/util/windows/net_group.rb | 89 | ||||
-rw-r--r-- | lib/chef/util/windows/net_use.rb | 108 | ||||
-rw-r--r-- | lib/chef/util/windows/net_user.rb | 196 | ||||
-rw-r--r-- | lib/chef/util/windows/volume.rb | 40 |
18 files changed, 236 insertions, 562 deletions
diff --git a/lib/chef/util/backup.rb b/lib/chef/util/backup.rb index 0cc009ca1f..2142869e36 100644 --- a/lib/chef/util/backup.rb +++ b/lib/chef/util/backup.rb @@ -16,7 +16,7 @@ # limitations under the License. # -require 'chef/util/path_helper' +require "chef/util/path_helper" class Chef class Util @@ -49,7 +49,7 @@ class Chef def backup_filename @backup_filename ||= begin time = Time.now - nanoseconds = sprintf("%6f", time.to_f).split('.')[1] + nanoseconds = sprintf("%6f", time.to_f).split(".")[1] savetime = time.strftime("%Y%m%d%H%M%S.#{nanoseconds}") backup_filename = "#{path}.chef-#{savetime}" backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows @@ -78,8 +78,16 @@ class Chef Chef::Log.info("#{@new_resource} removed backup at #{backup_file}") end + def unsorted_backup_files + # If you replace this with Dir[], you will probably break Windows. + fn = Regexp.escape(::File.basename(path)) + Dir.entries(::File.dirname(backup_path)).select do |f| + !!(f =~ /\A#{fn}.chef-[0-9.]*\B/) + end.map {|f| ::File.join(::File.dirname(backup_path), f)} + end + def sorted_backup_files - Dir[Chef::Util::PathHelper.escape_glob(prefix, ".#{path}") + ".chef-*"].sort { |a,b| b <=> a } + unsorted_backup_files.sort { |a,b| b <=> a } end end end diff --git a/lib/chef/util/diff.rb b/lib/chef/util/diff.rb index c2dc6e045c..3b42f576c0 100644 --- a/lib/chef/util/diff.rb +++ b/lib/chef/util/diff.rb @@ -40,8 +40,8 @@ # CONNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN THE # SOFTWARE. -require 'diff/lcs' -require 'diff/lcs/hunk' +require "diff/lcs" +require "diff/lcs/hunk" class Chef class Util @@ -64,7 +64,7 @@ class Chef def use_tempfile_if_missing(file) tempfile = nil unless File.exists?(file) - Chef::Log.debug("file #{file} does not exist to diff against, using empty tempfile") + Chef::Log.debug("File #{file} does not exist to diff against, using empty tempfile") tempfile = Tempfile.new("chef-diff") file = tempfile.path end @@ -97,9 +97,9 @@ class Chef return "No differences encountered\n" if diff_data.empty? # write diff header (standard unified format) - ft = File.stat(old_file).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z') + ft = File.stat(old_file).mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.%N %z") diff_str << "--- #{old_file}\t#{ft}\n" - ft = File.stat(new_file).mtime.localtime.strftime('%Y-%m-%d %H:%M:%S.%N %z') + ft = File.stat(new_file).mtime.localtime.strftime("%Y-%m-%d %H:%M:%S.%N %z") diff_str << "+++ #{new_file}\t#{ft}\n" # loop over diff hunks. if a hunk overlaps with the last hunk, @@ -139,7 +139,7 @@ class Chef return "(new content is binary, diff output suppressed)" if is_binary?(new_file) begin - Chef::Log.debug("running: diff -u #{old_file} #{new_file}") + Chef::Log.debug("Running: diff -u #{old_file} #{new_file}") diff_str = udiff(old_file, new_file) rescue Exception => e @@ -176,7 +176,7 @@ class Chef end def encode_diff_for_json(diff_str) - diff_str.encode!('UTF-8', :invalid => :replace, :undef => :replace, :replace => '?') + diff_str.encode!("UTF-8", :invalid => :replace, :undef => :replace, :replace => "?") end end diff --git a/lib/chef/util/dsc/configuration_generator.rb b/lib/chef/util/dsc/configuration_generator.rb index 0d7296eae9..e2c09aeea8 100644 --- a/lib/chef/util/dsc/configuration_generator.rb +++ b/lib/chef/util/dsc/configuration_generator.rb @@ -16,7 +16,7 @@ # limitations under the License. # -require 'chef/util/powershell/cmdlet' +require "chef/util/powershell/cmdlet" class Chef::Util::DSC class ConfigurationGenerator @@ -27,9 +27,9 @@ class Chef::Util::DSC def configuration_document_from_script_code(code, configuration_flags, imports, shellout_flags) Chef::Log.debug("DSC: DSC code:\n '#{code}'") - generated_script_path = write_document_generation_script(code, 'chef_dsc', imports) + generated_script_path = write_document_generation_script(code, "chef_dsc", imports) begin - configuration_document_from_script_path(generated_script_path, 'chef_dsc', configuration_flags, shellout_flags) + configuration_document_from_script_path(generated_script_path, "chef_dsc", configuration_flags, shellout_flags) ensure ::FileUtils.rm(generated_script_path) end @@ -72,7 +72,7 @@ class Chef::Util::DSC if configuration_flags configuration_flags.map do | switch, value | if merged_configuration_flags.key?(switch.to_s.downcase.to_sym) - raise ArgumentError, "The `flags` attribute for the dsc_script resource contained a command line switch :#{switch.to_s} that is disallowed." + raise ArgumentError, "The `flags` attribute for the dsc_script resource contained a command line switch :#{switch} that is disallowed." end merged_configuration_flags[switch.to_s.downcase.to_sym] = value end @@ -97,7 +97,7 @@ Configuration '#{configuration_name}' def generate_import_resource_statements(imports) if imports imports.map do |resource_module, resources| - if resources.length == 0 || resources.include?('*') + if resources.length == 0 || resources.include?("*") "Import-DscResource -ModuleName #{resource_module}" else "Import-DscResource -ModuleName #{resource_module} -Name #{resources.join(',')}" @@ -114,7 +114,7 @@ Configuration '#{configuration_name}' def write_document_generation_script(code, configuration_name, imports) script_path = "#{@config_directory}/chef_dsc_config.ps1" - ::File.open(script_path, 'wt') do | script | + ::File.open(script_path, "wt") do | script | script.write(configuration_code(code, configuration_name, imports)) end script_path @@ -131,7 +131,7 @@ Configuration '#{configuration_name}' end def get_configuration_document(document_path) - ::File.open(document_path, 'rb') do | file | + ::File.open(document_path, "rb") do | file | file.read end end diff --git a/lib/chef/util/dsc/lcm_output_parser.rb b/lib/chef/util/dsc/lcm_output_parser.rb index 754fde3e8b..ac847adffa 100644 --- a/lib/chef/util/dsc/lcm_output_parser.rb +++ b/lib/chef/util/dsc/lcm_output_parser.rb @@ -16,9 +16,9 @@ # limitations under the License. # -require 'chef/log' -require 'chef/util/dsc/resource_info' -require 'chef/exceptions' +require "chef/log" +require "chef/util/dsc/resource_info" +require "chef/exceptions" class Chef class Util @@ -109,7 +109,7 @@ class Chef # What If: [machinename]: LCM: [op_action op_type] message # extract op_action, op_type, and message operation, info = match.captures - op_action, op_type = operation.strip.split(' ').map {|m| m.downcase.to_sym} + op_action, op_type = operation.strip.split(" ").map {|m| m.downcase.to_sym} else op_action = op_type = :info if match = line.match(/^.*?:.*?: \s+(.*)/) diff --git a/lib/chef/util/dsc/local_configuration_manager.rb b/lib/chef/util/dsc/local_configuration_manager.rb index f8398341e5..deaa83ef09 100644 --- a/lib/chef/util/dsc/local_configuration_manager.rb +++ b/lib/chef/util/dsc/local_configuration_manager.rb @@ -16,8 +16,8 @@ # limitations under the License. # -require 'chef/util/powershell/cmdlet' -require 'chef/util/dsc/lcm_output_parser' +require "chef/util/powershell/cmdlet" +require "chef/util/dsc/lcm_output_parser" class Chef::Util::DSC class LocalConfigurationManager @@ -47,7 +47,7 @@ class Chef::Util::DSC def run_configuration_cmdlet(configuration_document, apply_configuration, shellout_flags) Chef::Log.debug("DSC: Calling DSC Local Config Manager to #{apply_configuration ? "set" : "test"} configuration document.") - test_only_parameters = ! apply_configuration ? '-whatif; if (! $?) { exit 1 }' : '' + test_only_parameters = ! apply_configuration ? "-whatif; if (! $?) { exit 1 }" : "" start_operation_timing command_code = lcm_command_code(@configuration_path, test_only_parameters) @@ -90,7 +90,7 @@ EOH end def whatif_not_supported?(what_if_exception_output) - !! (what_if_exception_output.gsub(/[\r\n]+/, '').gsub(/\s+/, ' ') =~ /A parameter cannot be found that matches parameter name 'Whatif'/i) + !! (what_if_exception_output.gsub(/[\r\n]+/, "").gsub(/\s+/, " ") =~ /A parameter cannot be found that matches parameter name 'Whatif'/i) end def dsc_module_import_failure?(what_if_output) @@ -105,13 +105,13 @@ EOH Parser::parse(what_if_output) rescue Chef::Exceptions::LCMParser => e Chef::Log::warn("Could not parse LCM output: #{e}") - [Chef::Util::DSC::ResourceInfo.new('Unknown DSC Resources', true, ['Unknown changes because LCM output was not parsable.'])] + [Chef::Util::DSC::ResourceInfo.new("Unknown DSC Resources", true, ["Unknown changes because LCM output was not parsable."])] end end def save_configuration_document(configuration_document) ::FileUtils.mkdir_p(@configuration_path) - ::File.open(configuration_document_path, 'wb') do | file | + ::File.open(configuration_document_path, "wb") do | file | file.write(configuration_document) end end @@ -121,7 +121,7 @@ EOH end def configuration_document_path - File.join(@configuration_path,'..mof') + File.join(@configuration_path,"..mof") end def clear_execution_time diff --git a/lib/chef/util/dsc/resource_store.rb b/lib/chef/util/dsc/resource_store.rb index fdcecc2b3c..761ade9989 100644 --- a/lib/chef/util/dsc/resource_store.rb +++ b/lib/chef/util/dsc/resource_store.rb @@ -16,9 +16,9 @@ # limitations under the License. # -require 'chef/util/powershell/cmdlet' -require 'chef/util/powershell/cmdlet_result' -require 'chef/exceptions' +require "chef/util/powershell/cmdlet" +require "chef/util/powershell/cmdlet_result" +require "chef/exceptions" class Chef class Util @@ -53,7 +53,7 @@ class DSC def add_resource(new_r) count = resources.count do |r| - r['ResourceType'].casecmp(new_r['ResourceType']) == 0 + r["ResourceType"].casecmp(new_r["ResourceType"]) == 0 end if count == 0 resources << new_r @@ -72,9 +72,9 @@ class DSC def find_resources(name, module_name, rs) found = rs.find_all do |r| - name_matches = r['Name'].casecmp(name) == 0 + name_matches = r["Name"].casecmp(name) == 0 if name_matches - module_name == nil || (r['Module'] and r['Module']['Name'].casecmp(module_name) == 0) + module_name == nil || (r["Module"] and r["Module"]["Name"].casecmp(module_name) == 0) else false end @@ -84,7 +84,7 @@ class DSC # Returns a list of dsc resources def query_resources - cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, 'get-dscresource', + cmdlet = Chef::Util::Powershell::Cmdlet.new(nil, "get-dscresource", :object) result = cmdlet.run result.return_value diff --git a/lib/chef/util/file_edit.rb b/lib/chef/util/file_edit.rb index 4d2a9c03eb..d321764a0a 100644 --- a/lib/chef/util/file_edit.rb +++ b/lib/chef/util/file_edit.rb @@ -15,8 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'chef/util/editor' -require 'fileutils' +require "chef/util/editor" +require "fileutils" class Chef class Util @@ -61,7 +61,7 @@ class Chef #search the file line by line and match each line with the given regex #if matched, delete the match (all occurrences) from the line def search_file_delete(regex) - search_file_replace(regex, '') + search_file_replace(regex, "") end #search the file line by line and match each line with the given regex diff --git a/lib/chef/util/path_helper.rb b/lib/chef/util/path_helper.rb index 66c2e3f19f..4a079d3419 100644 --- a/lib/chef/util/path_helper.rb +++ b/lib/chef/util/path_helper.rb @@ -16,212 +16,10 @@ # limitations under the License. # +require "chef-config/path_helper" + class Chef class Util - class PathHelper - # Maximum characters in a standard Windows path (260 including drive letter and NUL) - WIN_MAX_PATH = 259 - - def self.dirname(path) - if Chef::Platform.windows? - # Find the first slash, not counting trailing slashes - end_slash = path.size - loop do - slash = path.rindex(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]/, end_slash - 1) - if !slash - return end_slash == path.size ? '.' : path_separator - elsif slash == end_slash - 1 - end_slash = slash - else - return path[0..slash-1] - end - end - else - ::File.dirname(path) - end - end - - BACKSLASH = '\\'.freeze - - def self.path_separator - if Chef::Platform.windows? - File::ALT_SEPARATOR || BACKSLASH - else - File::SEPARATOR - end - end - - def self.join(*args) - args.flatten.inject do |joined_path, component| - # Joined path ends with / - joined_path = joined_path.sub(/[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+$/, '') - component = component.sub(/^[#{Regexp.escape(File::SEPARATOR)}#{Regexp.escape(path_separator)}]+/, '') - joined_path += "#{path_separator}#{component}" - end - end - - def self.validate_path(path) - if Chef::Platform.windows? - unless printable?(path) - msg = "Path '#{path}' contains non-printable characters. Check that backslashes are escaped with another backslash (e.g. C:\\\\Windows) in double-quoted strings." - Chef::Log.error(msg) - raise Chef::Exceptions::ValidationFailed, msg - end - - if windows_max_length_exceeded?(path) - Chef::Log.debug("Path '#{path}' is longer than #{WIN_MAX_PATH}, prefixing with'\\\\?\\'") - path.insert(0, "\\\\?\\") - end - end - - path - end - - def self.windows_max_length_exceeded?(path) - # Check to see if paths without the \\?\ prefix are over the maximum allowed length for the Windows API - # http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx - unless path =~ /^\\\\?\\/ - if path.length > WIN_MAX_PATH - return true - end - end - - false - end - - def self.printable?(string) - # returns true if string is free of non-printable characters (escape sequences) - # this returns false for whitespace escape sequences as well, e.g. \n\t - if string =~ /[^[:print:]]/ - false - else - true - end - end - - # Produces a comparable path. - def self.canonical_path(path, add_prefix=true) - # First remove extra separators and resolve any relative paths - abs_path = File.absolute_path(path) - - if Chef::Platform.windows? - # Add the \\?\ API prefix on Windows unless add_prefix is false - # Downcase on Windows where paths are still case-insensitive - abs_path.gsub!(::File::SEPARATOR, path_separator) - if add_prefix && abs_path !~ /^\\\\?\\/ - abs_path.insert(0, "\\\\?\\") - end - - abs_path.downcase! - end - - abs_path - end - - def self.cleanpath(path) - path = Pathname.new(path).cleanpath.to_s - # ensure all forward slashes are backslashes - if Chef::Platform.windows? - path = path.gsub(File::SEPARATOR, path_separator) - end - path - end - - def self.paths_eql?(path1, path2) - canonical_path(path1) == canonical_path(path2) - end - - # Paths which may contain glob-reserved characters need - # to be escaped before globbing can be done. - # http://stackoverflow.com/questions/14127343 - def self.escape_glob(*parts) - path = cleanpath(join(*parts)) - path.gsub(/[\\\{\}\[\]\*\?]/) { |x| "\\"+x } - end - - def self.relative_path_from(from, to) - pathname = Pathname.new(Chef::Util::PathHelper.cleanpath(to)).relative_path_from(Pathname.new(Chef::Util::PathHelper.cleanpath(from))) - end - - # Retrieves the "home directory" of the current user while trying to ascertain the existence - # of said directory. The path returned uses / for all separators (the ruby standard format). - # If the home directory doesn't exist or an error is otherwise encountered, nil is returned. - # - # If a set of path elements is provided, they are appended as-is to the home path if the - # homepath exists. - # - # If an optional block is provided, the joined path is passed to that block if the home path is - # valid and the result of the block is returned instead. - # - # Home-path discovery is performed once. If a path is discovered, that value is memoized so - # that subsequent calls to home_dir don't bounce around. - # - # See self.all_homes. - def self.home(*args) - @@home_dir ||= self.all_homes { |p| break p } - if @@home_dir - path = File.join(@@home_dir, *args) - block_given? ? (yield path) : path - end - end - - # See self.home. This method performs a similar operation except that it yields all the different - # possible values of 'HOME' that one could have on this platform. Hence, on windows, if - # HOMEDRIVE\HOMEPATH and USERPROFILE are different, the provided block will be called twice. - # This method goes out and checks the existence of each location at the time of the call. - # - # The return is a list of all the returned values from each block invocation or a list of paths - # if no block is provided. - def self.all_homes(*args) - paths = [] - if Chef::Platform.windows? - # By default, Ruby uses the the following environment variables to determine Dir.home: - # HOME - # HOMEDRIVE HOMEPATH - # USERPROFILE - # Ruby only checks to see if the variable is specified - not if the directory actually exists. - # On Windows, HOMEDRIVE HOMEPATH can point to a different location (such as an unavailable network mounted drive) - # while USERPROFILE points to the location where the user application settings and profile are stored. HOME - # is not defined as an environment variable (usually). If the home path actually uses UNC, then the prefix is - # HOMESHARE instead of HOMEDRIVE. - # - # We instead walk down the following and only include paths that actually exist. - # HOME - # HOMEDRIVE HOMEPATH - # HOMESHARE HOMEPATH - # USERPROFILE - - paths << ENV['HOME'] - paths << ENV['HOMEDRIVE'] + ENV['HOMEPATH'] if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] - paths << ENV['HOMESHARE'] + ENV['HOMEPATH'] if ENV['HOMESHARE'] && ENV['HOMEPATH'] - paths << ENV['USERPROFILE'] - end - paths << Dir.home if ENV['HOME'] - - # Depending on what environment variables we're using, the slashes can go in any which way. - # Just change them all to / to keep things consistent. - # Note: Maybe this is a bad idea on some unixy systems where \ might be a valid character depending on - # the particular brand of kool-aid you consume. This code assumes that \ and / are both - # path separators on any system being used. - paths = paths.map { |home_path| home_path.gsub(path_separator, ::File::SEPARATOR) if home_path } - - # Filter out duplicate paths and paths that don't exist. - valid_paths = paths.select { |home_path| home_path && Dir.exists?(home_path) } - valid_paths = valid_paths.uniq - - # Join all optional path elements at the end. - # If a block is provided, invoke it - otherwise just return what we've got. - joined_paths = valid_paths.map { |home_path| File.join(home_path, *args) } - if block_given? - joined_paths.each { |p| yield p } - else - joined_paths - end - end - end + PathHelper = ChefConfig::PathHelper end end - -# Break a require loop when require chef/util/path_helper -require 'chef/platform' -require 'chef/exceptions' diff --git a/lib/chef/util/powershell/cmdlet.rb b/lib/chef/util/powershell/cmdlet.rb index 47d63a2b85..6d9bd67afa 100644 --- a/lib/chef/util/powershell/cmdlet.rb +++ b/lib/chef/util/powershell/cmdlet.rb @@ -16,9 +16,9 @@ # limitations under the License. # -require 'mixlib/shellout' -require 'chef/mixin/windows_architecture_helper' -require 'chef/util/powershell/cmdlet_result' +require "mixlib/shellout" +require "chef/mixin/windows_architecture_helper" +require "chef/util/powershell/cmdlet_result" class Chef class Util @@ -38,7 +38,7 @@ class Powershell when :object @json_format = true else - raise ArgumentError, "Invalid output format #{output_format.to_s} specified" + raise ArgumentError, "Invalid output format #{output_format} specified" end @cmdlet = cmdlet @@ -48,11 +48,11 @@ class Powershell attr_reader :output_format def run(switches={}, execution_options={}, *arguments) - streams = { :json => CmdletStream.new('json'), - :verbose => CmdletStream.new('verbose'), + streams = { :json => CmdletStream.new("json"), + :verbose => CmdletStream.new("verbose"), } - arguments_string = arguments.join(' ') + arguments_string = arguments.join(" ") switches_string = command_switches_string(switches) @@ -114,12 +114,12 @@ class Powershell def command_switches_string(switches) command_switches = switches.map do | switch_name, switch_value | if switch_name.class != Symbol - raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name.to_s}'. The switch must be specified as a Symbol'" + raise ArgumentError, "Invalid type `#{switch_name} `for PowerShell switch '#{switch_name}'. The switch must be specified as a Symbol'" end validate_switch_name!(switch_name) - switch_argument = '' + switch_argument = "" switch_present = true case switch_value @@ -133,13 +133,13 @@ class Powershell when String switch_argument = escape_string_parameter_value(switch_value) else - raise ArgumentError, "Invalid argument type `#{switch_value.class}` specified for PowerShell switch `:#{switch_name.to_s}`. Arguments to PowerShell must be of type `String`, `Numeric`, `Float`, `FalseClass`, or `TrueClass`" + raise ArgumentError, "Invalid argument type `#{switch_value.class}` specified for PowerShell switch `:#{switch_name}`. Arguments to PowerShell must be of type `String`, `Numeric`, `Float`, `FalseClass`, or `TrueClass`" end - switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(' ').strip : '' + switch_present ? ["-#{switch_name.to_s.downcase}", switch_argument].join(" ").strip : "" end - command_switches.join(' ') + command_switches.join(" ") end class CmdletStream @@ -154,8 +154,8 @@ class Powershell def read if File.exist? @filename - File.open(@filename, 'rb:bom|UTF-16LE') do |f| - f.read.encode('UTF-8') + File.open(@filename, "rb:bom|UTF-16LE") do |f| + f.read.encode("UTF-8") end end end diff --git a/lib/chef/util/powershell/cmdlet_result.rb b/lib/chef/util/powershell/cmdlet_result.rb index f1fdd968b1..531636a4ff 100644 --- a/lib/chef/util/powershell/cmdlet_result.rb +++ b/lib/chef/util/powershell/cmdlet_result.rb @@ -16,7 +16,7 @@ # limitations under the License. # -require 'chef/json_compat' +require "chef/json_compat" class Chef class Util diff --git a/lib/chef/util/powershell/ps_credential.rb b/lib/chef/util/powershell/ps_credential.rb index 01f8c27b6c..660ef32472 100644 --- a/lib/chef/util/powershell/ps_credential.rb +++ b/lib/chef/util/powershell/ps_credential.rb @@ -16,7 +16,7 @@ # limitations under the License. # -require 'chef/win32/crypto' if Chef::Platform.windows? +require "chef/win32/crypto" if Chef::Platform.windows? class Chef::Util::Powershell class PSCredential @@ -29,6 +29,9 @@ class Chef::Util::Powershell "New-Object System.Management.Automation.PSCredential('#{@username}',('#{encrypt(@password)}' | ConvertTo-SecureString))" end + alias to_s to_psobject + alias to_text to_psobject + private def encrypt(str) diff --git a/lib/chef/util/selinux.rb b/lib/chef/util/selinux.rb index 778da042e3..f9fbe82922 100644 --- a/lib/chef/util/selinux.rb +++ b/lib/chef/util/selinux.rb @@ -20,8 +20,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'chef/mixin/shell_out' -require 'chef/mixin/which' +require "chef/mixin/shell_out" +require "chef/mixin/which" class Chef class Util diff --git a/lib/chef/util/threaded_job_queue.rb b/lib/chef/util/threaded_job_queue.rb index 824cd0a3c4..d01ecd81a6 100644 --- a/lib/chef/util/threaded_job_queue.rb +++ b/lib/chef/util/threaded_job_queue.rb @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -require 'thread' +require "thread" class Chef class Util diff --git a/lib/chef/util/windows.rb b/lib/chef/util/windows.rb index 777fe4adbb..7d29a67ac5 100644 --- a/lib/chef/util/windows.rb +++ b/lib/chef/util/windows.rb @@ -15,42 +15,10 @@ # See the License for the specific language governing permissions and # limitations under the License. # -#requires: gem install windows-pr -require 'windows/api' -require 'windows/error' -require 'windows/handle' -require 'windows/unicode' -require 'windows/msvcrt/buffer' -require 'windows/msvcrt/string' -require 'windows/network/management' class Chef class Util class Windows - protected - - include ::Windows::Error - include ::Windows::Unicode - include ::Windows::MSVCRT::Buffer - include ::Windows::MSVCRT::String - include ::Windows::Network::Management - - PTR_SIZE = 4 #XXX 64-bit - - def lpwstr_to_s(buffer, offset) - str = 0.chr * (256 * 2) #XXX unhardcode this length (*2 for WCHAR) - wcscpy str, buffer[offset*PTR_SIZE,PTR_SIZE].unpack('L')[0] - wide_to_multi str - end - - def dword_to_i(buffer, offset) - buffer[offset*PTR_SIZE,PTR_SIZE].unpack('i')[0] || 0 - end - - #return pointer for use with pack('L') - def str_to_ptr(v) - [v].pack('p*').unpack('L')[0] - end end end end diff --git a/lib/chef/util/windows/net_group.rb b/lib/chef/util/windows/net_group.rb index 924bd392f9..5f6bc6a03b 100644 --- a/lib/chef/util/windows/net_group.rb +++ b/lib/chef/util/windows/net_group.rb @@ -16,91 +16,70 @@ # limitations under the License. # -require 'chef/util/windows' +require "chef/util/windows" +require "chef/win32/net" #wrapper around a subset of the NetGroup* APIs. -#nothing Chef specific, but not complete enough to be its own gem, so util for now. -class Chef::Util::Windows::NetGroup < Chef::Util::Windows +class Chef::Util::Windows::NetGroup private - def pack_str(s) - [str_to_ptr(s)].pack('L') - end - - def modify_members(members, func) - buffer = 0.chr * (members.size * PTR_SIZE) - members.each_with_index do |member,offset| - buffer[offset*PTR_SIZE,PTR_SIZE] = pack_str(multi_to_wide(member)) - end - rc = func.call(nil, @name, 3, buffer, members.size) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) - end + def groupname + @groupname end public def initialize(groupname) - @name = multi_to_wide(groupname) + @groupname = groupname end def local_get_members - group_members = [] - handle = 0.chr * PTR_SIZE - rc = ERROR_MORE_DATA - - while rc == ERROR_MORE_DATA - ptr = 0.chr * PTR_SIZE - nread = 0.chr * PTR_SIZE - total = 0.chr * PTR_SIZE - - rc = NetLocalGroupGetMembers.call(nil, @name, 0, ptr, -1, - nread, total, handle) - if (rc == NERR_Success) || (rc == ERROR_MORE_DATA) - ptr = ptr.unpack('L')[0] - nread = nread.unpack('i')[0] - members = 0.chr * (nread * PTR_SIZE ) #nread * sizeof(LOCALGROUP_MEMBERS_INFO_0) - memcpy(members, ptr, members.size) - - # 1 pointer field in LOCALGROUP_MEMBERS_INFO_0, offset 0 is lgrmi0_sid - nread.times do |i| - sid_address = members[i * PTR_SIZE, PTR_SIZE].unpack('L')[0] - sid_ptr = FFI::Pointer.new(sid_address) - member_sid = Chef::ReservedNames::Win32::Security::SID.new(sid_ptr) - group_members << member_sid.to_s - end - NetApiBufferFree(ptr) - else - raise ArgumentError, get_last_error(rc) - end + begin + Chef::ReservedNames::Win32::NetUser::net_local_group_get_members(nil, groupname) + rescue Chef::Exceptions::Win32NetAPIError => e + raise ArgumentError, e.msg end - group_members end def local_add - rc = NetLocalGroupAdd.call(nil, 0, pack_str(@name), nil) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) + begin + Chef::ReservedNames::Win32::NetUser::net_local_group_add(nil, groupname) + rescue Chef::Exceptions::Win32NetAPIError => e + raise ArgumentError, e.msg end end def local_set_members(members) - modify_members(members, NetLocalGroupSetMembers) + begin + Chef::ReservedNames::Win32::NetUser::net_local_group_set_members(nil, groupname, members) + rescue Chef::Exceptions::Win32NetAPIError => e + raise ArgumentError, e.msg + end end def local_add_members(members) - modify_members(members, NetLocalGroupAddMembers) + begin + Chef::ReservedNames::Win32::NetUser::net_local_group_add_members(nil, groupname, members) + rescue Chef::Exceptions::Win32NetAPIError => e + raise ArgumentError, e.msg + end end def local_delete_members(members) - modify_members(members, NetLocalGroupDelMembers) + begin + Chef::ReservedNames::Win32::NetUser::net_local_group_del_members(nil, groupname, members) + rescue Chef::Exceptions::Win32NetAPIError => e + raise ArgumentError, e.msg + end + end def local_delete - rc = NetLocalGroupDel.call(nil, @name) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) + begin + Chef::ReservedNames::Win32::NetUser::net_local_group_del(nil, groupname) + rescue Chef::Exceptions::Win32NetAPIError => e + raise ArgumentError, e.msg end end end diff --git a/lib/chef/util/windows/net_use.rb b/lib/chef/util/windows/net_use.rb index 62d7e169dc..e70df6e42b 100644 --- a/lib/chef/util/windows/net_use.rb +++ b/lib/chef/util/windows/net_use.rb @@ -20,62 +20,19 @@ #see also: WNetAddConnection2 and WNetAddConnection3 #see also cmd.exe: net use /? -require 'chef/util/windows' +require "chef/util/windows" +require "chef/win32/net" class Chef::Util::Windows::NetUse < Chef::Util::Windows - - private - - USE_NOFORCE = 0 - USE_FORCE = 1 - USE_LOTS_OF_FORCE = 2 #every windows API should support this flag - - USE_INFO_2 = [ - [:local, nil], - [:remote, nil], - [:password, nil], - [:status, 0], - [:asg_type, 0], - [:refcount, 0], - [:usecount, 0], - [:username, nil], - [:domainname, nil] - ] - - USE_INFO_2_TEMPLATE = - USE_INFO_2.collect { |field| field[1].class == Fixnum ? 'i' : 'L' }.join - - SIZEOF_USE_INFO_2 = #sizeof(USE_INFO_2) - USE_INFO_2.inject(0) do |sum, item| - sum + (item[1].class == Fixnum ? 4 : PTR_SIZE) - end - - def use_info_2(args) - USE_INFO_2.collect { |field| - args.include?(field[0]) ? args[field[0]] : field[1] - } - end - - def use_info_2_pack(use) - use.collect { |v| - v.class == Fixnum ? v : str_to_ptr(multi_to_wide(v)) - }.pack(USE_INFO_2_TEMPLATE) + def initialize(localname) + @use_name = localname end - def use_info_2_unpack(buffer) - use = Hash.new - USE_INFO_2.each_with_index do |field,offset| - use[field[0]] = field[1].class == Fixnum ? - dword_to_i(buffer, offset) : lpwstr_to_s(buffer, offset) + def to_ui2_struct(use_info) + use_info.inject({}) do |memo, (k,v)| + memo["ui2_#{k}".to_sym] = v + memo end - use - end - - public - - def initialize(localname) - @localname = localname - @name = multi_to_wide(localname) end def add(args) @@ -84,38 +41,45 @@ class Chef::Util::Windows::NetUse < Chef::Util::Windows args = Hash.new args[:remote] = remote end - args[:local] ||= @localname - use = use_info_2(args) - buffer = use_info_2_pack(use) - rc = NetUseAdd.call(nil, 2, buffer, nil) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) + args[:local] ||= use_name + ui2_hash = to_ui2_struct(args) + + begin + Chef::ReservedNames::Win32::Net.net_use_add_l2(nil, ui2_hash) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e end end - def get_info - ptr = 0.chr * PTR_SIZE - rc = NetUseGetInfo.call(nil, @name, 2, ptr) - - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) + def from_use_info_struct(ui2_hash) + ui2_hash.inject({}) do |memo, (k,v)| + memo[k.to_s.sub("ui2_", "").to_sym] = v + memo end + end - ptr = ptr.unpack('L')[0] - buffer = 0.chr * SIZEOF_USE_INFO_2 - memcpy(buffer, ptr, buffer.size) - NetApiBufferFree(ptr) - use_info_2_unpack(buffer) + def get_info + begin + ui2 = Chef::ReservedNames::Win32::Net.net_use_get_info_l2(nil, use_name) + from_use_info_struct(ui2) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e + end end def device get_info()[:remote] end - #XXX should we use some FORCE here? + def delete - rc = NetUseDel.call(nil, @name, USE_NOFORCE) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) + begin + Chef::ReservedNames::Win32::Net.net_use_del(nil, use_name, :use_noforce) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e end end + + def use_name + @use_name + end end diff --git a/lib/chef/util/windows/net_user.rb b/lib/chef/util/windows/net_user.rb index 5df1a8aaa4..b8e9b63571 100644 --- a/lib/chef/util/windows/net_user.rb +++ b/lib/chef/util/windows/net_user.rb @@ -16,100 +16,71 @@ # limitations under the License. # -require 'chef/util/windows' -require 'chef/exceptions' +require "chef/util/windows" +require "chef/exceptions" +require "chef/win32/net" +require "chef/win32/security" #wrapper around a subset of the NetUser* APIs. #nothing Chef specific, but not complete enough to be its own gem, so util for now. class Chef::Util::Windows::NetUser < Chef::Util::Windows private - - LogonUser = Windows::API.new('LogonUser', 'SSSLLP', 'I', 'advapi32') - - DOMAIN_GROUP_RID_USERS = 0x00000201 - - UF_SCRIPT = 0x000001 - UF_ACCOUNTDISABLE = 0x000002 - UF_PASSWD_CANT_CHANGE = 0x000040 - UF_NORMAL_ACCOUNT = 0x000200 - UF_DONT_EXPIRE_PASSWD = 0x010000 - - #[:symbol_name, default_val] - #default_val duals as field type - #array index duals as structure offset - - #OC-8391 - #Changing [:password, nil], to [:password, ""], - #if :password is set to nil, windows user creation api ignores the password policy applied - #thus initializing it with empty string value. - USER_INFO_3 = [ - [:name, nil], - [:password, ""], - [:password_age, 0], - [:priv, 0], #"The NetUserAdd and NetUserSetInfo functions ignore this member" - [:home_dir, nil], - [:comment, nil], - [:flags, UF_SCRIPT|UF_DONT_EXPIRE_PASSWD|UF_NORMAL_ACCOUNT], - [:script_path, nil], - [:auth_flags, 0], - [:full_name, nil], - [:user_comment, nil], - [:parms, nil], - [:workstations, nil], - [:last_logon, 0], - [:last_logoff, 0], - [:acct_expires, -1], - [:max_storage, -1], - [:units_per_week, 0], - [:logon_hours, nil], - [:bad_pw_count, 0], - [:num_logons, 0], - [:logon_server, nil], - [:country_code, 0], - [:code_page, 0], - [:user_id, 0], - [:primary_group_id, DOMAIN_GROUP_RID_USERS], - [:profile, nil], - [:home_dir_drive, nil], - [:password_expired, 0] - ] - - USER_INFO_3_TEMPLATE = - USER_INFO_3.collect { |field| field[1].class == Fixnum ? 'i' : 'L' }.join - - SIZEOF_USER_INFO_3 = #sizeof(USER_INFO_3) - USER_INFO_3.inject(0){|sum,item| - sum + (item[1].class == Fixnum ? 4 : PTR_SIZE) - } - - def user_info_3(args) - USER_INFO_3.collect { |field| - args.include?(field[0]) ? args[field[0]] : field[1] - } - end - - def user_info_3_pack(user) - user.collect { |v| - v.class == Fixnum ? v : str_to_ptr(multi_to_wide(v)) - }.pack(USER_INFO_3_TEMPLATE) + NetUser = Chef::ReservedNames::Win32::NetUser + Security = Chef::ReservedNames::Win32::Security + + USER_INFO_3_TRANSFORM = { + name: :usri3_name, + password: :usri3_password, + password_age: :usri3_password_age, + priv: :usri3_priv, + home_dir: :usri3_home_dir, + comment: :usri3_comment, + flags: :usri3_flags, + script_path: :usri3_script_path, + auth_flags: :usri3_auth_flags, + full_name: :usri3_full_name, + user_comment: :usri3_usr_comment, + parms: :usri3_parms, + workstations: :usri3_workstations, + last_logon: :usri3_last_logon, + last_logoff: :usri3_last_logoff, + acct_expires: :usri3_acct_expires, + max_storage: :usri3_max_storage, + units_per_week: :usri3_units_per_week, + logon_hours: :usri3_logon_hours, + bad_pw_count: :usri3_bad_pw_count, + num_logons: :usri3_num_logons, + logon_server: :usri3_logon_server, + country_code: :usri3_country_code, + code_page: :usri3_code_page, + user_id: :usri3_user_id, + primary_group_id: :usri3_primary_group_id, + profile: :usri3_profile, + home_dir_drive: :usri3_home_dir_drive, + password_expired: :usri3_password_expired, + } + + def transform_usri3(args) + args.inject({}) do |memo, (k,v)| + memo[USER_INFO_3_TRANSFORM[k]] = v + memo + end end - def user_info_3_unpack(buffer) - user = Hash.new - USER_INFO_3.each_with_index do |field,offset| - user[field[0]] = field[1].class == Fixnum ? - dword_to_i(buffer, offset) : lpwstr_to_s(buffer, offset) + def usri3_to_hash(usri3) + t = USER_INFO_3_TRANSFORM.invert + usri3.inject({}) do |memo, (k,v)| + memo[t[k]] = v + memo end - user end def set_info(args) - user = user_info_3(args) - buffer = user_info_3_pack(user) - rc = NetUserSetInfo.call(nil, @name, 3, buffer, nil) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) + begin + rc = NetUser::net_user_set_info_l3(nil, @username, transform_usri3(args)) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e end end @@ -117,52 +88,34 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows def initialize(username) @username = username - @name = multi_to_wide(username) end - LOGON32_PROVIDER_DEFAULT = 0 - LOGON32_LOGON_NETWORK = 3 + LOGON32_PROVIDER_DEFAULT = Security::LOGON32_PROVIDER_DEFAULT + LOGON32_LOGON_NETWORK = Security::LOGON32_LOGON_NETWORK #XXX for an extra painful alternative, see: http://support.microsoft.com/kb/180548 def validate_credentials(passwd) - token = 0.chr * PTR_SIZE - res = LogonUser.call(@username, nil, passwd, - LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, token) - if res == 0 + begin + token = Security::logon_user(@username, nil, passwd, + LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT) + return true + rescue Chef::Exceptions::Win32APIError return false end - ::Windows::Handle::CloseHandle.call(token.unpack('L')[0]) - return true end def get_info - ptr = 0.chr * PTR_SIZE - rc = NetUserGetInfo.call(nil, @name, 3, ptr) - - if rc == NERR_UserNotFound - raise Chef::Exceptions::UserIDNotFound, get_last_error(rc) - elsif rc != NERR_Success - raise ArgumentError, get_last_error(rc) + begin + ui3 = NetUser::net_user_get_info_l3(nil, @username) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e end - - ptr = ptr.unpack('L')[0] - buffer = 0.chr * SIZEOF_USER_INFO_3 - memcpy(buffer, ptr, buffer.size) - NetApiBufferFree(ptr) - user_info_3_unpack(buffer) + usri3_to_hash(ui3) end def add(args) - user = user_info_3(args) - buffer = user_info_3_pack(user) - - rc = NetUserAdd.call(nil, 3, buffer, rc) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) - end - - #usri3_primary_group_id: - #"When you call the NetUserAdd function, this member must be DOMAIN_GROUP_RID_USERS" - NetLocalGroupAddMembers(nil, multi_to_wide("Users"), 3, buffer[0,PTR_SIZE], 1) + transformed_args = transform_usri3(args) + NetUser::net_user_add_l3(nil, transformed_args) + NetUser::net_local_group_add_member(nil, "Users", args[:name]) end def user_modify(&proc) @@ -182,15 +135,16 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows end def delete - rc = NetUserDel.call(nil, @name) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) + begin + NetUser::net_user_del(nil, @username) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e end end def disable_account user_modify do |user| - user[:flags] |= UF_ACCOUNTDISABLE + user[:flags] |= NetUser::UF_ACCOUNTDISABLE #This does not set the password to nil. It (for some reason) means to ignore updating the field. #See similar behavior for the logon_hours field documented at #http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx @@ -200,7 +154,7 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows def enable_account user_modify do |user| - user[:flags] &= ~UF_ACCOUNTDISABLE + user[:flags] &= ~NetUser::UF_ACCOUNTDISABLE #This does not set the password to nil. It (for some reason) means to ignore updating the field. #See similar behavior for the logon_hours field documented at #http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx @@ -209,6 +163,6 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows end def check_enabled - (get_info()[:flags] & UF_ACCOUNTDISABLE) != 0 + (get_info()[:flags] & NetUser::UF_ACCOUNTDISABLE) != 0 end end diff --git a/lib/chef/util/windows/volume.rb b/lib/chef/util/windows/volume.rb index 08c3a53793..7b24ec37aa 100644 --- a/lib/chef/util/windows/volume.rb +++ b/lib/chef/util/windows/volume.rb @@ -18,42 +18,42 @@ #simple wrapper around Volume APIs. might be possible with WMI, but possibly more complex. -require 'chef/util/windows' -require 'windows/volume' +require "chef/win32/api/file" +require "chef/util/windows" class Chef::Util::Windows::Volume < Chef::Util::Windows - - private - include Windows::Volume - #XXX not defined in the current windows-pr release - DeleteVolumeMountPoint = - Windows::API.new('DeleteVolumeMountPoint', 'S', 'B') unless defined? DeleteVolumeMountPoint - - public + attr_reader :mount_point def initialize(name) name += "\\" unless name =~ /\\$/ #trailing slash required - @name = name + @mount_point = name end def device - buffer = 0.chr * 256 - if GetVolumeNameForVolumeMountPoint(@name, buffer, buffer.size) - return buffer[0,buffer.size].unpack("Z*")[0] - else - raise ArgumentError, get_last_error + begin + Chef::ReservedNames::Win32::File.get_volume_name_for_volume_mount_point(mount_point) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e end end def delete - unless DeleteVolumeMountPoint.call(@name) - raise ArgumentError, get_last_error + begin + Chef::ReservedNames::Win32::File.delete_volume_mount_point(mount_point) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e end end def add(args) - unless SetVolumeMountPoint(@name, args[:remote]) - raise ArgumentError, get_last_error + begin + Chef::ReservedNames::Win32::File.set_volume_mount_point(mount_point, args[:remote]) + rescue Chef::Exceptions::Win32APIError => e + raise ArgumentError, e end end + + def mount_point + @mount_point + end end |