module Puppet::Util::POSIX
Utility methods for interacting with POSIX objects; mostly user and group
Constants
- LOCALE_ENV_VARS
This is a list of environment variables that we will set when we want to override the
POSIXlocale- USER_ENV_VARS
This is a list of user-related environment variables that we will unset when we want to provide a pristine environment for “exec” runs
Public Class Methods
Returns an array of all the groups that the user's a member of.
# File lib/puppet/util/posix.rb 15 def groups_of(user) 16 begin 17 require_relative '../../puppet/ffi/posix' 18 groups = get_groups_list(user) 19 rescue StandardError, LoadError => e 20 Puppet.debug("Falling back to Puppet::Etc.group: #{e.message}") 21 22 groups = [] 23 Puppet::Etc.group do |group| 24 groups << group.name if group.mem.include?(user) 25 end 26 end 27 28 uniq_groups = groups.uniq 29 if uniq_groups != groups 30 Puppet.debug(_('Removing any duplicate group entries')) 31 end 32 33 uniq_groups 34 end
Private Class Methods
# File lib/puppet/util/posix.rb 37 def get_groups_list(user) 38 raise LoadError, "The 'getgrouplist' method is not available" unless Puppet::FFI::POSIX::Functions.respond_to?(:getgrouplist) 39 40 user_gid = Puppet::Etc.getpwnam(user).gid 41 ngroups = Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS 42 43 while true do # rubocop:disable Lint/LiteralInCondition 44 FFI::MemoryPointer.new(:int) do |ngroups_ptr| 45 FFI::MemoryPointer.new(:uint, ngroups) do |groups_ptr| 46 old_ngroups = ngroups 47 ngroups_ptr.write_int(ngroups) 48 49 if Puppet::FFI::POSIX::Functions::getgrouplist(user, user_gid, groups_ptr, ngroups_ptr) != -1 50 groups_gids = groups_ptr.get_array_of_uint(0, ngroups_ptr.read_int) 51 52 result = [] 53 groups_gids.each do |group_gid| 54 group_info = Puppet::Etc.getgrgid(group_gid) 55 result |= [group_info.name] if group_info.mem.include?(user) 56 end 57 return result 58 end 59 60 ngroups = ngroups_ptr.read_int 61 if ngroups <= old_ngroups 62 ngroups *= 2 63 end 64 end 65 end 66 end 67 end
Public Instance Methods
Retrieve a field from a POSIX Etc object. The id can be either an integer or a name. This only works for users and groups. It's also broken on some platforms, unfortunately, which is why we fall back to the other method search_posix_field in the gid and uid methods if a sanity check fails
# File lib/puppet/util/posix.rb 75 def get_posix_field(space, field, id) 76 raise Puppet::DevError, _("Did not get id from caller") unless id 77 78 if id.is_a?(Integer) 79 if id > Puppet[:maximum_uid].to_i 80 Puppet.err _("Tried to get %{field} field for silly id %{id}") % { field: field, id: id } 81 return nil 82 end 83 method = methodbyid(space) 84 else 85 method = methodbyname(space) 86 end 87 88 begin 89 return Etc.send(method, id).send(field) 90 rescue NoMethodError, ArgumentError 91 # ignore it; we couldn't find the object 92 return nil 93 end 94 end
Get the GID
# File lib/puppet/util/posix.rb 158 def gid(group) 159 get_posix_value(:group, :gid, group) 160 end
Determine what the field name is for users and groups.
# File lib/puppet/util/posix.rb 128 def idfield(space) 129 case space.intern 130 when :gr, :group; return :gid 131 when :pw, :user, :passwd; return :uid 132 else 133 raise ArgumentError.new(_("Can only handle users and groups")) 134 end 135 end
Determine what the method is to get users and groups by id
# File lib/puppet/util/posix.rb 138 def methodbyid(space) 139 case space.intern 140 when :gr, :group; return :getgrgid 141 when :pw, :user, :passwd; return :getpwuid 142 else 143 raise ArgumentError.new(_("Can only handle users and groups")) 144 end 145 end
Determine what the method is to get users and groups by name
# File lib/puppet/util/posix.rb 148 def methodbyname(space) 149 case space.intern 150 when :gr, :group; return :getgrnam 151 when :pw, :user, :passwd; return :getpwnam 152 else 153 raise ArgumentError.new(_("Can only handle users and groups")) 154 end 155 end
A degenerate method of retrieving name/id mappings. The job of this method is to retrieve all objects of a certain type, search for a specific entry and then return a given field from that entry.
# File lib/puppet/util/posix.rb 99 def search_posix_field(type, field, id) 100 idmethod = idfield(type) 101 integer = false 102 if id.is_a?(Integer) 103 integer = true 104 if id > Puppet[:maximum_uid].to_i 105 Puppet.err _("Tried to get %{field} field for silly id %{id}") % { field: field, id: id } 106 return nil 107 end 108 end 109 110 Etc.send(type) do |object| 111 if integer and object.send(idmethod) == id 112 return object.send(field) 113 elsif object.name == id 114 return object.send(field) 115 end 116 end 117 118 # Apparently the group/passwd methods need to get reset; if we skip 119 # this call, then new users aren't found. 120 case type 121 when :passwd; Etc.send(:endpwent) 122 when :group; Etc.send(:endgrent) 123 end 124 nil 125 end
Get the UID
# File lib/puppet/util/posix.rb 163 def uid(user) 164 get_posix_value(:passwd, :uid, user) 165 end
Private Instance Methods
Get the specified id_field of a given field (user or group), whether an ID name is provided
# File lib/puppet/util/posix.rb 171 def get_posix_value(location, id_field, field) 172 begin 173 field = Integer(field) 174 rescue ArgumentError 175 # pass 176 end 177 if field.is_a?(Integer) 178 name = get_posix_field(location, :name, field) 179 return nil unless name 180 id = get_posix_field(location, id_field, name) 181 check_value = id 182 else 183 id = get_posix_field(location, id_field, field) 184 return nil unless id 185 name = get_posix_field(location, :name, id) 186 check_value = name 187 end 188 189 if check_value != field 190 check_value_id = get_posix_field(location, id_field, check_value) if check_value 191 192 if id == check_value_id 193 Puppet.debug("Multiple entries found for resource: '#{location}' with #{id_field}: #{id}") 194 return id 195 else 196 Puppet.debug("The value retrieved: '#{check_value}' is different than the required state: '#{field}', searching in all entries") 197 return search_posix_field(location, id_field, field) 198 end 199 else 200 return id 201 end 202 end