module Puppet::Etc

On Ruby 2.1.0 and later, Etc returns strings in variable encoding depending on environment. The string returned will be labeled with the environment's encoding (Encoding.default_external), with one exception: If the environment encoding is 7-bit ASCII, and any individual character bit representation is equal to or greater than 128 - x80 - 0b10000000 - signifying the smallest 8-bit big-endian value, the returned string will be in BINARY encoding instead of environment encoding.

Barring that exception, the returned string will be labeled as encoding Encoding.default_external, regardless of validity or byte-width. For example, ruby will label a string containing a four-byte characters such as “u{2070E}” as EUC_KR even though EUC_KR is a two-byte width encoding.

On Ruby 2.0.x and earlier, Etc will always return string values in BINARY, ignoring encoding altogether.

For Puppet we specifically want UTF-8 as our input from the Etc module - which is our input for many resource instance 'is' values. The associated 'should' value will basically always be coming from Puppet in UTF-8 - and written to disk as UTF-8. Etc is defined for Windows but the majority calls to it return nil and Puppet does not use it.

That being said, we have cause to retain the original, pre-override string values. `puppet resource user` (Puppet::Resource::User.indirection.search('User', {})) uses self.instances to query for user(s) and then iterates over the results of that query again to obtain state for each user. If we've overridden the original user name and not retained the original, we've lost the ability to query the system for it later. Hence the Puppet::Etc::Passwd and Puppet::Etc::Group structs.

We only use Etc for retrieving existing property values from the system. For setting property values, providers leverage system tools (i.e., `useradd`)

@api private

Public Class Methods

endgrent() click to toggle source

closes handle to /etc/group file

   # File lib/puppet/etc.rb
51 def endgrent
52   ::Etc.endgrent
53 end
endpwent() click to toggle source

closes handle to /etc/passwd file

   # File lib/puppet/etc.rb
68 def endpwent
69   ::Etc.endpwent
70 end
getgrent() click to toggle source

Etc::getgrent returns an Etc::Group struct object On first call opens /etc/group and returns parse of first entry. Each subsquent call returns new struct the next entry or nil if EOF. Call ::endgrent to close file.

   # File lib/puppet/etc.rb
46 def getgrent
47   override_field_values_to_utf8(::Etc.getgrent)
48 end
getgrgid(id) click to toggle source

Etc::getgrid searches /etc/group file for an entry corresponding to id. returns an Etc::Group struct corresponding to the entry or raises ArgumentError if none

   # File lib/puppet/etc.rb
95 def getgrgid(id)
96   override_field_values_to_utf8(::Etc.getgrgid(id))
97 end
getgrnam(groupname) click to toggle source

Etc::getgrnam searches /etc/group file for an entry corresponding to groupname. returns an Etc::Group struct corresponding to the entry or raises ArgumentError if none

   # File lib/puppet/etc.rb
88 def getgrnam(groupname)
89   override_field_values_to_utf8(::Etc.getgrnam(groupname))
90 end
getpwent() click to toggle source

Etc::getpwent returns an Etc::Passwd struct object On first call opens /etc/passwd and returns parse of first entry. Each subsquent call returns new struct for the next entry or nil if EOF. Call ::endgrent to close file.

   # File lib/puppet/etc.rb
63 def getpwent
64   override_field_values_to_utf8(::Etc.getpwent)
65 end
getpwnam(username) click to toggle source

Etc::getpwnam searches /etc/passwd file for an entry corresponding to username. returns an Etc::Passwd struct corresponding to the entry or raises ArgumentError if none

   # File lib/puppet/etc.rb
81 def getpwnam(username)
82   override_field_values_to_utf8(::Etc.getpwnam(username))
83 end
getpwuid(id) click to toggle source

Etc::getpwuid searches /etc/passwd file for an entry corresponding to id. returns an Etc::Passwd struct corresponding to the entry or raises ArgumentError if none

    # File lib/puppet/etc.rb
102 def getpwuid(id)
103   override_field_values_to_utf8(::Etc.getpwuid(id))
104 end
group() { |cur_group| ... } click to toggle source

Etc::group returns a Ruby iterator that executes a block for each entry in the /etc/group file. The code-block is passed a Group struct. See getgrent above for more details.

    # File lib/puppet/etc.rb
109 def group
110   # The implementation here duplicates the logic in https://github.com/ruby/etc/blob/master/ext/etc/etc.c#L523-L537
111   # Note that we do not call ::Etc.group directly, because we
112   # want to use our wrappers for methods like getgrent, setgrent,
113   # endgrent, etc.
114   return getgrent unless block_given?
115 
116   setgrent
117   begin
118     while cur_group = getgrent #rubocop:disable Lint/AssignmentInCondition
119       yield cur_group
120     end
121   ensure
122     endgrent
123   end
124 end
setgrent() click to toggle source

effectively equivalent to IO#rewind of /etc/group

   # File lib/puppet/etc.rb
56 def setgrent
57   ::Etc.setgrent
58 end
setpwent() click to toggle source

effectively equivalent to IO#rewind of /etc/passwd

   # File lib/puppet/etc.rb
73 def setpwent
74   ::Etc.setpwent
75 end

Private Class Methods

override_field_values_to_utf8(struct) click to toggle source

Utility method for overriding the String values of a struct returned by the Etc module to UTF-8. Structs returned by the ruby Etc module contain members with fields of type String, Integer, or Array of Strings, so we handle these types. Otherwise ignore fields.

@api private @param [Etc::Passwd or Etc::Group struct] @return [Puppet::Etc::Passwd or Puppet::Etc::Group struct] a new struct

object with the original struct values overridden to UTF-8, if valid. For
invalid values originating in UTF-8, invalid characters are replaced with
'?'. For each member the struct also contains a corresponding
:canonical_<member name> struct member.
    # File lib/puppet/etc.rb
160 def override_field_values_to_utf8(struct)
161   return nil if struct.nil?
162   new_struct = struct.is_a?(Etc::Passwd) ? puppet_etc_passwd_class.new : puppet_etc_group_class.new
163   struct.each_pair do |member, value|
164     if value.is_a?(String)
165       new_struct["canonical_#{member}".to_sym] = value.dup
166       new_struct[member] = Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(value).scrub
167     elsif value.is_a?(Array)
168       new_struct["canonical_#{member}".to_sym] = value.inject([]) { |acc, elem| acc << elem.dup }
169       new_struct[member] = value.inject([]) do |acc, elem|
170         acc << Puppet::Util::CharacterEncoding.override_encoding_to_utf_8(elem).scrub
171       end
172     else
173       new_struct["canonical_#{member}".to_sym] = value
174       new_struct[member] = value
175     end
176   end
177   new_struct
178 end
puppet_etc_group_class() click to toggle source

@api private Defines Puppet::Etc::Group struct class. Contains all of the original member fields of Etc::Group, and additional “canonical_” versions of these fields as well. API compatible with Etc::Group. Because Struct.new defines a new Class object, we memoize to avoid superfluous extra Class instantiations.

    # File lib/puppet/etc.rb
144 def puppet_etc_group_class
145   @group_class ||= Struct.new(*Etc::Group.members, *Etc::Group.members.map { |member| "canonical_#{member}".to_sym })
146 end
puppet_etc_passwd_class() click to toggle source

@api private Defines Puppet::Etc::Passwd struct class. Contains all of the original member fields of Etc::Passwd, and additional “canonical_” versions of these fields as well. API compatible with Etc::Passwd. Because Struct.new defines a new Class object, we memoize to avoid superfluous extra Class instantiations.

    # File lib/puppet/etc.rb
134 def puppet_etc_passwd_class
135   @password_class ||= Struct.new(*Etc::Passwd.members, *Etc::Passwd.members.map { |member| "canonical_#{member}".to_sym })
136 end